Swift委托 - 何时在代表上使用弱指针



有人可以解释何时以及何时不将'弱'分配用于Swift中的代表指针,为什么?

我的理解是,如果您使用未定义为类的协议,则不能或不想将您的委托指针分配给弱。

protocol MyStructProtocol{
    //whatever
}
struct MyStruct {
    var delegate: MyStructProtocol?
}

但是,当您的协议定义为类类型协议时,您确实要将您的委托设置为弱指针?

protocol MyClassProtocol: class{
    //whatever
}
class MyClass {
    weak var delegate: MyClassProtocol?
}

我正确吗?在Apple的Swift指南中,班级协议示例并不使用弱作业,但是在我的测试中,如果我的代表没有弱参考,我会看到强有力的参考周期。

您通常制作类方案weak,以避免"强参考周期"(以前称为"保留周期")的风险。(请注意,我们现在通过将AnyObject协议添加到协议的继承列表中;请参阅仅课程协议;我们不再使用class关键字。)未能使委托weak并不意味着您本质上具有强大的参考循环,但仅仅是您可以有一个。

使用struct类型,强大的参考周期风险大大降低,因为struct类型不是"参考"类型,因此很难创建强大的参考周期。但是,如果委派对象是类对象,那么您可能需要使协议成为类协议并使其变得弱。

我认为,将班级代表weak仅部分减轻了强大的参考周期的风险。这也是一个所有权问题。大多数委托协议是相关对象没有对代表所有权所有权的业务的情况,而仅仅是有关对象提供的能力,可以将某物(或请求某些内容)告知代表。例如,如果您希望视图控制器具有一些文本字段委托方法,则文本字段无权通过视图控制器声称所有权。

Rob说:

这确实是"所有权"的问题

这是真的。"强有力的参考周期"就是正确的所有权。

在下面的示例中,我们不使用weak var。然而,这两个对象都会引起分配。为什么?

protocol UserViewDelegate: class {
    func userDidTap()
}
class Container {
    let userView = UserView()
    let delegate = Delegate()
    init() {
        userView.delegate = delegate
    }
    deinit {
        print("container deallocated")
    }
}
class UserView {
    var delegate: UserViewDelegate?
    func mockDelegatecall() {
        delegate?.userDidTap()
    }
    deinit {
        print("UserView deallocated")
    }
}
class Delegate: UserViewDelegate {
    func userDidTap() {
        print("userDidTap Delegate callback in separate delegate object")
    }
}

用法:

var container: Container? = Container()
container?.userView.mockDelegatecall()
container = nil // will deallocate both objects

内存所有权图(没有周期)

    +---------+container +--------+
    |                             |
    |                             |
    |                             |
    |                             |
    |                             |
    |                             |
    v                             v
userView +------------------> delegate

为了创建强大的参考周期,周期需要完成。delegate需要重新指向container,但事实并非如此。所以这不是问题。但纯粹是出于所有权的原因,正如Rob在这里所说的那样:

在对象层次结构中,子对象不应保持对 parent 对象的强烈参考。那是一个危险信号,表明有很强的参考周期

因此,无论泄漏如何,仍将weak用于您的委托对象。


在下面的示例中,我们不使用weak var。因此,两个班级都不会交易。

protocol UserViewDelegate: class {
    func userDidTap()
}
class Container: UserViewDelegate {
    let userView = UserView()
    init() {
        userView.delegate = self
    }
    func userDidTap() {
        print("userDidTap Delegate callback by Container itself")
    }
    deinit {
        print("container deallocated")
    }
}
class UserView {
    var delegate: UserViewDelegate?
    func mockDelegatecall() {
        delegate?.userDidTap()
    }
    deinit {
        print("UserView deallocated")
    }
}

用法:

var container: Container? = Container()
container?.userView.mockDelegatecall()
container = nil // will NOT deallocate either objects

内存所有权图(具有周期)

     +--------------------------------------------------+
     |                                                  |
     |                                                  |
     +                                                  v
 container                                           userview
     ^                                                  |
     |                                                  |
     |                                                  |
     +------+userView.delegate = self //container+------+

使用weak var将避免强大的参考周期

委托应该始终通常很弱。

假设ba的代表。现在adelegate属性是b

在您希望b释放c

时发布的情况下

c有很强的参考bc DealLocates,您希望bc交易。但是,使用a中强大的代表属性,b永远不会被划分,因为a强烈坚持b。使用弱参考,一旦b失去了c的强参考,b就会在c DealLocs时进行交易。

通常这是预期的行为,这就是为什么您要使用weak属性。

最新更新