[英]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.