本文共 1692 字,大约阅读时间需要 5 分钟。
在开发中,当我们想要在运行时立即执行某个方法时,通常会调用NSObject的performSelector:方法。虽然这一方法很方便,但也有潜在的问题需要注意。
首先,performSelector:方法的关键在于它是在当前线程的runloop中执行的,并且默认的runloop mode是NSDefaultRunLoopMode。这种方式具有高度的动态性,但也带有一些缺点。例如:
内存泄漏风险:调用者不对performSelector返回的对象负责,尤其是当调用方法如alloc、new、copy等时,系统会开辟内存空间但无法保证内存的正确管理。这会导致内存泄漏。
动态方法的潜在问题:由于performSelector的Selector不确定,编译器无法有效校验方法的返回类型,这也会产生编译警告。
接下来,我们可以了解如何最好地使用performSelector方法,以及如何解决这些潜在问题。
使用NSInvocation:通过动态的方式构造一个NSInvocation对象来执行方法。这种方法可以避免内存泄漏问题。
SEL sel = NSSelectorFromString(@"new");NSMethodSignature *method = [[self class] instanceMethodSignatureForSelector:sel];NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:method];[invocation setSelector:sel];[invocation setTarget:self];[invocation invoke];
使用块重构代码:可以采用代码块的方式来实现动态方法调用。
使用UIApplication的sendAction方法:这是一种更简便的方式,可以在主线程上发送动态方法。
[UIApplication.sharedApplication sendAction:NSSelectorFromString(@"new") to:self from:nil forEvent:nil];
当使用performSelector传递 Selector或对象时,特别是涉及 Construc累方法,及时释放内存可以避免内存泄漏。通过使用CFBridgingRelease,可以强制释放内存。
id obj = CFBridgingRelease(((void *(*)(id, SEL))[self methodForSelector:NSSelectorFromString(@"new")])(self, NSSelectorFromString(@"new")));
performSelector的底层实现主要依赖于objc_msgSend。通过查看NSObject的源码可以发现,该方法会在runloop中执行Selector,直到找到匹配的方法。如果Selector不再存在于类中,会触发运行时错误。
performSelector还可以用于延迟执行任务。通过在runloop中设置计时器,可以将任务推迟到某个时间点执行。
performSelector还支持指定特定runloop模式来执行任务,这可以让任务执行时更加精确。
performSelector还支持在其他线程执行任务,这对于需要跨线程通信的场景非常有用。
通过以上方法,我们可以更安全、灵活地使用performSelector进行动态方法调用,同时尽量避免内存泄漏和其他潜在问题。
如果对这一系列方法的实现细节感兴趣,可以参考相关的官方文档和源码分析。
转载地址:http://dlttz.baihongyu.com/