简体   繁体   English

如果从Block调用的方法使用self,是否需要使用弱self指针?

[英]Do I need to use a weak self pointer if a method called from a Block uses self?

Using self. 使用self. in blocks causes retain cycles, so I need to create a reference to weakSelf . in块会导致保留周期,因此我需要创建对weakSelf的引用。 I understand this 我明白了

BUT! 但!

If from my block I call a method which uses self ", does this too cause a retain cycle? For instance, if I reload a UITableView from a block and in some of my UITableView delegate methods I call self. , am I causing a retain cycle? Does that mean I have to pass around this weak reference everywhere? Seems hokey. 如果从我的块中调用了一个使用self的方法,是否还会引起保留周期?例​​如,如果我从一个块中重新加载UITableView ,并且在我的某些UITableView委托方法中将其称为self. ,是否导致了保留周期?这是否意味着我必须到处都绕过这个弱引用?

I might be misreading your question, but your wording: 我可能会误解您的问题,但是您的措辞是:

If from my block I call a method which uses "self.", does this too cause a retain cycle? 如果从我的代码块中调用了一个使用“ self。”的方法,这是否还会导致保留周期? For instance if I reload a UITableView from a block and in some of my UITableView delegates I call "self.", I'm causing a retain cycle? 例如,如果我从一个块中重新加载一个UITableView,并且在我的某些UITableView委托中将其称为“自我”,那是否会导致保留周期? That means I have to pass around this weakReference everywhere? 这意味着我必须到处绕过这个弱引用?

suggests you are misunderstanding what self is. 暗示您误解了self是什么。 Hopefully if so the following will help and not hinder your understanding... 希望如果是这样,以下内容将对您有所帮助,而不会妨碍您的理解...

What is self ? 什么是self

The identifier self is just the name of one of the parameters to your method, it is just passed implicitly rather then explicitly like the other parameters. 标识符self只是方法中参数之一的名称,它只是隐式地传递,而不是像其他参数一样显式地传递。 For example if you have a class: 例如,如果您有一个课程:

@implementation MyClass

- (void) someMethod:(NSInteger)value
{
   ... self ... value
}

@end

then the method is effectively (ie bending the facts just a little for clarity): 那么该方法是有效的 (即,为了清楚起见,将事实稍微弯曲一点):

- (void) someMethod:(NSInteger)value withObject:(MyClass *)self
{
   ... self ... value
}

When an instance method is called the value passed for the self parameter is a reference to the instance the method should operate on, eg the call 调用实例方法时,为self参数传递的值是对该方法应在其上进行操作的实例的引用,例如,调用

MyClass *someInstance = ...

[someInstance someMethod:42];

is effectively a call to: 实际上是呼吁:

someMethod:42 withObject:someInstance

Strong reference cycles 强大的参考周期

An object - which includes both instances of classes and blocks - is kept alive as long as there exists a strong reference to the object. 只要存在对对象的强烈引用,对象(包括类实例块实例)都将保持活动状态。

If an object A holds a strong references, eg in an instance variable or property, to an object B , then B will be kept alive at least (there could be other strong references to B ) as long as A is alive. 如果一个对象A持有强引用,例如,在一个实例变量或特性,一个对象B ,然后B将保持存活至少 (有可能是对其他强引用B ),只要A是活的。

If an object A holds a strong reference to B and B holds one to A then you have a strong reference cycle - each object is keeping the other alive and neither will ever be collected. 如果对象A拥有对B的强引用,而对象B拥有对A强引用,那么您将拥有一个强大的参考周期 -每个对象都使另一个对象保持活动状态,并且永远不会被收集。 This could lead to a memory leak - where unused memory is never collected - unless both A and B are meant to live from creation till the end of the program. 可能会导致内存泄漏-永远不会收集未使用的内存-除非AB都应该从创建到程序结束一直存在。

Further, you do not create a strong reference cycle simply by having references stored in the local variables and parameters of methods. 此外,您不能仅仅通过将引用存储在方法的局部变量和参数中来创建强大的引用周期。 By their nature such variables, and therefore their contents, are transient and are destroyed when the method returns. 从本质上讲,此类变量及其内容是瞬态的,并在方法返回时被销毁。

Using self in blocks 在块中使用self

using "self." 使用“自我”。 in blocks causes retain cycles so I need to create a reference to weakSelf. 块中会导致保留周期,因此我需要创建对weakSelf的引用。

Not quite. 不完全的。 When you use self in a block, either directly or indirectly by referencing an instance variable, then the compiler will warn you that you may create a reference cycle. 当您通过引用实例变量直接或间接在块中使用self时,编译器将警告您可能会创建一个引用循环。 ( Note : There are other ways to create reference cycles, both with and without using blocks, and the compiler will not warn you at all. Managing cycles is just something you need to be aware of.) 注意 :有其他使用或不使用块来创建参考周期的方法,编译器根本不会警告您。管理周期只是您需要注意的事情。)

You will only actually create a cycle if you store a reference to the block in the object referenced by self . 如果将对块的引用存储在self引用的对象中,则实际上只会创建一个循环。 However this is not bad in itself, as long as at some point you break the cycle manually - say by storing nil in the variable referencing the block - the cycle need not be problematic at all. 但是,这本身并不坏,只要您在某个时候手动中断循环(例如,通过将nil存储在引用该块的变量中),该循环根本就不会有问题。

Finally... 最后...

You have nothing per se to worry about with your: 本身无需担心:

UITableView delegates I call "self." 我将UITableView委托称为“自我”。

as that self is just a local parameter to the delegate whose initial value, at some point going back up the call chain, came from you evaluating your weakSelf reference and determining that it was not nil and then calling methods on it. 因为该self只是该委托的局部参数,因此其初始值(在某些时候返回调用链)来自于您评估的weakSelf引用并确定其不是nil ,然后在其上调用方法。

