0

I'm trying to figure out the weakself/strongself reference for nested blocks

What are the difference between:

__weak __typeof__(self) weakSelf = self;

[self.networkCall_1 completionHandler:^(id response) {
    if(response) {
        [weakSelf.networkCall_2 completionHandler:^(id response2) {

        }];
    }
}];

and:

__weak __typeof__(self) weakSelf = self;

[self.networkCall_1 completionHandler:^(id response) {
    __typeof__(self) strongSelf = weakSelf;
    if(response) {
        [strongSelf.networkCall_2 completionHandler:^(id response2) {

        }];
    }
}];

My understanding is that if we're not using strongSelf within the inner block, the self might gets deallocated, thus networkCall_2 might be nil, and never called. If that's the case, if I don't care if networkCall_2 finishes, I can just use weakSelf ? Also, what happens if networkCall_2 is running, and self gets deallocated? Does it finish or terminate the call? Thank you!

rmaddy
  • 314,917
  • 42
  • 532
  • 579
ordinaryman09
  • 2,761
  • 4
  • 26
  • 32

1 Answers1

2

(@trungduc was close to an answer but unfortunately has decided to delete it. So now as I may have inadvertently helped lose you your only answer to date I'll see if I can help you out.)

This answer only applies when using ARC

What are the difference between:

The probably surprising answer is that in this particular example not a lot...

When Objective-C makes a method call on an object it ensures that the object is alive across the call. So in the call:

[weakSelf.networkCall_2 completionHandler:^(id response2) {...}];

which is shorthand (using dot notation) for:

[[weakSelf networkCall_2] completionHandler:^(id response2) {...}];

First weakSelf is loaded and a strong reference held to the result, call this reference A. Then the property (method) networkCall_2 is called on A and a strong reference held to its result, call this B. At this point the compiler is free to drop the strong reference A as it is not used passed this point. Finally the call to the method completionHandler: on B is called. After that returns, which may be before the passed completion block has been invoked, the compiler is free to drop the strong reference B.

If A or B is nil above then the calls on them simply return nil and nothing much happens.

Note: your code is a little unusual, more common might be something like:

[weakSelf networkCall_2:<some argument> completionHandler:^(id response2) {...}];

that is networkCall_2:completionHandler: is a method on the object referenced by weakSelf. If this is what your actual code looks like then the above still applies and the compiler will hold a strong reference across the call to whatever weakSelf references.

Turning now to:

__typeof__(self) strongSelf = weakSelf;
if(response) {
    [strongSelf.networkCall_2 completionHandler:^(id response2) {...}];

The compiler first loads weakSelf and holds a strong reference to it in strongSelf. Then the property (method) networkCall_2 is called on strongSelf and a strong reference held to its result, call this B. At this point the compiler is free to drop the strong reference strongSelf as it is not used passed this point. Etc.

(Note: "free to drop" in both cases above does not mean the compiler will immediately drop it, it may defer that until the end of the if or block.)

Notice the similarity in the two descriptions? In this particular example there is really no difference between using weakSelf and strongSelf. So why does some code use the strongSelf pattern? Consider:

__typeof__(self) strongSelf = weakSelf;
if (strongSelf) // object still exists)
{
   // *all* three methods will be called
   [strongSelf method1];
   [strongSelf method2];
   [strongSelf method3];
}

vs:

[weakSelf method1]; // call method1 if weakSelf is not nil
[weakSelf method2]; // call method2 if weakSelf is *still* not nil
[weakSelf method3]; // call method3 if weakSelf is *still* not nil

Using the strongSelf pattern above ensures that either 0 (if weakSelf is nil) or 3 method calls are made. If the weakSelf pattern is used 0, 1, 2 or 3 methods may be called.

Your particular example has the same result either way as there is only one use of strongSelf/weakSelf, in the above the result can differ as there are multiple uses of strongSelf/weakSelf.

Which leaves us with your question:

Also, what happens if networkCall_2 is running, and self gets deallocated? Does it finish or terminate the call?

This question reads as though networkCall_2 is a method not a property, see the note above, we'll cover both cases:

  1. If here you are referring to the self of the method networkCall_2 then as covered above Objective-C will keep a strong reference across the call to any object a method is invoked on, so self cannot be deallocated during a call. So an active call is never terminated due to its self disappearing.

  2. If your networkCall_2 is indeed a property as shown then the object referenced by weakSelf (the A above) will not be deallocated across the property call, as in (1). However when then calling the completionHandler: method on whatever object the property call returned (the B above) then A could be deallocated across that call (unless that call holds a strong reference to A by other means).

Hope I understood your question correctly and if so I think your answer boils down to knowing:

An object on which a method (or property) is called will not be deallocated during that method (or property) call.

HTH

Community
  • 1
  • 1
CRD
  • 52,522
  • 5
  • 70
  • 86
  • Sorry for deleting my answer. I'm confused with your questions and 40k reputations. It makes me not sure about my knowledge. Luckily, now we have a better answer. Thanks for the answer. I'm learned a lot from it. – trungduc Dec 14 '17 at 09:41
  • @trungduc - no apology required and it's nice to know this answer helped you as well. – CRD Dec 14 '17 at 09:48
  • I have one question. Do we have any official document about *An object on which a method (or property) is called will not be deallocated during that method (or property) call* ? I tried but i can't find it. – trungduc Dec 14 '17 at 09:58
  • @trungduc – One of ARC's roles is to ensure that referenced objects that are required stay around. In a method call the passed arguments are required (ignoring any special cases), and the object a method is called on is an implicit argument to the method – it is what is assigned to the implicit parameter `self`. *[cont in next comment]* – CRD Dec 14 '17 at 19:19
  • For the uses of `weakSelf` above we can further turn to the [*4.2 Semantics*](http://clang.llvm.org/docs/AutomaticReferenceCounting.html#semantics) of the Clang ARC does where we find under *Reading*, 1st bullet: "For `__weak` objects, the current pointee is retained and then released at the end of the current full-expression." So `weakSelf` is loaded and retained across the call ("full-expression"). This restriction to the scope of the retain is why `[weakSelf method1]; ... [weakSelf method3]` above does three seperate load/retain/releases, and the need for the `strongSelf` pattern. HTH – CRD Dec 14 '17 at 19:23
  • "When Objective-C makes a method call on an object it ensures that the object is retained across the call" is not true in general. But it is true when making a call on a weak reference. – newacct Jan 15 '18 at 21:46
  • "In a method call the passed arguments are required (ignoring any special cases), and the object a method is called on is an implicit argument to the method – it is what is assigned to the implicit parameter self." As a special rule, ARC does not retain `self`, unlike other parameters which it does retain. – newacct Jan 15 '18 at 21:47
  • @newacct - So just to clarify your two comments, are you saying that ARC ensures all parameters **except** a non-weak `self` are alive across a call, and this exception is due to a special rule in ARC? (Note I used "are alive" and not "retained" on purpose to clarify what you are stating the overall semantics are.) [I think you might be referring to 7.3 as the "special rule" which says "Since it’s extremely uncommon to actually do so, even unintentionally" which I'd say wasn't worth mentioning here, but maybe you disagree.] – CRD Jan 15 '18 at 22:22
  • @CRD: yes . . . – newacct Jan 17 '18 at 03:17
  • @newacct - If that is the case then I am going to disagree with you. I cannot see how explaining that Apple "chose to make this optimizing assumption and shift some amount of risk to the user", i.e. that ARC is in fact **not** safe in rare circumstances due to a little heralded optimisation which requires *manual memory management* by the programmer, would enhance rather then confuse the readers of this Q&A. You are of course free to disagree and explain to readers the precise semantics of (semi-)automatic reference counting by adding your own answer and I encourage you to do so! – CRD Jan 17 '18 at 09:51
  • @CRD: You said that when you make a method call on an object, the object is retained across the call. That is actually not true; nothing in ARC provides for that. There is a special rule when making a method call on a weak reference that retains it for the call, but not for making a method call on a strong reference. Parameters passed to a method are retained inside the method (not by the caller) if the method was compiled in ARC, but the `self` parameter is not retained inside the method even if it is compiled in ARC. – newacct Jan 20 '18 at 17:32
  • @newacct - I see, you've read "retained" as "`retain`'ed" which wasn't the intention. I try to be careful with word choice to avoid such issues but failed here, *mea culpa*. I've changed it to "alive". There is no implication here that ARC must, or will, perform any specific memory management operation at this point; what it will do (ignoring any special ownership annotations) is analyse the code to ensure that what needs to be kept alive is, and achieve that (ignoring the 7.3 case) by performing memory management operations at points it determines they are required to meet that requirement. – CRD Jan 20 '18 at 23:35
  • @CRD: ARC actually does guarantee it will perform specific memory management operations. The semantics section of the ARC spec specifies that, for example, assigning to a `__strong` variable retains the new value and releases the old value. A value that is stored in a `__strong` variable is guaranteed to be retained until something else is assigned to the variable or the variable's lifetime ends. This is how it guarantees that strong references keep their pointees "alive"; but `self` is not retained so `self` is not guaranteed to be kept "alive" during a call. – newacct Jan 21 '18 at 04:50
  • @newacct - Of course ARC makes certain guarantees about when certain things happen, and in other places it is less specific, and I didn't say otherwise, I'm just not specifying those points in this answer as that detail is not important the resultant aliveness is. As to *guarantees* rather than *asumptions* over `self` we're back to 7.3 and as above I don't think delving into that in the context of this Q&A would be helpful, especially as it concerns `weak`. You are free to add an answer that explains how ARC is unsafe and how you believe that impacts the situation in this Q&A, go for it! – CRD Jan 21 '18 at 05:17
  • @CRD , thank you for your answers. It clarifies some of my confusion. Now I wonder suppose we have instead of [self networkCall1], we have [[APIManager] networkCall1:param1 completion:^(id response) { self.responseId = response.objectId; }. Do we need to use strongSelf here or not? I'm a bit confused on where to use strongSelf vs just self. Thanks! – ordinaryman09 Apr 12 '18 at 00:56
  • @ordinaryman09 - That is a different question: should a block capture a reference strongly or weakly. Two main issues effect the answer (i) reference cycles and (ii) whether the block should ensure captured references stay alive/gracefully handle objects no longer alive. Search SO and you should find answers to these. If you've still got a question after doing this ask a new one - but make sure you note which other Q&A's you've read and why they don't help you or your new question is likely to be closed as a duplicate. – CRD Apr 12 '18 at 22:42