4

As we know, using strong self within a block can lead to retain cycles and memory leak. Is the common practice to use weak self in a block, or is it better to assign the weak self to strong within the block and then use it as such so the weak self is not released during block execution? Does it matter since weak self will be zero-ed out anyway?

Boon
  • 40,656
  • 60
  • 209
  • 315

4 Answers4

6

Due to the volatile nature of weak variables, you should use them with care. If you are using weak variables in a multithreading environment, it is considered good practice to assign the weak variable to a strong one and check for nil before using. This will ensure that the object will not be released in the middle of your method, causing unexpected results.

Consider the following case:

__weak id var;

//...

if(var != nil)
{
    //var was released here on another thread and there are not more retaining references.
    [anotherObj performActionWithAnObjThatMustNotBeNil:var]; //<- You may crash here.
}

The compiler can be configured to throw a warning on a consecutive access of a weak variable.

On the other hand, if your use is in the main thread, and all calls to the object are on the main thread, this problem is moot, since the object will either be released before the block call or after, thus it being safe to access the weak variable directly.

Léo Natan
  • 56,823
  • 9
  • 150
  • 195
  • 1
    "This will ensure that the object will not be released during your use" The object cannot be deallocated "during" a use. It can only be deallocated before or after uses. So if you either 1) don't have more than one use of the variable, or 2) don't care about different uses of the variable getting different values, then it is not a problem. – newacct Apr 26 '14 at 03:28
  • 2
    @newacct That's not accurate. When using ARC in your code, it will not release during use. However passing the object to an external library, the object can be released in the middle of a method call, causing trouble. – Léo Natan Apr 26 '14 at 04:58
  • No, it cannot be released in the middle of a method call. It doesn't matters what it is passed to. – newacct Apr 26 '14 at 07:49
  • 2
    @newacct That's not true and I can speak to that from experience. Entering a method does not retain an object. – Léo Natan Apr 26 '14 at 07:57
  • Yes but we are specifically talking about `__weak` variables here. Using a `__weak` variable retains it until the end of the expression. – newacct Apr 26 '14 at 08:00
  • @newacct Again, my experience proves otherwise. – Léo Natan Apr 26 '14 at 09:04
  • In programming, "experience" "proves" little. We have formal specifications that say what is correct. – newacct Apr 27 '14 at 04:10
  • Can you please link to these specs? – Léo Natan Apr 27 '14 at 04:30
  • ARC specification. section 4.2 Semantics "Reading occurs when performing a lvalue-to-rvalue conversion on an object lvalue. For __weak objects, the current pointee is retained and then released at the end of the current full-expression. This must execute atomically with respect to assignments and to the final release of the pointee." http://clang.llvm.org/docs/AutomaticReferenceCounting.html#semantics – newacct Apr 27 '14 at 04:54
  • That looks interesting, thanks. I will update my answer accordingly. Still interesting what we experienced. – Léo Natan Apr 27 '14 at 06:26
  • @newacct It says released at the end of current full-expression. That does not mean end of method, so it's technically still possible for the self reference to become nil somewhere in the execution of the method due to threading. Am I right? – Boon Apr 27 '14 at 09:04
  • @Boon Yes, it can, when you are dealing with multiple threads. It can be released in thread X while being used (in the middle of a method) in thread Y. – Léo Natan Apr 27 '14 at 10:49
  • @Boon: The full expression is at least the method call. An object cannot be deallocated when there is at least one strong reference to it. Retaining it before the method call and releasing it after means there is a strong reference to it for the entire duration of the method call. – newacct Apr 27 '14 at 19:44
  • @newacct Can you elaborate on "retaining it before the method call" in the context of block? Do you need to assign a weak to strong or that's redundant? – Boon Apr 27 '14 at 23:50
  • @Boon: According to the ARC specification, by using a `__weak` variable directly (not assigning it to anything) in an expression (here, method call), the object pointed to by the variable is retained before evaluating the expression, and released after. – newacct Apr 28 '14 at 18:54
  • I have to wonder if there is a bug in the compiler that caused my grievances. – Léo Natan Apr 28 '14 at 18:56
  • @newacct Thank you. How would the ARC specification behave in a multithreaded situation? If the variable can be released at the end of the expression, technically we should still assign it to strong to make it not be released right? – Boon Apr 28 '14 at 19:33
  • @Boon: Only if you care about it not being deallocated in between two uses of the variable. – newacct Apr 29 '14 at 04:16
  • The official documentation is correct, but if you're confused by it and need reassurance by an actual Swift team member, I got a quote from one: http://stackoverflow.com/a/35716299/9636 – Heath Borders Mar 01 '16 at 06:43
  • The exact meaning of "full expression" is critical here. The definition (see C11 6.8) is too long but, for example, `if(x) [y x]` has *two* full expressions, with a sequence point between, and the reference is released between the condition being validated an the call being made. On the other hand, `return x?[y x]:nil` is a single full expression. If you view the disassembly of the former, you'll see `objc_loadWeakRetained` ... `objc_release` ... `objc_loadWeakRetained`, and between the second and third of those the object could be destroyed, so that the third results in `nil`. – Tim Sylvester Nov 11 '20 at 18:05
