为什么使用 "selector" 会导致编译器警告,而 @selector(selector) 不会?
Why does using "selector" cause a compiler warning, but @selector(selector) doesn't?
我在胡闹,只是尝试实现一个简单的非块/委托回调函数。
Class A.m
@implementation noblocks
-(void)logSomethingAndNotify:(id)object andCallSelector:(SEL)selector {
//some task
NSLog(@"TRYING THIS OUT");
//implement callback functionality
if ([object respondsToSelector:@selector(selector)]) {
[object performSelector:@selector(selector) withObject:object];
}
}
@end
Class B.m
- (void)viewDidLoad {
[super viewDidLoad];
ClassA *noblock = [noblocks new];
[ClassA logSomethingAndNotify:self andCallSelector:@selector(addSubviewAfterDelay)];
}
-(void)addSubviewAfterDelay {
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
UIView *view = [[UIView alloc]initWithFrame:CGRectMake(50, 50, 100, 100)];
view.backgroundColor = [UIColor blueColor];
[self.view addSubview:view];
});
}
在ClassA的实现文件中,如果我改成这条语句:
if ([object respondsToSelector:@selector(selector)]) {
[object performSelector:@selector(selector) withObject:object];
}
选择器作为方法参数的表示方式
if ([object respondsToSelector:selector]) {
[object performSelector:selector withObject:object];
}
然后我从编译器收到内存泄漏警告。
我知道考虑到 Objective-C 的动态运行时,向未知对象发送无选择器消息可能会出现问题 - 我们不知道该方法的 return 类型,所以不能'不确定我们应该保留对象 returned(如果有的话)。我不理解的是为什么使用 performSelector:@selector(selector) 与仅使用 performSelector:selector DOESN'T 导致任何 ARC警告。
这个问题与解决编译器警告的其他问题不重复 - 我的问题不是关于为什么显示错误,而是为什么一种方式显示警告而另一种方式不显示。
@selector(selector)
并没有按照您的想法行事。 :)
考虑:
SEL selector = @selector(hash);
NSLog(@"%s %s", selector, @selector(selector));
此输出(依赖于 SEL 实际上是一个 char* 作为实现细节的事实,你永远不应该依赖这样的实验之外):
asdfasdfa[71281:7385265] hash selector
@selector(selector)
生成一个不会触发 ARC 内存警告的常量值,因为编译器可以适当地推断代码路径。
即考虑:
SEL bob = @selector(dobbs);
NSLog(@"%s %s", bob, @selector(bob));
产生:
asdfasdfa[71313:7394673] hobbs bob
变量 bob
指的是 SEL
dobbs
而 @selector(bob)
产生 SEL
bob
.
在动态选择器值上使用 performSelector:
时出现警告的原因是,在 Cocoa 内存管理中,某些方法旨在 return 保留 (+ 1)实例(即调用者有责任释放它)。默认情况下,这是名称以 alloc
、retain
、new
、copy
或 mutableCopy
开头的方法。所有其他方法 return 非保留实例(或保留和自动释放;任何不需要调用者释放的实例)。
performSelector:
,顾名思义,并不暗示 return 保留的实例,也没有用 ns_returns_retained
注释。因此,ARC 将认为它不是 return 保留实例(如果您使用它来执行 "normal" 方法,这是正确的)。但是由于我们在编译时不知道选择器是什么,可能是您正在传递那些 return 保留实例的选择器之一,在这种情况下它会出错并导致泄漏。
如果选择器在编译时被硬编码,编译器可以在编译时检查名称并验证它不是那些 return 保留实例之一。
我在胡闹,只是尝试实现一个简单的非块/委托回调函数。
Class A.m
@implementation noblocks
-(void)logSomethingAndNotify:(id)object andCallSelector:(SEL)selector {
//some task
NSLog(@"TRYING THIS OUT");
//implement callback functionality
if ([object respondsToSelector:@selector(selector)]) {
[object performSelector:@selector(selector) withObject:object];
}
}
@end
Class B.m
- (void)viewDidLoad {
[super viewDidLoad];
ClassA *noblock = [noblocks new];
[ClassA logSomethingAndNotify:self andCallSelector:@selector(addSubviewAfterDelay)];
}
-(void)addSubviewAfterDelay {
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
UIView *view = [[UIView alloc]initWithFrame:CGRectMake(50, 50, 100, 100)];
view.backgroundColor = [UIColor blueColor];
[self.view addSubview:view];
});
}
在ClassA的实现文件中,如果我改成这条语句:
if ([object respondsToSelector:@selector(selector)]) {
[object performSelector:@selector(selector) withObject:object];
}
选择器作为方法参数的表示方式
if ([object respondsToSelector:selector]) {
[object performSelector:selector withObject:object];
}
然后我从编译器收到内存泄漏警告。
我知道考虑到 Objective-C 的动态运行时,向未知对象发送无选择器消息可能会出现问题 - 我们不知道该方法的 return 类型,所以不能'不确定我们应该保留对象 returned(如果有的话)。我不理解的是为什么使用 performSelector:@selector(selector) 与仅使用 performSelector:selector DOESN'T 导致任何 ARC警告。
这个问题与解决编译器警告的其他问题不重复 - 我的问题不是关于为什么显示错误,而是为什么一种方式显示警告而另一种方式不显示。
@selector(selector)
并没有按照您的想法行事。 :)
考虑:
SEL selector = @selector(hash);
NSLog(@"%s %s", selector, @selector(selector));
此输出(依赖于 SEL 实际上是一个 char* 作为实现细节的事实,你永远不应该依赖这样的实验之外):
asdfasdfa[71281:7385265] hash selector
@selector(selector)
生成一个不会触发 ARC 内存警告的常量值,因为编译器可以适当地推断代码路径。
即考虑:
SEL bob = @selector(dobbs);
NSLog(@"%s %s", bob, @selector(bob));
产生:
asdfasdfa[71313:7394673] hobbs bob
变量 bob
指的是 SEL
dobbs
而 @selector(bob)
产生 SEL
bob
.
在动态选择器值上使用 performSelector:
时出现警告的原因是,在 Cocoa 内存管理中,某些方法旨在 return 保留 (+ 1)实例(即调用者有责任释放它)。默认情况下,这是名称以 alloc
、retain
、new
、copy
或 mutableCopy
开头的方法。所有其他方法 return 非保留实例(或保留和自动释放;任何不需要调用者释放的实例)。
performSelector:
,顾名思义,并不暗示 return 保留的实例,也没有用 ns_returns_retained
注释。因此,ARC 将认为它不是 return 保留实例(如果您使用它来执行 "normal" 方法,这是正确的)。但是由于我们在编译时不知道选择器是什么,可能是您正在传递那些 return 保留实例的选择器之一,在这种情况下它会出错并导致泄漏。
如果选择器在编译时被硬编码,编译器可以在编译时检查名称并验证它不是那些 return 保留实例之一。