Implementing a category within the implementation of another interface in Obj-C - objective-c

I have a custom class in Obj-C called RouteManager which contains an array of NSStrings. Each string is a bus stop name which is used as a key for a dictionary to get the rest of the information for the bus stop (basically, just [busStopDictionary allkeys]). In one of the situations where my app uses this array, I want to return the array sorted by the distance from the user. I've started setting up the code to be able to call sortedArrayUsingSelector on my array with the following method:
- (NSComparisonResult)compareByDistance:(NSString*) otherStop
{
// Return appropriate NSOrdered enum here based on comparison of
// self and otherStop
}
My problem is that in the case where compareByDistance is a method of RouteManager, self refers to the instance of RouteManager. However, I need self to refer to the NSString that the compare is being called on. So, I assumed I needed to setup a category, as such:
#interface NSString (Support)
-(NSComparisonResult) compareByDistance:(NSString*)otherStop;
#end
This got my self reference correct, however this comparison uses values from the RouteManager class. When implemented as seen above, the NSString (Support) implementation obviously complains that those values are undeclared.
That should provide enough background info for my question. How do I go about doing this? I would like my category of NSString, which consists solely of the method compareByDistance, to be able to use values from the current instance of my class, RouteManager, which inherits from NSObject. Ideally, I feel as though the category should somehow be within RouteManager. I feel there has to be some way to accomplish this that is cleaner than passing the necessary values into compareByDistance. Thanks in advance for any and all help.

Your best bet would be to define a custom class for a bus stop, instead of storing them as strings and dictionaries.
Make the BusStop class have properties for Name, Location and whatever else. Implement the compareByDistance: method on the BusStop class.
You can still use a dictionary if you need to look them up by name. Just store them with the name as the dictionary's key, and the BusStop object as the dictionary's value.

Related

Passing class objects as parameters in Objective C

Hey so I am new to Xcode and I have a few questions.
I want to make a Grade-book Application, that implements quite a few classes. For example, I created a class called "AssignmentList" which basically stores a list of assignment grades in the form of a double.
Another class, called "Course" has a NSMutableArray of AssignmentList objects.
If I wanted to retrieve an AssignmentList parameter for a course, my Java instinct would tell me to make a getter method like this:
-(AssignmentList)getAssignmentList(Course);
However, I can't seem to do that in Objective C, and only works when I do this:
-(id)getAssignmentList(Course);
How do I go about this? I wanted to create a UITableView listing all the AssignmentList objects of a Course, but I can't even set a UITableViewCell with the name of the AssignmentList because it won't let me use the getName method I made, because getName only works for an AssignmentList object, but the getAssignmentList method returns an (id).
Assuming an AssignmentList is a class with instances and not some kind of collection (e.g. an NSArray of Assignments), then your declaration needs to be something like:
-(AssignmentList *) getAssignmentList:(Course *) course;
The * is important: in Objective-C you don't really pass around objects, you pass around pointers to objects. (Well, that's true in Java, too, but you don't have to be explicit about it.)
When you declare something id that means pointer to something of unknown type so you don't need the *.

Objective-C id & NSObject

I understand that id is for any Object type even objects that do not inherit NSObject such as things from Cocoa. I have been told to almost always use id but what if I were making an API and had a method that I wanted to make it clear that it should only take a certain type of object such an object called Animal, would I still use
(id) animal
or would I do
(Animal) animal
Thanks so much!
id is a generic pointer to an object -- it's like void *, except that the pointer must point to an Objective-C object. So yes, you could use id in most situations where a more specific object pointer type would work, but it's usually better to use the more specific type:
- (id)animal; // OK if 'animal' could be any type of object
- (Animal*)animal; // much better if you know that 'animal' points to an object of type 'Animal'
You'll find plenty of examples if you look at any Cocoa or Cocoa Touch class. Let's look at a little bit of UIView:
- (BOOL)isDescendantOfView:(UIView *)view; // returns YES for self.
- (UIView *)viewWithTag:(NSInteger)tag; // recursive search. includes self
As you can see, the first method takes a UIView* as a parameter. If you try to pass something other than a pointer to an instance of UIView, the compiler will complain.
The second method returns a UIView*, and you can use the result directly as the receiver of other messages that UIView understands:
[[topView viewWithTag:someTag] removeFromSuperview];
Being specific about the types of parameters and return values lets the compiler help you make sure that you're sending appropriate messages to your objects and getting appropriate values back.
You can use any type starting from Animal and then up through inheritance chain to NSObject and id. Any would be valid. But in most cases you need to use just Animal because this is the very type you need to work with

Checking if object exist in object array without looping

