Objective-C之performSelector编译器内存泄露警告

前言


performSelector may cause a leak because its selector is unknown

ARC环境下,我们使用-(id)performSelector:方法时,编译器会提示可能导致内存泄露的警告。大家如果一直使用ARC进行iOS开发,没有经历MRC时期的话,可能心里会有疑问,为什么Apple提供的API会报警告呢,又为什么会报内存泄露的警告?接下来,将详细讨论该问题。

原因


其实,产生该问题的原因是ARC,运行时系统需要知道调用方法的返回值类型,我们知道,方法的返回值包括:void,int,NSString *,id等,ARC通常可以通过定义实例方法对象的头中获取信息。

ARC对于返回值有3种处理情况:

  1. 忽略非对象类型(void,int等)
  2. 当新建对象值不再需要时release(如init,copy或带有ns_returns_retained属性的方法)
  3. 不做任何处理,且假设返回的对象值会在局部作用域内有效(在最里层的autorelease pool结束之前都有效)

调用-(id)performSelector:,编译器会假设调用方法的返回值是一个对象,且不会对返回值进行retain/release,所以,如果你调用如上讨论的第2种情况下的方法,将导致内存泄露,因为,调用的方法会返回一个新的对象。

如果#SEL返回值类型为void或非对象类型,是可以安全的使用-(id)performSelector:的。

解决方法


  1. 使用编译器指令移除警告。(注意,该方法治标不治本,对于上面讨论的内存泄露情况,仍然存在)

    1
    2
    3
    4
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
    [self performSelector: NSSelectorFromString(@"someMethod") withObject: nil];
    #pragma clang diagnostic pop
  2. 使用NSInvocation封装方法调用。

  3. 对于返回值为void,int等值类型,可以使用- performSelector:withObject:afterDelay:来执行,由于它不会在当前RunLoop时调用,所以会立即返回,且在接下来的某个RunLoop中执行,也就没有了返回值。也可以使用运行时方法objc_msgSend(self, NSSelectorFromString(@"someMethod"));

附录


  1. Swift已经废弃了-(id)performSelector:方法。