Having a look at the block of NSArray creation methods in NSArray.h.
Is there a legitimate reason for the methods that are returning id to not return instancetype?
Apple even went through the effort of adding inline comments to let us know that id
in this case returns an NSArray.
@interface NSArray (NSArrayCreation)
+ (instancetype)array;
+ (instancetype)arrayWithObject:(id)anObject;
+ (instancetype)arrayWithObjects:(const id [])objects count:(NSUInteger)cnt;
+ (instancetype)arrayWithObjects:(id)firstObj, ... NS_REQUIRES_NIL_TERMINATION;
+ (instancetype)arrayWithArray:(NSArray *)array;
- (instancetype)init; /* designated initializer */
- (instancetype)initWithObjects:(const id [])objects count:(NSUInteger)cnt; /* designated initializer */
- (instancetype)initWithObjects:(id)firstObj, ... NS_REQUIRES_NIL_TERMINATION;
- (instancetype)initWithArray:(NSArray *)array;
- (instancetype)initWithArray:(NSArray *)array copyItems:(BOOL)flag;
+ (id /* NSArray * */)arrayWithContentsOfFile:(NSString *)path;
+ (id /* NSArray * */)arrayWithContentsOfURL:(NSURL *)url;
- (id /* NSArray * */)initWithContentsOfFile:(NSString *)path;
- (id /* NSArray * */)initWithContentsOfURL:(NSURL *)url;
@end
The only thing I could come up with these particular methods was this guidance from Apple
"The array representation at the location identified by aURL must contain only property list >objects (NSString, NSData, NSArray, or NSDictionary objects). The objects contained by this >array are immutable, even if the array is mutable."
However, this still to me doesn't explain the use of id over instancetype as they are still allowing NSArray sublclasses to return their own instancetype
NSDictionary follows the exact same pattern, where creating a dictionary with the contents of a file or URL uses id
and all other creation methods use instancetype
- (instancetype)initWithObjectsAndKeys:(id)firstObject, ... NS_REQUIRES_NIL_TERMINATION;
- (instancetype)initWithDictionary:(NSDictionary *)otherDictionary;
- (instancetype)initWithDictionary:(NSDictionary *)otherDictionary copyItems:(BOOL)flag;
- (instancetype)initWithObjects:(NSArray *)objects forKeys:(NSArray *)keys;
+ (id /* NSDictionary * */)dictionaryWithContentsOfFile:(NSString *)path;
+ (id /* NSDictionary * */)dictionaryWithContentsOfURL:(NSURL *)url;
- (id /* NSDictionary * */)initWithContentsOfFile:(NSString *)path;
- (id /* NSDictionary * */)initWithContentsOfURL:(NSURL *)url;
I am aware that Apple is just getting around to replacing id
in foundation classes to instancetype
but do the patterned inconsistencies in its usage within single classes act as guidance towards our own usage, or did they just not get around to finishing classes that they began working on?
to expand just a bit I wanted to explore the return type of dictionaryWithContentsOfFile
when called on NSMutableDictionary
NSString * plistPath = [[NSBundle mainBundle] pathForResource:@"myFile" ofType:@"plist"];
NSMutableDictionary *myDictionary = [NSMutableDictionary dictionaryWithContentsOfFile:plistPath];
if ([ myDictionary isKindOfClass:[NSMutableDictionary class]])
{
NSLog(@"This is a mutable dictionary why id and not instancetype?");
[myDictionary setObject:@"I can mutate the dictionary" forKey:@"newKey"];
}
NSLog (@"%@", myDictionary[@"newKey"]);
return YES;
}
The following was output to my console:
This is a mutable dictionary why id and not instancetype?
I can mutate the dictionary
Therefore, I am able to add new keys and objects to the dictionary.
Class clusters may return a class other than the class you create them from. This is generally true with Foundation classes as they will create some kind of optimized class in many cases. That class will still return YES from isKindOfClass:
Also some toll free bridged classes return a class that is shared between Foundation and Core Foundation. One example is NSCFString.