Why is it that sending any selector to a Nil object does nothing, but sending an “invalid” selector to any NSObject raises an exception?

前端 未结 3 609
囚心锁ツ
囚心锁ツ 2020-12-16 17:47

Does anyone know why NextStep/Apple decided to take the \"convenient method\" of doing nothing when passing a Nil object a message, but the \"Java method\" of raising an exc

3条回答
  •  北海茫月
    2020-12-16 18:37

    From the good ol' documentation:

    In Objective-C, it is valid to send a message to nil—it simply has no effect at runtime.

    As for the other problem of the unrecognized selector behavior, an old implementation file of NSObject (from the MySTEP library) shows that the culprit is the NSObject method -doesNotRecognizeSelector:, which looks a bit as follows:

    - (void) doesNotRecognizeSelector:(SEL)aSelector
    {
        [NSException raise:NSInvalidArgumentException
                    format:@"NSObject %@[%@ %@]: selector not recognized", 
                            object_is_instance(self)?@"-":@"+",
                            NSStringFromClass([self class]), 
                            NSStringFromSelector(aSelector)];
    }
    

    Which means that ObjC methods could feasibly be tinkered with so that they do not in fact have to raise an error. Which means the decision was entirely arbitrary, just like the decision to switch to "method-eating" messages to nil. A feat which can be done through method swizzling NSObject (wholly dangerous, as it will raise an EXC_BAD_ACCESS, or EXC_I386_BPT on mac, but at least it doesn't raise an exception)

    void Swizzle(Class c, SEL orig, SEL new)
    {
        Method origMethod = class_getInstanceMethod(c, orig);
        Method newMethod = class_getInstanceMethod(c, new);
        if(class_addMethod(c, orig, method_getImplementation(newMethod), method_getTypeEncoding(newMethod)))
            class_replaceMethod(c, new, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
        else
            method_exchangeImplementations(origMethod, newMethod);
    }
    
    -(void)example:(id)sender {
        Swizzle([NSObject class], @selector(doesNotRecognizeSelector:), @selector(description));
        [self performSelector:@selector(unrecog)];
    }
    

    The category:

    @implementation NSObject (NoExceptionMessaging)
    
    -(void)doesNotRecognizeSelector:(SEL)aSelector {
        NSLog(@"I've got them good ol' no exception blues.");
    }
    @end
    

提交回复
热议问题