1

If I weakly reference self in a closure:

{[weak self] in self!.doSomethinmg()}

And self is deallocated, yet the closure continues to exist. Is it possible that self in the closure can become non-nil at some point in the future -- pointing to some random new object as determined by the runtime environment?

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
shoe
  • 952
  • 1
  • 20
  • 44
  • 3
    You should not force-unwrap self in the first place. – Martin R Feb 01 '18 at 09:18
  • @MartinR Yes, but if a weak reference can end up pointing to a different object entirely, then it doesn't even matter if you check if it's non-nil, because it will be non-nil, but also not what it was intended to be. – shoe Feb 01 '18 at 09:20
  • The weak reference is set to `nil` when the pointed-to object is deallocated. – Martin R Feb 01 '18 at 09:33
  • @shoe for example if you are calling webservice using alamofire. before you get the response. you dismiss \ pop view controller . results your `self` will be `nil` and you are doing some UI setup like setting labels, or tables then your app will crash. using weak / unowned self you make sure about proper deallocation of object as well as be safer for such a crash – Prashant Tukadiya Feb 01 '18 at 09:48

3 Answers3

5

A pointer is a variable whose value is a memory address. The same is true for a weak pointer, but the value of a weak pointer is (due to some runtime magic) set to nil if the pointed-to object is deallocated. It is irrelevant if any new object is created at the same memory location later, the weak pointer variable stays nil.

So

{ [weak self] in /* ... */ }

creates a closure with a weak pointer variable. That variable is set to nil when the pointed-to object is deallocated. That may happen before the closure is called or during the execution of the closure, so even

{ [weak self] in
   if (self != nil) self!.doSomethinmg()
}

is unsafe and can crash if the object is deallocated between the test and the method call.

But at any point, the weak pointer either points to the original object instance or is nil. It will never point to some other object instance. (That may happen with unowned references.)

As Lorenzo says, don't force unwrap the weak pointer. Either use optional chaining or create a strong reference inside the closure.

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
1

It depends on what you want to achieve.

Martin is suggesting the right thing. In order to guarantee you don't have any crash and continue to deal with that instance you can use weak/strong dance. Otherwise, use self?.

So for example, with the weak/strong dance you can do the following:

{ [weak self] in 
    if let strongSelf = self {
        strongSelf.doSomething()
    }
}

Otherwise

{ [weak self] in 
    self?.doSomething()
}

While in the first case the instance that belongs to self will continue to exist until the closure will be discarded, in the second case, instead, since the instance will be put to nil, it will behave as a sort of non-op.

Lorenzo B
  • 33,216
  • 24
  • 116
  • 190
  • 1
    I don't think that there is any difference between the two versions. – Martin R Feb 01 '18 at 09:35
  • Compare https://stackoverflow.com/questions/45327236/is-it-safe-to-force-unwrap-variables-that-have-been-optionally-accessed-in-the-s, in particular @dfri's answer. If `self` is not nil then `self?.doSomething()` keeps the object alive during the method call. – Martin R Feb 01 '18 at 09:37
  • Agree with @MartinR No difference between two blocks. it is clear example of optional chaining . method will only been executed if self is non nil – Prashant Tukadiya Feb 01 '18 at 09:45
0

If all the strong references to an instance are removed, then the instance will eventually be deallocated. But that doesn't necessarily mean the references that were referring to it will be reused. All the strong references are obviously gone because they had to be in order for it to be deallocated in the first place. So whatever that means for the memory that those references required for their referring duties is irrelevant to the application, because the references don't exist anywhere in it anymore.

Which leaves unowned and weak references left. Apple makes it pretty clear that attempting to access the value of an unowned reference after the instance has been deallocated is a no, no:

If you try to access the value of an unowned reference after that instance has been deallocated, you’ll get a runtime error.

So that is also irrelevant to the application, simply because it's a hard-rule not to be broken.

Which leaves, last but not least, weak references. Apple says this about weak references:

Because a weak reference does not keep a strong hold on the instance it refers to, it’s possible for that instance to be deallocated while the weak reference is still referring to it. Therefore, ARC automatically sets a weak reference to nil when the instance that it refers to is deallocated.

...

You can check for the existence of a value in the weak reference, just like any other optional value, and you will never end up with a reference to an invalid instance that no longer exists.

The key take away here is that the reference is set to nil. And even though nil represents nothing, it's still a valid value. And I feel it may be safe to assume that the runtime environment won't steal a reference and use it for another purpose when it's still pointing to a valid value. Imagine what it'd be like otherwise.

So the relevance to the application here for weak references is just that they may become nil at some point; and as @MartinR showed in his answer, this may even happen mid-execution of the closure. So the final solution would seem to be creating a strong reference from the weak reference before using it, aka the "weak/strong dance":

Either:

{ [weak self] in 
    if let strongSelf = self {
        strongSelf.doSomething()
    }
}

or

{ [weak self] in 
    guard let strongSelf = self else {   return   }
    strongSelf.doSomething()
}

If the closure consists of a single optional chain:

{ [weak self] in 
    self?.doSomething()
}

will simply result in a non-op if self is nil, but no idea if this bears any guarantee that self won't be deallocated mid-execution of the closure if it's arbitrarily longer:

{ [weak self] in 
    self?.doSomething()
    ...
    self?.doSomethingDownTheLine()
}

So in this case, doing the weak/strong dance will guarantee that your closure will be all-or-nothing:

{ [weak self] in 
    guard let strongSelf = self else {   return   }
    strongSelf.doSomething()
    ...
    strongSelf.doSomethingDownTheLine()
}
shoe
  • 952
  • 1
  • 20
  • 44