Objective-C: Should we use weak self within block or assign weak self to strong before using it?

StackOverflow https://stackoverflow.com/questions/23297049

  •  09-07-2023
  •  | 
  •  

Frage

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?

War es hilfreich?

Lösung

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.

Andere Tipps

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.

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

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.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top