Swift 4闭包问题:无法在闭包内访问其他本地属性
哈哈,这个问题我之前也踩过坑!核心矛盾就是闭包要引用结构体的属性,但初始化阶段self还没完全就绪,同时协议又限制了属性的存储方式对吧?别着急,有几个靠谱的方案可以解决:
方案一:直接捕获初始化参数(最推荐,适用于属性不可变的情况)
如果你的过滤属性是不可变的(比如用let声明),完全可以在初始化器里让闭包直接捕获初始化时的参数值,而不是去引用self的属性。这样既避开了self未初始化的问题,又完美满足协议要求。
举个具体的代码例子:
// 先定义你的协议 protocol Filterable { associatedtype Element var filterValue: String { get } var filterClosure: (Element) -> Bool { get } } // 结构体实现 struct StringFilter: Filterable { typealias Element = String let filterValue: String let filterClosure: (String) -> Bool init(filterValue: String) { self.filterValue = filterValue // 直接捕获初始化参数filterValue,不用依赖self self.filterClosure = { string in string.contains(filterValue) } } }
原理很简单:初始化器里的filterValue是已经完全初始化的局部变量,闭包可以安全捕获它。这个闭包会持有该值的副本,和结构体的filterValue属性完全一致,完全能实现你想要的数组过滤逻辑。
方案二:用懒加载闭包(适用于需要引用可变属性或self的情况)
如果你的过滤属性是可变的,或者闭包需要调用结构体的其他方法/属性,那可以把闭包声明为lazy var。别担心协议限制——协议只要求属性的类型和访问权限,并不限制它是否是懒加载的。
代码示例:
struct MutableStringFilter: Filterable { typealias Element = String var filterValue: String // 懒加载闭包:第一次访问时才初始化,此时self已经完全就绪 lazy var filterClosure: (String) -> Bool = { [unowned self] string in // 这里可以安全访问self.filterValue return string.contains(self.filterValue) } init(filterValue: String) { self.filterValue = filterValue // 不需要在这里初始化闭包,懒加载会自动处理 } }
注意:结构体是值类型,不存在循环引用问题,所以用unowned self或者直接写self都没问题。不过懒加载的闭包只会初始化一次,如果修改了filterValue,闭包不会自动更新——如果需要响应属性变化,就得结合didSet:
struct DynamicStringFilter: Filterable { typealias Element = String var filterValue: String { didSet { // 属性变化时重新生成闭包 filterClosure = { string in string.contains(self.filterValue) } } } var filterClosure: (String) -> Bool init(filterValue: String) { self.filterValue = filterValue self.filterClosure = { string in string.contains(filterValue) } } }
总结一下
- 不可变过滤属性优先选方案一,简单直接无副作用;
- 可变属性或需要引用
self的场景用方案二,结合懒加载或didSet处理; - 协议只约束属性的类型和访问权限,懒加载完全符合协议要求,不用有顾虑。
内容的提问来源于stack exchange,提问作者flanker
相关产品推荐
相关产品推荐

