46

有人可以解释何时以及何时不使用 Swift 中的委托指针的“弱”赋值,为什么?

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

protocol MyStructProtocol{
    //whatever
}

struct MyStruct {
    var delegate: MyStructProtocol?
}

但是,当您的协议被定义为类类型协议时,您是否希望将您的委托设置为弱指针?

protocol MyClassProtocol: class{
    //whatever
}

class MyClass {
    weak var delegate: MyClassProtocol?
}

我对么?在 Apple 的 swift guide 中,类协议示例没有使用弱分配,但在我的测试中,如果我的代表没有被弱引用,我会看到强引用循环。

4

3 回答 3

52

您通常会制定类协议weak来避免“强引用循环”(以前称为“保留循环”)的风险。(注意,我们现在通过将AnyObject协议添加到协议的继承列表中来做到这一点;请参阅Class-Only Protocols;我们不再使用class关键字。)未能创建委托weak并不意味着您天生就有一个强引用循环,而是只是你可以拥有一个。

但是,对于struct类型,强引用循环的风险大大降低,因为struct类型不是“引用”类型,因此更难创建强引用循环。但是如果委托对象是一个类对象,那么您可能希望使协议成为类协议并使其变弱。

在我看来,让班级代表weak只是部分地减轻了强引用循环的风险。这也是所有权的问题。大多数委托协议都是这样的情况,即所讨论的对象没有声称对委托拥有所有权的业务,而只是在所讨论的对象提供通知委托某事(或请求某事)的能力的情况下。例如,如果您希望视图控制器具有一些文本字段委托方法,则文本字段无权声明对视图控制器的所有权。

于 2015-05-05T17:13:11.553 回答
9

代表通常应该总是很弱。

可以说b是 的代表a。现在adelegate财产是b

在你想b释放的情况c

如果c持有对bc解除分配的强引用,您希望b使用 解除分配ca但是,在,中使用强大的委托属性b将永远不会被释放,因为a它会强烈地坚持下去b。使用弱引用,一旦b失去强引用cb就会在cdeallocs时dealloc。

通常这是预期的行为,这就是您想要使用weak属性的原因。

于 2015-05-05T15:16:45.400 回答
8

正如罗布所说:

这真的是一个“所有权”的问题

这是非常真实的。“强大的参考周期”就是要获得所有权。

在下面的示例中,我们没有使用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 所说:

在对象层次结构中,子对象不应维护对对象的强引用。那是一个红旗,表示强参考周期

因此,无论泄漏如何,仍然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将避免强引用循环

于 2019-10-07T20:24:29.437 回答