1

I have a function similar to the following

class Bar {
  deinit {
    print("Do some cleanup")
  }
}

func foo() -> Bar {
  return Bar()
}

The scope of Bar is clear when calling it like this:

func useFoo() {
    let bar = foo()
    runFunctionA()
    // bar goes out of scope: "Do some cleanup" is printed
}

However, what happens when the return value is ignored, will it go immediately out of scope?

func useFoo() {
    let _ = foo()
    // "Do some cleanup" is printed here?
    runFunctionA()
    // "Do some cleanup" is printed here?
}

Also, does it make a difference if let _ = foo() is used or only _ = foo()?

Hamish
  • 78,605
  • 19
  • 187
  • 280
Jan Rüegg
  • 9,587
  • 8
  • 63
  • 105
  • 2
    I believe the lifetime of the `Bar` instance is unspecified in both cases (it could be deallocated before or after `runFunctionA()`). In particular, in the first case, the optimiser is free to notice that `bar` isn't used and deallocate the instance before `runFunctionA()`. This is why we have the `withExtendedLifetime(_:_:)` function – in order to guarantee a given value remains alive for the duration of the closure call. – Hamish Nov 23 '17 at 10:07

1 Answers1

1

In second case, there is no one taking the ownership of returned object and hence ARC releases the object immediately. So, you must see Do some cleanup, before runFunctionA() call.

Also, let _ = foo() is similar to _ = foo

Now, you must be thinking what crap I am writing when you must be looking at different results in your playground.

The thing is, playgrounds are not meant for checking memory related code. Refer this.

If you don't trust me, just check your code in an actual project. I sure did.

Bar:

class Bar {
    deinit {
        print("Do some cleanup")
    }

    class func foo() -> Bar {
        return Bar()
    }
}

ViewController:

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        useFoo()
        useFooAgain()
    }
    func useFoo() {
        let bar = Bar.foo()
        print("inside useFoo")
    }
    func useFooAgain() {
        let _ = Bar.foo()
        print("inside useFooAgain")
    }
}

Output:
inside useFoo
Do some cleanup
Do some cleanup
inside useFooAgain

Puneet Sharma
  • 9,369
  • 1
  • 27
  • 33
  • 1
    Reading my mind with your comment about playgrounds ;) Thanks for the answer! Do you know if this is documented/specified somewhere, or if this is up to the compiler (in the second case, I guess the first case is clear)? – Jan Rüegg Nov 23 '17 at 10:04
  • 1
    @JanRüegg: I was checking for the official statement, but have not found so far. If I remember correctly, in earlier versions of swift , there was only let _ = available and they added only _ later. It is not making any difference for deinit call though (It is definitely acting weird in playground). Let us dig deeper. If i found something, I will update the answer. – Puneet Sharma Nov 23 '17 at 10:08