Pourquoi l'autonomie est libéré lors de l'appel [propriétaire de soi]
Je suis en train d'écrire une simple clôture en tant que gestionnaire d'achèvement, et à l'intérieur de la fermeture de définir la valeur de texte d'une zone de texte:
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()
}
}
mais sur la ligne où j'ai défini [propriétaire d'auto], je suis EXC_BREAKPOINT(code=EXC_I386_BPT,subcode=0x0)
, et c'est en montrant un peu de code assembleur comme suit:
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)
Je ne suis pas sûr de ce que j'ai fait de mal ici, basée sur la documentation. J'ai mis à jour la question contient l'ensemble de la classe.
Aussi j'ai mis à jour la question de mettre à jour la propriété text de TextView sur le thread principal
- C'est exactement ce que le
[unowned self]
est pour. - reportez-vous ici developer.apple.com/library/prerelease/ios/documentation/swift/...
- Votre objet est libéré avant la completionHandler est invoquée. Vous avez besoin de garder une référence à quelque part.
- pouvez-vous nous donner plus de contexte pour ce
self
est? - l'auto est une instance de UIViewController et ce code est à l'intérieur de ibaction d'un bouton, appuyez sur
- d'accord, mais pourquoi? selon la documentation d'apple sur la Résolution Forte des Cycles de Référence pour des Fermetures, il ne devrait pas (corrigez-moi si je me trompe)
- J'ai récemment discuté d'un problème semblable, stackoverflow.com/questions/24137090/... Il semble y avoir quelques bug dans le cadre de parce que le contrôleur peut apparemment être libéré alors qu'il y a toujours des références (par exemple, quand ils sont encore sur l'écran).
- BTW, vous n'avez pas besoin
[unowned self]
ici, car il n'y a pas de conserver cycle de toute façon - le contrôleur ne conserve pas la fermeture. - vous avez raison, mais j'ai changé d'fermeture à être un [@]paresseux propriété de UIViewController classe et l'a transmis à dataTaskWithURL comme completionHandler paramètre, toujours la même question, comme vous l'avez mentionné, je crois que c'est un bug dans le cadre
- La même erreur dans l'une des circonstances différentes (je l'ai fait volontairement un cycle de conserver seulement pour des fins de test ) ... Il y a une réponse qui explique en partie le problème (je dis en partie, car il n'y a pas d'explication détaillée de ce qui se passe sous le capot et pourquoi il est aléatoire etc) Encore, il peut vous donner une idée de ce qui se passe.
Vous devez vous connecter pour publier un commentaire.
Juste pour être clair au sujet de toutes les réponses ici, de la façon dont vous devriez corriger cette erreur dépend de vos besoins.
Le problème avec le code dans la question d'origine, c'est que
self
, qui est unUIViewController
, est accessible à partir d'un de manière asynchrone exécutéNSOperation
bloc, et ce qui est fait à l'intérieur du gestionnaire d'achèvement d'un asynchroneNSURLSessionTask
.Par le temps de l'exécution atteint
self.textArea!.text
,self
est devenunil
. Dans quelles circonstances unUIViewController
être libéré? Lorsque vous sortez de la pile de navigation, lorsqu'il est rejeté, etc. Je suppose que le code pourbtnSendRequestTapped(:)
ci-dessus n'est pas complet et a unpopViewController()
quelque part.Les solutions sont:
1: À utiliser
weak
au lieu deunowned
. La différence entre les deux est queweak
signifie la capture d'objet (self
) peut êtrenil
et la transforme en option, tandis queunowned
signifie que vous êtes certainself
ne sera jamaisnil
et vous pouvez accéder àself
comme est. À l'aide deweak
moyens de déballage deself
avant de l'utiliser. Si c'est nul, de ne rien faire dans le code.2: L'autre solution est d'annuler la
NSURLSessionTask
et son gestionnaire d'achèvement (qui est distribué dans unNSOperationQueue
propriété deNSURLSession
appelédelegateQueue
) si leUIViewController
est sauté hors de la pile de navigation.3: Gardez à l'aide de
[unowned self]
, mais ne pas afficher la vue-contrôleur jusqu'à ce que l'opération de bloc est terminé. Personnellement, je préfère cette approche.En bref, garder
self
d'être libéré jusqu'à ce que vous êtes en train de faire avec elle.weakSelf
doit être appeléstrongSelf
.Je ne suis pas certain pourquoi, mais je pense que c'est en travaillant à l'aide de
weak
au lieu deunowned
. Cela pourrait être un bug.[weak self]
et puis, à l'aide deself!
doivent être identiques pour faire[unowned self]
et puis, à l'aide deself
. Tout écart doit être un bug.Votre
self
est d'obtenir libéré avant la fin de bloc s'exécute. N'oubliez pas queunowned
est la même chose queunsafe_unretained
et ne sera pas remis à zéro-out. Vous pouvez essayer de[weak self]
à la place, mais vous devrez y accéder comme ceci:weak self
? Ce n'est pas un propriétaire de référence, il ne peut pas planter, mais c'est encore de ne pas aller travailler.self
a clairement été libéré il y a donc deux possibilités ici, 1) Pour éviter de s'écraser en utilisant[weak self]
et laissezself
à zéro lorsqu'il est relâché; ou 2) Garder une référence forte àself
ailleurs jusqu'à la fermeture des pistes. La façonself.txtArea
a été appelé suggère queself
est unUIViewController
, qui vous ne devriez probablement pas le garder en vie plus longtemps que le point de vue de la hiérarchie de la gardeunowned
n'est PAS le même queunsafe_unretained
et il SERA remis à zéro-out. C'est la façon dont ils peuvent garantir à se bloquer lorsque vous accédez à elle après que l'objet est libéré.unowned
va à un état indéfini, au lieu d'être égal à zéro. De toute façon, il se bloque de façon fiable pour accéder à cet état, au moins dans la version debug.Vous meilleur à l'aide de
weak self
dans ce cas, juste au cas où vous pop sur le point de vue de la pile de navigation.J'ai trouvé que ce qui m'arrive quand je suis nul avec les derniers forte référence à une variable à l'intérieur d'une notification de fermeture, causant de cet objet deinit serais sur place, mais ensuite, la même notification continue à être propagée à d'autres objets, et l'un des objets est l'observation de cette même notification a été le deinit avais objet qui faisait référence à soi en tant que [propriétaire de soi]. Apparemment, même si l'objet est deinit l'avais toujours essaie d'exécuter le bloc de code associé à la notification et les accidents. J'ai aussi vu cela se produire lorsque la dernière référence forte est nul, une suite et une notification est sur le point d'être créé pour être propagées à cet objet. Il se bloque toujours, même si le deinit fait un NSNotificationCenter.defaultCenter().removeObserver(auto).
Dans ces cas, une solution que j'ai utilisée avec un certain succès, est d'enrouler une dispatch_async autour du néant comme suit:
Cela provoque la variable qui se néant suite à être détruits après la notification entièrement propage. Bien qu'il a travaillé pour moi une fois, il semble toujours être délicat.
La ligne du bas: La seule solution qui semble avoir été plein de preuve pour moi a été d'éliminer l'utilisation de la fermeture, remplacement de la notification de l'observateur avec les plus âgés de l'objet/sélecteur de type d'observateur.
Je pense que c'est très possible, c'est un bug avec la façon dont [propriétaire d'auto] œuvres, et qu'Apple va juste besoin de le fixer. J'ai vu d'autres personnes de parler des problèmes qu'ils ont eu avec [propriétaire d'auto], la nécessité d'utiliser [faible auto] plutôt que comme une solution de contournement. Je pense que c'est le même problème.
J'ai rencontré le même problème, l'exécution de Xcode 6.4. Modification de la propriété de la faiblesse semble avoir résolu le problème.
Deux choses:
J'ai découvert que si le "propriétaire de l'objet" est un objet Objective-C Classe", par exemple un UIViewController, le programme sera de s'écraser. Tandis que si le "propriétaire de l'objet" est un objet de Swift "Classe", il sera OK.