Using KVC to get a list of objects' properties - objective-c

In this SO post, the chosen answer uses the syntax below to get a list of frequentFlyerNumbers from a list of passengers this way.
NSArray *frequentFlyerNumbers =
someFlight.passengers.frequentFlyerNumbers;
How is this possible given that passengers is an array and compiler can't infer the type that goes into the array?
I got the following error when implementing passengers.
Property 'frequentFlyerNumbers' not found on object of type
'NSMutableArray *'

The answer you cite is poorly written. The code as presented there won't work.
Dot-syntax, in plain code, is just shorthand for an accessor: someFlight.passengers means [someFlight passengers], which from the context appears to return a NSMutableArray. NSMutableArray of course doesn't have a frequentFlyerNumbers property itself.
To get the effect you want, do something like this:
NSArray *frequentFlyerNumbers = [someFlight valueForKeyPath:#"passengers.frequentFlyerNumbers"];
This will work, and return an array of frequentFlyerNumbers. When there's an array or set along a key path, it "projects" the subsequent path across all members of that array or set, producing an array or set of the results.

Related

Sorting objective-C array: where does 'element' come from?

I'm reading a book on Objective-C. A part dealing with sorting arrays in an Addressbook. The sort method is like this:
- (void) sort
{
[book sortUsingSelector: #selector(compareNames:);
}
The 'compareNames' method is like this:
//Compare the two names from the specified address cards
-(NSComparisonResult) compareNames: (id) element
{
return [name compare: [element name]];
}
I kinda can see how it works. By comparing each element of the array to the next until all the 'names' are swapped in alphabetical order.
What I don't get (as it hasn't been mentioned earlier) is where the '(id) element' argument comes from? The 'name' I get cause that's been initialized earlier. But where does 'element' come from? Is that a fixed object that comes with NSArray? Earlier is described how to print an element by e.g. NSLogging nummer[1]. In case of this address book one element or card in that book could be printed by NSLogging book[32]. But how does 'element' fit in that?
The 'name' I get cause that's been initialized earlier.
I'm not sure you understand how this works. The method corresponding to the sorting selector must compare two objects that are members of the array. No "static" objects, no ahead-of-time-initialized stuff, nothing.
This method will be called on an object in the array, and its argument will be another object in the array, to which it should compare the first object (its self argument). The implementation of sortUsingSelector: uses the return value of this method to decide if two objects are in the right order. Internally, it is called like this:
id oneObject = array[firstIndex];
id otherObject = array[secondIndex];
NSComparisonResult cmpRes =
[oneObject performSelector:#selector(comparisonSelector)
withObject:otherObject];
if (cmpRes == NSOrderedDescending) {
// swap the objects if they're in the wrong order
}
Maybe the asymmetry is confusing; in principle, NSArray could as well use a freestanding function that takes two objects and returns the comparison result. Actually, there indeed is a method (sortUsingComparator:) that takes a block which isn't called "on an object" (since it's not a method), but it's simply called with two arguments: the two objects to compare.
It's pretty simple. The sorting algorithm is based on object-comparison. Let's say we have 3 objects in the array: [A, B, C].
Then the method compareNames: is called on the following objects:
[A compareNames:B]
[B compareNames:C]
[A compareNames:C]
The element is just some other object in the array.

How can I access the property in an object that has an unknown class?

My initial code was
[[[show Episodes] lastObject] setDescription:nodeContent];
This gets the last object from the Episodes array and tries to set the episode's description property using nodeContent. This does not work because I think the compiler does not know the class of the episode object.
I then tried providing a cast
((EpisodeRepresentation *)[[show Episodes] lastObject]).description = nodeContent;
This doesn't work either. I have also tried unsuccessfully
EpisodeRepresentation *rep = [[show Episodes] lastObject];
rep.description = nodeContent;
All these ways leave description null (nodeContent is definitely not null, it is a NSString).
Your array is empty. Try [[show Episodes] count] to get the number of objects in the array.
BTW: Your naming of classes and methods seems odd. Classes usually begin with Captal letters and methods with lower case letters.
3 possibilities
array has no value make a check
show.episods doesnt have this object
nodecontent is null
description is an NSobject property which you are overriding here.Better use another property name for description

Filling NSMutableArray for later use in obj-c

