performSelector 方法的自动俘获特性
来源:互联网 发布:最新电视直播软件 编辑:程序博客网 时间:2024/06/08 11:49
局部变量自动俘获
偶然在调试中发现,performSelector 方法具有自动俘获变量的特性。试看如下代码:
CGFloat c = _addViewShowing ? 0 : 80; if([self respondsToSelector:@selector(jsq_setToolbarBottomLayoutGuideConstant:)]){ [self performSelector:@selector(jsq_setToolbarBottomLayoutGuideConstant:) withObject:nil]; ... }
这里请注意 [self performSelector:@selector(jsq_setToolbarBottomLayoutGuideConstant:) withObject:nil]; 一句。在调用 performSelector 方法时,会自动把变量 CGFloat c 俘获到 jsq_setToolbarBottomLayoutGuideConstant: 方法调用中去。也就是说,相当于向该方法传递了参数 c。
值得注意的是,变量 c 的类型必须和 jsq_setToolbarBottomLayoutGuideConstant: 方法参数的类型相同,否则不会自动俘获。例如, c 变量为 CGFloat,而方法jsq_setToolbarBottomLayoutGuideConstant: 的参数同样也为 CGFloat:
- (void)jsq_setToolbarBottomLayoutGuideConstant:(CGFloat)constant
如果你将 c 的类型改成 int,则 c 不会被自动俘获。
利用这个特性,我们可以在 performSelector 调用时,自动传递变量给目标方法,而不用通过 withObject 来传参。
自动俘获方法参数
如果我们将上述代码定义为一个方法:
-(void)setToolbarSpaceToBottom:(CGFloat)constant{ // 调用私有方法 jsq_setToolbarBottomLayoutGuideConstant CGFloat c = _addViewShowing ? 0 : 80; if([self respondsToSelector:@selector(jsq_setToolbarBottomLayoutGuideConstant:)]){ [self performSelector:@selector(jsq_setToolbarBottomLayoutGuideConstant:) withObject:nil]; }}
则变量 c 可以省略,因为 performSelector 会自动俘获方法参数 constant,将之传递给 jsq_setToolbarBottomLayoutGuideConstant: 调用。于是这个方法可以写成:
-(void)setToolbarSpaceToBottom:(CGFloat)constant{ if([self respondsToSelector:@selector(jsq_setToolbarBottomLayoutGuideConstant:)]){ [self performSelector:@selector(jsq_setToolbarBottomLayoutGuideConstant:) withObject:nil]; }}
自动俘获的代价
上述代码同时会带来一个负面作用,即在两个 @selector 引用的地方出现两个相同编译警告:
Undeclared selector ‘jsq_setToolbarBottomLayoutGuideConstant:’
这是因为 jsq_setToolbarBottomLayoutGuideConstant: 方法来自于父类,它是私有的(没有将方法进行静态声明——即未在头文件中声明)。对一切未静态声明的方法进行 performSelector 时,编译器都会提示 Undeclared selector。
你可以用下面的技术消除它们,但这会导致自动俘获失效。
不能使用自动俘获的情况
要消灭编译警告,我们可以使用 NSSelectorFromString 来引用 selector。
例如:
CGFloat c = constant; SEL sel=NSSelectorFromString(@"jsq_setToolbarBottomLayoutGuideConstant:"); if([self respondsToSelector:sel]){ // 忽略编译器警告 #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" [self performSelector:sel withObject:nil]; #pragma clang diagnostic pop }
但这种情况下,变量 c 不会被自动俘获。如果你在 jsq_setToolbarBottomLayoutGuideConstant: 方法的第一行代码加上断点运行程序,当程序运行到断点处时,打印参数的值,你会发现其值为 NaN ,如果继续执行代码,App 会崩溃。
这种情况下,我们无法使用自动俘获,因此只能使用 withObject 来传递参数了。但由于 CGFloat 不是 NSObject,无法用 [performSelector: withObjectd:] 来传参,因此要使用 NSInvocation 来调用:
-(void)setToolbarSpaceToBottom:(CGFloat)constant{ SEL sel=NSSelectorFromString(@"jsq_setToolbarBottomLayoutGuideConstant:"); NSInvocation *invoc = [NSInvocation invocationWithMethodSignature:[[self class] instanceMethodSignatureForSelector:sel]]; [invoc setSelector:sel]; [invoc setTarget:self]; [invoc setArgument:&constant atIndex:2];//"Indices 0 and 1 indicate the hidden arguments self and _cmd" [invoc performSelector:@selector(invoke) withObject:nil];}
- performSelector 方法的自动俘获特性
- 取消performSelector操作的方法
- 消除performSelector:警告的方法
- performSelector调用方法和直接self 调用方法的区别
- performSelector:withObject:方法
- 取消performSelector:方法
- IOS performSelector: 方法简述
- iOS 类的实例方法调用 NSInvocation performSelector
- 剖析带有afterDealy参数的performSelector方法实现
- 如何俘获一个 IT 男的心
- 如何俘获女神的芳心-求指教
- performSelector引发的感想
- 关于performSelector的执行
- performSelector的简单用法
- iOS 延时执行方法 performSelector
- 050.performSelector 动态调用方法
- 050.performSelector 动态调用方法
- performSelector
- 试用阿里云RDS的MySQL压缩存储引擎TokuDB
- SQL 這個子查詢最多只能傳回一個記錄
- 持有对象(容器)Map && Collection(List、Set、Stack、Queue)
- Swift中去掉字符串格式数字小数点后多余的0 (自写)
- C# SQL查询代码
- performSelector 方法的自动俘获特性
- HTML 5 简介
- TokuDB的特点验证
- 【算法】链表
- ios开发autolayout之VFL语言使用总结
- .htaccess详解及.htaccess参数说明
- database link问题解决
- Android TabLayout与ViewPager实现动态Tab
- 文章标题