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
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.
I'm not certain why, but I think it's working using weak
instead of unowned
. This could be a bug.
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"
}
)
Your self
is getting deallocated before your completion block runs. Don't forget that unowned
is just the same as unsafe_unretained
and will not be zeroed-out. You can try [weak self]
instead but you will have to access it like this:
self?.txtArea!.text = "123"
You best using weak self
in that case, just in case you pop out the view from the navigation stack.
Update: 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.
}
})
I've found that this happens to me when I'm nil'ing out the last strong reference to a variable while inside of a notification closure, causing that object to be deinit'd on the spot, but then the same notification continues to be propagated to other objects-- and one of the objects which is observing that same notification was the deinit'd object- which was referencing self as [unowned self]. Apparently although the object is deinit'd it still tries to execute the block of code associated with the notification and crashes. I've also seen this happen when the last strong reference is nil'd out and a notification is about to be generated that will propagate to that object. It still crashes even though the deinit did a NSNotificationCenter.defaultCenter().removeObserver(self).
In those cases, one solution I've used with some success is to wrap a dispatch_async around the nil like so:
dispatch_async(dispatch_get_main_queue(), { () -> Void in
someVariable = nil
})
This causes the variable that's getting nil'd out to be destroyed after the notification fully propagates. Although it's worked for me once, it seems to still be touchy.
The bottom line: The only solution that seems to have been full-proof for me has been to eliminate the use of the closure, replacing the notification observer with the older object/selector type observer.
I think it's very possible it's a bug with the way [unowned self] works, and that Apple is just going to need to fix it. I've seen other people talk about problems they've had with [unowned self], needing to use [weak self] instead as a workaround. I think it's the same problem.
I ran into the same problem, running Xcode 6.4. Changing owned to weak seems to have solved the problem.
Two things:
I found if the "unowned object" is a object of "Objective-C Class", for example a UIViewController, the program will be crashing. While if the "unowned object" is a object of "Swift Class", it will be OK.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.