简体   繁体   English

在这个区块中强烈捕获自我可能会导致保留周期

[英]capturing self strongly in this block is likely to lead to a retain cycle

How can I avoid this warning in xcode. 如何在xcode中避免此警告。 Here is the code snippet: 这是代码片段:

[player(AVPlayer object) addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(0.1, 100)
queue:nil usingBlock:^(CMTime time) {
    current+=1;

    if(current==60)
    {
        min+=(current/60);
        current = 0;
    }

    [timerDisp(UILabel) setText:[NSString stringWithFormat:@"%02d:%02d",min,current]];///warning occurs in this line
}];

The capture of self here is coming in with your implicit property access of self.timerDisp - you can't refer to self or properties on self from within a block that will be strongly retained by self . 捕获self这里来与你的隐含属性访问self.timerDisp -你不能指self或性质上self从将被牢固地保持块内self

You can get around this by creating a weak reference to self before accessing timerDisp inside your block: 您可以通过在访问块中的timerDisp之前创建对self的弱引用来解决此问题:

__weak typeof(self) weakSelf = self;
[player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(0.1, 100)
                                     queue:nil
                                usingBlock:^(CMTime time) {
                                                current+=1;

                                                if(current==60)
                                                {
                                                    min+=(current/60);
                                                    current = 0;
                                                }

                                                 [weakSelf.timerDisp setText:[NSString stringWithFormat:@"%02d:%02d",min,current]];
                                            }];
__weak MyClass *self_ = self; // that's enough
self.loadingDidFinishHandler = ^(NSArray *receivedItems, NSError *error){
    if (!error) {
       [self_ showAlertWithError:error];
    } else {
       self_.items = [NSArray arrayWithArray:receivedItems];
       [self_.tableView reloadData];
    }
};

And one very important thing to remember: do not use instance variables directly in block, use it as a properties of weak object, sample: 还有一件非常重要的事情要记住:不要直接在块中使用实例变量,将其用作弱对象的属性,示例:

self.loadingDidFinishHandler = ^(NSArray *receivedItems, NSError *error){
        if (!error) {
           [self_ showAlertWithError:error];
        } else {
           self_.items = [NSArray arrayWithArray:receivedItems];
           [_tableView reloadData]; // BAD! IT ALSO WILL BRING YOU TO RETAIN LOOP
        }
 };

and don't forget to do: 并且不要忘记做:

- (void)dealloc {
    self.loadingCompletionHandler = NULL;
}

another issue can appear if you will pass weak copy of not retained by anybody object: 如果您将传递任何对象未保留的弱副本,则会出现另一个问题:

MyViewController *vcToGo = [[MyViewCOntroller alloc] init];
__weak MyViewController *vcToGo_ = vcToGo;
self.loadingCompletion = ^{
    [vcToGo_ doSomePrecessing];
};

if vcToGo will be deallocated and then this block fired I believe you will get crash with unrecognized selector to a trash which is contains vcToGo_ variable now. 如果vcToGo将被解除分配,然后这个块被解雇,我相信你会因为无法识别的选择器而崩溃,现在包含vcToGo_变量的垃圾箱。 Try to control it. 试着控制它。

Better version 更好的版本

__strong typeof(self) strongSelf = weakSelf;

Create a strong reference to that weak version as the first line in your block. 创建对该弱版本的强引用作为块中的第一行。 If self still exists when the block starts to execute and hasn't fallen back to nil, this line ensures it persists throughout the block's execution lifetime. 如果块在块开始执行时仍然存在并且没有回落到nil,则该行确保它在整个块的执行生命周期中持续存在。

So the whole thing would be like this: 所以整件事情都是这样的:

// Establish the weak self reference
__weak typeof(self) weakSelf = self;

[player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(0.1, 100)
                                 queue:nil
                            usingBlock:^(CMTime time) {

    // Establish the strong self reference
    __strong typeof(self) strongSelf = weakSelf;

    if (strongSelf) {
        [strongSelf.timerDisp setText:[NSString stringWithFormat:@"%02d:%02d",min,current]];
    } else {
        // self doesn't exist
    }
}];

I have read this article many times. 我多次读过这篇文章。 This is an excellent article by Erica Sadun on How To Avoid Issues When Using Blocks And NSNotificationCenter 这是Erica Sadun关于如何避免使用块和NSNotificationCenter时出现问题的优秀文章


Swift update: Swift更新:

