weak strong

Discussion in 'iOS Programming' started by grandM, Jan 18, 2019.

  1. grandM macrumors 65816

    grandM

    Joined:
    Oct 14, 2013
    #1
    Hi guys

    I'm going through a book on closures and I have a question about it.

    Code:
    extension Editor {
    func editTutorial(_ tutorial: Tutorial) {
    
        queue.async() {
    
          [weak self] in
    
          guard let self = self else {
    
            print("I no longer exist so no feedback for you!")
    
            return
    
          }
    
          DispatchQueue.main.async {
    
            print(self.feedback(for: tutorial))
    
          }
    
        }
      }
    }
    
    This author tries to explain the need for using the weak keyword. Doing so he states '‘If self is not nil it will capture a strong reference and make sure it sticks around until the print statement finishes'.

    I am a bit confused now. This code is used to make sure that the object still exists to perform the feedback, and hence the print. This would imply that self not being nil no longer becomes a weak self but a strong one through the guard statement.

    If that to be the case then the closure now points strongly to the Editor object. The Editor object points strong to the closure. So how can the Editor object still be deallocated? Or is a closure deallocated the moment it reached the end of its statements? Because I thought this was not the case. Moreover the Editor object points strongly to it?

    Thanks for your help. I'm hurting my head over this.
     
  2. PhoneyDeveloper macrumors 68040

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #2
    Do you know what a retain cycle is? Declaring the closure to have a weak capture of self prevents a retain cycle. If self has been deinited already when the closure is executed then the closure simply exits.
     
  3. grandM thread starter macrumors 65816

    grandM

    Joined:
    Oct 14, 2013
    #3
    Yes I know what a retain cycle is. The weak keyword breaks the cycle. It is used so that if the instance is to be deallocated the self value in the closure no longer will prohibit self being deallocated.

    However in this example the author first looks if self is not nil the moment the asynchronous method lifts off. If it is not nil the author wants to make sure that self is not deallocated until the feedback(for:) method has finished its task. So he uses the guard statement to create a new strong reference to self, although the assigned self was a weak reference.

    This confuses me for two reasons
    1 I did not know that a weak reference can be turned into a strong one using guard
    2 If the closure at that moment has a strong reference to self, it seems self no longer can be deallocated because once more we have a retain cycle?

    Or is a copy of self stored in the closure instead of a reference to self using the capture list? In that case self could be deallocated entering the closure because a copy of self would be present in the closure and not a pointer to the self object? But then that would be a call-by-value copy. In that case changes to self no longer would be taken into account executing the closure?
     
  4. PhoneyDeveloper, Jan 19, 2019
    Last edited: Jan 19, 2019

    PhoneyDeveloper macrumors 68040

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #4
    This is apparently a swift 4.2 feature. That code wouldn't compile in older versions of swift, I believe. It was typically written like:

    Code:
    guard let strongSelf = self else { return }
    strongSelf.doSomething()
    
    but the use of strongSelf everywhere is also ugly. I think the meaning of the guard statement in your example is the same as in the strongSelf example. It just unwraps the variable. It doesn't do any other magic.

    This is the proposal

    https://github.com/apple/swift-evol...sals/0079-upgrade-self-from-weak-to-strong.md
     
  5. grandM thread starter macrumors 65816

    grandM

    Joined:
    Oct 14, 2013
    #5
    Superb link!

    1 It does transform a weak reference into a strong one. So it does more than unwrapping. Or is weak an Optional wrapping a strong counterpart?
    2 When all the statements in the closure are finished are the captured and other variables set to nil, free to be removed from memory?

    Thanks once more
     
  6. Mascots macrumors 68000

    Mascots

    Joined:
    Sep 5, 2009
    #6
    When you unwrap any optional that represents a weak reference, you are given a strong reference and the reference count is increased - the weak reference itself is still available and is not modified. You are asking if the object is available and then provided strong reference as a promise by the system that the object will be kept around since you are accessing it in a way which a deallocation would cause a crash that could never be anticipated or caught.

    This strong reference is, however, temporary. Because it was created within the closure and not captured by the closure, the reference count will be decremented when the scope is completed.
     
  7. grandM thread starter macrumors 65816

    grandM

    Joined:
    Oct 14, 2013
    #7
    1 Do I understand you correctly that the weak self is always an Optional? If so is an unowned self an implicitly unwrapped Optional?

    2 The async defines a closure. Does this closure stick around indefinitely or is it removed once the function ends its statements?

    3 This out of scope thing always confuses me. Is this the same as stating that the statements of the closure are all executed and you leave the closure. Hence the variables of the closure are no longer needed and can be removed from memory when iOS needs the memory? This would of course imply that the async closure ceases to exist having executed all its statements?

    4 I get the impression you imply that the closure sticks around because you refer to the capture list capturing that weak reference to self. In that case it seems a bit strange that the strong reference would be removed?

    Thanks for your answer.
     
  8. PhoneyDeveloper macrumors 68040

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #8
    Yes. Yes.

    It's up to the semantics of the queue.async method to decide the lifetime of the closure. For something like DispatchQueue.main.async the lifetime is one execution of the closure.

    It's not really different than any function scope. The function executes. Then it's done. No traces of it remain. A retain cycle can change that.

    Again, only for the life of the closure.

    Thanks for your answer.
     

Share This Page