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

时间:2014-06-12 06:40:14

标签: 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()
    }
}

但在我定义[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)

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

上的TextView的文本属性

7 个答案:

答案 0 :(得分:41)

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

原始问题中代码的问题是selfUIViewController,正在从异步执行的NSOperation块进行访问,这是在异步 NSURLSessionTask的完成处理程序内完成的。

当运行时到达self.textArea!.text时,self变为nil。在什么情况下会UIViewController被解除分配?当您将其从导航堆栈中弹出,当它被解除时等等。我猜测上面btnSendRequestTapped(:)的代码是否完整并且某处有popViewController()。< / p>

解决方案是:

1:使用weak代替unowned。两者之间的区别在于weak表示捕获的对象(self)可能是nil并将其转换为可选对象,而unowned表示您确定self 1}}永远不会是nil,您可以按原样访问self。使用weak表示在使用self之前展开{[weak self] in if let strongSelf = self { // ... strongSelf.textArea!.text = "123" } } 。如果它没有,请在代码中不执行任何操作。

NSURLSessionTask

2:另一种解决方案是取消NSOperationQueue及其完成处理程序(将其分派到名为{{1}的NSURLSession delegateQueue属性中如果UIViewController被弹出导航堆栈。

func btnSendRequestTapped() {
    // pop the view controller here...
    sessionTask.cancel()
    sessionTask.delegateQueue.cancelAllOperations()
}

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

简而言之,请确保self不被取消分配,直到您真正完成它为止。

答案 1 :(得分: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"
        }
    )

答案 2 :(得分:3)

在完成块运行之前,您的self已被取消分配。不要忘记unownedunsafe_unretained相同,不会被清零。您可以尝试[weak self],但您必须像这样访问它:

self?.txtArea!.text = "123"

答案 3 :(得分:3)

在这种情况下,最好使用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.
            }
    })
  

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

答案 4 :(得分:2)

我发现了&#34;无主要对象&#34;是&#34; Objective-C Class&#34;的对象,例如UIViewController,程序将崩溃。如果&#34;无主要对象&#34;是&#34; Swift Class&#34;的对象,它会没问题。

答案 5 :(得分:2)

我发现当我在通知闭包内部对变量进行最后一次强引用时,这种情况发生在我身上,导致该对象在现场被取消,但之后相同通知继续传播到其他对象 - 其中一个正在观察同一通知的对象是deinit'd对象 - 它将自我引用为[无主自我]。显然,虽然该对象已被删除,但它仍然试图执行与通知和崩溃相关的代码块。我也看到过这种情况发生在最后一个强引用为零并且将要生成将传播到该对象的通知时。即使deinit执行了NSNotificationCenter.defaultCenter()。removeObserver(self),它仍然会崩溃。

在这些情况下,我已经成功使用的一个解决方案就是将dispatch_async包装在nil周围,如下所示:

dispatch_async(dispatch_get_main_queue(), { () -> Void in
   someVariable = nil
})

这会导致在通知完全传播后销毁nil'd的变量。虽然它曾经对我有用,但似乎仍然很敏感。

底线:对我来说似乎完全证明的唯一解决方案是消除闭包的使用,用旧的对象/选择器类型观察者替换通知观察者。

我认为很可能这是[无主的自我]工作方式的错误,并且Apple只需要修复它。我已经看到其他人谈论他们与[无主的自我]有过的问题,需要使用[弱自我]而不是作为一种解决方法。我认为这是同样的问题。

答案 6 :(得分:2)

我遇到了同样的问题,运行Xcode 6.4。改变所有者到弱者似乎已经解决了这个问题。

两件事:

  1. _swift_abortRetainUnowned不会一直发生。
  2. _swift_abortRetainUnowned在分配闭包时发生,而不是在它被执行时发生,因此在我的情况下我非常确定self不会被释放。