隐式地保留了“self”块; 明确指出“self”,以表明这是预期的行为。

69

考虑以下情况:

- (void) someMethod
{
    dispatch_async(dispatch_get_main_queue(), ^{
        myTimer = [NSTimer scheduledTimerWithTimeInterval: 60
                                                           target: self
                                                         selector: @selector(doSomething)
                                                         userInfo: nil
                                                          repeats: NO];
    });
}

myTimer在私有接口中声明:

@interface MyClass()
{
    NSTimer * myTimer;
}
@end

如何修复以下警告:

Block隐式保留'self'; 明确地提到'self'以指示这是预期的行为

根据我目前所找到的,大多数建议都涉及放置以下内容:

- (void) someMethod
{
    __typeof__(self) __weak wself = self;
    dispatch_async(dispatch_get_main_queue(), ^{
        wself.myTimer = [NSTimer scheduledTimerWithTimeInterval: 60
                                                           target: self
                                                         selector: @selector(doSomething)
                                                         userInfo: nil
                                                          repeats: NO];
    });
}

不过,我的myTimer是一个ivar,这意味着不能访问任何属性。

我想问的问题是:

  1. 我需要关注/在意吗?
  2. 我应该将myTimer声明为一个属性吗?

我在我的代码中经常使用ivars。 我刚刚向我的项目添加了“-Weverything”标志,以查看是否可以找到任何潜在的问题,这是迄今为止最常见的警告。 我没有问题通过使我的ivars成为属性来进行修复,但在这样做之前,我希望确保我能够更好地理解它们。


除了,wself是一个ivar,这意味着wself无法访问任何属性。- 什么? - user529758
@H2CO3 抱歉,那应该是“除了myTimer是一个实例变量”。 - Kyle
今天升级到Xcode 9.3后,我开始收到这个警告,并遵循Xcode自己的建议更新项目设置。突然间,我的项目上出现了300多个警告信息。 - Can Poyrazoğlu
这是Swift引起的警告吗?如果是,为什么我们仍然要反对内存管理的复杂性,而不是编写代码呢? - Tjalsma
5个回答

78

myTimer 替换为 self->myTimer 将修复您的警告。

当您在代码中使用 iVar _iVar 时,编译器会将代码替换为 self->_iVar,如果您在块内部使用它,则该块将捕获 self 而不是 iVar 本身。此警告只是为了确保开发人员理解这种行为。


5
我也遇到了这个错误,但我有一个疑问:它会导致任何内存泄漏吗? - Surbhi Garg
2
相反,weak指针意味着保留计数不会改变。唯一的问题是,在使用之前,self将被释放。 - bauerMusic
1
不过,据我所知,在方法内分配dispatch_async(而不是强烈持有它)不会创建保留循环。块可能会保留self,但是self不会保留块。所以我认为这些警告只是在倡导“良好习惯”? - bauerMusic
最近遇到了这个警告,我想知道是否有更好的解决方案,不需要在我的代码中每个 _iVar 前面加上 self->:因为我的块中有很多这样的变量,这会让代码看起来非常杂乱无章。 - Andrea
4
你也可以将所有的块逻辑移至一个方法内,并在块中调用该方法。 - bsarr007
显示剩余2条评论

75

详细信息

Xcode: 9.2、10.2、11.0 (11A420a)

Objective-C库中的警告

我有一个Swift项目。当我使用Objective-C库(如Bolts、FBSDKCoreKit和FBSDKLoginKit)时会出现警告Block implicitly retains 'self'; explicitly mention 'self' to indicate this is intended behavior

  • Bolts
  • FBSDKCoreKit
  • FBSDKLoginKit

enter image description here

解决方案1(手动)

CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = NO

解决方案2(自动)

将以下内容添加到您的Podfile末尾:

post_install do |installer|
      installer.pods_project.targets.each do |target|
           target.build_configurations.each do |config|
                config.build_settings['CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF'] = 'NO'
           end
      end
 end

结果

在此输入图片描述


谢谢@Vasily Bodnarchuk,我成功解决了Xcode 9.2中的错误。将标志更改为“否”是否会有其他影响? - user1872384
1
我在三个Swift项目中更改了此标志。我没有错误。 - Vasily Bodnarchuk
12
将这个标志打开或关闭对你的代码编译或操作没有任何影响。你只是不再收到这个非常有用的警告,该警告会提醒你在没有注意到的情况下隐式引用了 self。如果你被卡在一个第三方库中,而在那里你得到了大量这些警告,我可以理解想要将其关闭。但我建议在你自己的代码中保持这个警告开启,并用显式引用替换隐式引用 self,确保在这些情况下没有强引用循环。 - Rob

15

如果您因为 Bolts/FBSDKCoreKit/FBSDKLoginKit而收到这些警告,请避免使用 Vasily 的回答,而是为这些特定的依赖项取消警告。

选项1

提及每个 Pod 而不仅仅是 FacebookCore,并添加 inhibit_warnings: true

pod 'FacebookCore', inhibit_warnings: true
pod 'Bolts', inhibit_warnings: true
pod 'FBSDKCoreKit', inhibit_warnings: true
pod 'FBSDKLoginKit', inhibit_warnings: true

选项2

或者在你的Podfile中添加以下内容,以使所有的pod静默:

inhibit_all_warnings!

结论

您仍将收到有关自己代码的警告。在某些情况下,未收到这些警告可能会带来问题,这就是为什么我认为这是更好的解决方案的原因。

下次更新Facebook SDK时,请查看是否可以删除inhibit_warnings:trueinhibit_all_warnings!


我认为Vasily的回答没有问题。它仅适用于Pods项目,而不是整个项目。 - Starsky
@Starsky 这是一个问题,因为它适用于所有的Pods,所以它会隐藏其他Pods的警告。 - Arnaud

5
这解决了我的Xcode 9.3问题。
- (void) someMethod{

    __weak MyClass *wSelf = self;

    dispatch_async(dispatch_get_main_queue(), ^{

    MyClass *sSelf = wSelf;
    if(sSelf != nil){
        wself.myTimer = [NSTimer scheduledTimerWithTimeInterval: 60
                                                       target: self
                                                     selector:@selector(doSomething)
                                                     userInfo: nil
                                                      repeats: NO];
       }

   });
}

2

最近我遇到了同样的问题,@Vasily Bodnarchuk的答案似乎很有帮助。

然而,在持续集成环境中,无法在运行时更改CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF标志为NO。 因此,为了隔离问题,我尝试检查Cocoapods安装的所有依赖GEMS,并发现gem XCODEPROJ版本1.5.7会在执行pod install命令时将CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF设置为YES。 解决方法是通过执行sudo gem install xcodeproj -v 1.5.1XCODEPROJ还原到早期版本1.5.1。 还原后,只需执行pod install即可将标志始终设置为NO。


它对我起作用了!!!答案应该被接受为正确的。它救了我的一天。谢谢@bhuvan - Pallav Trivedi
2
@PallavTrivedi - 我认为这不应该成为被接受的答案。别误会,这个答案在成功消除警告方面是正确的。但是这个警告非常有用。我建议让你的self引用显式化(在你可以一眼看到可能存在强引用循环问题的代码处),而不是让不太明显的隐式引用self逃脱注意力。苹果之所以添加了这个有用的警告,是有原因的。 - Rob
我同意,我只是因为回答相同的方式而被踩了。不要通过删除它来消除警告声音,而是通过不正确地引用自身来消除警告声音。这可能会创建保留周期。 - Joel Teply

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接