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

nwa*_*les 42 delegates memory-leaks memory-management swift strong-reference-cycle

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

我的理解是,如果你使用一个未定义为类的协议,你不能也不想将你的委托指针指向weak.

protocol MyStructProtocol{
    //whatever
}

struct MyStruct {
    var delegate: MyStructProtocol?
}
Run Code Online (Sandbox Code Playgroud)

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

protocol MyClassProtocol:Class{
    //whatever
}

class MyClass {
    weak var delegate: MyClassProtocol?
}
Run Code Online (Sandbox Code Playgroud)

我对么?在Apple的快速指南中,类协议示例并未使用弱分配,但在我的测试中,如果我的代表没有被弱引用,我会看到强大的引用周期.

Rob*_*Rob 44

您通常会将类协议(使用class关键字定义)设置为弱,以避免出现"强参考周期"(以前称为"保留周期")的风险.未能使代表弱,并不意味着你本身就有一个强大的参考周期,而只是你可以拥有一个.

struct但是,对于类型,强大的参考周期风险会大大减少,因为struct类型不是"参考"类型,因此创建强大的参考周期更加困难.但是如果委托对象是一个类对象,那么您可能希望使协议成为类协议并使其变弱.

在我看来,让班级代表变弱只是为了减轻强大的参考周期的风险.这真的是一个"所有权"的问题.大多数委托协议都是这样的情况:有问题的对象没有业务声明对委托的所有权,而仅仅是有问题的对象提供了向委托人通知某事(或请求某些东西)的能力.

  • 绝对。在对象层次结构中,子对象不应维护对父对象的强引用。这是一个红色标记,表示参考周期很强。请注意,在此VC示例中,强引用循环并不总是表现为泄漏,但在特殊情况下可能会出现泄漏,因此,强烈建议人们通过使委托具有弱属性来完全避免潜在的问题。 (2认同)

Sch*_*cal 8

代表们(编辑:一般)应该总是很弱.

让我们说b是代表a.现在adelegate财产是b.

在一个情况下,你需要b释放的时候c是走了

如果c拥有强大的引用bc解除分配,则需要b取消分配c.然而,在使用一个强大的委托财产a,b永远不会释放,因为a是抱着一种b强烈.使用弱引用,一旦b失去强引用c,b将在cdeallocs 时释放.

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

  • 我还是很困惑.如果我不能将弱分配给非类型协议,这是否意味着它会导致保留周期?我何时使用类协议与非类协议?如果我使用结构,我只使用非类协议与类协议类? (3认同)
  • FWIW,说代表们应该总是弱势,这有点强烈.考虑`URLSession`,它保持对其[`delegate`](https://developer.apple.com/documentation/foundation/urlsession/1411530-delegate)的强引用,直到会话失效.但这只能起作用,因为它们在会话失效时手动解析强引用.但作为一般经验法则,这是正确的,代表通常是弱属性.+1 (3认同)

Hon*_*ney 7

正如罗布所说:

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

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

在以下示例中,我们没有使用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")
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

var container: Container? = Container()
container?.userView.mockDelegatecall()
container = nil // will deallocate both objects
Run Code Online (Sandbox Code Playgroud)

内存所有权图(没有循环)

    +---------+container +--------+
    |                             |
    |                             |
    |                             |
    |                             |
    |                             |
    |                             |
    v                             v
userView +------------------> delegate
Run Code Online (Sandbox Code Playgroud)

为了创建一个强引用循环,循环需要是完整的。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")
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

var container: Container? = Container()
container?.userView.mockDelegatecall()
container = nil // will NOT deallocate either objects
Run Code Online (Sandbox Code Playgroud)

内存所有权图(有循环)

     +--------------------------------------------------+
     |                                                  |
     |                                                  |
     +                                                  v
 container                                           userview
     ^                                                  |
     |                                                  |
     |                                                  |
     +------+userView.delegate = self //container+------+
Run Code Online (Sandbox Code Playgroud)

使用weak var将避免强引用循环