Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Closure : Use unresolved identifier 'self'

Tags:

swift

I'm parsing the Swift Language Guide tutorial (from Apple iOS dev library) and for every chapter I create a separate swift file. In each file I create multiple functions where I isolate snippets of code that they provide. Everything worked on until testing the Strong Reference Cycles for Closures. For some reason if the class that contains a closure (for a computed property) is declared inside a function, then the closure cannot see the "self" reference of the enclosing class. Any ideas why ? It works fine if the class is not declared inside a function.

func strongRefClosure() {
    class HTMLElement {

        let name: String
        let text: String?

        lazy var asHTML: () -> String = {
            if let text = self.text {
                return "<\(self.name)>\(text)</\(self.name)>"
            } else {
                return "<\(self.name) />"
            }
        }

        init(name: String, text: String? = nil) {
            self.name = name
            self.text = text
        }

        deinit {
            println("\(name) is being deinitialized")
        }
    }

    var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
    println(paragraph!.asHTML())
}
like image 875
Sorin J Avatar asked Mar 31 '15 10:03

Sorin J


2 Answers

Looks very much like a bug (or at least, a behaviour of function-local structs/classes I can’t find documented). This works fine:

struct Foo {
    let someVal = 5
    lazy var someLazy: String = {
        return toString(self.someVal)
    }()
}

var foo = Foo()
foo.someLazy // string "5"

But this doesn’t:

func outer() {
    struct Foo {
        let someVal = 5
        lazy var someLazy: String = {
            // error: use of unresolved identifier 'self'
            return toString(self.someVal)
        }()
    }

    var foo = Foo()
    foo.someLazy
}

An inner struct inside an outer struct works, though:

struct Outer {
    struct Foo {
        let someVal = 5
        lazy var someLazy: String = {
            return toString(self.someVal)
        }()
    }

    var foo = Foo()
}

var outer = Outer()
outer.foo.someLazy

As @JeremyP says, you should file a radar.

like image 117
Airspeed Velocity Avatar answered Oct 20 '22 21:10

Airspeed Velocity


For me, I have this bug in XCode 8GM, Swift 3, iOS 10.0.

tl;dr

  • Closure-as-instance-property has bug
  • Closure-as-computed-property is cured!

Actual code

Buggy:

let permissionStatusHandler = { (status: CKApplicationPermissionStatus, error: Error?) in
        switch status {
        case .granted:
            self.fetchUserRecordID() // Buggy: Use of unresolved identifier 'self'
        case .initialState:
            self.requestDiscoverability() // Buggy: Use of unresolved identifier 'self'
        case .couldNotComplete:
            error.then { print(#function, $0.localizedDescription) }
            fallthrough
        case .denied:
            self.iCloud.presentiCloudAlert(for: status)
        }
    }

Kosher:

var permissionStatusHandler: (CKApplicationPermissionStatus, Error?) -> () { return
        { (status: CKApplicationPermissionStatus, error: Error?) in
            switch status {
            case .granted:
                self.fetchUserRecordID()
            case .initialState:
                self.requestDiscoverability()
            case .couldNotComplete:
                error.then { print(#function, $0.localizedDescription) }
                fallthrough
            case .denied:
                self.iCloud.presentiCloudAlert(for: status)
            }
        }
    }    

Hope this helps some other soul out there.

like image 1
AmitaiB Avatar answered Oct 20 '22 22:10

AmitaiB