awakeFromNib, sorties et Story-boards: la documentation est-elle erronée?
selon le NSObject UIKit Additions référence , les variables de sortie doivent être fixées au moment où awakeFromNib
est appelé (accent toute mine):
L'infrastructure de chargement nib envoie un message awakeFromNib à chaque objet recréé à partir d'une archive nib, mais seulement après que tous les objets de l'archive ont été chargés et initialisés. quand un objet reçoit un message de réveil, il est garanti à avoir toutes ses connexions de sortie et d'action déjà établies.
...
Important: parce que l'ordre dans lequel les objets sont instanciés à partir d'une archive n'est pas garanti, vos méthodes d'initialisation ne doivent pas envoyer de messages à d'autres objets de la hiérarchie. les Messages à d'autres objets peuvent être envoyés en toute sécurité à partir de l'intérieur d'une méthode awakeFromNib.
typiquement, vous mettez en œuvre awakeFromNib pour les objets qui nécessitent des réglages supplémentaires qui ne peuvent pas être effectués au moment de la conception. Par exemple, vous pouvez utiliser cette méthode pour personnaliser la configuration par défaut de n'importe quels contrôles pour correspondre aux préférences de l'utilisateur ou les valeurs dans d'autres contrôles. Vous pouvez également l'utiliser pour restaurer les contrôles préalables de l'état de votre demande.
cependant, cela ne correspond pas à mes tests, du moins en utilisant des Storyboards. Les résultats du test suivant semblent indiquer contredire la documentation:
- créer une nouvelle Application de vue unique dans Xcode.
- faites glisser un second ViewController sur le storyboard.
- donne au premier ViewController un bouton, et crée une séquence modale à partir de ce bouton qui affiche le second ViewController.
- créer un fichier de classe ViewController pour le second ViewController.
- créer un label sur le second ViewController sur le storyboard et créer une sortie appelée
someLabel
à partir de lui à la classe ViewController correspondante. - ajouter le texte suivant
awakeFromNib
mise en œuvre du Controller second ViewController:
.
- (void) awakeFromNib {
[super awakeFromNib];
if (self.someLabel == nil) {
NSLog(@"someLabel property is nil");
}
else {
NSLog(@"someLabel property is not nil");
}
if (_someLabel == nil) {
NSLog(@"_someLabel is nil");
}
else {
NSLog(@"_someLabel is not nil");
}
}
- Exécuter l'application dans le simulateur et cliquez sur le bouton.
quand je fais cela, je constate le suivant enregistré:
2013-07-01 09:24:35.755 test[498:c07] someLabel property is nil
2013-07-01 09:24:35.758 test[498:c07] _someLabel is nil
en conséquence de ce comportement, quand j'ai besoin de mes contrôleurs de vue d'avoir une certaine logique d'initialisation qui implique leurs prises, j'ai besoin d'utiliser un hack comme celui proposé dans la réponse ici afin de pouvoir utiliser les sorties. Si je comprends la documentation correctement, le fait que je suis forcé d'utiliser ce hack est un bug dans le comportement D'UIKit, et je devrait pour pouvoir mettre cette initialisation dans awakeFromNib
et il suffit d'utiliser les prises sans aucun piratage.
Je ne peux trouver aucune autre mention de cette question sur internet, bien que, ce qui semble étrange compte tenu de l'importance fondamentale d'un peu de fonctionnalité cela semble (à moi) être. Je n'ai jamais utilisé de vrais fichiers nib, seulement des story-boards, donc je manque une certaine perspective là-dessus, et la documentation sur ce truc est verbeuse et assez difficile qu'en tant qu'internaute novice pour iOS Je ne suis pas sûr que j'ai compris correctement. Est-ce un véritable UIKit bug, ou Ai - je mal compris la documentation d'une certaine manière-peut-être que cette méthode n'est même pas destinée à être utilisée en conjonction avec storyboards?
2 réponses
Réponse Courte
votre contrôleur de vue et sa hiérarchie de vues sont chargés à partir de fichiers nib séparés à l'exécution.
votre contrôleur de vue est chargé en premier et reçoit awakeFromNib
lorsque son nib est chargé, mais son nib de hiérarchie de vue n'a pas encore été chargé, donc dans awakeFromNib
vous ne devriez pas supposer que des sorties à la hiérarchie de vue ont été définies encore.
votre contrôleur de vue reçoit viewDidLoad
après que sa hiérarchie de vues ait été chargée, donc dans viewDidLoad
vous pouvez supposer que toutes les sorties ont été réglées.
Longue Réponse
quand Xcode construit votre application, il compile votre storyboard. Le résultat est un paquet (un dossier que Finder traite comme un fichier) contenant un Info.plist
et un tas de fichiers .nib
. Exemple tiré d'un de mes projets:
:; pwd
/Users/mayoff/Library/<snip>/Pinner.app/Base.lproj/Main.storyboardc
:; ll
total 80
drwxr-xr-x 10 mayoff staff 340 May 11 22:13 ./
drwxr-xr-x 4 mayoff staff 136 May 11 22:13 ../
-rw-r--r-- 1 mayoff staff 1700 May 11 22:13 AccountCollection.nib
-rw-r--r-- 1 mayoff staff 1110 May 11 22:13 AccountEditor.nib
-rw-r--r-- 1 mayoff staff 2999 May 11 22:13 BYZ-38-t0r-view-8bC-Xf-vdC.nib
-rw-r--r-- 1 mayoff staff 439 May 11 22:13 Info.plist
-rw-r--r-- 1 mayoff staff 7621 May 11 22:13 LqH-9K-CeF-view-OwT-Ts-HoG.nib
-rw-r--r-- 1 mayoff staff 6570 May 11 22:13 OZq-QF-pn5-view-xSR-gK-reL.nib
-rw-r--r-- 1 mayoff staff 2473 May 11 22:13 UINavigationController-ZKB-z3-xgf.nib
-rw-r--r-- 1 mayoff staff 847 May 11 22:13 UIPageViewController-ufv-JN-y6U.nib
Le Info.plist
mappe les noms de scène en votre storyboard aux nibs correspondants:
:; plutil -p Info.plist
{
"UIViewControllerIdentifiersToNibNames" => {
"AccountCollection" => "AccountCollection"
"UINavigationController-ZKB-z3-xgf" => "UINavigationController-ZKB-z3-xgf"
"UIPageViewController-ufv-JN-y6U" => "UIPageViewController-ufv-JN-y6U"
"AccountEditor" => "AccountEditor"
}
"UIStoryboardDesignatedEntryPointIdentifier" => "UINavigationController-ZKB-z3-xgf"
"UIStoryboardVersion" => 1
}
une scène n'apparaît dans cette liste que si elle a un numéro d'identification du storyboard, ou si une séquence s'y connecte, ou si c'est la scène initiale.
la liste des fichiers nib dans Info.plist
do et non contient les hiérarchies de vues de ces contrôleurs de vue. Chacun de ces fichiers nib contient le contrôleur de vue de sa scène et d'autres objets de haut niveau dans la scène, mais pas la vue vue du contrôleur ou de l'un de ses sous-vues.
un fichier nib séparé contient la hiérarchie des vues pour la scène. Le nom de la hiérarchie de vue nib dérivé des ID d'objet du contrôleur de vue et de sa vue de haut niveau. Vous pouvez voir L'ID d'objet de n'importe quel objet dans votre storyboard dans le "Identity Inspector" dans Xcode. Par exemple, l'ID du contrôleur de vue de ma "account Collection" est BYZ-38-t0r
et L'ID de sa vue est 8bC-Xf-vdC
, donc la hiérarchie de vue pour la scène est en dossier BYZ-38-t0r-view-8bC-Xf-vdC.nib
. Le fichier NIB de la scène contient le nom de sa hiérarchie de vues:
:; strings - AccountCollection.nib |grep -e '-.*-'
UIPageViewController-ufv-JN-y6U
BYZ-38-t0r-view-8bC-Xf-vdC <---------
UpstreamPlaceholder-5Hn-fK-fqQ
UpstreamPlaceholder-8GL-mk-Rao
q1g-aL-SLo.title
si une scène n'a pas de hiérarchie de vues, alors il y aura juste un fichier nib pour le contrôleur de vue et pas de fichier NIB séparé pour la hiérarchie de vue. Par exemple, une scène UIPageViewController
n'a pas de hiérarchie de vue dans le storyboard donc il n'y a pas de NIB de hiérarchie de vue correspondant à UIPageViewController-ufv-JN-y6U.nib
.
alors qu'est-ce que tout cela en rapport avec votre question? voici quoi: lorsque votre application charge une scène à partir du "storyboard", elle charge le fichier nib contenant le contrôleur de vue (et d'autres objets de premier niveau). Lorsque le chargeur nib termine de charger ce fichier nib, il envoie awakeFromNib
à tous les objets qu'il vient de charger. Cela inclut votre contrôleur de vue, mais il ne pas inclure vos vues, parce que vos vues n'étaient pas dans ce fichier nib.
plus tard, quand votre le contrôleur de vue est demandé pour sa propriété view
, il charge le fichier nib contenant sa hiérarchie de vue. Le contrôleur de vue passe lui-même à -[UINib instantiateWithOwner:options:]
comme l'argument owner
. C'est ainsi que le chargeur nib peut connecter des objets dans la hiérarchie de la vue aux sorties et actions du contrôleur de la vue.
quand le chargeur nib termine le chargement de la hiérarchie de vue nib, il envoie awakeFromNib
à tous les objets qu'il vient de charger. Puisque votre contrôleur de vue était not un de ces objets, votre contrôleur de vue ne pas recevoir un message awakeFromNib
en ce moment.
lorsque instantiateWithOwner:options:
retourne, le contrôleur de vue s'envoie le message viewDidLoad
. C'est l'occasion d'apporter des changements à la hiérarchie des vues.
attendent que leur vue soit accessible pour créer réellement leur vue. Puisque le bouton est dans la vue du contrôleur de la vue, il ne sera pas encore instancié.