如何在纯Swift中(无@objc)创建协议弱引用?完整指南及示例
\n\n想象一下:您正在用Swift编写应用程序,使用Delegate模式,突然——砰!——您的对象无法释放,内存不断增长,应用程序开始卡顿。熟悉吗?很可能您遇到了retain cycle(强引用循环)。经典问题是:委托持有对拥有者的强引用,而拥有者也持有对委托的强引用。首先想到的解决方案是让协议引用变为弱引用(weak)。但Swift会带来一个惊喜:如果协议没有标记@objc,您不能直接声明weak var delegate: MyProtocol。编译器会报错。为什么?更重要的是——如何解决?
在本文中,我们将探讨如何在纯Swift中实现无@objc的weak protocol reference。您将了解为什么这是一个问题,有哪些解决方案,以及如何编写优雅、安全且无内存泄漏的代码。开始吧!
\n\n为什么weak protocol reference不能“开箱即用”?
\n\n让我们回顾一下Swift中weak的工作原理。弱引用(weak)只能用于引用类型(类)。为什么?因为只有类对象通过ARC(自动引用计数)进行内存管理。结构体(struct)和枚举(enum)是值类型,它们会被复制,因此“弱引用”的概念对它们没有意义。
当您编写:
\nweak var delegate: MyProtocol?\n编译器不知道MyProtocol将由类、结构体还是枚举实现。万一它是结构体呢?那么weak就无法应用。因此出现错误:“'weak' may only be applied to class and class-bound protocol types”。
以前(在Objective-C中),所有协议默认只能由类实现,因此weak可以工作。在纯Swift中,协议是通用的——这很好,但也增加了复杂性。
解决方案#1:class-bound protocol(仅类协议)
\n\n最简单且最常用的方法是明确指定协议只能由类实现。为此,在协议声明中使用关键字AnyObject(或已弃用的class)。
示例:
\nprotocol MyDelegate: AnyObject {\n func didSomething()\n}\n\nclass ViewController {\n weak var delegate: MyDelegate?\n\n func doSomething() {\n delegate?.didSomething()\n }\n}\n\nclass SomeClass: MyDelegate {\n func didSomething() {\n print("委托已触发!")\n }\n}\n\n// 使用\nlet vc = ViewController()\nlet some = SomeClass()\nvc.delegate = some\nvc.doSomething() // 输出:"委托已触发!"\n\n一切正常。MyDelegate现在只能由类(class)实现,而weak是合法的。这是Apple推荐的委托方法。
为什么这很好?
\n- \n
- 简单且可读性强。 \n
- 完全支持ARC。 \n
- 无需
@objc。 \n
缺点?
\n- \n
- 您失去了用结构体实现协议的可能性。但对于委托来说,这通常不是必需的。 \n
解决方案#2:使用泛型包装器(适用于特殊情况)
\n\n如果您必须允许协议既可由类实现也可由结构体实现,同时又想要弱引用怎么办?这种情况很少见,但有可能。例如,您正在编写一个库,用户既可以传递类也可以传递结构体。这时可以使用包装器。
\n\n思路:创建一个结构体,它存储对实现协议对象的弱引用。对于类——使用we