본문 바로가기
프로그래밍 놀이터/iOS

[iOS Study] 상태 복원

by 돼지왕 왕돼지 2016. 3. 26.
반응형


 [iOS Study] 상태 복원


출처 : 아론 힐리가스의 IOS 프로그래밍


addObserver:selector:name:object:, AppDelegate, application: viewControllerWithRestorationIdentifierPath:coder:, application:shouldRestoreApplicationState:, application:shouldSaveApplicationState:, application:willFinishLaunchingWithOptions, background, child view controller, CODER, command, decodeRestorableStateWithCoder, defaultCenter, encodeRestorableStateWithCoder, header, ignoreSnapshotOnNextApplicationLaunch, indexPathForElementWithModelIdentifier:inView:, IOS, ios study, ios tutorial, mainApplication, memory kill, modal, modelIdentifierForElementAtIndexPath:inView:, node name, nscoder, NSIndexPath, NSNotification, nsnotificationcenter, NSStringFromClass, Path, protocol, restoration class, restoration identifier, restoration identifier array, restorationClass, restorationIdentifier, rootviewcontroller, self class, simulate, simulate memory kill, snapsho ignore, Snapshot, state restoration, TableViewController, tabviewcontroller, UIApplication, UIApplicationDidBecomeActiveNotification, UIApplicationWillresignActiveNotification, UICollectionView, UIDataSourceModelAssociation, UIImageView, UINavigationController, UINavigationViewController, UIScrollView, UITableView, UITextField, UITextView, UIView, UIViewController, UIViewControllerRestoration, UIWebview, viewcontroller, viewControllerWithRestorationIdentifierPath:coder:, window, [iOS Study] 상태 복원, 객체 클래스, 기본값, 노드, 노드 이름, 디코딩, 루트 노드, 마지막, 멀티태스킹 화면, 메모리, 메모리 킬, 모달, 모델 객체 위치, 민감한 정보, 백그라운드, 백그라운드 모드, 복원, 복원 시작 전, 복원 식별자, 복원 식별자 배열, 복원 클래스 미지정, 복원 클래스 지정, 뷰, 비활성, 상태 복원, 스냅샷, 스냅샷 무시, 스크롤 위치, 시스템 메모리, 식별자, 식별자 경로, 식별자 배열, 실행 이미지, 아론 힐리가스, 아카이빙, 앱 델리게이트, 앱 수명, 이름, 인코딩, 자식 뷰 컨트롤러, 재시작, 저장할 데이터, 정지, 제한, 조상, 콘텐츠 오프셋, 클래스, 클래스 문서, 클래스명, 트리, 파일 시스템, 행, 홈


-

앱의 수명은 제한돼 있다.

만약 시스템이 더 많은 메모리가 필요하고 자신의 앱이 백그라운드 상태에 있다면, iOS는 아마 그 앱을 죽여 시스템에 메모리를 반환하도록 할 것이다.

사용자는 이러한 사실을 알 필요가 없다.

항상 앱의 마지막 장면으로 돌아가야 할 것이다.



-

앱에 상태 복원(state restoration)을 적용해야 한다.

상태 복원은 데이터를 저장하는 데 사용했던 아카이빙과 매우 비슷하게 작동한다.

앱이 잠시 중단 상태로 들어가면 뷰 컨트롤러 계층의 스냅샷(snapshot)이 저장된다.

사용자가 다시 열기 전에 앱이 죽었다면 시작 시에 그 상태가 복원될 것이다.

(만약 앱이 죽지 않았다면 모든 것은 메모리에 남아있고 상태를 복원할 필요가 없다.)



-

iOS 시뮬레이터에서 홈 버튼을 눌러 앱이 백그라운드로 진입하도록 한다.

이제 시스템이 그 앱을 죽이는 것과 같이 하기 위해 Xcode 로 돌아가서 정지 버튼을 누른다. (Command + - )

그리고 나서 앱을 다시 시작하면, Memory Kill 을 simulate 할 수 있다.



-

앱이 백그라운드로 들어가면 iOS 는 사용자 인터페이스의 스냅샷 이미지를 찍는다.

그리고 앱을 다시 시작하면 앱이 메모리에 올라올 때까지 이 스냅샷이 실행 이미지로 사용된다.


앱에 상태 복원 기능이 없다면 사용자는 잠시 동안만 앱의 이전 상태를 볼 수 있고 화면은 다시 처음 시작 상태로 바뀔 것이다.