For example, in swift a simple method with success block would be: 例如,在swift中,带有成功块的简单方法是:

func doSomeThingWithSuccessBlock(success: () -> ()) {
    success()
}

When we call this method and need to use self in the success block. 当我们调用此方法并需要在成功块中使用self时。 We'll be using the [weak self] and guard let features. 我们将使用[weak self]guard let功能。

    doSomeThingWithSuccessBlock { [weak self] () -> () in
        guard let strongSelf = self else { return }
        strongSelf.gridCollectionView.reloadData()
    }

This so-called strong-weak dance is used by popular open source project Alamofire . 这种所谓的强弱舞蹈被流行的开源项目Alamofire

For more info check out swift-style-guide 有关更多信息,请查看swift-style-guide

In another answer, Tim said: 在另一个答案中,蒂姆说:

you can't refer to self or properties on self from within a block that will be strongly retained by self. 你不能在一个自我强烈保留的区块内引用自我或自我属性。

This isn't quite true. 这不是真的。 It's OK for you to do this so long as you break the cycle at some point. 只要你在某个时刻打破循环,你就可以这样做。 For example, let's say you have a timer that fires that has a block that retains self and you also keep a strong reference to the timer in self. 例如,假设您有一个触发计时器,该计时器具有保留自身的块,您还可以自我保持对计时器的强引用。 This is perfectly fine if you always know that you will destroy the timer at some point and break the cycle. 如果你总是知道你会在某个时刻破坏定时器并打破循环,那就完全没问题了。

In my case just now, I had this warning for code that did: 就我刚才的情况而言,我对代码执行了此警告:

[x setY:^{ [x doSomething]; }];

