161

在我的TextViewTableViewCell中,我有一个变量来跟踪一个块和一个配置方法,该块被传入和分配。
这是我的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)
        }
    }
}

当我在我的方法中使用configure方法时cellForRowAtIndexPath,如何在我传入的块中正确使用弱自我。
这是我没有弱自我的情况:

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]

4

11 回答 11

185

如果self在闭包中可以为零,请使用[weak self]

如果self在闭包中永远不会为零,请使用[unowned self]

如果当你使用[unowned self]时它崩溃了,我猜想 self 在那个闭包中的某个时候是 nil ,这就是为什么你必须使用[weak self]来代替。

我真的很喜欢手册中关于在闭包中使用strongweakunowned的整个部分:

https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html

注意:我使用术语闭包而不是,这是较新的 Swift 术语:

ios中块(Objective C)和闭包(Swift)之间的区别

于 2014-10-27T23:32:17.553 回答
33

放在你的关闭[unowned self]之前(text: String)...。这称为捕获列表,并将所有权说明放置在闭包中捕获的符号上。

于 2014-06-28T16:11:18.593 回答
33

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

请参阅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) }
于 2016-12-14T12:12:16.120 回答
33

**为 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()

PS:由于我有一些赞成票,我想推荐阅读有关转义闭包的阅读。

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

对于那些在捕获列表中使用 [weak self] 的人,请注意 self 可能为 nil,所以我要做的第一件事是使用警卫语句检查它

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

如果你想知道引号是什么,这self是一个在闭包内使用 self 而无需将名称更改为thisweakSelf或其他什么的专业技巧。

于 2016-07-28T11:37:37.593 回答
26

使用捕获列表

定义捕获列表

捕获列表中的每一项都是 weak 或 unowned 关键字与对类实例(例如 self)或使用某个值初始化的变量(例如 delegate = self.delegate!)的引用的配对。这些配对写在一对方括号内,用逗号分隔。

将捕获列表放在闭包的参数列表之前,如果提供了返回类型:

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
}

附加说明

于 2015-10-19T09:22:05.017 回答
14

从 swift 4.2 开始,我们可以这样做:

_ = { [weak self] value in
    guard let self = self else { return }
    print(self) // will never be nil
}()
于 2018-05-05T19:43:16.163 回答
8

斯威夫特 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

于 2019-02-04T19:20:02.180 回答
3

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

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

于 2014-06-28T16:19:39.420 回答
1

From ,如果您在闭包之前通过Swift 5.3,则不必在闭包中解包。self[self]in

请参阅someFunctionWithEscapingClosure { [self] in x = 100 }快速文档

于 2021-02-21T15:41:19.400 回答
0

如果你崩溃了,那么你可能需要[弱自我]

我的猜测是,您正在创建的块仍然以某种方式连接起来。

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

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

看看这是否可以防止崩溃。(这只是一个猜测)。

于 2015-05-11T20:01:29.827 回答
0

【闭包和强引用循环】

如您所知,Swift 的闭包可以捕获实例。这意味着您可以self在闭包内使用。特别是escaping closure[About]可以创建一个strong reference cycle[About]。顺便说一句,您必须明确使用selfinside escaping closure

Swift 闭包具有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- 当您确定实例所有者的生命周期大于关闭时使用它

[弱与无主]

于 2020-04-02T21:09:46.737 回答