Martin Cazares
Martin Cazares

Reputation: 13705

How to set weak reference to a class member e.g. callback method?

While playing with Swift's ARC mechanism, I noticed that you cannot create a weak reference to a non-class member or you would get the compiling error "weak cannot be applied to non-class type X", so if I would like to pass a callback to another class for example:

class A{
    var b:B?
    init(){
        b = B(callback)
    }

    func callback(name:String){
        print("name: \(name)")
    }

    func trigger(){
        if b != nil{
            b!.somethingHappenedHenceCallback()
        }
    }
}
class B{
    var method:((name:String)->Void)
    init(method:(name:String)->Void){
        self.method = method
    }

    func somethingHappenedHenceCallback(){
        if method != nil{
            method(name: "From class B")
        }
    }
}
var a:A = A()
a.trigger()

In the previous example I'm passing the "callback" method to the class B, so, if I need to be notified when something happens, the class B can call it and it will be executed in class A, and it works perfectly, the only problem is that it creates a strong reference to A from B, and it will not allow A to be deinited when A is set to nil because B is keeping a strong reference, so I know that if I do the following it will work:

class A{
    var b:B?
    init(){
        b = B(a:self)
    }

    func callback(name:String){
        print("name: \(name)")
    }

    func trigger(){
        if b != nil{
            b!.somethingHappenedHenceCallback()
        }
    }
}
class B{
    var a:A?
    init(a:A){
        self.a = a
    }

    func somethingHappenedHenceCallback(){
        if a != nil{
            a!.callback("From class BB")
        }
    }
}

var a:A = A()
a.trigger()

However, is there a way to make a weak reference to the member only(in this case the method) instead of having to pass a reference to the whole class?

Regards!

Upvotes: 1

Views: 2776

Answers (1)

Wes Campaigne
Wes Campaigne

Reputation: 4170

Rather than just passing a reference to callback to the initializer for B, create a closure that uses a weak or unowned reference to self:

class A{
var b:B?
init(){
    b = B { [unowned self] in self.callback($0) }
}
...

Also, you have a couple other issues in your code:

if b != nil{
    b!.somethingHappenedHenceCallback()
}

is not idiomatic swift; the preferred way to express this is

if let x = b {
    x.somethingHappenedHenceCallback()
}

And if method != nil is unnecessary, and could possibly give a compiler error; the type given for method is not an optional and therefore it can never be nil

Upvotes: 4

Related Questions