Whenever I implement a method in my own code that can accept or return objects of more than one class, I always try to use the most specific superclass available. For example, if I were going to implement a method that might return an NSArray * or an NSDictionary * depending on its input, I would give that method a return type of NSObject *, since that's the most direct common superclass. Here's an example:
@interface MyParser()
- (BOOL)stringExpressesKeyValuePairs:(NSString *)string;
- (BOOL)stringExpressesAListOfEntities:(NSString *)string;
- (NSArray *)parseArrayFromString:(NSString *)string;
- (NSDictionary *)parseDictionaryFromString:(NSString *)string;
@end
@implementation MyParser
- (NSObject *)parseString:(NSString *)string {
if ([self stringExpressesKeyValuePairs:string]) {
return [self parseDictionaryFromString:string];
}
else if ([self stringExpressesAListOfEntities:string]) {
return [self parseArrayFromString:string];
}
}
// etc...
@end
I've noticed many cases in Foundation and other APIs where Apple uses (id) in certain method signatures when (NSObject *) would be more precise. For example, here's a method of NSPropertyListSerialization:
+ (id)propertyListFromData:(NSData *)data
mutabilityOption:(NSPropertyListMutabilityOptions)opt
format:(NSPropertyListFormat *)format
errorDescription:(NSString **)errorString
The possible return types from this method are NSData, NSString, NSArray, NSDictionary, NSDate, and NSNumber. It seems to me that a return type of (NSObject *) would be a better choice than (id), since the caller would then be able to call NSObject methods like retain without a type-cast.
I generally try to emulate the idioms established by the official frameworks, but I also like to understand what motivates them. I'm sure that Apple has some valid reason for using (id) in cases like this, but I'm just not seeing it. What am I missing?