在这个区块中捕获自我很可能会导致一个保留周期。

时间:2022-09-07 10:50:50

How can I avoid this warning in xcode. Here is the code snippet:

如何在xcode中避免这个警告。下面是代码片段:

[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
}];

5 个解决方案

#1


487  

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.

这里自我的捕获与你的内隐属性访问有关。timerDisp -你不能从一个被自己强烈保留的块中引用self或属性。

You can get around this by creating a weak reference to self before accessing timerDisp inside your block:

您可以通过在您的块中访问timerDisp之前创建一个弱引用来解决这个问题:

__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]];
                                            }];

#2


51  

__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:

还有一件非常重要的事情要记住:不要直接在block中使用实例变量,把它用作弱对象的属性,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. Try to control it.

如果vcToGo将被释放,然后这个块被触发,我相信您将会在一个包含vcToGo_变量的垃圾中遇到未识别的选择器。试图控制它。

#3


35  

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时,self仍然存在,这条线将确保它在整个块的执行生命周期中始终存在。

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在使用block和NSNotificationCenter时如何避免问题的优秀文章。


Swift update:

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. We'll be using the [weak self] and guard let features.

当我们调用这个方法时,需要在成功块中使用self。我们将使用[弱自我]和守卫让特性。

    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- styleguide。

#4


14  

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.

你不能从一个将会被自我强烈保留的块中引用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.

这不是真的。只要你在某个时刻打破循环,你就可以这么做。例如,假设您有一个计时器,它有一个可以保留self的块,并且您还可以在self中保持对计时器的强引用。如果你总是知道你会在某个时刻破坏定时器并打破这个循环,这是非常好的。

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). 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.

现在,我碰巧知道,如果clang检测到方法以“set”开头(还有另外一个我在这里没有提到的特殊情况),那么它只会发出这个警告。对我来说,我知道没有的危险保持循环,所以我改变了方法名“useY:“当然,这可能不是适当的在所有情况下,通常需要使用弱引用,但我认为它值得注意解决方案的情况下,帮助他人。

#5


0  

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. Casting self is overkill. 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:

增加2美分提高精度和风格。在大多数情况下,您只会在这个块中使用一个或几个self,很可能只是更新一个滑块。铸造自我是过火了。相反,最好是显式的,只选择在块中真正需要的对象。例如,如果它是UISlider*的实例,例如,_timeSlider,在块声明之前执行以下操作:

UISlider* __weak slider = _timeSlider;

Then just use slider inside the block. 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.

然后在块内使用滑块。从技术上讲,这是更精确的,因为它缩小了潜在的保留周期,只有你需要的对象,而不是所有内部的对象。

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. In the example above, _timeSlider is actually a property stored as a weak reference, e.g:

此外,最可能被转换为弱指针的对象已经是self内部的弱指针,同时最小化或完全消除了保留周期的可能性。在上面的示例中,_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. Declaring SomeType* __weak variable in this order reads more naturally from right to left as: variable is a weak pointer to SomeType.

就编码风格而言,与C和c++一样,变量声明最好从右向左读。在这个顺序中声明SomeType* __weak变量,从右到左读起来更自然:变量是指向某一类型的弱指针。

#1


487  

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.

这里自我的捕获与你的内隐属性访问有关。timerDisp -你不能从一个被自己强烈保留的块中引用self或属性。

You can get around this by creating a weak reference to self before accessing timerDisp inside your block:

您可以通过在您的块中访问timerDisp之前创建一个弱引用来解决这个问题:

__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]];
                                            }];

#2


51  

__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:

还有一件非常重要的事情要记住:不要直接在block中使用实例变量,把它用作弱对象的属性,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. Try to control it.

如果vcToGo将被释放,然后这个块被触发,我相信您将会在一个包含vcToGo_变量的垃圾中遇到未识别的选择器。试图控制它。

#3


35  

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时,self仍然存在,这条线将确保它在整个块的执行生命周期中始终存在。

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在使用block和NSNotificationCenter时如何避免问题的优秀文章。


Swift update:

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. We'll be using the [weak self] and guard let features.

当我们调用这个方法时,需要在成功块中使用self。我们将使用[弱自我]和守卫让特性。

    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- styleguide。

#4


14  

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.

你不能从一个将会被自我强烈保留的块中引用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.

这不是真的。只要你在某个时刻打破循环,你就可以这么做。例如,假设您有一个计时器,它有一个可以保留self的块,并且您还可以在self中保持对计时器的强引用。如果你总是知道你会在某个时刻破坏定时器并打破这个循环,这是非常好的。

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). 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.

现在,我碰巧知道,如果clang检测到方法以“set”开头(还有另外一个我在这里没有提到的特殊情况),那么它只会发出这个警告。对我来说,我知道没有的危险保持循环,所以我改变了方法名“useY:“当然,这可能不是适当的在所有情况下,通常需要使用弱引用,但我认为它值得注意解决方案的情况下,帮助他人。

#5


0  

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. Casting self is overkill. 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:

增加2美分提高精度和风格。在大多数情况下,您只会在这个块中使用一个或几个self,很可能只是更新一个滑块。铸造自我是过火了。相反,最好是显式的,只选择在块中真正需要的对象。例如,如果它是UISlider*的实例,例如,_timeSlider,在块声明之前执行以下操作:

UISlider* __weak slider = _timeSlider;

Then just use slider inside the block. 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.

然后在块内使用滑块。从技术上讲,这是更精确的,因为它缩小了潜在的保留周期,只有你需要的对象,而不是所有内部的对象。

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. In the example above, _timeSlider is actually a property stored as a weak reference, e.g:

此外,最可能被转换为弱指针的对象已经是self内部的弱指针,同时最小化或完全消除了保留周期的可能性。在上面的示例中,_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. Declaring SomeType* __weak variable in this order reads more naturally from right to left as: variable is a weak pointer to SomeType.

就编码风格而言,与C和c++一样,变量声明最好从右向左读。在这个顺序中声明SomeType* __weak变量,从右到左读起来更自然:变量是指向某一类型的弱指针。