Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing self from instance properties which are closures

Tags:

swift

I'm using Xcode6-beta2, but I've had the same issue since the very first public beta. My Swift subclass of Obj-C UIViewController looks like this:

class SomeVC: UIViewController {     var c1: () -> () = {         println(self)     }      var c2: () -> () {         get {             return { println(self) }         }     }      var c3: () -> () {         return { println(self) }     }      override func viewDidAppear(animated: Bool) {         super.viewDidAppear(animated)         c1()         c2()         c3()     } } 

When the VC is shown, I see the following lines printed out:

(Function) <_TtC12SwiftiOSTest6SomeVC: 0x10bf1ed10> <_TtC12SwiftiOSTest6SomeVC: 0x10bf1ed10> 

(c2 and c3 differ only in that it is not necessary to include the get {...} for a computed property if it is only gettable.)

So, the first closure's self seems to refer to the function/closure type itself, whereas the other ones' self refer to the view controller (as I would expect). The only difference between c1 and c2/c3 is that the former is a stored property, the latter are computed properties, but I would still expect the closures and their captured values be the same, i.e. self to always refer to the enclosing class. The way things are now, there seems to be no obvious way for the c1 closure to access methods / properties of the enclosing class.

Is this something documented somewhere (I read the Swift book and didn't find anything), or is it just a beta-compiler bug of some kind, which should be filed somewhere?

like image 308
wujek Avatar asked Jun 19 '14 11:06

wujek


People also ask

How do closures capture references to variables by default?

In Swift, closures capture the variables they reference: variables declared outside of the closure but that you use inside the closure are retained by the closure by default, to ensure they are still alive when the closure is executed.

What are Swift closures?

Closures are self-contained blocks of functionality that can be passed around and used in your code. Closures in Swift are similar to blocks in C and Objective-C and to lambdas in other programming languages.

What could be the alternative to closure?

Webpack, Babel, UglifyJS, and TypeScript are the most popular alternatives and competitors to Closure Compiler.


1 Answers

This looks interesting, so I dig little deeper. Found that, you can access the class instance variables within the closure like self.instanceVariable. Then the closure will capture the self within it. So now the self refers to the class instance itself. Your closure should be a lazy property.

A lazy property means that you can refer to self within the default closure, because the lazy property will not be accessed until after initialization has been completed and self is known to exist.

You are missing @lazy so that self is unknown to the closure thats why it is printing it as (Function) my guess.

class TableViewController: UIViewController { var name = "anil" // Since swift 2.0 came out @lazy is replaced by lazy lazy  var c1: () -> () = {     println(self)     println(self.name)  }  var c2: () -> () { get {     return { println(self) } } }  var c3: () -> () { return { println(self) } }     override func viewDidLoad() {         super.viewDidLoad()         c1()         c2()         c3()         } } 

Output

<_TtC12TableViewApp19TableViewController: 0x10d54e000>
anil
<_TtC12TableViewApp19TableViewController: 0x10d54e000> <_TtC12TableViewApp19TableViewController: 0x10d54e000>


Update

Assigning closure to a class instance variable results strong reference cycle. You should avoid this. Swift uses Capture list for that

If you assign a closure to a property of a class instance, and the closure captures that instance by referring to the instance or its members, you will create a strong reference cycle between the closure and the instance. Swift uses capture lists to break these strong reference cycles. For more information, see Strong Reference Cycles for Closures.

So the correct usage of closure could be

@lazy  var c1: () -> () = {     [unowned self] in     println(self)     println(self.name)  } 

Reference: Swift programming guide

Edit
@lazy has been changed to lazy

like image 102
Anil Varghese Avatar answered Oct 02 '22 09:10

Anil Varghese