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

后端 未结 4 672
悲&欢浪女
悲&欢浪女 2021-01-15 14:46

Using self. in blocks causes retain cycles, so I need to create a reference to weakSelf. I understand this

BUT!

If from my block I

相关标签:
4条回答
  • 2021-01-15 15:19

    First of all: self does NOT cause a retain cycle. 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.

    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. 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. But in this case you can have a dangling pointer or a nil'd reference.

    0 讨论(0)
  • 2021-01-15 15:26

    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? 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? That means I have to pass around this weakReference everywhere?

    suggests you are misunderstanding what self is. Hopefully if so the following will help and not hinder your understanding...

    What is 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. For example if you have a class:

    @implementation MyClass
    
    - (void) someMethod:(NSInteger)value
    {
       ... self ... value
    }
    
    @end
    

    then the method is effectively (i.e. 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, e.g. the call

    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, e.g. 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.

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

    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

    using "self." in blocks causes retain cycles so I need to create a reference to 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. (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. 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.

    Finally...

    You have nothing per se to worry about with your:

    UITableView delegates I call "self."

    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.

    HTH

    0 讨论(0)
  • 2021-01-15 15:35

    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. And if you store that block in a property of self, you have a cycle.

    So if you store a block as a property in self, then the block shouldn't capture 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.

    0 讨论(0)
  • 2021-01-15 15:41

    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.

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

    However, when using a __weak pointer in a block, the retain count is untouched.

    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). One of the features of Objective-C is that if you call a method on nil pointer, it returns nil. This is guaranteed by the fact that objc_msgSend(nil, @selector(anyselector), ...) always return nil. Note that SEL is just a const char[] under the covers, so it doesn't affect retain counts by any means.

    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.

    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.

    0 讨论(0)
提交回复
热议问题