-

상태 복원을 하려면 앱이 종료되기 전에 시스템은 이 트리의 각 노드를 돌면서 “이름이 무엇인가?”, “클래스는 무엇인가”, “저장해야 할 데이터를 가지는가?” 등에 대해 알아봐야 한다.

앱이 종료되는 동안 이 트리의 정보는 파일시스템에 저장된다.



-

실제로 노드의 이름은 복원 식별자(restoration identifier)라 불리고, 이것은 보통 객체의 클래스명이다.

복원 클래스(restoration class)는 보통 객체의 클래스이다.

그 데이터는 객체의 상태를 갖는다.

예를 들어, 탭 뷰 컨트롤러의 데이터는 현재 어느 탭이 선택되었는지를 포함한다.

앱이 재시작하면 시스템은 저장된 정보로부터 뷰 컨트롤러와 뷰의 트리를 다시 만든다.

각 노드는 마음과 같다.


     시스템은 그 노드에 대한 새로운 뷰 컨트롤러를 만들기 위해 복원 클래스를 요청한다.

     새로운 노드에는 복원 식별자 배열이 전해진다. 생성된 노드의 식별자와 모든 조상들의 식별자이다. 이 배열에서 첫 번째 식별자는 루트 노드의 식별자이다. 마지막은 재생성된 노드의 식별자이다.

     새로운 노드는 상태 데이터가 주어진다. 이 데이터는 NSCoder 의 형태로 온다.



-

기본적으로 앱에서 상태 복원은 비활성화돼 있다.

복원을 사용하려면 AppDelegate 에 다음을 구현해주어야 한다.


- (BOOL)application:(UIApplication *)application shouldSaveApplicationState:(NSCoder *)coder{

     return YES;

}


- (BOOL)application:(UIApplication *)application shouldRestoreApplicationState:(NSCoder *)coder{

     return YES;

}







-

앱의 상태가 저장될 때 윈도우의 rootViewController 에 restorationIdentifier 를 요청한다.

루트 뷰 컨트롤러가 restorationIdentifier 를 가지고 있다면 상태를 저장(또는 인코딩)하도록 요청받는다.

그리고 나서 같은 방식으로 자식 뷰 컨트롤러의 처리를 책임진다.

그리고 차례차례 그들의 자식 뷰 컨트롤러에 똑같이 처리하도록 책임을 넘긴다.

이 계층 구조상의 어떤 뷰 컨트롤러가 restorationIdentifier 를 가지고 있지 않다면 그것은(자식 뷰 컨트롤러가 restorationIdentifiers 를 가지고 있든 없든 간에 자식 뷰 컨트롤러도 함께) 저장 상태에서 제외된다.



-

아래와 같이 식별자 세팅이 가능하다.


navController.restorationIdentifier = NSStringFromClass([navController class]);


self.restorationIdentifier = NSStringFromClass([self class]);

self.restorationClass = [self class];



-

복원 클래스를 설정해주지 않으면, 앱 델리게이트가 이들 뷰 컨트롤러의 새로운 인스턴스를 만들도록 요청받을 것이다.



-

상태복원을 사용하는 경우에는, application:willFinishLaunchingWithOptions: 메소드에서 상태 복원이 시작되기 전에window를 설정하는 등의 해야 할 것들을 처리해야 한다.



-

restorationClass 를 지정한 UIViewController 는 header 파일에 UIViewControllerRestoration protocol 을 따르도록 하고,

구현 클래스에서 필수메소드인 아래 method 를 구현해주어야 한다.


+ (UIViewController *)viewControllerWithRestorationIdentifierPath:(NSArray *)path coder:(NSCoder *)coder{

return [[self alloc] init];

}



-

복원 식별자 경로는 뷰 컨트롤러의 상태가 저장되는 순산의 뷰 컨트롤러와 그 조상들을 나타내는 복원 식별자의 배열이다.

그것이 복원 경로를 보여준다.

모달로 표시되는 ViewController 의 복원 경로는 조금 이상하게 보일 수 있다.

UINavigationController 에 포함된 ViewController 가 다른 ViewController를 모달로 띄울 때, 띄우는 ViewController 가 아닌 그 부모에 해당하는 UINavigationController 가 해당 모달을 띄우는 것이다.

따라서 띄우는 ViewController 는 식별자 경로에 없다.


ex) UINavigationController / BNRItemsViewController



-

