Swift 委托 - 何时在委托上使用弱指针
Posted
技术标签:
【中文标题】Swift 委托 - 何时在委托上使用弱指针【英文标题】:Swift delegation - when to use weak pointer on delegate 【发布时间】:2015-07-15 09:31:30 【问题描述】:有人能解释一下何时以及何时不使用 Swift 中的委托指针的“弱”赋值,为什么?
我的理解是,如果您使用未定义为类的协议,您不能也不想将委托指针分配给弱。
protocol MyStructProtocol
//whatever
struct MyStruct
var delegate: MyStructProtocol?
但是,当您的协议被定义为类类型协议时,您是否希望将您的委托设置为弱指针?
protocol MyClassProtocol: class
//whatever
class MyClass
weak var delegate: MyClassProtocol?
我说的对吗?在 Apple 的 swift guide 中,类协议示例没有使用弱分配,但在我的测试中,如果我的代表没有被弱引用,我会看到强引用循环。
【问题讨论】:
这似乎相关:blog.xebia.com/2014/10/09/… 如果你声明你的协议为protocol MyStructProtocol : class ...
,那么你可以让代理weak
。见***.com/a/24104371/1271826。
@Rob 这是否意味着如果我不将我的协议声明为一个类,那么我的委托指针将导致一个保留周期?
未能让您的代表weak
并不总是会导致强引用循环,而只会增加这种可能性。
试试这个How can I make a weak protocol reference in 'pure' Swift (without @objc)
【参考方案1】:
您通常制定类协议weak
以避免“强引用循环”(以前称为“保留循环”)的风险。 (注意,我们现在通过将AnyObject
协议添加到协议的继承列表中来做到这一点;请参阅Class-Only Protocols;我们不再使用class
关键字。)未能创建代理weak
并不意味着您天生就有一个强大的参考循环,但仅仅是你可以拥有一个。
但是,使用struct
类型,强引用循环风险大大降低,因为struct
类型不是“引用”类型,因此更难创建强引用循环。但是如果委托对象是一个类对象,那么您可能希望将协议设为类协议并使其变弱。
在我看来,让班级代表weak
只是部分地减轻了强引用循环的风险。这也是所有权的问题。大多数委托协议都是这样的情况,即相关对象没有声称对委托拥有所有权的业务,而只是在相关对象提供通知委托某事(或请求某事)的能力的情况下。例如,如果您希望视图控制器具有一些文本字段委托方法,则文本字段无权声明对视图控制器的所有权。
【讨论】:
希望看到一个示例,说明结构协议何时创建强引用循环,何时不创建,但这个答案让我明白了很多。 所以根据您的要点,如果我理解正确,Apples Swift 文档中的 SnakesAndLadders 示例将通过它的 DiceGameDelegate 创建一个强大的参考循环? developer.apple.com/library/ios/documentation/Swift/Conceptual/… 那里没有强引用循环。考虑对象所有权图。想象一下拥有tracker
和game
的某个主对象(例如视图控制器或其他)。而game
也对tracker
有很强的引用。但是没有循环强引用循环。一切都好。要拥有强引用循环,您需要game
的委托来引用本身拥有game
的对象。但情况并非如此。因此没有强参考循环。
当然。在对象层次结构中,子对象不应维护对父对象的强引用。这是一个危险信号,表明一个强大的参考周期。请注意,在这个 VC 示例中,强引用循环并不总是表现为泄漏,但在特殊情况下可能会出现,因此建议通过将委托设置为弱属性来完全避免潜在问题。
@nwales 使用 Rob 的 cmets,我写了一个更清晰的示例 here,其中缺少 weak
不会产生强引用循环。【参考方案2】:
代表应该总是通常是弱的。
假设b
是a
的代表。现在a
的delegate
属性是b
。
如果您希望b
在c
消失时释放
如果c
持有对b
的强引用并且c
被解除分配,您希望b
与c
解除分配。但是,在a
中使用强委托属性,b
将永远不会被释放,因为a
会强烈地保留b
。使用弱引用,一旦b
失去来自c
的强引用,b
将在c
释放时释放。
通常这是预期的行为,这就是您要使用 weak
属性的原因。
【讨论】:
我还是一头雾水。如果我不能将弱分配给非类类型协议,这是否意味着它会导致保留周期?何时使用类协议与非类协议?如果我使用结构,我只使用非类协议与类协议与类? @nwales 我知道这是一条旧评论,但是如果两者(a)您都使用参考(class
)类型,则您使用class
协议; (b) 您需要weak
参考。否则,没有必要将其声明为class
协议。仅在您的协议需要时指定 class
(例如,它是委托协议)。所以,如果你使用struct
(一个值类型)或者你使用class
,但不需要担心强引用循环(例如,用于定义委托接口以外的其他东西的协议),那么就不要让它成为class
协议。
FWIW,说代表应该总是软弱有点强。考虑URLSession
,它会强烈引用其delegate
,直到会话失效。但是,这仅适用,因为它们在会话无效时手动解析强引用。但作为一般经验法则,这是正确的,委托通常是弱属性。 +1【参考方案3】:
正如罗布所说:
真的是“所有权”的问题
确实如此。 “强大的参考周期”就是要获得所有权。
在以下示例中,我们没有使用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 所说here:
在对象层次结构中,子对象不应维护对父对象的强引用。那是一个红旗,表示强引用循环
因此,无论是否泄漏,仍将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 委托 - 何时在委托上使用弱指针的主要内容,如果未能解决你的问题,请参考以下文章
iOS Swift:闭包(回调)与委托,何时使用? [关闭]