Ryan Callery
Ryan Callery

Reputation: 33

declaring functions in swift using the closure syntax

Reading through Advanced Swift and it gives the following example

“In Swift, you can define functions in two ways. One is with the func keyword. The other way is to use a closure expression. Consider this simple function to double a number:

func doubler(i: Int) -> Int {
    return i * 2
}
[1, 2, 3, 4].map(doubler) // [2, 4, 6, 8]

And here’s the same function written using the closure expression syntax. Just like before, we can pass it to map:

let doublerAlt = { (i: Int) -> Int in return i*2 }
[1, 2, 3, 4].map(doublerAlt) // [2, 4, 6, 8]”

I was playing around with this and wrote the following code in a collection view cell class.

let setupView = {(label: UILabel) in
    addSubview(label)
    label.topAnchor.constraint(equalTo: topAnchor).isActive = true
    label.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
    label.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
    label.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
}

func setupViews(label: UILabel) {
    addSubview(label)
    label.topAnchor.constraint(equalTo: topAnchor).isActive = true

    label.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
    label.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
    label.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
}

The top one gives me errors but the bottom one using the func keyword works fine. I would think they should both work. I'm wondering if someone might be able to explain.

Upvotes: 2

Views: 59

Answers (1)

Sweeper
Sweeper

Reputation: 274730

A closure is closed, so you can't access things outside the closure inside the closure, by default.

self is something outside the closure, so you need to capture it in order to use it inside the closure. You didn't capture self, so you can't call self.addSubView.

self is kind of a special case. To capture it, you just need to write it out explicitly:

self.addSubView(label)
// and
self.topAnchor
self.leftAnchor
// etc

However, this will cause a retain cycle. The closure holds a strong reference to self all the time, and self holds a strong reference to the closure all the time. Neither can be deallocated. Therefore, you should capture self with unowned:

lazy var setupView = {[unowned self] (label: UILabel) in
    self.addSubview(label)
    label.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
    label.leftAnchor.constraint(equalTo: self.leftAnchor).isActive = true
    label.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
    label.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
}

Upvotes: 3

Related Questions