调用[无主自我]时为什么要取消分配自我?

CjC*_*oax 35 swift

我正在尝试编写一个简单的闭包作为完成处理程序,并在闭包内设置文本框的文本值:

class ViewController: UIViewController {

    @IBOutlet var textArea : UITextView

    let sessionConfig = NSURLSessionConfiguration.defaultSessionConfiguration()

    let session:NSURLSession?


    init(coder aDecoder: NSCoder!)  {
        super.init(coder: aDecoder)

        session = NSURLSession(configuration: sessionConfig)
    }


    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }

    @IBAction func btnSendRequestTapped(sender : AnyObject) {

        let url:NSURL  = NSURL(string: "https://www.google.com")

        let sessionTask:NSURLSessionTask =
        session!.dataTaskWithURL(url, completionHandler: {
            [unowned self]
            (data:NSData!,response:NSURLResponse!,error:NSError!) -> Void in
            let st:String = NSString(data: data,encoding: NSUTF8StringEncoding)

            println("\(st)")

            NSOperationQueue.mainQueue().addOperationWithBlock({
                () -> Void in
                self.textArea!.text = st
                })
            })

        sessionTask.resume()
    }
}
Run Code Online (Sandbox Code Playgroud)

但是在我定义[unowned self]的行上,我得到了EXC_BREAKPOINT(code=EXC_I386_BPT,subcode=0x0),并且它显示了一些汇编代码如下:

libswift_stdlib_core.dylib`_swift_abortRetainUnowned:
0x1001bb980:  pushq  %rbp
0x1001bb981:  movq   %rsp, %rbp
0x1001bb984:  leaq   0x176a7(%rip), %rax       ; "attempted to retain deallocated object"
0x1001bb98b:  movq   %rax, 0x792ce(%rip)       ; gCRAnnotations + 8
0x1001bb992:  int3   
0x1001bb993:  nopw   %cs:(%rax,%rax)
Run Code Online (Sandbox Code Playgroud)

根据文档,我不确定我在这里做错了什么.我已经更新了包含全班的问题.此外,我已更新问题,以更新主线程上TextView的文本属性

Mat*_*ros 45

只是为了清楚这里的所有答案 - 你应该如何解决这个错误取决于你的需求.

原始问题中代码的问题在于self,UIViewController异步执行的NSOperation块访问,这是在异步 的完成处理程序内完成的NSURLSessionTask.

到运行时到达时self.textArea!.text,self已成为nil.在什么情况下会UIViewController被解除分配?当你将它从导航堆栈中弹出,当它被解除时,等等.我猜测btnSendRequestTapped(:)上面的代码是不完整的并且有一个popViewController()地方.

解决方案是:

1:用来weak代替unowned.两者之间的区别在于,这weak意味着捕获的对象(self)可以nil将其转换为可选对象,同时unowned意味着您确定self永远不会,nil并且您可以self按原样访问.使用weak意味着self在使用之前解开.如果它是零,则在代码中不执行任何操作.

{[weak self] in
    if let strongSelf = self {
        // ...
        strongSelf.textArea!.text = "123"
    }
}
Run Code Online (Sandbox Code Playgroud)

2:另一种解决方案是,如果从导航堆栈弹出,则取消NSURLSessionTask它的完成处理程序(将被调度到被调用的NSOperationQueue属性中).NSURLSessiondelegateQueueUIViewController

func btnSendRequestTapped() {
    // pop the view controller here...
    sessionTask.cancel()
    sessionTask.delegateQueue.cancelAllOperations()
}
Run Code Online (Sandbox Code Playgroud)

3:继续使用[unowned self],但在操作块完成之前不要弹出视图控制器.我个人更喜欢这种方法.

简而言之,self在实际完成之前不要被解除分配.

  • 在你的(完美的)例子和解释中,变量`weakSelf`应该被称为`strongSelf`. (2认同)

Das*_*ash 9

我不确定为什么,但我认为它正在使用weak而不是unowned.这可能是一个错误.

session.dataTaskWithURL(url, completionHandler: {
        [weak self]
        (data:NSData!,response:NSURLResponse!,error:NSError!) -> Void in
        let st:String = NSString(data: data,encoding: NSUTF8StringEncoding)

        self!.txtArea!.text = "123"
        }
    )
Run Code Online (Sandbox Code Playgroud)

  • 做'[self self]`然后使用`self!`应该与`[unowned self]`相同然后使用`self`.任何差异都将是一个错误. (9认同)

Tal*_*ion 5

weak self在这种情况下最好使用,以防万一您从导航堆栈中弹出视图。

更新:Swift 3.0

let task:URLSessionTask = session.dataTask(with: url, completionHandler: {
        [weak self] (data:Data?, response: URLResponse?, error:Error?) in

        /// Cool tip instead of using *strongSelf*, use ` as:
            if let `self` = self {
               /// Run your code here, this will ensure if self was deallocated, it won't crash.
            }
    })
Run Code Online (Sandbox Code Playgroud)

斯威夫特 2.2

let sessionTask:NSURLSessionTask =
        session!.dataTaskWithURL(url, completionHandler: {
            [weak self] (data:NSData!,response:NSURLResponse!,error:NSError!) -> Void in
                /// Cool tip instead of using *strongSelf*, use ` as:
                if let `self` = self {
                   /// Run your code here, this will ensure if self was deallocated, it won't crash.
                }
            })
Run Code Online (Sandbox Code Playgroud)