[英]Why self is deallocated when calling [unowned self]
我正在尝试编写一个简单的闭包作为完成处理程序,并在闭包内部设置文本框的文本值:
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()
}
}
但是在我定义了[ EXC_BREAKPOINT(code=EXC_I386_BPT,subcode=0x0)
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的text属性
只是为了弄清这里的所有答案-如何解决此错误取决于您的需要。
原始问题中的代码存在的问题是,从异步执行的NSOperation
块访问了self
(即UIViewController
,并且这是在异步 NSURLSessionTask
的完成处理程序中完成的。
到运行库到达self.textArea!.text
, self
变为nil
。 在什么情况下会释放UIViewController
? 当您将其从导航堆栈中弹出,被关闭时,等等。我猜测上面btnSendRequestTapped(:)
的代码不完整,并且在某个地方有popViewController()
。
解决方案是:
1:使用weak
而不是unowned
。 两者之间的区别在于, weak
表示被捕获的对象( self
)可能为nil
并将其转换为可选对象, unowned
意味着您确定self
永远不会为nil
并且可以按原样访问self
。 使用weak
意味着在使用self
之前先解开self
。 如果为零,则在代码中不执行任何操作。
{[weak self] in
if let strongSelf = self {
// ...
strongSelf.textArea!.text = "123"
}
}
2:另一种解决方案是取消NSURLSessionTask
和其完成处理程序(其被分派到一个NSOperationQueue
的属性NSURLSession
称为delegateQueue
如果) UIViewController
被弹出导航堆栈的。
func btnSendRequestTapped() {
// pop the view controller here...
sessionTask.cancel()
sessionTask.delegateQueue.cancelAllOperations()
}
3:继续使用[unowned self]
,但是在完成操作块之前不要弹出视图控制器。 我个人更喜欢这种方法。
简而言之,在真正完成self
之前,不要释放self
。
我不确定为什么,但是我认为使用weak
而不是unowned
是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"
}
)
在完成区块运行之前,您的self
已被释放。 不要忘了, unowned
只是一样unsafe_unretained
并不会归零了。 您可以改用[weak self]
但您必须像这样访问它:
self?.txtArea!.text = "123"
在这种情况下,您最好使用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.
}
})
斯威夫特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.
}
})
我发现,当我在通知闭包内取消对变量的最后一个强引用时,这会发生在我身上,导致该对象在现场被取消初始化,但随后相同的通知继续被传播到其他对象-观察到相同通知的对象之一就是已取消初始化的对象-该对象将self称为[unown self]。 显然,尽管该对象已取消初始化,但它仍尝试执行与通知关联的代码块并崩溃。 我也看到过这种情况,当最后一个强引用被取消并且即将生成通知时,该通知将传播到该对象。 即使deinit执行了NSNotificationCenter.defaultCenter()。removeObserver(self),它仍然崩溃。
在这些情况下,我成功使用的一种解决方案是在nil周围包裹一个dispatch_async,如下所示:
dispatch_async(dispatch_get_main_queue(), { () -> Void in
someVariable = nil
})
这将导致在通知完全传播之后被淘汰的变量被销毁。 尽管它曾经对我有用,但它似乎仍然很敏感。
最重要的是:对我来说,唯一可行的解决方案是消除闭包的使用,用较旧的对象/选择器类型观察器代替通知观察器。
我认为这很可能是[无人认领的自我]运作方式的错误,而Apple仅需要对其进行修复。 我见过其他人谈论他们在[无主的自我]时遇到的问题,需要使用[弱的自我]作为解决方法。 我认为这是同样的问题。
我在运行Xcode 6.4时遇到了同样的问题。 从拥有变为弱者似乎已经解决了这个问题。
两件事情:
我发现如果“未拥有的对象”是“ Objective-C类”的对象,例如UIViewController,则程序将崩溃。 如果“无主对象”是“ Swift类”的对象,则可以。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.