NSArray objects addresses - ios

So unlike C where an array = &array = &array[0], was trying to have a quick look at the structure of a NSArray and how its objects were stored and have a question with the below code.
NSString *str1 = [NSString stringWithFormat:#"abc"];
NSString *str2 = [NSString stringWithFormat:#"xyz"];
NSString *str3 = [NSString stringWithFormat:#"123"];
NSMutableArray *someArray = (NSMutableArray*)#[#"1",#"2",#"3"];
NSMutableArray *original = (NSMutableArray*)#[str1, str2, [someArray mutableCopy]];
NSMutableArray *deep = [original mutableCopy];
[[original objectAtIndex:2] addObject:str3];
for (id obj in original) {
NSLog(#"\nIn Original:: \nvalue is:%#, at :%p; Address of object: %p\n",obj, obj, &obj);
}
for (id obj in deep) {
NSLog(#"\nIn Deep:: \nvalue is:%#, at :%p; Address of object: %p\n",obj, obj, &obj);
}
NSLog(#"\n Address of Original : %p \n", &original);
NSLog(#"\n Address IN Original : %p \n", original);
NSLog(#"\n Address in first object of original : %p \n", [original objectAtIndex:0]);
Sample o/p.
In Original::
object is:abc, at :0x7f8a5a58e750; Address of object pointer is : 0x7fff50cdd750
object is:xyz, at :0x7f8a5a58ff70; Address of object pointer is : 0x7fff50cdd750
object is:(
1,
2,
3,
123
), at :0x7f8a5a591200; Address of object: 0x7fff50cdd750
In Deep::
object is:abc, at :0x7f8a5a58e750; Address of object pointer is : 0x7fff50cdd708
object is:xyz, at :0x7f8a5a58ff70; Address of object pointer is : 0x7fff50cdd708
object is:(
1,
2,
3,
123
), at :0x7f8a5a591200; Address of object pointer: 0x7fff50cdd708
Address of Original : 0x7fff50cdd760
Address IN Original : 0x7f8a5a591230
Address in first object of original : 0x7f8a5a58e750
Im getting the same address for &obj for all the elements in the arrays above. Anything Im missing here? Thanks for your help.

The obj is a pointer which holds the address of another variable. If you change the value of that pointer it won't change it's address. In details,
id obj = original[0];
If you use
NSLog(#"%p",obj);
It'll print the address of the object contained in original[0]. And if you use
NSLog(#"%p",&obj);
It'll print the address of obj.
So even if you change the value like:
obj = original[1];
NSLog(#"%p",&obj);
Will give you same pointer address (Address of obj is not changing only the value of obj is changing)

-[NSArray mutableCopy] doesn't do a deep copy. Even if it did, those are constant strings you're trying to copy. NSString is immutable, so actually making a copy would be a waste of time and memory.

If you want to do a deep copy on an array, you need to do something like this:
NSMutableArray *copiedArray = [NSMutableArray new];
for (id obj in originalArray) {
[copiedArray addObject:[obj copy]];
}
Of course, this only works if all of the objects in your array support NSCopying.
You could get clever and make a category on NSArray that does something similar. You should check, though, that all of your objects conform to NSCopying.

Related

Concatenating values to the same key in an NSMutableDictionary

I am getting data from my database and the data is being retrieved using a while loop.
success = [db executeQuery:#"SELECT * FROM apidataTwo;"];
while([success next]){
int first = [success intForColumn:#"id"];
NSString *id = [NSString stringWithFormat:#"%d",first];
[_tempArray addObject:id];
NSString *country_name = [success stringForColumn:#"country_name"];
[_tempArray addObject:country_name];
NSString *breezometer_description = [success stringForColumn:#"breezometer_description"];
[_tempArray addObject:breezometer_description];
NSString *country_description = [success stringForColumn:#"country_description"];
[_tempArray addObject:country_description];
NSString *dateString= [success stringForColumn:#"dateString"];
[_dateSectionArray addObject:dateString];
[_dataDictionary setObject:_tempArray forKey:dateString];
}
Suppose we get the same key in different iterations of the loop. When I pass the array to the NSMutableDictionary, the previous values will be replaced and lost.
And if I keep updating the NSMutableArray, then the values of a previous key will also be added to a different key.
So in situations like this when we want to concatenate the values to the same key, then what should be our approach.
The dictionary should look like this:
{
2016-10-05" = (
5,
"United States",
"Fair Air Quality",
"Good air quality"
);
"2016-10-06" = (
5,
"United States",
"Fair Air Quality",
"Good air quality"
);
}
Once you have figured out the key for this batch of data, try to retrieve an object from the dictionary for that key. If objectForKey: returns nil, then create a new mutable array. Then set that array as the dictionary's object for that key.
Every new batch of data is then added to the array, not to the dictionary. Here's a sketch of the structure:
while( /* processing data */){
// Collect this batch
NSArray * entry = ...;
// Figure out the dictionary key for the batch.
// (it doesn't have to be a string, this is just for example)
NSString * key = ...;
// Try to retrieve the object for that key
NSMutableArray * entries = _dataDictionary[key];
// If the result is `nil`, the key is not in the dictionary yet.
if( !entries ){
// Create a new mutable array
entries = [NSMutableArray array];
// Add that to the dictionary as the value for the given key
_dataDictionary[key] = entries;
}
// Now `entries` is a valid `NSMutableArray`, whether it already
// existed or was just created. Add this batch.
[entries addObject:entry];
// Move on to the next batch.
}

Objective-c NSMutableDictionary set with an array keep empty

I'm new here, but I use to read this site when I need something, but today, I can't find an answer to my question.
I'll try to explain my problem with enough details.
I need to add an array into a NSMutableDictionary at a specific key. The key added into it is correctly up, but my dictionary value keep empty. Here is my code :
dictionarySection = [[NSMutableDictionary alloc] initWithObjects:arraySectionValues forKeys:arraySectionKeys];
dictionaryClip = [[NSMutableDictionary alloc] initWithCapacity:[arraySectionKeys count]];
NSArray *tabSection = [dictionarySection allKeys];
id key,value;
for (int j=0; j<tabSection.count; j++)
{
array = [NSMutableArray array];
key = [tabSection objectAtIndex: j];
value = [dictionarySection objectForKey: key];
//NSLog (#"Key: %# for value: %#", key, value);
for (SMXMLElement *clip in [books childrenNamed:#"clip"]) {
if([[clip valueWithPath:#"categorie"] isEqualToString:value]){
[array addObject:[clip valueWithPath:#"titre"]];
}
}
NSLog(#"Test array %#",array);
[dictionaryClip setObject:array forKey:key];
[array removeAllObjects];
NSLog(#"Test dictionary %#",dictionaryClip);
}
Here the NSLog result :
2015-07-15 14:34:48.272 test[15533:390301] Test array (
"CDS : ITV Philippe Dunoyer",
"FLASH INFO NCI : crise des banques",
"Les Roussettes sont-elles dangereuses ?",
"Flash infos banques gr\U00e8ve",
"CDS : ITV Paul Langevin",
"CDS : ITV Valls",
"CDS : ITV Victor Tutugoro",
"CDS : ITV Roch Wamytan",
"NCGLAN 20",
"Flash Info : dispositif anti-d\U00e9linquance"
)
2015-07-15 14:34:48.273 test[15533:390301] key : 0
2015-07-15 14:34:48.273 test[15533:390301] Test dictionary {
0 = (
);
}
As we can see, the array is filled, the dictionary's key is correct, but the array isn't into my dictionary.
How may I suppose to fill my dictionary with this array?
Thanks a lot guy(s) for answer(s) :)
Ps : excuse my english :(
You are calling removeAllObjects: method for same instance of array which you are passing in dictionary so it objects are being removed in stored array. Try to pass that array's copy or a new instance of array with same objects.
Example:
[dictionaryClip setObject:[array copy] forKey:key];
In Objective-C arrays are reference types.
The method setObject:forKey: puts a pointer to the array into the dictionary, the array is not copied.
If you remove all objects from the array, they also disappear in the dictionary

-[Person componentsSeparatedByString:]: unrecognized selector sent to instance

I'm new to programming, and I feel a little intimidated posting, and I'm stuck. I don't want a quick fix! Please help me understand this.
I've created a custom method with a name, age, height and gender. It gets called when the NSMutableArray adds custom objects to the array. For some reason I cannot pull said items out of the NSMutableArray. Let's say the age needs to be printed out. I get a error saying...
-[Person componentsSeparatedByString:]: unrecognized selector sent to instance
Person.m
- (id)initWithName:(NSString *)n
age:(int)a
height:(float)h
gender:(char)g
{
self = [super init];
if (self) {
self.name = n;
self.age = a;
self.height = h;
self.gender = g;
}
return self;
}
- (NSString *)description
{
NSString *descriptionString = [[NSString alloc] initWithFormat:#"%#, %d, %.1f, %c",
self.name,
self.age,
self.height,
self.gender];
NSLog(#"Description String: %#", descriptionString);
return descriptionString;
}
When adding objects to the NSMutableArray they get converted to a NSString? How do I get the peoples age without the whole strings name and height in the NSLog?
ViewController.m
[self.people addObject:[[Person alloc] initWithName:#"Jake" age:29 height:73.5 gender:'f']];
[self.people addObject:[[Person alloc] initWithName:#"Jerry" age:24 height:82.3 gender:'m']];
[self.people addObject:[[Person alloc] initWithName:#"Jessica" age:29 height:67.2 gender:'f']];
NSString *mystring1 = [self.people objectAtIndex:0];
NSLog(#"%#", mystring1);
// Works
//NSString *list = #"Norm, 42, 73.2, m";
NSArray *listItems = [self.people[0] componentsSeparatedByString:#", "];
NSLog(#"List Items: %#", listItems[1]);// age
Output
Description String: Jake, 29, 73.5, f
Jake, 29, 73.5, f
-[Person componentsSeparatedByString:]: unrecognized selector sent to instance
Solved:
Notice the extra [ age];
int age = [[self.people objectAtIndex:0]age];
NSLog(#"%d", age);
I think the reason it seems like your object is getting converted to a string is this line,
NSString *mystring1 = [self.people objectAtIndex:0];
You need to specify the property if that is all you want to print
NSString *nameString = [[self.people objectAtIndex:0]name];
You can't perform componentsSeparatedByString on your Person object since it's not a string and doesn't have that method. (NSMutableArray objects are not automatically converted to NSStrings.) But to get the age of your Person, should be fairly simple anyway. Just access Person's age property:
int age = self.people[0].age;
NSLog(#"Age: %d", age);
Edit: You can technically use componentsSeparatedByString on your mystring1 NSString, like so:
NSArray *listItems = [mystring1 componentsSeparatedByString:#", "];
NSLog(#"List Items: %#", listItems[1]);// age
But again, I don't see the point of doing this when you can access the Person's age property directly.
The componentsSeparatedByString method is an instance method of NSString class. You can't call it on your Person class in this way.
I don't know why you need that code, if you are trying to access age, then:
NSLog(#"Age %d", self.people[0].age);
is enough. If you are trying to achieve any other thing, then you can get the components like:
NSArray *listItems = [self.people[0].description componentsSeparatedByString:#", "];
NSLog(#"List Items: %#", listItems[1]);// age
One would think this would work; however, I can not call .age on my array.
NSLog(#"Age %d", self.people[0].age);
Gives the following output...
Property 'age' not found on object of type 'id'
One would also think componentsSeparatedByString would work. It doesn't split my string by the comma and gives an error.
-[Person componentsSeparatedByString:]: unrecognized selector sent to instance
Solved READ UP
I think you are getting confused about why when you log the object you are getting a string, but you can't perform any methods on it.
And here is why you think you are getting a string, you've implemented the description property, which comes from the NSObject protocol. When you do a log of an object with a %# parameter you are getting the result of this property, which is a string.
So looking at your code:
NSString *mystring1 = [self.people objectAtIndex:0]; //1
NSLog(#"%#", mystring1); //2
The first thing is that you are getting the first object out of your array. objectAtIndex returns an id, which is a pointer to an NSObject. It is not a strongly typed return. You are allocating it to an NSString which is wrong, because it as actually a person object. But since you aren't calling any NSString methods on it, the compiler is not flagging this incorrect assignment.
The second thing is that you are just getting the result of the description property, as I've already mentioned.
Your edited solution:
int age = [[self.people objectAtIndex:0]age];
Works, but here is why: you are getting the objectAtIndex:0 which returns an id. Then you are sending it the message age. Objective-C is a dynamic language, which means you can send any message to any object (although you get a run-time crash if the object does not implement the method) In this case, your Person object does implement an age method (since it's a public property) so you get an age back.
A different way of doing it:
NSInteger age;
Person *person = self.people.firstObject;
if (person) {
age = person.age;
} else {
age = NSNotFound;
}
Why am I doing it this way?
Firstly, I am getting the firstObject out of the array and putting it into a typed variable. This is safer, as if there is no object at index 0 (i.e. the array is empty) then you won't crash your app. firstObject returns the first object if it exists, on a nil if it doesn't.
Secondly, Only if the person has been correctly extracted from the array, do I assign the age to an NSInteger variable. You should prefer the specific types of NSInteger over int (and CGFloat over float) because they will use the correctly sized variable when running on 32-bit or 64-bit` systems.
Thirdly, if person cannot be created I am assigned the value of NSNotFound to the age. This is a typedef for a very large number, one that you can compare against. Just returning 0 in age is not enough to tell you there was an error. The person could have an age of 0. with this code you can test the age with:
if (age != NSNotFound) {
// This is a valid age, do something with it
} else {
// A person object could not be extracted from the array, this is not a valid age
}
Is this overkill? Not really. When you start writing real, complex apps, this sort of defensive programming will come naturally to you. Arrays can be empty, your data could be incorrectly created, etc. Writing code that gracefully handles these eventualities is the real skill of programming. Unfortunately, It's a bit more long winded, and most tutorials that you find on the web show you the simple, happy path.
I hope this gives you a better idea of what your code is doing and what more you could be doing to write robust code.

setObject:forKey: of NSMutableDictionary overwrites all data in dictionary

for ( int cnt = 0 ; cnt < nPeople ; cnt++ )
{
ABRecordRef ref = CFArrayGetValueAtIndex(allPeople, cnt);
NSString *firstName = (NSString *)ABRecordCopyValue(ref, kABPersonFirstNameProperty);
NSString *lastName = (NSString *)ABRecordCopyValue(ref, kABPersonLastNameProperty);
NSString *fullName;
/* skipped code at here : code to merge firstName and lastName to fullName. In my country, many of us don't separate first name and last name */
// tempKeyString : NSString variable that has key of fullNameArray value for nameDictionary.
// fullNameArray : to keep some fullName variables for tempKeyString.
if (!tempKeyString) // there's no tempKeyString, a.k.a. it's the first fullName.
{
// it's not important to know about GetUTF8String:fullName. It's for my own language.
tempKeyString = [self GetUTF8String:fullName];
[fullNameArray addObject:fullName];
}
else
{
if ([tempKeyString characterAtIndex:0] == [[self GetUTF8String:fullName] characterAtIndex:0]) // if fullName has the same tempKey with fullNameArray.
{
[fullNameArray addObject:fullName];
}
else // if fullName has different tempKey with fullNameArray.
{
//tempKey : key data for fullNameArray
NSString *tempKey = [tempKeyString substringToIndex:1];
// tempDict : to keep the deep copy of nameDictionary before adding new key.
NSDictionary *tempDict = [nameDictionary mutableDeepCopy];
// add new key (tempKey) with new value (fullNameArray)
// PROBLEM : ALL values (including previous values) in dictionary(nameDictionary) are overwritten to a new value(fullNameArray).
[nameDictionary setObject:fullNameArray forKey:tempKey];
//empties fullNameArray so that it can get the new fullName of the new tempKey.
[fullNameArray removeAllObjects];
//refresh tempKeyString, and add the new fullName.
tempKeyString = [self GetUTF8String:fullName];
[fullNameArray addObject:fullName];
...
}
}
}
I'm trying to make a NSMutableDictionary object from the contacts of my iPhone. Why I make a NSMutableDictionary typed object is that I need indexes for contacts, and it doesn't look easy to make indexes from ABAddressRef typed object directly. I also need to make searching function..
There was no problem when I just coded, but after debugging the only problem makes me crazy. After I apply the array named fullNameArray with the key named tempKey to the namedDictionary, I can find the nameDictionary has all values with them of fullNameArray. All previous data were overwritten! I tried to make a deep copied version of previous nameDictionary before applying fullNameArray and copy it to the newer nameDictionary. However, when I checked the breakpoint at the third line, I can't find the previous data at the tempDict.
I added more codes and comments. It might help more than my explanation.. any questions are pleased!
I tried to find the reason from here - StackOverflow -, and other webpages all the night, but I couldn't find any similar problems.. please help me! Thank you so much in advance!!
The reason why it get emptied is because
[nameDictionary setObject:fullNameArray forKey:tempKey];
here, you set up your dictionary with the object "fullNameArray", then
[fullNameArray removeAllObjects];
remove all the values inside this array, effectively, removing your object in the "nameDictionary", they are the same object, it's not a deep copy of fullNameArray that you store inside your dictionary. Why did you need to store anything into your array anyway? You're only storing 1 value.
[nameDictionary setObject:fullName forKey:tempKey];
will do what you need. Sorry if I mistaken your question, it's quite hard to understand

How do I get properties out of NSDictionary?

I have a webservice that returns data to my client application in JSON. I am using TouchJson to then deserialize this data into a NSDictionary. Great. The dictionary contains a key, "results" and results contains an NSArray of objects.
These objects look like this when printed to log i.e
NSLog(#"Results Contents: %#",[resultsArray objectAtIndex:0 ]);
Outputs:
Results Contents: {
creationdate = "2011-06-29 22:03:24";
id = 1;
notes = "This is a test item";
title = "Test Item";
"users_id" = 1;
}
I can't determine what type this object is. How do I get the properties and values from this object?
Thanks!
To get the content of a NSDictionary you have to supply the key for the value that you want to retrieve. I.e:
NSString *notes = [[resultsArray objectAtIndex:0] valueForKey:#"notes"];
To test if object is an instance of class a, use one of these:
[yourObject isKindOfClass:[a class]]
[yourObject isMemberOfClass:[a class]]
To get object's class name you can use one of these:
const char* className = class_getName([yourObject class]);
NSString *className = NSStringFromClass([yourObject class]);
For an NSDictionary, you can use -allKeys to return an NSArray of dictionary keys. This will also let you know how many there are (by taking the count of the array). Once you know the type, you can call
[[resultsArray objectAtIndex:0] objectForKey:keyString];
where keyString is one of #"creationdate", #"notes", etc. However, if the class is not a subclass of NSObject, then instead use:
[[resultsArray objectAtIndex:0] valueForKey:keyString];
for example, you probably need to do this for keystring equal to #"id".

Resources