5

There are two possible questions here that are easy to get confused:

Is it possible for a __weak reference to become nil in the middle of a method?

id __strong strongObject = ...;
id __weak weakObject = strongObject;
dispatch_async(dispatch_get_main_queue(), ^{
  [weakObject method1]; // if weakObject is non-nil here
  [weakObject method2]; // can it become non-nil here?
});

Yes! Xcode will even warn you about it.

Is it possible for self to become nil in the middle of a method if the method is called on a __weak lvalue as below?

id __strong strongObject = ...;
id __weak weakObject = strongObject;
dispatch_async(dispatch_get_main_queue(), ^{
  // is it possible for weakObject to be deallocated
  // while methodUsingSelf is being called?
  [weakObject methodUsingSelf];
});

- (void)methodUsingSelf {
  NSLog(@"%@", self);  // Could this be non-nil
  NSLog(@"%@", self);  // while this is nil?
}

No! Joe Groff, of the Swift team at Apple, said so:

self is guaranteed kept alive by ObjC ARC while a method on self is executing.

Clang's official ARC documentation covers this case in the Semantics/Reading subsection:

Reading occurs when performing a lvalue-to-rvalue conversion on an object lvalue.

For __weak objects, the current pointee is retained and then released at the end of the current full-expression. This must execute atomically with respect to assignments and to the final release of the pointee.

Thus, calling a method on a __weak variable, is roughly equivalent to the following Manual Retain/Release (MRR) code:

id retainedObject = ...;
id assignedObject = strongObject;
dispatch_async(dispatch_get_main_queue(), ^{
  {
    [assignedObject retain];
    [assignedObject methodUsingSelf];
    [assignedObject release];
  }
});    

Of course, in MRR, [assignedObject retain]; might crash because the object assignedObject points to might have been deallocated, so assignedObject might point to garbage. ARC doesn't have this problem because it zeroes weak references.

Community
  • 1
  • 1
Heath Borders
  • 30,998
  • 16
  • 147
  • 256
1

I think that even if using the weak will work and be retained as long as needed, assigning it to strong before using will make it more readable and "worries free"...:

__weak id weakThing = thing;
thing.someBlock = ^{
    if (weakThing) {
        id strongThing = weakThing;
        strongThing doThisWithThat...
    }
};

Compiler won't complain and it is safe and maybe not less importantly - easy to understand for John Doe who will try to read this code tomorrow....

Aviel Gross
  • 9,770
  • 3
  • 52
  • 62
  • 3
    That's almost right although I would do the assignment before the condition. That way there's no chance of the variable being released between the check and the assignment. – Mark Stickley Jun 30 '14 at 10:35
0

You can continue to use the weak self. The only time you'd need to use strong self is if you are trying to access a self->ivar directly, instead of going through a property.

Nick
  • 2,361
  • 16
  • 27
  • That's semiaccurate and indeed the compiler gives a warning when trying to access a weak variable more than once. – Léo Natan Apr 25 '14 at 15:31
  • Using weak will have the issue Leo mentioned, which is that it may be deallocated while you are in the block, no? – Boon Apr 25 '14 at 18:55
  • That is only a risk if you are not keeping a strong reference to self elsewhere. Most of the time, that is not the case. ie. if a UIView is added as a subview, it is safe to access weakSelf because the superview.subviews array is keeping a strong pointer. – Nick Apr 25 '14 at 19:33
  • 2
    Why rely on some *other* object to keep a strong reference for you when you can so easily create your own strong reference and eliminate that non-obvious dependency? The point of a strong reference is to say "I'm using this object and I don't want it to go away." So, if you don't want an object to go away, create a strong reference to it -- don't rely on some other object to do that. It doesn't cost much (if anything) to create your own strong reference. – Caleb Apr 27 '14 at 06:56