Ok, so to answer this question, first we need to know what is the class cluster design pattern ?
From Apple's documentations:
Class clusters are a design pattern that the Foundation framework
makes extensive use of. Class clusters group a number of private
concrete subclasses under a public abstract superclass. The grouping
of classes in this way simplifies the publicly visible architecture of
an object-oriented framework without reducing its functional richness.
Class clusters are based on the Abstract Factory design pattern.
So the super class will decide what type we will have for our newly created object
Now because these methods are shared between NSArray
and NSMutableArray
, the results could be different, then they return id
, because we don't know what object will be returned.(mutableArray or immutableArray).
+ (id /* NSArray * */)arrayWithContentsOfFile:(NSString *)path;
+ (id /* NSArray * */)arrayWithContentsOfURL:(NSURL *)url;
- (id /* NSArray * */)initWithContentsOfFile:(NSString *)path;
- (id /* NSArray * */)initWithContentsOfURL:(NSURL *)url;
These methods return only NSArray
if the message was sent to NSArray
and NSMutableArray
if the method was sent to NSMutableArray
. Thats why they return instancetype
+ (instancetype)arrayWithObjects:(id)firstObj, ... NS_REQUIRES_NIL_TERMINATION;
+ (instancetype)arrayWithArray:(NSArray *)array;
- (instancetype)init;
Ok, so we said that the methods above return only instance type of the receiver. But what if we want arrayWithArray
method to always return immutableArray
no matter who is the receiver ?
That means NSMutableArray
will get different type than instanceType, because NSArray is not of NSMutableArray type, in this case we would change the method to be like this:
// from
+ (instancetype)arrayWithArray:(NSArray *)array;
// to
+ (id)arrayWithArray:(NSArray *)array;
We say now return id, despite the object's type.
UPDATE:
Example similar to your code
NSString *filePath = [[NSBundle mainBundle]pathForResource:@"myFile" ofType:@"plist"];
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithContentsOfFile:filePath];
NSMutableDictionary *dict2 = [[NSMutableDictionary alloc] init];
NSLog(@"%@", NSStringFromClass([dict class])); // prints __NSCFDictionary // converted to immutable
NSLog(@"%@", NSStringFromClass([dict2 class])); // prints __NSDictionaryM, its mutable
[dict setObject:@"obj" forKey:@"key"]; // this will do nothing, because its immutable, we can't add new object
Here is what Apple say about using isKindOfClass
: to check the mutability of class cluster
Be careful when using this method on objects represented by a class
cluster. Because of the nature of class clusters, the object you get
back may not always be the type you expected. If you call a method
that returns a class cluster, the exact type returned by the method is
the best indicator of what you can do with that object. For example,
if a method returns a pointer to an NSArray object, you should not use
this method to see if the array is mutable, as shown in the following
code:
// DO NOT DO THIS!
if ([myArray isKindOfClass:[NSMutableArray class]])
{
// Modify the object
}
Link: https://developer.apple.com/library/ios/documentation/cocoa/reference/foundation/Protocols/NSObject_Protocol/Reference/NSObject.html#//apple_ref/occ/intfm/NSObject/isKindOfClass: