如何正确处理带参数的 Swift 块中的弱自我

在我的 TextViewTableViewCell中,我有一个变量来跟踪一个块,还有一个 configure 方法来传入和分配块。
这是我的 TextViewTableViewCell课程:

//
//  TextViewTableViewCell.swift
//


import UIKit


class TextViewTableViewCell: UITableViewCell, UITextViewDelegate {


@IBOutlet var textView : UITextView


var onTextViewEditClosure : ((text : String) -> Void)?


func configure(#text: String?, onTextEdit : ((text : String) -> Void)) {
onTextViewEditClosure = onTextEdit
textView.delegate = self
textView.text = text
}


// #pragma mark - Text View Delegate


func textViewDidEndEditing(textView: UITextView!) {
if onTextViewEditClosure {
onTextViewEditClosure!(text: textView.text)
}
}
}

当我在 cellForRowAtIndexPath方法中使用 configure 方法时,如何在传入的块中正确地使用弱 self。
这就是我没有软弱的自我所拥有的:

let myCell = tableView.dequeueReusableCellWithIdentifier(textViewCellIdenfitier) as TextViewTableViewCell
myCell.configure(text: body, onTextEdit: {(text: String) in
// THIS SELF NEEDS TO BE WEAK
self.body = text
})
cell = bodyCell

更新 : 使用 [weak self]我得到了以下工作:

let myCell = tableView.dequeueReusableCellWithIdentifier(textViewCellIdenfitier) as TextViewTableViewCell
myCell.configure(text: body, onTextEdit: {[weak self] (text: String) in
if let strongSelf = self {
strongSelf.body = text
}
})
cell = myCell

当我用 [unowned self]代替 [weak self]并取出 if语句时,应用程序崩溃了。对于如何使用 [unowned self]有什么想法吗?

132976 次浏览

[unowned self]放在 (text: String)...之前。这称为 捕获物清单,并将所有权指令放置在闭包中捕获的符号上。

您可以在块参数之前的捕获列表中使用[弱 self ]或[ unowner self ]。捕获列表是可选的语法。

[unowned self]在这里工作得很好,因为单元格永远不会为零。否则你可以使用 [weak self]

如果闭包中的 自我可以为零,则使用 [虚弱的自我]

如果在闭包中 自我永远不会为零,则使用 [无主的自我]

如果当你使用 [无主的自我]的时候它崩溃了,我会猜测 self 在那个闭包中的某个点是零,这就是为什么你必须使用 [虚弱的自我]来代替它。

我非常喜欢手册中关于在闭包中使用 <坚强>坚强软弱无主的的整个部分:

Https://developer.apple.com/library/content/documentation/swift/conceptual/swift_programming_language/automaticreferencecounting.html

注意: 我使用的术语是 了结而不是 拦截,后者是最新的 Swift 术语:

Ios 中块(目标 C)与闭合(Swift)的区别

如果你崩溃了,你可能需要[虚弱的自己]

我的猜测是,你创造的这个模块仍然在某种程度上连接着。

创建 prepareForReuse 并尝试清除其中的 onTextViewEditClosure 块。

func prepareForResuse() {
onTextViewEditClosure = nil
textView.delegate = nil
}

看看这样能不能防止坠机。(这只是个猜测)。

使用 俘虏名单

定义捕获列表