How do you fill a NSMutableArray with a set capacity for later use?
Basically I want to set up a NSMutableArray to act as a map for my game objects, so I have this line...
gameObjects = [[NSMutableArray alloc] initWithCapacity:mapWidth*mapHeight];
Which I had hoped would create and fill my MutableArray so I can get then access it with this kind of index...
int ii = (cellY*mapWidth)+cellX;
NSDictionary *currentObject = [gameObjects objectAtIndex:ii];
But I just learned initWithCapacity doesn't fill the array, so should I create blank objects to fill it with, or is there a Null that I can fill it with? Also would I do that with 2 for loops or is there an instruction something like "initWith:myObject" ?
I want to be able to check at a certain index within the array to see if there's an object there or not, so I need to be able to acces that index point, and I can only do that if there's something there or I get an out of bounds error.
I'll be using this NSMutableArray pretty much as a grid of objects, it's a 1 dimensional array organised as a 2 dimensional array, so I need to be able to fill it with mapWidth*mapHeight of something, and then calculate the index and do a check on that index within the array.
I've looked on here and googled but couldn't find anything like what I'm asking.
Thanks for any advice.
I think what you are looking for is [NSNull null]. It is exactly what you want- a placeholder value.
You can find more information on the topic in this question.
initWithCapacity is just a performance optimization -- it has no effect on the array behavior, it just keeps the code "under the covers" from having to repeatedly enlarge the internal array as you add more entries.
So if you want a "pre-allocated" array, you'd need to fill it with NSNull objects or some such. You can then use isKindOfClass to tell if the object is the right type, or simply == compare the entry to [NSNull null]. (Since there's only ever one NSNull object it always has the same address).
(Or you could use a C-style array of pointers with nil values for empty slots.)
But you might be better off using an NSMutableDictionary instead -- no need to pre-fill, and if the element isn't there you get a nil pointer back. For keys use a NSNumber object that corresponds to what would have been your array index.
initWithCapacity only hints to NSMutableArray that it should support this many objects. It won't actually have any objects in it until you add them. Besides, every entry in the array is a pointer to an object, not a struct like you'd normally have in a standard c array.
You need to change how you're thinking about the problem. If you don't add an object to the array, it's not in there. So either you pre-fill the array with "empty" objects as you've said, which is weird. Or you can add the objects as you need them.

CoreData Optional Relationships and JSON null

I have a CoreData model with 2 objects. The first object (Images) contains a list of image names. The second object (Book) has a relationship with a field from the Images object called imageId. The data type is set to int16 and is marked optional with a default value of 0, as not every book will have an image.
I request a JSON representation of the Book object from a WCF service. One of the book objects returned has "ImageId" : null as its value. So when converting the JSON object into a managed object in Objective-C I get the following error message:
Unacceptable type of value for attribute: property = "imageId"; desired type = NSNumber; given type = NSNull; value =
How do you handle this conversion? I thought of checking for null and then setting the value to 0 but this doesn't seem correct.
Many thanks.
*UPDATE *
In the process of implementing Daniels solution I discovered that other fields could also be returned from the service as null. Is it therefore possible to modify the NSDictionary category to enable it to traverse itself and replace any instance of with nil? I am using jsonKit as my JSON parser and it seems to return the parsed result as a NSMutableDictionary with NSDictionary objects representing my JSON objects. I have spent much of the day trying to modify my objects but as they are returned as NSDictionaries they are immutable. I could simply use Daniels solution on every field that needed checking for however if the service changed and another field was returned as then this would break my app. Alternatively I could implement Daniels solution on every field just incase it was ever but not sure if thats a good solution especially for performance.
Some JSON libraries let you set an option so that JSON nulls are simply omitted from the dictionary as opposed to using NSNull. If you do that, then it will work because [theDict objectForKey:#"imageID"] will return nil.
But other than that, the right thing to do is look out for NSNull explicitly. Making an NSDictionary category is a handy way to do it:
#implementation NSDictionary (DDNullGetter)
- (id)nonNullObjectForKey:(id)key
{
id val = [self objectForKey:key];
return (val == [NSNull null] ? nil : val);
}
#end

Instance pointers in an array

I'm trying to save pointers of class instances into a mutable array. I'm able to do this but getting them back into use seems to be a problem. The next is how I inserted the pointers into the array:
Class *class = [Class new];
/* Do something with the instance */
[instanceArray addObject:class];
I am able to retrieve the wanted index from the array but when I try to access the instance variables etc. I only get compiler shouting at me or sometimes I get every variable as zero.
How am I supposed to get the instance back to use from the array? I know they are pointers but playing with them didn't seem to work.
Using addObject: the element is inserted at the end of the array. To retrieve it you can use -[NSArray objectAtIndex:]
Pointer arithmetic works in C since then your array is just a pointer to the first index, and array[i] is the same as *(array + i). In Objective-C this may still be done, however you're using an NSArray object. Now you don't have a pointer to the first object anymore, but to the instance of the class. To retrieve the objects stored in the array, you'll have to call the corresponding methods.
EDIT
So you are able to retrieve it from the array and then your pointer is not nil, so you do have access to the object. You know in Objective-C, all instance variables are private so you can't access them from outside. You'd have to declare them as properties first (please refer to the documentation). Also, when you declare a variable to be of type id, accessing a property with dot-syntax doesn't work, it will cause a compiler error.