Now I happen to know that clang will only produce this warning if it detects the method starts with “set” (and one other special case that I won't mention here). 现在我碰巧知道clang只会在检测到方法以“set”开头时才会产生这个警告(另外还有一个我不会在这里提及的特殊情况)。 For me, I know there is no danger of there being a retain loop, so I changed the method name to “useY:” Of course, that might not be appropriate in all cases and usually you will want to use a weak reference, but I thought it worth noting my solution in case it helps others. 对我来说,我知道没有保留循环的危险,所以我将方法名称更改为“useY:”当然,这可能不适合所有情况,通常你会想要使用弱引用,但是我认为值得注意我的解决方案,以防它帮助别人。

Many times, this is not actually a retain cycle . 很多时候, 这实际上并不是一个保留周期

If you know that it's not, you need not bring fruitless weakSelves into the world. 如果你知道事实并非如此,那么你就不需要将无用的弱者带入这个世界。

Apple even forces these warnings upon us with the API to their UIPageViewController , which includes a set method (which triggers these warnings–as mentioned elsewhere–thinking you are setting a value to an ivar that is a block) and a completion handler block (in which you'll undoubtedly refer to yourself). Apple甚至通过API向他们的UIPageViewController强制发出这些警告,其中包括一个set方法(触发这些警告 - 如其他地方所述 - 认为你正在为一个块的ivar设置一个值)和一个完成处理程序块(在你无疑会提到自己)。

Here's some compiler directives to remove the warning from that one line of code: 这是一些编译器指令,用于从一行代码中删除警告:

#pragma GCC diagnostic push
#pragma clang diagnostic ignored "-Warc-retain-cycles"
    [self.pageViewController setViewControllers:@[newViewController] direction:navigationDirection animated:YES completion:^(BOOL finished) {
        // this warning is caused because "setViewControllers" starts with "set…", it's not a problem
        [self doTheThingsIGottaDo:finished touchThePuppetHead:YES];
    }];
#pragma GCC diagnostic pop

Adding two cents on improving precision and style. 增加两美分,提高精度和风格。 In most cases you will only use one or a couple of members of self in this block, most likely just to update a slider. 在大多数情况下,您只能在此块中使用一个或几个self成员,很可能只是更新滑块。 Casting self is overkill. 施展self是过度的。 Instead, it's better to be explicit and cast only the objects that you truly need inside the block. 相反,最好是显式并在块中转换您真正需要的对象。 For example, if it's an instance of UISlider* , say, _timeSlider , just do the following before the block declaration: 例如,如果它是UISlider*的实例,比如_timeSlider ,则在块声明之前执行以下操作:

UISlider* __weak slider = _timeSlider;

Then just use slider inside the block. 然后只需在块内使用slider Technically this is more precise as it narrows down the potential retain cycle to only the object that you need, not all the objects inside self . 从技术上讲,这更精确,因为它将潜在的保留周期缩小到仅需要的对象,而不是self内的所有对象。

Full example: 完整示例:

UISlider* __weak slider = _timeSlider;
[_embeddedPlayer addPeriodicTimeObserverForInterval:CMTimeMake(1, 1)
     queue:nil
     usingBlock:^(CMTime time){
        slider.value = time.value/time.timescale;
     }
];

Additionally, most likely the object being cast to a weak pointer is already a weak pointer inside self as well minimizing or eliminating completely the likelihood of a retain cycle. 另外,很可能将对象强制转换为弱指针已经是self内部的弱指针,同时最小化或完全消除了保留周期的可能性。 In the example above, _timeSlider is actually a property stored as a weak reference, eg: 在上面的示例中, _timeSlider实际上是存储为弱引用的属性,例如:

@property (nonatomic, weak) IBOutlet UISlider* timeSlider;

In terms of coding style, as with C and C++, variable declarations are better read from right to left. 在编码风格方面,与C和C ++一样,从右到左更好地读取变量声明。 Declaring SomeType* __weak variable in this order reads more naturally from right to left as: variable is a weak pointer to SomeType . 按此顺序声明SomeType* __weak variable从右到左更自然地读取: variable is a weak pointer to SomeType

I ran into this warning recently and wanted to understand it a bit better. 我最近遇到了这个警告,想要更好地理解它。 After a bit of trial and error, I discovered that it originates from having a method start with either "add" or "save". 经过一些试验和错误,我发现它源于一个方法以“添加”或“保存”开始。 Objective C treats method names starting with "new", "alloc", etc as returning a retained object but doesn't mention (that I can find) anything about "add" or "save". Objective C将以“new”,“alloc”等开头的方法名称视为返回一个保留对象,但没有提及(我可以找到)关于“add”或“save”的任何内容。 However, if I use a method name in this way: 但是,如果我以这种方式使用方法名称:

[self addItemWithCompletionBlock:^(NSError *error) {
            [self done]; }];

I will see the warning at the [self done] line. 我将在[自我完成]线上看到警告。 However, this will not: 但是,这不会:

[self itemWithCompletionBlock:^(NSError *error) {
    [self done]; }];

I will go ahead and use the "__weak __typeof(self) weakSelf = self" way to reference my object but really don't like having to do so since it will confuse a future me and/or other dev. 我将继续使用“__weak __typeof(self)weakSelf = self”方式来引用我的对象,但实际上不喜欢这样做,因为它会混淆未来的我和/或其他开发者。 Of course, I could also not use "add" (or "save") but that's worse since it takes away the meaning of the method. 当然,我也不能使用“添加”(或“保存”),但这更糟糕,因为它消除了方法的含义。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 避免“在此块中强烈捕获自我可能会导致保留周期”消息 - Avoiding the “capturing self strongly in this block is likely to lead to a retain cycle” message “使用可达性”“在此区块中强烈捕获'自我'很可能导致保留周期” - “Capturing 'self' strongly in this block is likely to lead to a retain cycle” using Reachability 如何在此块中强烈修复“捕获'块'可能会导致保留周期” - How to fix “Capturing 'block' strongly in this block is likely to lead to a retain cycle” iOS 5 Twitter Framework和completionHandler块 - “在这个块中强烈捕获'自我'可能会导致保留周期” - iOS 5 Twitter Framework & completionHandler block - “Capturing 'self' strongly in this block is likely to lead to a retain cycle” 在这种情况下,如何解决警告“在此块中强烈捕获‘自我’可能会导致保留周期”? - How to resolve warning "Capturing 'self' strongly in this block is likely to lead to a retain cycle" in this case? 警告:在此块中强烈捕获$ XXX可能会导致“ AFHTTPClient.m”中的保留周期 - Warning: Capturing $XXX strongly in this block is likely to lead to a retain cycle in 'AFHTTPClient.m' 块中的@synchronized(self)是否会导致保留周期? - Does @synchronized(self) in a block lead to a retain cycle? 强烈阻止捕捉“自我” - Block capturing 'self' strongly 捕获“自身”时,弱保留的块如何导致保留周期 - How can a weakly retained block cause a retain cycle when capturing “self” 缺少“可能导致保留周期”警告是否意味着将不会创建强参考周期? - Does the absence of a “likely to lead to a retain cycle” warning imply a strong reference cycle will not be created?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM