Swift 委托 - 何时在委托上使用弱指针
Swift delegation - when to use weak pointer on delegate
有人可以解释何时以及何时不对 Swift 中的委托指针使用 'weak' 赋值,为什么?
我的理解是,如果您使用未定义为 class 的协议,您不能也不想将委托指针分配给 weak。
protocol MyStructProtocol{
//whatever
}
struct MyStruct {
var delegate: MyStructProtocol?
}
但是,当您的协议被定义为 class 类型协议时,您是否希望将委托设置为弱指针?
protocol MyClassProtocol: class{
//whatever
}
class MyClass {
weak var delegate: MyClassProtocol?
}
我说的对吗?在 Apple 的 swift 指南中,class 协议示例没有使用弱分配,但在我的测试中,如果我的代表没有被弱引用,我会看到强引用循环。
代表应该总是通常是弱者。
假设 b
是 a
的代表。现在 a
的 delegate
属性 是 b
.
如果您希望 b
在 c
消失时释放
如果 c
持有对 b
的强引用并且 c
解除分配,您希望 b
与 c
解除分配。但是,在 a
中使用强委托 属性,b
将永远不会被释放,因为 a
强烈保留 b
。使用弱引用,一旦 b
失去来自 c
的强引用,b
将在 c
释放时释放。
通常这是预期的行为,这就是为什么您要使用 weak
属性.
您通常制定 class 协议 weak
以避免“强引用循环”(以前称为“保留循环”)的风险。 (注意,我们现在通过将 AnyObject
协议添加到协议的继承列表中来做到这一点;参见 Class-Only Protocols;我们不再使用 class
关键字。)未能使委托 weak
并不意味着你天生就拥有强大的参考循环,而只是你 可以 拥有一个。
但是,对于 struct
类型,强引用循环风险大大降低,因为 struct
类型不是“引用”类型,因此更难创建强引用循环。但是,如果委托对象是一个 class 对象,那么您可能希望将协议设为 class 协议并使其变弱。
在我看来,让 class 代表 weak
只是部分地减轻了强引用循环的风险。这也是一个所有权问题。大多数委托协议都是这样的情况,其中所讨论的对象没有业务要求对委托拥有所有权,而只是所讨论的对象提供通知委托人某事(或请求某事)的能力。例如,如果您希望视图控制器具有一些文本字段委托方法,则文本字段无权声明对视图控制器的所有权。
正如罗布所说:
It's really a question of "ownership"
这是真的。 'Strong reference cycle' 就是要获得所有权。
在下面的示例中,我们没有使用 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 所说 :
In an object hierarchy, a child object should not maintain strong references to the parent object. That is a red flag, indicating a strong reference cycle
因此,无论泄漏如何,仍然对您的委托对象使用 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
将避免强引用循环
有人可以解释何时以及何时不对 Swift 中的委托指针使用 'weak' 赋值,为什么?
我的理解是,如果您使用未定义为 class 的协议,您不能也不想将委托指针分配给 weak。
protocol MyStructProtocol{
//whatever
}
struct MyStruct {
var delegate: MyStructProtocol?
}
但是,当您的协议被定义为 class 类型协议时,您是否希望将委托设置为弱指针?
protocol MyClassProtocol: class{
//whatever
}
class MyClass {
weak var delegate: MyClassProtocol?
}
我说的对吗?在 Apple 的 swift 指南中,class 协议示例没有使用弱分配,但在我的测试中,如果我的代表没有被弱引用,我会看到强引用循环。
代表应该总是通常是弱者。
假设 b
是 a
的代表。现在 a
的 delegate
属性 是 b
.
如果您希望 b
在 c
消失时释放
如果 c
持有对 b
的强引用并且 c
解除分配,您希望 b
与 c
解除分配。但是,在 a
中使用强委托 属性,b
将永远不会被释放,因为 a
强烈保留 b
。使用弱引用,一旦 b
失去来自 c
的强引用,b
将在 c
释放时释放。
通常这是预期的行为,这就是为什么您要使用 weak
属性.
您通常制定 class 协议 weak
以避免“强引用循环”(以前称为“保留循环”)的风险。 (注意,我们现在通过将 AnyObject
协议添加到协议的继承列表中来做到这一点;参见 Class-Only Protocols;我们不再使用 class
关键字。)未能使委托 weak
并不意味着你天生就拥有强大的参考循环,而只是你 可以 拥有一个。
但是,对于 struct
类型,强引用循环风险大大降低,因为 struct
类型不是“引用”类型,因此更难创建强引用循环。但是,如果委托对象是一个 class 对象,那么您可能希望将协议设为 class 协议并使其变弱。
在我看来,让 class 代表 weak
只是部分地减轻了强引用循环的风险。这也是一个所有权问题。大多数委托协议都是这样的情况,其中所讨论的对象没有业务要求对委托拥有所有权,而只是所讨论的对象提供通知委托人某事(或请求某事)的能力。例如,如果您希望视图控制器具有一些文本字段委托方法,则文本字段无权声明对视图控制器的所有权。
正如罗布所说:
It's really a question of "ownership"
这是真的。 'Strong reference cycle' 就是要获得所有权。
在下面的示例中,我们没有使用 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 所说
In an object hierarchy, a child object should not maintain strong references to the parent object. That is a red flag, indicating a strong reference cycle
因此,无论泄漏如何,仍然对您的委托对象使用 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
将避免强引用循环