简体   繁体   中英

Swift protocol & weak references with class

If I have a protocol:

protocol SomeProtocol {
    func doSomething()
}

and in a helper class, I have a reference to a protocol variable:

class someClass {
    var delegate: SomeProtocol? 
}

because SomeProtocol isn't marked with : class , it's assumed that delegate can be anything and in the case of value types (structs and enums) there's no need for weak var because value types can't create strong references. In fact, the compiler doesn't allow weak var on anything but class types.

However, nothing stops you from setting a class as the delegate and if the protocol isn't marked with : class (as SomeProtocol is), weak var` can't be used and that creates a retain cycle.

class MyClass: NSObject, SomeProtocol {
    func doSomething() { }
}

struct MyStruct: SomeProtocol {
    func doSomething() { }
}

let someClass = SomeClass()
let myStruct = MyStruct()
someClass.delegate = myStruct 
// After myStruct gets assigned to the delegate, do the delegate and the struct refer to the same instance or does the struct get copied?D

let myClass = MyClass()
someClass.delegate = myClass // can't use weak var so myClass is retained

Given the above example, in the case of using delegates and datasource, shouldn't : class always be used? Basically any protocol that is used to maintain a reference should always be restricted to class objects right?

Right. If you a are trying to break retain cycle with weak reference, you have to use classes because weak modifier works only with reference types (classes).

: class is the preferred approach most of the time. As an alternative answer, though, you can set delegate = nil in the deinit method of the parent object.

For example, say the parent object is an NSOperation subclass that has a SomeClass property, and this property has a delegate that implements SomeProtocol :

protocol SomeProtocol {
    func doSomething()
}

class SomeClass {
    var delegate: SomeProtocol?
}

class CustomOperation: NSOperation {
    let foo: SomeClass
}

We'll make a class that implements the protocol too:

class SomeProtocolImplementation: SomeProtocol {
    func doSomething() {
        print("Hi!")
    }
}

Now we can assign foo in init() :

class CustomOperation: NSOperation {
    let foo: SomeClass

    override init() {
        foo = SomeClass()
        foo.delegate = SomeProtocolImplementation()
        super.init()
    }
}

This creates a retain cycle. However, consider this deinit method:

    deinit {
        foo.delegate = nil
    }

Now, whenever CustomOperation will be deallocated, foo.delegate will be set to nil , breaking the retain cycle. Then when the autorelease pool drains at the end of the run loop, both SomeClass and SomeProtocolImplementation will be deallocated.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM