iOS 2018系列:破解iOS采访或成为iOS专家(4)
第4章:闭包和强大的闭包参考周期
面试是一个非常热门的话题,因为它提供了从初学者到专家级别的问题,而且大多数工程师在讨论中都对它感到困惑,即使您尝试在现实生活中使用它,也可能会遇到一些情况,混淆了使用什么和不使用什么!
Swift中的闭包类似于C和Objective-C中的闭包。 闭包是可以独立传递的功能块,可以在代码中传递和使用。
闭包表达式语法
逃逸关闭
当闭包作为函数的参数传递给闭包时,闭包被认为是对函数的转义 ,但是在函数返回后会被调用。 声明将闭包作为其参数之一的函数时,可以在参数的类型之前编写@escaping
,以指示允许对闭包进行转义。
reversedNames = names.sorted(by:{s1中的s1,s2> s2})// (第1行 )
reversedNames = names.sorted(by:{$ 0> $ 1})// (第2行)
这两行都在做相同的事情, 第1行称为单表达式闭包的隐式返回, 第2行称为速记参数名 ,可用于通过$0
, $1
, $2
名称引用闭包的参数值。上。
封闭的强大参考周期
之所以会出现这种强引用循环,是因为闭包(如类)是引用类型 。
案例:如果您将闭包分配给类实例的属性,并且该闭包的主体捕获该实例。
说明:之所以可能发生此捕获,是因为闭包的主体访问实例的属性,例如self.someProperty
,或者因为闭包调用了实例上的方法,例如self.someMethod()
。 在任何一种情况下,这些访问都会导致闭包“捕获” self
,从而创建一个强大的参考周期。
解决方案:弱引用或无主引用,弱引用或无主引用的适当选择取决于代码不同部分之间的关系。
问题:无主还是弱?
您应该使用两种参考类型中的哪一种?
“只要有效,在引用生命周期中的某个时候变为零就使用弱引用。 相反,当您知道引用在初始化期间被设置为永远不会为零时,请使用无主引用。”
“当闭包及其捕获的实例将始终相互引用且始终在同一时间解除分配时,将闭包中的捕获定义为未拥有的引用。”
有两种可能的方案:
- 闭包与捕获的对象具有相同的生存期,因此闭包只有在对象可以到达之前才可以到达。 外部对象和闭包具有相同的生存期(例如,和对象及其父对象之间的简单反向引用)。 在这种情况下,您应该将引用声明为Unown 。
一个常见的例子是在许多小闭包的例子中使用的[unowned self]
,这些小闭包在其父母的上下文中做某事,并且没有被其他地方引用或传递而不会超过其父母的寿命。 - 闭包的生存期独立于所捕获的对象之一,当该对象不再可访问时,仍然可以引用闭包。 在这种情况下,您应该将引用声明为弱引用,并在使用它之前确认它不是零(不要强行打开包装)。
一个常见的示例是[weak delegate = self.delegate!]
您可以在某些闭包示例中看到一个完全不相关的(按生命周期而言)的委托对象。
懒变
惰性存储的属性是直到首次使用才计算其初始值的属性。 您可以通过 在声明之前 编写 lazy
修饰符来 表示一个惰性存储属性 。
延迟关键字
起初,延迟的操作并不明显。 Defer将等待执行一段代码,直到当前作用域(循环,方法等)退出为止。 无论范围是从干净地退出,从防护中退出还是在引发错误时,它都会执行该代码。
这是最基本的示例。 defer块存在于代码主体之前,但是直到之后才被调用。
例如,如果您需要确保在离开当前范围之前要清理掉资源,这将很有用。
结论
仅在防御上使用弱引用是否有意义? 不,从性能和代码清晰度两方面来看。
使用正确的类型的Capture修饰符可以使我们的代码具有明显的生命周期特征,并且更难以得出错误的结论,即当别人(或将来您)阅读您编写的内容时,代码的行为方式。
希望你喜欢它!
下一章:快速了解类,结构和枚举。