performSelector 注意问题及原理

一:首先使用performSelector时要特别注意内存泄漏问题,下面代码演示:
创建一个控制器ZWWTestThreadViewController,从上个控制器push到该控制器,

ZWWLog的宏定义:

#ifdef DEBUG
#define ZWWLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);
#else
#define ZWWLog(...)
#endif

ZWWTestThreadViewController.m输入代码:

- (void)viewDidLoad {
    [super viewDidLoad];


    ZWWLog(@"retainCount=%ld",CFGetRetainCount((__bridge CFTypeRef)self));
    //performSelector调用方法
    [self performSelector:@selector(testPerformSelectorFunc) withObject:self afterDelay:10.0];

    //直接调用方法
//    [self testPerformSelectorFunc];
    ZWWLog(@"retainCount=%ld",CFGetRetainCount((__bridge CFTypeRef)self));
}

- (void)viewDidDisappear:(BOOL)animated{
    [super viewDidDisappear:animated];
    ZWWLog();
}

//
- (void)testPerformSelectorFunc{
    ZWWLog();
}

- (void)dealloc
{
    ZWWLog(@"对象被销毁");
    ZWWLog(@"retainCount=%ld",CFGetRetainCount((__bridge CFTypeRef)self));
}

情况1:当未达到10s就点击那navigationbar上的返回按钮退出此界面,打印结果:
这里写图片描述

结果:
1.执行performSelector后,self.retainCount是会增加的,先不管为什么是8,10(参考链接
2.testPerformSelectorFunc在viewDidDisappear之后执行,也就是在页面返回disappear以后,dealloc方法并没有执行,控制器对象并没有真正销毁,造成了内存泄漏。而等达到10s时间的时候,虽然该控制器已经disappear了,但还会执行该页面的代码testPerformSelectorFunc方法,执行完这个方法,才执行了dealloc方法,对象才得到了释放,证明执行完performSelector方法后self.retainCount会对应减少

情况2:达到10s,再退出页面,打印结果:
这里写图片描述

结果:
1.执行viewDidDisappear之后就会执行dealloc,对象得到了真正释放。执行dealloc时reatainCount肯定为1,之前reatainCount是几不是特别重要(参考链接

解决方法:
在界面或者控制器退出的时候取消那些还没有来得及执行的延时函数,代码很简单:

[NSObject cancelPreviousPerformRequestsWithTarget:self]

当然你也可以一个一个得这样用:

[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(method1:) object:nil]

加上了这个以后,dealloc方法就会被调用,问题解决!

viewDidDisappear方法里面添加代码 [NSObject cancelPreviousPerformRequestsWithTarget:self];

- (void)viewDidDisappear:(BOOL)animated{
    [super viewDidDisappear:animated];
    ZWWLog();
    [NSObject cancelPreviousPerformRequestsWithTarget:self];
}

运行结果:
这里写图片描述
结论:可以看到即使没到10s退出该控制器,也会立即执行dealloc方法,对象销毁,而且testPerformSelectorFunc不会在viewDidDisappear之后再执行

二:下面我们再说下performSelector原理及用法

在上面viewDidLoad可以看到,我们调用方法(发送消息)时经常是直接调用方法,那和performSelector区别是什么?

- (void)viewDidLoad {
    [super viewDidLoad];


    ZWWLog(@"retainCount=%ld",CFGetRetainCount((__bridge CFTypeRef)self));
    //performSelector调用方法
    [self performSelector:@selector(testPerformSelectorFunc) withObject:self afterDelay:10.0];

    //直接调用方法
//    [self testPerformSelectorFunc];
    ZWWLog(@"retainCount=%ld",CFGetRetainCount((__bridge CFTypeRef)self));
}

1、performSelector是运行时系统负责去找方法的,在编译时候不做任何校验;如果直接调用编译时会自动校验。如果testPerformSelectorFunc方法不存在,那么直接调用 在编译时候就能够发现(借助Xcode提示方法全名就发现),但是使用performSelector的话一定是在运行时候才能发现(此时程序崩溃);Cocoa支持在运行时向某个类添加方法,即方法编译时不存在,但是运行时候存在,这时候必然需要使用performSelector去调用。所以有时候如果使用了performSelector,为了程序的健壮性,会使用检查方法- (BOOL)respondsToSelector:(SEL)aSelector;

2、直接调用方法时候,一定要在头文件中声明该方法的使用,也要将头文件import进来。而使用performSelector时候,可以不用import头文件包含方法的对象,直接用performSelector调用即可。

猜你喜欢

转载自blog.csdn.net/wei371522/article/details/81216853