Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why self is deallocated when calling [unowned self]

Tags:

swift

I'm trying write a simple closure as completion handler, and inside the closure set the text value of a textbox:

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

but on the line where I've defined [unowned self], I'm getting EXC_BREAKPOINT(code=EXC_I386_BPT,subcode=0x0), and it's showing some assembly code as follow:

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) 

I'm not sure what I've done wrong in here, based on the documentation. I've updated the question to contains the whole class. Also I've updated the question to update the text property of TextView on the main thread

like image 826
Amir Rezvani Avatar asked Jun 12 '14 06:06

Amir Rezvani


People also ask

What is the difference between weak self and unowned self?

Use a weak reference whenever it is valid for that reference to become nil at some point during its lifetime. Conversely, use an unowned reference when you know that the reference will never be nil once it has been set during initialization. In general, be very careful when using unowned.

Why do we use weak self?

In Swift, [weak self] prevents closures from causing memory leaks in your application. This is because when you use [weak self], you tell the compiler to create a weak reference to self. In other words, the ARC can release self from memory when necessary.

Where can you use unowned?

This is the case the doc is saying, the instance and the closure will always be mutually referring, and always (conceptually) be deallocated at the same time. You should better use `unowned` in such a case. With using `unowned`, the instance and the closure will always (actually) be deallocated at the same time.

Should we always use weak self in closure?

Using [weak self] is only required within situations in which capturing self strongly would end up causing a retain cycle, for example when self is being captured within a closure that's also ultimately retained by that same object.


1 Answers

Just to be clear about all the answers here--how you should fix this error depends on your needs.

The problem with the code in the original question is that self, which is a UIViewController, is being accessed from an asynchronously executed NSOperation block, and this is being done inside the completion handler of an asynchronous NSURLSessionTask.

By the time the runtime reaches self.textArea!.text, self has become nil. Under what circumstances will a UIViewController be deallocated? When you pop it out of the navigation stack, when it is dismissed, etc. I'm guessing that the code for btnSendRequestTapped(:) above isn't complete and has a popViewController() somewhere.

The solutions are:

1: To use weak instead of unowned. The difference between the two is that weak means the captured object (self) may be nil and turns it into an optional, while unowned means that you are certain self will never be nil and you can access self as is. Using weak means unwrapping self before using it. If it's nil, do nothing in the code.

{[weak self] in     if let strongSelf = self {         // ...         strongSelf.textArea!.text = "123"     } } 

2: The other solution is to cancel the NSURLSessionTask and its completion handler (which is dispatched into an NSOperationQueue property of NSURLSession called delegateQueue) if the UIViewController is being popped out of the navigation stack.

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

3: Keep using [unowned self], but don't pop the view controller until the operation block is done. I personally prefer this approach.

In short, keep self from being deallocated until you're actually done with it.

like image 165
MLQ Avatar answered Oct 23 '22 08:10

MLQ