ios 如何正确处理带有参数的 Swift 块中的 Weak Self

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24468336/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 01:09:12  来源:igfitidea点击:

How to Correctly handle Weak Self in Swift Blocks with Arguments

iosswiftretain-cycle

提问by NatashaTheRobot

In my TextViewTableViewCell, I have a variable to keep track of a block and a configure method where the block is passed in and assigned.
Here is my TextViewTableViewCellclass:

在 my 中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)
        }
    }
}

When I use the configure method in my cellForRowAtIndexPathmethod, how do I properly use weak self in the block that I pass in.
Here is what I have without the weak self:

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

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

UPDATE: I got the following to work using [weak self]:

更新:我得到了以下使用[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

When I do [unowned self]instead of [weak self]and take out the ifstatement, the app crashes. Any ideas on how this should work with [unowned self]?

当我执行[unowned self]而不是[weak self]取出if语句时,应用程序崩溃。关于这应该如何工作的任何想法[unowned self]

回答by TenaciousJay

If selfcould be nil in the closure use [weak self].

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

If selfwill never be nil in the closure use [unowned self].

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

If it's crashing when you use [unowned self]I would guess that self is nil at some point in that closure, which is why you had to go with [weak self]instead.

如果当你使用[unowned self]时它崩溃了,我猜在那个闭包的某个时刻 self 为零,这就是为什么你不得不使用[weak self]代替。

I really liked the whole section from the manual on using strong, weak, and unownedin closures:

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

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

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

Note: I used the term closureinstead of blockwhich is the newer Swift term:

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

Difference between block (Objective C) and closure (Swift) in ios

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

回答by ikuramedia

Put [unowned self]before (text: String)...in your closure. This is called a capture listand places ownership instructions on symbols captured in the closure.

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

回答by LightMan

**EDITED for Swift 4.2:

**为 Swift 4.2 编辑:

As @Koen commented, swift 4.2 allows:

正如@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()

P.S.: Since I am having some up-votes, I would like to recommend the reading about escaping closures.

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

EDITED: As @tim-vermeulen has commented, Chris Lattner said on Fri Jan 22 19:51:29 CST 2016, this trick should not be used on self, so please don't use it. Check the non escaping closures info and the capture list answer from @gbk.**

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

For those who use [weak self] in capture list, note that self could be nil, so the first thing I do is check that with a guard statement

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

If you are wondering what the quote marks are around selfis a pro trick to use self inside the closure without needing to change the name to this, weakSelfor whatever.

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

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

如果你想知道引号周围self是什么是在闭包中使用 self 而不需要将名称更改为thisweakSelf或其他什么的专业技巧。

回答by Ferran Maylinch

EDIT: Reference to an updated solution by LightMan

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

See LightMan's solution. Until now I was using:

请参阅LightMan 的解决方案。到目前为止,我一直在使用:

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

Or:

或者:

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

Usually you don't need to specify the parameter type if it's inferred.

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

You can omit the parameter altogether if there is none or if you refer to it as $0in the closure:

如果没有参数或$0在闭包中引用它,则可以完全省略该参数:

input.action = { [weak self] in
    self?.someCall(
[1,2,3,4,5].forEach { self.someCall(
lazy var someClosure: (Int, String) -> String = {
    [unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) -> String in
    // closure body goes here 
} 
) }
) // call is done if self isn't nil }

Just for completeness; if you're passing the closure to a function and the parameter is not @escaping, you don't need a weak self:

只是为了完整性;如果您将闭包传递给函数并且参数不是@escaping,则不需要weak self

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

回答by gbk

Use Capture list

使用捕获列表

Defining a Capture List

Each item in a capture list is a pairing of the weak or unowned keyword with a reference to a class instance (such as self) or a variable initialized with some value (such as delegate = self.delegate!). These pairings are written within a pair of square braces, separated by commas.

Place the capture list before a closure's parameter list and return type if they are provided:

定义捕获列表

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

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

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

If a closure does not specify a parameter list or return type because they can be inferred from context, place the capture list at the very start of the closure, followed by the in keyword:

如果闭包没有指定参数列表或返回类型,因为它们可以从上下文中推断出来,请将捕获列表放在闭包的最开始,然后是 in 关键字:

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

    self.method(parameter)
}

additional explanations

补充说明

回答by eonist

As of swift 4.2 we can do:

从 swift 4.2 开始,我们可以执行以下操作:

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

回答by Alan McCosh

Swift 4.2

斯威夫特 4.2

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
        }
    }
}

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

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

回答by Rufus

You can use [weak self] or [unowned self] in the capture list prior to your parameters of the block. The capture list is optional syntax.

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

[unowned self]works good here because the cell will never be nil. Otherwise you can use [weak self]

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

回答by Michael Gray

If you are crashing than you probably need [weak self]

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

My guess is that the block you are creating is somehow still wired up.

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

Create a prepareForReuse and try clearing the onTextViewEditClosure block inside that.

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

##代码##

See if that prevents the crash. (It's just a guess).

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

回答by yoAlex5

Closure and strong reference cycles[About]

闭包和强引用循环[关于]

As you know Swift's closure can capture the instance. It means that you are able to use selfinside a closure. Especially escaping closure[About]can create a strong reference cyclewhich. By the way you have to explicitly use selfinside escaping closure.

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

Swift closure has Capture Listfeature which allows you to avoid such situation and break a reference cycle because do not have a strong reference to captured instance. Capture List element is a pair of weak/unownedand a reference to class or variable.

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

For example

例如

##代码##
  • weak- more preferable, use it when it is possible
  • unowned- use it when you are sure that lifetime of instance owner is bigger than closure
  • weak- 更可取的是,在可能的情况下使用它
  • unowned- 当您确定实例所有者的生命周期大于闭包时使用它