I would like to check if my NSMutableArray contains my custom object. But if I understand correct contains functions searches for the same object in array (placed at the same memory point)
if(![objectArray containsObject:objToCheck])
{
[objectArray addObject:objToCheck];
}
I know that objectArray has identical object with identical variable values compared to objToCheck, yet such if always returns false. Is there a way to check this without writing custom loop and comparing objects by their parameters?
Override the [NSObject isEqual:] method (actually it's part of the NSObject protocol) of your custom object and check whatever instance variables make sense to you for an object to be considered equal.
Here's an Apple Cocoa Competency article on the subject.
You might try creating a temporary NSSet from your array and testing against that for membership.

Can I use a class method to set a static variable?

I don't have enough C knowledge to work out the right thing to do here.
I have a large number of NSManagedObjects that are one step down from the level I am currently working at - my view controller is displaying a table of parent objects, with a detail indicator against each one showing the number of child objects.
The user is able to create a set of filter criteria which are stored in an NSDictionary. My child objects have a complex set of calculations which they perform, based on these criteria, to determine if they pass or do not pass the filter. This is externally represented by a simple read-only boolean property.
What I would like to do is have a static variable in the child object to hold the filter settings dictionary, so that I don't have to call out to user defaults or similar to get the filter settings for each child object.
At the point of the user creating the filter settings, I don't have any specific pointer to a child object, and I don't really want to create a fetch request just to get hold of one - so would something like this be appropriate?
.h:
#interface Child : NSManagedObject
+(void)setFilterSettings:(NSDictionary*)newFilterSettings;
#end
.m:
static NSDictionary *filterSettings;
#implementation Child
+(void)setFilterSettings:(NSDictionary*)newFilterSettings
{
filterSettings = newFilterSettings;
}
#end
And when the filter settings are made:
[Child setFilterSettings:newFilterSettings];
Assuming this is valid, what, if anything, do I need to do in terms of memory management? I am using ARC.
There's nothing wrong with the above code. This is the standard way to implement class variables, which do not otherwise exist in ObjC.
(Side note: I'm a huge fan of ARC, everyone should use it as soon as they can, but I still instinctively twitch when I see an unretained setter....)

How to test property existence and type based on NSString typed key?

In my quest to update a Core Data model within my iOS project, I'm querying a server for JSON objects that correspond - to some extent - with the managed entities of my model. The end result I'm striving for is a reliable update solution from JSON output.
For the examples in this question, I'll name the core data managed object existingObj and the incoming JSON deserialized dictionary updateDict. The tricky part is dealing with these facts:
Not all properties of the existingObj are present in the updateDict
Not all properties of the updateDict are available in the extistingObj.
Not all types of existingObj's properties match the JSON deserialized properties. (some strings may need a custom Objective-C wrapper).
updateDict may contain values for keys that are uninitialized (nil) in existingObj.
This means that while iterating through the updated dictionaries, there has to be some testing of properties back and forth. First I have to test whether the properties of the updateDict exist in existingObj, then I set the value using KVC, like so:
// key is an NSString, e.g. #"displayName"
if ([existingObj respondsToSelector:NSSelectorFromString(key)) {
[existingObj setValue:[updateDict objectForKey:key] forKey:key];
}
Although this part works, I don't like the fact that I'm actually testing for displayName as a getter, while I'm about to call the setDisplayName: setter (indirectly via KVC). What I'd rather to is something like [existingObj hasWritablePropertyWithName:key], but something that does this I can't find.
This makes for subquestion A: How does one test for a property setter, if you only have the property's name?
The next part is where I'd like to automate the property identification based on their types. If both the updateDict and the existingObj have an NSString for key #"displayName", setting the new value is easy. However, if the updateDict contains an NSString for key #"color" that is #"niceShadeOfGreen", I'd like to transform this into the right UIColor instance. But how do I test the type of the receiving property in existingObj so I know when to convert values and when to simply assign? I was hoping for something along the lines of typeOfSelector:
if ([existingObj typeOfSelector:sel] == [[updateDict objectForKey:key] class]) {
// regular assignment
} else {
// perform custom assignment
}
Of course this is boguscode. I can't rely on testing the type of the existingObj-property's value, for it may be unitialized or nil.
Subquestion B: How does one test for the type of a property, if you only have the property's name?
I guess that's it. I figured this must be a dupe of something that's already on here, but I couldn't find it. Maybe you guys can?
Cheers, EP.
P.S. If you'd have a better way to synchronize custom Objective-C objects to deserialized JSON objects, please do share! In the end, the result is what counts.
If you want to query whether an object has a setter for a given KVC key called key which corresponds to a declared property, you need to check whether it responds to a selector method called setKey: (starts with set, capitalise the first character in key, add a trailing colon). For instance,
NSString *key = #"displayName";
NSString *setterStr = [NSString stringWithFormat:#"set%#%#:",
[[key substringToIndex:1] capitalizedString],
[key substringFromIndex:1]];
if ([obj respondsToSelector:NSSelectorFromString(setterStr)]) {
NSLog(#"found the setter!");
[obj setValue:someValue forKey:key];
}
Two remarks:
Even though properties can have setters with names that do not follow the pattern described above, they wouldn’t be KVC compliant, so it is safe to check for set<Key>: since you’re using KVC to set the corresponding value.
KVC doesn’t use the setter method only. If it doesn’t find a setter method, it checks whether the class allows direct access to instance variables and, if so, use the instance variable to set the value. Also, if no setter method or instance variable is found, it sends -setValue:forUndefinedKey: to the receiver, whose class might have overridden the standard implementation that throws an exception. This is described in the Key-Value Coding Programming Guide.That said, if you’re always using properties, checking for the setter method should be safe.
As for your second question, it is not possible to query the runtime to know the actual Objective-C class of a property. From the runtime perspective, there’s an implementation specific type encoding for properties and general types (such as method parameters/return types). This type encoding uses a single encoding (namely #) for any Objective-C object, so the type encoding of an NSString property is the same as the type encoding of a UIColor property since they’re both Objective-C classes.
If you do need this functionality, one alternative is to process your classes and add a class method that returns a dictionary with keys and corresponding types for every property (or the ones you’re interested in) declared in that class and superclasses, or maybe some sort of description language. You’d have to do this on your own and rely on information not available during runtime.