有谁知道为什么NextStep/Apple决定采用"方便的方法",在传递Nil对象时不做任何事情,但是在将实例化对象传递给无效选择器时引发异常的"Java方法"?
例如,
// This does "nothing"
NSObject *object = Nil;
[object thisDoesNothing];
object = [[NSObject alloc] init];
// This causes an NSInvalidArgumentException to be raised
[object thisThrowsAnException];
Run Code Online (Sandbox Code Playgroud)
所以一方面,我们有方便不必检查Nil(假设我们不太关心方法调用的结果) - 但另一方面,如果我们的对象,我们必须检查异常没有回应方法?
如果我不确定该对象是否会响应,我要么:
@try {
[object thisThrowsAnException];
} @catch (NSException *e){
// do something different with object, since we can't call thisThrowsAnException
}
Run Code Online (Sandbox Code Playgroud)
要么,
if([object respondsToSelector:@selector(thisThrowsAnException)]) {
[object thisThrowsAnException];
}
else {
// do something different with object, since we can't call thisThrowsAnException
}
Run Code Online (Sandbox Code Playgroud)
(后者可能是更好的方法,因为如果object为Nil,则选择器不会引发异常,因此您的代码可能不会按照您希望的方式运行).
我的问题是:为什么Apple决定以这种方式实施它?
为什么不对实例化对象进行无法识别的选择器调用而不引发异常?
或者,如果您尝试在其上调用方法,为什么不让Nil对象引发异常?
rob*_*off 11
我无法完全回答你的问题,但我可以回答部分问题.Objective-C允许您发送消息,nil因为它使代码更加优雅. 你可以在这里阅读这个设计决定,我会窃取它的例子:
假设您想获取某人在办公室电话上拨打的最后一个电话号码.如果你不能发送消息nil,你必须这样写:
Office *office = [somePerson office];
// Person might not have an office, so check it...
if (office) {
Telephone *phone = [office telephone];
// The office might not have a telephone, so check it...
if (phone) {
NSString *lastNumberDialed = [phone lastNumberDialed];
// The phone might be brand new, so there might be no last-dialed-number...
if (lastNumberDialed) {
// Use the number, for example...
[myTextField setText:lastNumberDialed];
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在假设您可以发送消息nil(并始终nil返回):
NSString *lastNumberDialed = [[[somePerson office] telephone] lastNumberDialed];
if (lastNumberDialed) {
[myTextField setText:lastNumberDialed];
}
Run Code Online (Sandbox Code Playgroud)
至于为什么向对象发送无法识别的选择器会引发异常:我不确定.我怀疑这是一个bug而不是无害的常见问题.在我的代码中,我只希望在需要发送可选协议消息时(例如向委托发送可选消息)静默忽略无法识别的选择器.所以我希望系统将其视为一个错误,让我在相对罕见的情况下明确表示我不希望它是一个错误.
请注意,您可以通过几种不同的方式修改(在某种程度上)处理您自己的类中无法识别的选择器.看看的forwardingTargetForSelector:,forwardInvocation:,doesNotRecognizeSelector:,和resolveInstanceMethod:方法NSObject.
来自好的文档:
\n\n\n\n\n在 Objective-C 中,向 nil\xe2\x80\x94 发送消息是有效的,但在运行时没有任何作用。
\n
至于无法识别的选择器行为的另一个问题,NSObject 的旧实现文件(来自 MySTEP 库)显示罪魁祸首是 NSObject 方法-doesNotRecognizeSelector:,它看起来有点如下:
- (void) doesNotRecognizeSelector:(SEL)aSelector\n{\n [NSException raise:NSInvalidArgumentException\n format:@"NSObject %@[%@ %@]: selector not recognized", \n object_is_instance(self)?@"-":@"+",\n NSStringFromClass([self class]), \n NSStringFromSelector(aSelector)];\n}\nRun Code Online (Sandbox Code Playgroud)\n\n这意味着可以对 ObjC 方法进行修改,这样它们实际上就不必引发错误。这意味着该决定完全是任意的,就像将“方法吃”消息切换为 nil 的决定一样。可以通过方法调配 NSObject 来完成这一壮举(非常危险,因为它会在 mac 上引发 EXC_BAD_ACCESS 或 EXC_I386_BPT,但至少它不会引发异常)
\n\nvoid Swizzle(Class c, SEL orig, SEL new)\n{\n Method origMethod = class_getInstanceMethod(c, orig);\n Method newMethod = class_getInstanceMethod(c, new);\n if(class_addMethod(c, orig, method_getImplementation(newMethod), method_getTypeEncoding(newMethod)))\n class_replaceMethod(c, new, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));\n else\n method_exchangeImplementations(origMethod, newMethod);\n}\n\n-(void)example:(id)sender {\n Swizzle([NSObject class], @selector(doesNotRecognizeSelector:), @selector(description));\n [self performSelector:@selector(unrecog)];\n}\nRun Code Online (Sandbox Code Playgroud)\n\n类别:
\n\n@implementation NSObject (NoExceptionMessaging)\n\n-(void)doesNotRecognizeSelector:(SEL)aSelector {\n NSLog(@"I\'ve got them good ol\' no exception blues.");\n}\n@end\nRun Code Online (Sandbox Code Playgroud)\n