HTH 高温超导

First of all: self does NOT cause a retain cycle. 首先: self 不会引起保留周期。 This is an urban legend. 这是一个城市传奇。 The incorrectness is obvious: A single reference can never cause a cycle. 错误是显而易见的:单个引用永远不会导致循环。 The usage of self inside a block causes a retain cycle, if the block is directly or indirectly referred by self , too, for example via a property. 的使用self一个块内引起保留周期,如果该块是直接或间接地称为由self通过属性,也为例子。

To your Q: 给你的问:

If you "call" a method inside the block, the message probably has the receiver self , so you have a usage of self inside the block. 如果您在块内“调用”方法,则消息可能具有接收者self ,因此在块内您会使用self For this reason it is captured and retained. 出于这个原因,它被捕获和保留。

If you really have no usage of self inside the block by neither using self nor using a property of self you do not have a usage of self and it is not captured, therefore not retained. 如果你真的有没有使用self由块内既没有使用self也不使用的属性self不必的使用self并且不拍摄,因此不会保留。 But in this case you can have a dangling pointer or a nil'd reference. 但是在这种情况下,您可以使用一个悬空指针或一个零引用。

You don't need to worry about references while the block is executing - eventually it finishes doing whatever it does, and all these references go away. 您无需担心块执行时的引用-最终它完成了所要做的所有工作,所有这些引用都消失了。

What you need to worry about are the references that are captured when the block is created. 您需要担心的是创建块时捕获的引用。 These references stay until the block goes away. 这些引用将一直保留到该块消失为止。 So if your block has a reference to "self", that reference is there just because the block exists. 因此,如果您的块引用了“ self”,那么该引用就存在,只是因为该块存在。 And if you store that block in a property of self, you have a cycle. 而且,如果将该块存储在self的属性中,则将有一个循环。

So if you store a block as a property in self, then the block shouldn't capture self. 因此,如果您将一个块作为属性存储在self中,则该块不应捕获self。 That's easily done by letting it access and capture a weak copy of self. 通过让它访问并捕获自身的薄弱副本很容易做到。 Remember that when the block is executing, the weak copy of self may be nil. 请记住,在执行块时,自身的弱副本可能为零。 Which means the self object has already left our world, and your block might not need to do anything. 这意味着自我对象已经离开了我们的世界,您的障碍物可能不需要执行任何操作。

Short answer : no, in this situation self is not retained. 简短的回答 :不,在这种情况下自我不被保留。

Long answer 长答案

First of all, retaining self and a reference cycle are not the same thing. 首先,保持自我和参考周期不是同一回事。 Reference cycle is a cycle of strong references between a number of objects: A->B->C->A is a retain cycle. 引用循环是多个对象之间强引用的循环:A-> B-> C-> A是保留循环。

The general idea is, you want to always guarantee that if you are referencing self in a block, you don't reference this block strongly, and don't reference it through a chain of strong references. 通常的想法是,您要始终保证,如果您在块中引用self,则不会强烈引用此块,也不要通过一系列强引用来引用它。 In reality, retain cycles can be used purposefully if you are making sure you're breaking the retain cycle under certain conditions. 实际上,如果您确定要在某些条件下打破保留周期,则可以有目的地使用保留周期。 Not that I personally recommend this. 并不是我个人建议这样做。

Take a look at documentation on Apple's website . 看看Apple网站上的文档 It clearly states that values are captured in blocks, and capturing an object reference retains this object in block. 它清楚地指出,值是在块中捕获的,而捕获对象引用则将该对象保留在块中。

Basically what this means is that referencing an object in a block increments its retainCount by 1, and when this block gets deallocated, retainCount is decremented by 1. 基本上,这意味着引用一个块中的对象会将其keepCount递增1,并且当该块被释放时,retainCount递减1。

However, when using a __weak pointer in a block, the retain count is untouched. 但是,在块中使用__weak指针时,保留计数不变。

Here's an example: 这是一个例子:

- (void) doSomething {
   NSLog(@"%@", self);
}

- (void) callBlock {
   __weak typeof(self) weakSelf = self;
   dispatch_block_t block = ^{
      [weakSelf doSomething];
   };
}

When you write [obj method:params] this actually translates into following call: objc_msgSend(obj, @selector(method:), params) . 当您编写[obj method:params]它实际上转换为以下调用: objc_msgSend(obj, @selector(method:), params) One of the features of Objective-C is that if you call a method on nil pointer, it returns nil. Objective-C的功能之一是,如果在nil指针上调用一个方法,它将返回nil。 This is guaranteed by the fact that objc_msgSend(nil, @selector(anyselector), ...) always return nil. objc_msgSend(nil, @selector(anyselector), ...)始终返回nil的事实保证了这一点。 Note that SEL is just a const char[] under the covers, so it doesn't affect retain counts by any means. 请注意,SEL只是底层的const char [],因此它不会以任何方式影响保留计数。

Hence when the block will be executed, if your object was deallocated, the weak weakSelf variable will be nullified, and the block's body will translate into objc_msgSending to zero, which does nothing except of wasting few CPU cycles. 因此,当执行该块时,如果释放了对象,则弱的weakSelf变量将被无效,并且该块的主体将转换为objc_msgSending为零,除了浪费很少的CPU周期外,它什么也不做。

To sum it up, the Objective-C messaging system is implemented in such a way that calling a method does not retain this object or this method or this method's implementation, as it's a simple function call. 综上所述,Objective-C消息传递系统的实现方式是,调用方法不会保留此对象,该方法或该方法的实现,因为这是一个简单的函数调用。

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM