StackOverflow: awakeFromNib, outlets and storyboards: is the documentation wrong?
우리가 Xcode에서 App을 빌드할 때, 스토리보드를 컴파일하는 과정을 거치게 됩니다
그리고, 스토리보드 컴파일 결과물로 아래와 같이 Info.plist
와 .nib
파일들이 들어있는 package를 얻게 됩니다
:; 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
Info.plist
는 스토리보드의 scene 중 특정 조건에 부합하는 scene과 이에 대응되는 Nib파일이름이 포함됩니다
특정 조건이란, 스토리보드 ID를 가졌거나, Segue의 destination이거나, root scene에 해당하는 경우에만 Info.plist에 포함됩니다
:; 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
}
이제 본격적으로 Nib 파일에 대한 이야기를 해보겠습니다
결론부터 말하면, 스토리보드 컴파일의 결과로 만들어지는 Nib 파일의 종류는 2가지입니다
- (1) Scene의 top-level object만을 저장하는 Nib
- (2) subview 계층을 저장하는 Nib
◼ 위에서 설명한 Info.plist
에 저장되는 Nib을 말합니다
이 nib 파일들은 대응되는 scene에 대한 top-level object 정보를 포함하고 있습니다
(뷰컨이 바로 top-level object에 해당됩니다)
◼ 위 예시에서 보이는 AccountCollection.nib
/ AccountEditor.nib
같은 일반적인 이름의 Nib에 해당합니다
◼ 그리고, 'subview 계층을 저장하는 Nib 파일'의 이름을 가지고 있습니다
:; 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
◼ subview 계층은 별도의 nib 파일에 들어 있습니다
◼ 이 nib 파일의 네이밍은 '뷰컨 ID'과 'top-level subview의 ID'로 지정됩니다
(참고로, object ID는 'Identity Inspector'에서 확인할 수 있습니다)
예로, 'AccountCollection'이라는 scene의 뷰컨 ID가 BYZ-38-t0r
이고, 뷰컨 내 top-level view의 ID가 8bC-Xf-vdC
라고 가정해봅시다
이 scene의 view 계층의 nib 파일이름은 이 둘을 합친 BYZ-38-t0r-view-8bC-Xf-vdC.nib
가 됩니다
◼ 만약 scene이 subview 계층을 가지고 있지 않다면, 뷰컨에 대한 Nib만 갖고 subview 계층에 대한 Nib은 없을 것입니다
Nib file을 소스로 생성되는 객체는 초기화가 완료되면 이 메서드를 호출하게 됩니다
즉, 원래 awakeFromNib()
메서드가 호출된 시점에는 자신이 가진 IBOutlet들이 모두 초기화된 후 연결되어 접근이 가능한 상태여야 합니다
우리가 스토리보드에서 뷰컨 내에 구성한 view들은 awakeFromNib()
가 호출된 시점에 subview까지 전부 정상적으로 초기화되어 자신의 outlet/action에 접근이 가능합니다
하지만, 뷰컨의 awakeFromNib()
시점에는 outlet/action에 접근이 불가합니다.
이는 위에서 설명한 awakeFromNib()의 정의에 어긋납니다. 왜 이런 걸까요?
App이 스토리보드로부터 scene을 로드할 때, 뷰컨같은 top-level object Nib를 로딩합니다
이 때, Nib 로더는 top-level object Nib만 로딩하고 그에 속한 subview 계층 Nib는 로딩하지 않은 채
awakeFromNib()
메시지를 전달하게 됩니다
그래서 뷰컨의 awakeFromNib() 시점에는 outlet/action같은 subview 계층들이 초기화되지 않은 상태인 것입니다
subview 계층의 Nib 파일은 이후에 뷰컨이 loadView()
메서드를 통해 view
프로퍼티에 대한 요청이 올때서야 비로소 로드됩니다
이 과정에서 instantiateWithOwner()
메서드를 통해 subview를 Nib으로부터 unarchiving하고 subview 계층 object들을 owner인 뷰컨의 outlet/action에 연결하게 됩니다
Nib 로더가 subview 계층 Nib 로딩을 끝내면, awakeFromNib()
메시지를 subview 계층 object들에게 전달하게 됩니다
instantiateWithOwner()
가 종료되면 뷰컨에게 viewDidLoad()
에 대한 메시지가 전달됩니다