捕获列表中的每个项都是弱项或无主项的配对 关键字,引用类实例(如 self)或 使用某个值初始化的变量(例如 committee = 这些对写在一对正方形内 大括号,用逗号分隔。

将捕获列表置于闭包的参数列表之前并返回 如提供以下资料,请输入:

lazy var someClosure: (Int, String) -> String = {
[unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) -> String in
// closure body goes here
}

如果闭包未指定参数列表或返回类型,则为 可以推断出来 上下文中,将捕获列表放在闭包的最开始, 后跟 in 关键字:

lazy var someClosure: Void -> String = {
[unowned self, weak delegate = self.delegate!] in
// closure body goes here
}

附加解释

* * 为 Swift 4.2编辑:

正如@Koen 评论的那样,Swift 4.2允许:

guard let self = self else {
return // Could not get a strong reference for self :`(
}


// Now self is a strong reference
self.doSomething()

附注: 由于我有一些赞成票,我想推荐阅读关于 逃离关闭

编辑: 就像@tim-vermeulen 评论的那样,Chris Lattner 在2016年1月22日星期五19:51:29 CST 说,这个技巧不应该用在自己身上,所以请不要使用它。检查非转义闭包信息和来自@gbk 的捕获列表答案。**

对于那些在捕获列表中使用[弱 self ]的用户,请注意 self 可能为 null,因此我要做的第一件事是使用 Guard 语句检查它

guard let `self` = self else {
return
}
self.doSomething()

如果你想知道什么引号是围绕 self是一个专业的技巧,使用内闭合自我无需改变的名称为 这个软弱的自己或其他。

编辑: 参考 LightMan 更新的解决方案

到目前为止,我一直在使用:

input.action = { [weak self] value in
guard let this = self else { return }
this.someCall(value) // 'this' isn't nil
}

或者:

input.action = { [weak self] value in
self?.someCall(value) // call is done if self isn't nil
}

如果是推断的,通常不需要指定参数类型。

如果没有参数,或者在闭包中将其称为 $0,则可以完全省略该参数:

input.action = { [weak self] in
self?.someCall($0) // call is done if self isn't nil
}

只是为了完整性; 如果要将闭包传递给函数,而参数不是 @escaping,那么就不需要 weak self:

[1,2,3,4,5].forEach { self.someCall($0) }

至于 Swift 4.2,我们可以做到:

_ = { [weak self] value in
guard let self = self else { return }
print(self) //👈 will never be nil
}()

Swift 4.2

let closure = { [weak self] (_ parameter:Int) in
guard let self = self else { return }


self.method(parameter)
}

Https://github.com/apple/swift-evolution/blob/master/proposals/0079-upgrade-self-from-weak-to-strong.md

[闭合和强参考周期]

如你所知,斯威夫特的关闭可以捕捉到实例。这意味着您可以在闭包中使用 self。特别是 escaping closure< sup > [ About ] 可以创建一个 strong reference cycle< sup > [ About ] 。顺便说一下,您必须在 escaping closure中显式地使用 self

Swift 闭包具有 Capture List特性,它允许您避免这种情况并打破引用循环,因为没有对捕获的实例的强引用。Capture List 元素是一对 weak/unowned和对类或变量的引用。

比如说

class A {
private var completionHandler: (() -> Void)!
private var completionHandler2: ((String) -> Bool)!
    

func nonescapingClosure(completionHandler: () -> Void) {
print("Hello World")
}
    

func escapingClosure(completionHandler: @escaping () -> Void) {
self.completionHandler = completionHandler
}
    

func escapingClosureWithPArameter(completionHandler: @escaping (String) -> Bool) {
self.completionHandler2 = completionHandler
}
}


class B {
var variable = "Var"
    

func foo() {
let a = A()
        

//nonescapingClosure
a.nonescapingClosure {
variable = "nonescapingClosure"
}
        

//escapingClosure
//strong reference cycle
a.escapingClosure {
self.variable = "escapingClosure"
}
        

//Capture List - [weak self]
a.escapingClosure {[weak self] in
self?.variable = "escapingClosure"
}
        

//Capture List - [unowned self]
a.escapingClosure {[unowned self] in
self.variable = "escapingClosure"
}
        

//escapingClosureWithPArameter
a.escapingClosureWithPArameter { [weak self] (str) -> Bool in
self?.variable = "escapingClosureWithPArameter"
return true
}
}
}
  • weak-更可取,使用它时,它是 有可能
  • unowned-当您确定实例所有者的生存期大于闭包时使用它

[弱对无主]

Swift 5.3开始,如果在关闭 in之前传递 [self],则不必在关闭时展开 self

参考 这个迅速的医生中的 someFunctionWithEscapingClosure { [self] in x = 100 }