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.
Related
I understand I can return an NSDictionary by doing
- (NSDictionary *)keyWithValue {
return #{#"key" : #"value"};
}
but how can I return that without the enclosing #{} dictionary?
There is no tuples in Objective C unlike in Swift, Python etc. So the common way to return 2 different objects is to return an array or a dictionary.
You also can try something like:
- (NSString *)keyWithValue:(NSString**)value {
*value = #"value";
return #"key";
}
It should be used following way:
NSString *v;
NSString *k = [self keyWithValue:&v];
// now v contains #"value"
Objective-C, like C before it, doesn't allow the return of multiple values from a method. (Essentially, although a method or function can accept any number of arguments as input, it can only have a single return value.) There are historical and implementation reasons for this design but it can be frustrating when you simply have a pair/tuple to return.
If you have a method that has two distinct "results" that you need to return to the caller, you have a few choices. The very simplest in your case is to do something like what you are doing here and "wrapping" the values in a dictionary. You could similarly wrap them in a two-value array (which is a little less good since it relies on an implicit contract between caller and callee that there will be exactly two items in the array).
However, a clean and fairly standard approach here would be to create a small class with only two properties on it, and create, fill in, and return that instance with your pair of values. This arguably uses less runtime overhead than a collection object, and has the nice benefit of being semantically explicit and easy to understand for anyone else looking at it.
(There is yet another way, which involves passing pointers as arguments that are "outparams", but that's only idiomatic in rare circumstances in ObjC and I wouldn't recommend it here.)
There is no way to return a key value pair without a dictionary because that is the definition of the dictionary data structure. From apple docs:
The NSDictionary class declares the programmatic interface to objects that manage immutable associations of keys and values
You access the value with
[myDictionary objectForKey:#"myKey"];
If you want to use the returned key-value pair in another dictionary
NSMutableDictionary *otherDict = [NSMutableDictionary alloc] init];
[otherDict setObject:[myDictionary objectForKey:#"myKey"] forKey:#"myKey"];
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.
I've been reading through a few different programs that use the sortUsingSelector method to sort an array with string objects and I can't figure out how they do it.
Each program begins by defining the sort method as follows:
[myBook sort]; /**myBook is the name of the array in the addressBook class**/
-(void) sort
{
[book sortUsingSelector: #selector (compareNames:)];
}
/**compareNames is defined in the addressCard class**/
The sort method uses a selector method that seems to do all the work:
-(NSComparisonResult) compareNames: (AddressCard *) element
{
return [name compare: element.name];
}
Its important to note that there are two different classes: addressCard and addressBook.
I know the compare method returns NSOrderedAscending, NSOrderedSame, or NSOrderedDescending to the sortUsingSelector method. But, how do these methods go about sorting everything? I feel like I'm missing something huge. For instance, how does the compare method know which elements in the array to compare? I imagine that element[0] of the array is compared with element[1] and then a sort occurs then the next element is compared...Does the compare method have a default definition that I'm overlooking?
The compare: method (or any method you use as the parameter in sortUsingSelector:) just has one job: given exactly two objects (a pair), tell me how to order them. That is all it does.
It knows how to do this because it is defined by the class it is sent to. In this case, name is an NSString so we use NSString's definition of the compare: method - a definition that knows how to order strings (using rules about alphabetical order).
It is the sortUsingSelector: method that actually hands the compare: method pairs to, uh, compare. And how it chooses those pairs is its business. You are not told how it picks those pairs. Deciding what pairs to choose and in what order is a deep business; it is the subject of your Computer Science 101 class. But you are shielded deliberately from those details in this situation.
So I have a situation where im using a class as a kind of struct. now i want to override the isEqual: method so if this type of object is inside an array, i can use [objects indexOfObject:obj]
but, now say obj contains objects called duck, and chicken, i would like to go [objects indexOfObject:duck] and it will actually give me the index of obj
so i tried something like this:
- (BOOL)isEqual:(id)anObject {
if([anObject isKindOfClass:[Duck class]]){
return [anObject isEqual:_duck];
}
else if([anObject isKindOfClass:[Chicken class]]){
return [anObject isEqual:_chicken];
}
return [anObject isEqual:self];
}
which doesnt work and isnt even getting called... think that has to do with overriding hash, which i tried by just returning self.hash (effectively obj.hash) but that didnt work.
Do you think something like this is possible? or should i just use for loops to search through all my obj's to find which duck is contained in which obj and return the index of it (which i can do, just wanted to try be cool and neat at the same time)
It sounds using -isEqual: is a bad idea here.
You can't have a DuckAndChicken that compares equal to its Duck and its Chicken, (and vice versa) because in order to stay consistent, all ducks and chickens would then have to compare equal.
Example:
duck1 + chicken1 compares equal to duck1
chicken1 compares equal to duck1 + chicken1
duck2 + chicken1 compares equal to chicken1
duck2 compares equal to duck1
Universe explodes
The good news is that you don't have to use -indexObject: to retrieve from the array. NSArray has a handy -indexOfObjectPassingTest: method that should do what you want.
[ducksAndChickens indexOfObjectPassingTest:^BOOL(DuckAndChicken *obj, NSUInteger idx, BOOL *stop) {
if ([obj.duck isEqual:myDuck]) {
// woop woop...
}
}];
Yes, it sounds like a hash issue.
I would avoid trying to do this by overriding isEqual: because you're liable to break things that you don't think you'd break.
Instead add a custom method that you can call to determine your version of equivalence. Then have a helper method or a category on NSArray which adds my_indexOfObject: and does the iteration.
Its hard to tell what you really want to do from your example but could you perhaps have two dictionaries one where ducks are the key and one where chickens are the key and the object value is either the actual parent object or a NSNumber with the index in the array. This would make lookup much quicker though would take up more memory, and could make synchronisation between the three data structs.
so I'm having the most difficult of time pulling values out of an NSDictionary. Right now I just have a dictionary that is populated from a JSON call and it only contains a key named 'Success' with a value of 0 or 1.
How do I do a conditional on that value to check if its 0 or 1? I've tried a bunch of things, but I'm not getting anywhere. Here's my current code:
[[jsonDictionary objectForKey:#"Success"] isEqualToNumber:1]
I'm getting passing argument 1 of 'isEqualToNumber:' makes pointer from integer without a cast' as a warning, and the app crashes when it hits that line anyway.
And a subquestion, what's the difference between objectForKey and valueForKey? Which one should I use by default?
Anyway, this noob in Objective-C would truly appreciate some help on this. Thanks in advance!
Since dictionaries contain Objective-C objects, an entry containing a number is an NSNumber instance. NSNumber provides a convenience method, -intValue, for extracting its underlying int value:
if ([[jsonDictionary objectForKey:#"Success"] intValue] == 1) { … }
Note that NSNumber has other convenience methods for extracting its underlying value as other C data types.
In most cases, you should use -objectForKey: instead of -valueForKey:. The former is the canonical method to obtain an entry in the dictionary and is declared in NSDictionary. The latter is declared in NSObject and is used in Key-Value Coding contexts, where the key must be a valid KVC key, and there’s additional processing — for instance, if you’re using -valueForKey: in a dictionary with a key that starts with #, that character is stripped from the key and [super valueForKey:key] is called.
The number 1 is not an object pointer. Use an NSNumber instance instead if you want to use a number in an NSDictionary.
[[jsonDictionary objectForKey:#"Success"]
isEqualToNumber:[NSNumber numberWithInteger:1]]
[[jsonDictionary objectForKey:#"Success"] isEqualToNumber: [NSNumber numberWithInt:1]]
Number and Value Programming Topics: Using Numbers
NSNumber: What is the point ?
You can get the value of dictionary in different ways like checking
the value first.
Solution 1: Using simple if statement.
int value = 0;
if ([[jsonDictionary objectForKey:#"Success"]intValue]==1){
value = [[jsonDictionary objectForKey:#"Success"]intValue];
}
Solution 2: Using ternary operator
value = ([[jsonDictionary objectForKey:#"Success"]intValue]==1) ? 1:0;