본문 바로가기
모바일개발(Mobile Dev)/IOS개발(ObjectC)

[iOS 앱 만들기 001] 앱 델리게이트

by 테크한스 2015. 1. 14.
반응형

UIApplication

iOS앱은 Objective-C로 만들어지고, Objective-C는 C언어가 확장된 형태이며, 기본적인 구조는 C와 동일하다. (표준 C로 작성된 코드도 Xcode로 컴파일 할 수 있다.) 따라서 iOS앱 역시 예외없이 main 함수가 프로그램 전체의 본체가 된다. 코드 상으로는 그렇다고 하더라도 iOS앱의 본체는 사실상, 앱 객체로 이는 UIApplication의 인스턴스가 되는데, 앱의 초기 실행 과정을 살펴보면 다음과 같은 절차를 밟게 된다.

  1. main 함수가 실행된다.
  2. main 함수는 다시 UIApplicationMain 함수를 호출한다.
  3. 이 함수는 앱의 본체에 해당하는 객체인 UIApplication 객체를 생성한다.
  4. Info.plist 파일을 읽어들여 파일에 기록된 정보를 참고하여 그외에 필요한 데이터를 로드한다.
  5. 메인 nib 파일을 사용하는 경우 4.의 과정에서 로드된다.
  6. 메인 nib 파일이 없거나, 그 속에 앱 델리게이트가 없는 경우, 앱 델리게이트 객체를 만들고 앱 객체와 연결한다.
  7. 런루프를 만드는 등 실행에 필요한 준비를 마무리해 간다.
  8. 실행 완료를 앞두고 앱 객체가 앱 델리게이트에게application:didFinishLaunchingWithOptions: 메시지를 보낸다.

main.m 파일의 구성

위 내용은 주로 UIApplicationMain 함수가 하는 일을 설명했다. 실제로 이 함수는 앱의 구동의 도화선에 불을 붙이는 역할을 한다고 보면 된다. 이 함수의 원형을 파악하고 있는 것은 iOS앱이 어떤 식으로 로드되고 시작되는지, Xcode 상의 여러 객체들, nib 파일은 도대체 어디서 객체로 바뀌어서 짠하고 나타나는지 등을 풀어나가는 열쇠가 될 수 있다.

int UIApplicationMain(int argc, char *argv[], NSString *principalClassName, NSString * appDelegateClassName)

이 함수는 C함수로, 원형은 위와 같다. main의 고정 파라미터인 argc, argv를 그대로 넘겨받으면서 추가적으로 두 가지 인자를 더 받고 있다.principalClassName은 앱 객체가 될 클래스의 이름을 알려주는 것인데, 아주 아주 예외적이고 특수한 경우가 아니라면 우리는 'UIApplication' 클래스를 서브클래싱할 일이 없다. 따라서 이 인자는 거의 모든 경우에@"UIApplication" 이거나 NSStringFromClass([UIApplication class])가 되는데, 친절하게도 기본적으로 nil을 넣으면 UIApplication으로 처리해준다. 그리고 마지막으로 appDelegateClassName은 앱 델리게이트가 될 클래스의 이름을 의미한다.

만약, 메인 nib 파일을 쓰지 않는 프로젝트를 만들었다면 main.m 파일은 대략 다음과 같은 코드로 생성되어 있을 것이다.

    #import <UIKit/UIKit.h>
    #import "MYAppDelegate.h"

    int main(int argc, const char ** argv)
    {
        @autoreleasepool{
            return UIApplicationMain(argc, argv, nil, NSStringFromClass([MYAppDelegate class]));
        }
    }

메인 nib 파일을 사용하는 경우

메인 nib 파일의 이름은 주로 MainWindow.xib으로 되어 있는데 (.xib 이지만 관례적으로 계속 ‘닙’이라고 읽는다. 게다가 xib를 그대로 읽으려면 이거 원…암튼 영어권에서도 계속 nib 으로 읽는다.) 파일의 이름이 고정되어 있는 것은 아니고 Info.plist 파일에 디폴트로 그렇게 명시되어 있다.

이 프로퍼티 리스트 파일은 주로 <앱이름>-Info.plist라는 파일로 번들에 포함되는데 앱이 처음 론칭될 때 자동으로 읽어들여지고 이 파일에 기록된 여러 설정에 의해 앱의 초기화 방식이 결정되고 그외 다른 여러 곳에 이 값들이 사용될 수 있다.

메인 nib 파일을 사용하는 경우, 앱의 론칭 과정에서 이 nib 파일을 로드해서 그 속에 담겨있는 정보대로 윈도나 뷰 컨트롤러, 각종 뷰와 컨트롤 및 객체들을 복원해 내는데 여기에 앱 델리게이트가 포함되어 있다면 굳이 별도의 앱 델리게이트 객체를 만들지 않아도 된다. 따라서 이 경우라면 위의 main.m 파일은 다음과 같이 차이가 날 것이다.

    #import <UIKit/UIKit.h>
    #import "MYAppDelegate.h"

    int main(int argc, const char ** argv)
    {
        @autoreleasepool{
            return UIApplicationMain(argc, argv, nil, nil);
        }
    }

앱 델리게이트의 역할

앱 델리게이트는 이름 그대로 앱 객체의 대리인 역할을 한다. UIApplication은 서브 클래싱을 하는 경우도 드물고, 별로 그럴 이유도 없으며 그게 쉽지도 않다. 하지만 분명히 앱의 라이프 사이클의 여러 스테이지에서 수행되어야 하는 일은 있다. 따라서 앱 객체 클래스를 직접 서브 클래싱하지 않고 델리게이트를 통해 처리하게 된다. 이러한 패턴처럼 코코아 및 코코아 터치는 상속을 통한 커스텀 클래스를 만들어 나가면서 객체를 확장하기 보다는 다른 이런 언어의 특성들을 사용하여 가능한한 서브 클래싱을 피하는 쪽을 권장한다. (물론 경우에 따라서는 반드시 서브 클래싱 해야하는 객체들도 많이 있다.)

앱의 시작과 종료

앱이 시작되면 앱 델리게이트는application:didFinishLaunchingWithOptions:라는 메시지를 받게 된다. 이 메소드는 앱이 실행 준비를 거의 마쳤을 때 호출되는데, 이 때 앱에 필요한 크리티컬한 데이터의 초기화를 하면 된다. 단, 아직 앱이 화면에 나타나기 이전이므로 시간이 많이 걸리는 작업을 여기서 하면 안된다. 데이터의 초기화를 위해 이 메소드는 거의 모든 경우에 오버라이드 된다.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

특히 메인 nib 파일을 사용하지 않는 앱이라면, 윈도 및 메인 뷰가 없는 상태이므로, 이 메소드에서 생성하여 설치해야 한다.

그리고 앱에 종료되기 직전에는 다시 이를 알려주는 메시지가 전달된다. 앱 델리게이트에서는 잃어서는 안되는 사용자 데이터를 종료 전에 미리 저장해 둘 기회를 얻는 셈이다.

- (void)applicationWillTerminate:(UIApplication *)application

그 외에도 앱 델리게이트는 앱의 상태 변화에 대해 감지할 수 있다. 비활성화 된다거나 잠자기에 들어간다거나 저장된 상태로부터 복구된다거나 하는 것을 알아차리며, iOS 디바이스가 회전하였을 때, 방향에 따라 화면을 회전할 것인지를 결정할 수도 있다.

앱델리게이트의 주요 메소드에 대해서는 UIAppDelegateProtocol 공식 문서를 참고하면 된다.

반응형