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

时间:2014-06-28 15:33:20

标签: ios swift retain-cycle

在我的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方法时,如何在我传入的块中正确使用弱自我。
这是我没有弱自我的原因:

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]

的任何想法

11 个答案:

答案 0 :(得分:173)

如果 self 在闭包中可能为零,请使用 [弱自我]

如果 self 永远不会在闭包中使用 [无主自我]

如果你在使用 [无主自我] 时崩溃了,我猜在封闭的某个时刻自我是零,这就是为什么你必须选择 [弱自我] < / strong>相反。

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

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

注意:我使用了术语闭包而不是阻止这是更新的Swift术语:

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

答案 1 :(得分:30)

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

答案 2 :(得分:26)

**为Swift 4.2编辑:

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

  Bitmap bwBitmap = Bitmap.createBitmap( bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.RGB_565 );
  float[] hsv = new float[ 3 ];
  for( int col = 0; col < bitmap.getWidth(); col++ ) {
    for( int row = 0; row < bitmap.getHeight(); row++ ) {
      Color.colorToHSV( bitmap.getPixel( col, row ), hsv );
      if( hsv[ 2 ] > 0.5f ) {
        bwBitmap.setPixel( col, row, 0xffffffff );
      } else {
        bwBitmap.setPixel( col, row, 0xff000000 );
      }
    }
  }
  return bwBitmap;

P.S。:由于我有一些上升票,我想建议阅读escaping closures

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

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

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

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

如果您想知道guard let `self` = self else { return } self.doSomething() 周围的引号是什么,那么在封闭内部使用self是一个专业技巧,而无需将名称更改为 this weakSelf 或其他什么。

答案 3 :(得分:25)

使用Capture list

  

定义捕获列表

     

捕获列表中的每个项目都是弱者或无人物的配对   带有对类实例(如self)或a的引用的关键字   用一些值初始化的变量(例如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
}

additional explanations

答案 4 :(得分:23)

编辑:参考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 :(得分:7)

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

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

答案 6 :(得分:7)

Swift 4.2

holder.tvIcon.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Intent intent = new Intent("search_engines");
        intent.putExtra("url", url);
        int PreviousSelectedItem = selectedItem;
        selectedItem = i;

        // Set the static value in the MainActivity 
        // This can be accessed from all other classes
        MainActivity.selectedItem = i;

        intent.putExtra("selected", selectedItem);
        holder.tvIcon.setBackgroundColor(Color.parseColor("#30000000"));
        notifyItemChanged(PreviousSelectedItem);
        notifyDataSetChanged();
    }
});

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

答案 7 :(得分:3)

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

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

答案 8 :(得分:0)

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

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

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

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

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

答案 9 :(得分:0)

[Closure and strong reference cycles]

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

快速闭包具有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-在确保实例所有者的生存期大于闭包期限时使用它

答案 10 :(得分:0)

Swift 5.3 开始,如果在闭包中的 self 之前传递 [self],则不必在闭包中解包 in

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