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


151

在我的中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]


您能否在下面选择一个答案作为正确答案?还要注意,没有所有权的人不需要在封闭过程中加强自我。在这里,无主总比弱多好,因为单元和视图控制器的生命周期是关联的。
ikuramedia

1
我意识到[unown self]是更好的选择,但是当我使用它时,我的应用程序崩溃了。希望看到一个使用它来结束答案的代码示例。
NatashaTheRobot 2014年

1
从文档中:“像弱引用一样,无主引用不会对其所引用的实例保持强大的控制。但是,与弱引用不同,无主引用将始终具有一个值。”如果您的应用程序崩溃了,可能是因为在运行时将unown应用于了一个零值。
比尔·帕特森

在此处发布保护声明可能比让其绑定到StrongSelf更好。只是说,这就像是完美的候选人:-D
Daniel Galasko 2015年

@NatashaTheRobot,[弱自我]是什么语法?看起来像是在目标C中传递的消息。能否请您在问题中添加更多有关语法的内容。
Vignesh

Answers:


178

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

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

如果您在使用[unown self]时崩溃了,我猜想该封闭过程中的某个时刻self为零,这就是为什么您不得不使用[weak self]的原因

我真的很喜欢手册中的整个部分,内容是在闭包中使用strongweakunowned

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

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

iOS中块(目标C)和闭包(Swift)之间的区别


7
苹果在他们的第一个文档中将C语言扩展称为块“关闭”。(块或闭包最初是C的扩展。只有MM与Objective-C有关。)即使我也更喜欢术语“闭包”,因为C中的“块”与复合语句非常相关,因此在两种语言中都是错误的,因为即使它没有在对象(变量或常量)上闭合,它也被称为闭包。
阿敏·内格姆·阿瓦德

1
非常好的回答:)
iDevAmit

1
我建议不要使用unowned。不值得引起您的应用崩溃的风险。
凯尔·雷德费恩

32

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


2
感谢您命名,我想知道!
rob5408

3
我认为这个答案没有用。如果自我在执行关闭过程中变为零,则[unown self]将崩溃
Yunus Nedim Mehel

3
绝对没有理由以使用无主的,除(1)其他不寻常的情况,性能(这是完全不相关的在这里和编程的99.999%);(2)作为一种风格执法问题。“您应该始终使用弱者,永远不要拥有”的说法是非常合理的。
Fattie

29

**针对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日星期五CST时说,这个技巧不应该用于自我,所以请不要使用它。从@gbk检查非转义的闭包信息和捕获列表答案。**

对于那些在捕获列表中使用[弱自我]的人,请注意,自我可能为零,所以我要做的第一件事是使用保护性声明进行检查

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

如果您想知道引号周围self是一个诀窍,可以在闭包内使用self而不需要将名称更改为thisweakSelf或其他名称。



2
我倾向于将本地“自我”称为“ strongSelf”,以确保它不会与默认自我混淆,并且更容易发现您是否在为自己提供强大的自我参考。
贾斯汀·斯坦利

1
这不应该被使用,因为它是一个编译器错误:lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160118/...
蒂姆Vermeulen的

1
我认为Chris Lattner在以上链接中的评论只是关于不将变量命名为self(在反引号中)。用别的名字命名,例如nonOptionalSelf,就可以了。
OutOnAWeekend

1
如今(SWIFT 4.2){ [weak self] in guard let self = self else { return }可以在没有反引号被使用,实际上是支持:github.com/apple/swift-evolution/blob/master/proposals/...
公园。

26

使用捕获列表

定义捕获列表

捕获列表中的每个项目都是对weak或unown关键字的配对,这些关键字与对类实例的引用(例如self)或用某个值初始化的变量(例如委托= 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
}

附加说明


3
您将unown用作“ self”,这意味着您可以确定在访问“ self”时不会为零。然后,在“ self.delegate”上使用了强制展开(这也意味着您肯定会知道它不会为零)来将其分配给弱变量。如果您确定“ self.delegate”不会为零,那为什么不在unsigned中使用unown而不是弱的呢?
罗尼·莱斯什

26

编辑:引用由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) }

9

从4.2开始,我们可以:

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

其他人也有类似的解决方案,但“ this”是C ++恕我直言。“ strongSelf”是Apple的一项约定,任何浏览您的代码的人都会知道发生了什么。
David H,

1
@ David H IMO这句话strongSelf明确说明了变量的含义/副作用,如果代码的长度较长,则很好。感谢您的意见,但不知道c ++使用了这样的措辞。
eonist

3
由于雨燕4.2的您可以使用guard let self = self else { return }来展开[weak self]github.com/apple/swift-evolution/blob/master/proposals/...
阿梅尔Hukic

@AmerHukic👌。
eonist


3

您可以在捕获参数之前在捕获列表中使用[弱自我]或[无名自我]。捕获列表是可选语法。

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


1
该单元格不是自我,他不在单元格课程中,他可能在一个
视图控制器

0

如果您崩溃了,则可能需要[弱自我]

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

创建一个prepareForReuse,然后尝试清除其中的onTextViewEditClosure块。

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

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


0

封闭和强大的参考周期[关于]

如您所知,Swift的闭包可以捕获实例。这意味着您可以self在闭包内部使用。特别是escaping closure[About]可以创建一个strong reference cycle。顺便说一下,您必须显式使用self内部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 -在确定实例所有者的生存期大于关闭的生存期时使用它
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.