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
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.
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With