복원될 뷰 컨트롤러가 복원 클래스를 갖고 있지 않으면 대신에 앱 델리게이트에 해당 뷰 컨트롤러가 요청된다.


- (UIViewController *)application:(UIApplication *)application viewControllerWithRestorationIdentifierPath:(NSArray *)path coder:(NSCoder *)coder{

// 요청받은 ViewController 를 init 해서 return 해준다.

}



-

다른 관련 정보를 유지하기 위해 UIViewController 에 아카이빙과 매우 유사한 방식으로 관련 데이터를 인코딩할 기회가 주어진다.

실제로 두 인코딩 방식은 NSCoder 객체를 사용하여 작동한다.


- (void)encodeRestorableStateWithCoder:(NSCoder *)coder{


}


- (void)decodeRestorableStateWithCoder:(NSCoder *)coder{


}







-

뷰 컨트롤러가 복원 식별자를 가질 뿐만 아니라 UIView 하위 클래스도 뷰에 관한 정보를 확실히 저장하기 위해 복원 식별자를 가질 수 있다.

특히 UICollectionView, UIImageView, UIScrollView, UITableView, UITextField, UITextView, UIWebView 와 같은 하위 클래스들은 그것들의 정보를 저장할 수 있다.


이들 클래스 각각의 문서에는 어떤 정보가 보존되는지 서술돼 있다.

UITableView 의 저장되는 유용한 정보는 테이블뷰의 콘텐츠 오프셋(스크롤 위치)이다.


self.tableView.restorationIdentifier = @“BNRItemsViewControllerTableView”; // that's all !



-

UIDataSourceModelAssociation 프로토콜을 TableViewController 에 설정하면 아래의 메소드들을 구현할 수 있다.


- (NSString *)modelIdentifierForElementAtIndexPath:(NSIndexPath *)path inView:(UIView *)view{


}


- (NSIndexPath *)indexPathForElementWithModelIdentifier:(NSString *)identifier inView:(UIView *)view{


}


위의 메소드들을 구현하면 적절한 모델 객체를 위치, 선택시켜 상태 복원을 도울 수 있다.

앱의 상태가 저장될 때 선택된 행 또는 행들과 관련된 모델 객체의 식별자를 요청한다.

앱이 다시 시작되면 상태 복원 시스템은 그 식별자를 제공하고 그 모델 객체의 인덱스 패스를 요청한다.

재시작 시에 모델 객체는 UITableView 에서 위치가 바뀔지도 모른다.

하지만 매핑이 제대로 되는 한 해당하는 행이 선택된다.



-

시스템은 앱이 백그라운드 모드로 들어가면 앱의 스냅샷을 찍는다.

때때로 재시작 시에 사용자에게 보여줄 것(또한 멀티태스킹 화면에서 앱을 볼 때 사용자에게 보여줄 것)을 제어하길 원할 것이다.


예를 들어, 앱이 민감한 정보를 보여준다면(은행 앱이 계좌번호와 잔고를 보여주는 것처럼) 허가받지 않은 이에게 보이지 않도록 이 정보를 숨기길 원할 것이다.



-

스냅샷을 수정하는 것은 쉽다.

스냅샷이 찍히기 전에 자신이 원하는 화면이 찍히도록 뷰를 갱신한다.



-

NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];

[nc addObserver:self

     selector:@selector(applicationResigningActive:)

     name:UIApplicationWillResignActiveNotification

     object:nil];


[nc addObserver:self

     selector:@selector(applicationBecameActive:)

     name:UIApplicationDidBecomeActiveNotification

     object:nil];


- (void)applicationResigningActive:(NSNotification *)note{

     // 스냅샷 찍히기 전 뷰를 수정

}


- (void)applicationBecameActive:(NSNotification *)note{

     // 앱으로 돌아오기 직전이라, 스냅샷용으로 변경된 뷰를 되돌린다.

}



-

아래 함수를 불러주면 다음 번 실행 시에 스냅샷을 무시하도록 한다.


[[UIApplication mainApplication] ignoreSnapshotOnNextApplicationLaunch];









반응형

'프로그래밍 놀이터 > iOS' 카테고리의 다른 글

[iOS] keyboard type 바꾸기  (0) 2016.09.19
[iOS Study] NSUserDefaults  (2) 2016.03.27
[iOS Study] 코어 데이터  (0) 2016.03.25
[iOS Study] 지역화  (0) 2016.03.17
[iOS Study] UISplitViewController  (0) 2016.03.16

댓글