Is it safe to use isKindOfClass: against an NSString instance to determine type?

后端 未结 4 782
春和景丽
春和景丽 2020-12-02 23:14

From the isKindOfClass: method documentation in NSObject:

Be careful when using this method on objects represented by a class cluster. Because of the nature

4条回答
  •  旧巷少年郎
    2020-12-02 23:34

    Lets consider the following code:

    if ([dict isKindOfClass:[NSMutableDictionary class]])
    {
        [(NSMutableDictionary*)dict setObject:@1 forKey:@"1"];
    }
    

    I think the true purpose of the Apple's note quoted in the question is that this code could easily lead to crash. And the problem here lies in toll-free bridging with CoreFoundation. Lets consider in more details 4 variants:

    NSDictionary* dict = [NSDictionary new];
    NSMutableDictionary* dict = [NSMutableDictionary new];
    CFDictionaryRef dict = CFDictionaryCreate(kCFAllocatorDefault, NULL, NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
    CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
    

    Indeed, in ALL 4 variants the true class of the dict will be __NSCFDictionary. And all 4 variants will pass the test in the first code snippet. But in 2 cases (NSDictionary and CFDictionaryRef declarations) we will crash with log something like that:

    * Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[__NSCFDictionary setObject:forKey:]: mutating method sent to immutable object'

    So, things getting a little bit clearer. In 2 cases we are allowed to modify object, and in 2 cases are not. It depends on creation function, and probably mutability state is tracked by __NSCFDictionary object itself.

    But why do we use the same class for mutable and immutable objects? Possible answer - because of C language limitations (and CoreFoundation is a C API, as we know). What problem do we have in C? The declarations of mutable and immutable dictionary types should reflect the following: CFMutableDictionaryRef type is a subtype of CFDictionaryRef. But C has no mechanisms for this. But we want to be able to pass CFMutableDictionary object in function expecting CFDictionary object, and preferably without compiler emitting annoying warnings. What we have to do?

    Lets have a look at the following CoreFoundation type declarations:

    typedef const struct __CFDictionary * CFDictionaryRef;
    typedef struct __CFDictionary * CFMutableDictionaryRef;
    

    As we can see here, CFDictionary and CFMutableDictionary are represented by the same type, and differ only in the const modifier. So, here start troubles.

    The same generally applies to NSArray too, but the situation is a little bit more complicated. When you create NSArray or NSMutableArray directly, you will get some special classes (__NSArrayI and __NSArrayM respectively). For this classes crash in not reproduced. But when you create CFArray or CFMutableArray, the things remain the same. So be careful!

提交回复
热议问题