【问题标题】:How to Correctly handle Weak Self in Swift Blocks with Arguments如何正确处理带有参数的 Swift 块中的弱自我
【发布时间】:2014-08-19 12:55:21
【问题描述】:

在我的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)
        }
    }
}

当我在cellForRowAtIndexPath方法中使用configure方法时,如何在我传入的块中正确使用weak 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] 一起工作的任何想法?

【问题讨论】:

  • 那您能选择下面的答案作为正确答案吗?另请注意,使用 unowned 你不需要在你的闭包中加强 self 。 Unowned在这里总比weak好,因为你的单元格和视图控制器的生命周期是相互关联的。
  • 我意识到 [unowned self] 是更好的选择,但是当我使用它时我的应用程序崩溃了。希望看到使用它来结束答案的代码示例。
  • 来自文档:“与弱引用一样,无主引用不会对其所引用的实例保持强控制。然而,与弱引用不同的是,无主引用被假定始终具有值. " 如果您的应用程序崩溃,很可能是因为 unowned 在运行时被应用于一个为零的值。
  • 可能比 if let 绑定到 strongSelf 更好。只是说,这就像完美的候选人:-D
  • @NatashaTheRobot,[weak self] 是什么语法?看起来像是在目标 C 中传递的消息。请您在问题中添加更多关于语法的信息。

标签: ios swift retain-cycle


【解决方案1】:

如果 self 在闭包中可以为 nil,请使用 [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

注意:我使用术语 closure 而不是 block 这是较新的 Swift 术语:

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

【讨论】:

  • Apple 在他们的第一个 C 语言扩展文档中将块称为“闭包”。 (块或闭包最初是 C 的扩展。只有 MM 与 Objective-C 相关。)即使我也更喜欢术语“闭包”,因为 C 中的“块”经常与复合语句相关,它在这两种语言中都是一种错误,因为它被称为闭包,即使它不关闭对象(变量或常量)。
  • 回答得很好:)
  • 我建议永远不要使用unowned。不值得冒导致应用崩溃的风险。
【解决方案2】:

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

【讨论】:

  • 感谢命名,我想知道!
  • 我认为这个答案没有用。 [unowned self] 如果在闭包执行期间 self 变为 nil 会崩溃
  • 除了 (1) 在非常不寻常的情况下,绝对没有理由使用 unowned 来提高性能(这在这里完全不相关,并且在 99.999% 的编程中)和(2)作为样式执行问题。 “你应该总是使用weak, never unowned”这句话是非常合理的。
【解决方案3】:

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

P.S.:由于我有一些赞成票,我想推荐有关 escaping closures 的阅读。

已编辑:正如@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 的专业技巧或其他。

【讨论】:

【解决方案4】:

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

LightMan's solution。到目前为止,我一直在使用:

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

【讨论】:

    【解决方案5】:

    使用Capture list

    定义捕获列表

    捕获列表中的每个项目都是弱或无主的配对 关键字引用类实例(如 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
    }
    

    additional explanations

    【讨论】:

    • 您将 unowned 用于“self”,这意味着您确定“self”在访问时不会为 nil。然后你在“self.delegate”上使用了强制解包(这也意味着你肯定知道它不会是零)将它分配给一个弱变量。如果你确定 "self.delegate" 不会为 nil,为什么不在 "delegate" 上使用 unowned 而不是 weak?
    【解决方案6】:

    从 swift 4.2 开始? 我们可以做到:

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

    【讨论】:

    • 其他人也有类似的解决方案,但“this”是 C++ 恕我直言。 “strongSelf”是 Apple 的惯例,任何看过你代码的人都会知道发生了什么。
    • @David H IMO 短语strongSelf 明确解释了变量含义/副作用,如果代码具有更冗长的性质,这很好。感谢您的意见,但不知道 c++ 使用了这样的措辞。
    • 从 Swift 4.2 开始,您可以使用 guard let self = self else { return } 解开 [weak self] : github.com/apple/swift-evolution/blob/master/proposals/…
    • @AmerHukic ? .
    【解决方案7】:

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

    【讨论】:

      【解决方案8】:

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

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

      【讨论】:

      • 单元格不是 self ,他不在单元格类上,他可能在视图控制器上......
      【解决方案9】:

      Swift 5.3 开始,如果在in 之前通过[self],则不必在结束时解开self

      this swift doc中引用someFunctionWithEscapingClosure { [self] in x = 100 }

      【讨论】:

        【解决方案10】:

        如果你崩溃了,那么你可能需要 [weak self]

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

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

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

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

        【讨论】:

          【解决方案11】:

          [Closure and strong reference cycles]

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

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

          [weak vs unowned]

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-03-19
            • 2021-08-27
            • 2015-04-28
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多