프로젝트를 하나 생성하는 순간 우리는 @main이라는 것을 찾아볼 수 있다.
기본적으로 생성되어 있는 UIResponder와 UIApplicationDelegate를 상속한 AppDelegate라는 클래스 위에 @main이라는 키워드가 붙어 있는 것을 볼 수 있다.
(SwiftUI에서는 App을 상속받은 구조체 위에 @main이라는 키워드가 붙어 있다)
그렇담, @main이라는 것이 무엇이며 어떤 역할을 하는지 천천히 알아보자
iOS 애플리케이션의 Entry Point
Entry Point란 프로그램이 시작하는 지점, 즉, 진입점을 뜻하는데
Swift 컴파일러는 class나 struct에 명시된 @main이라는 annotation을 통해 전역범위에 있는 코드를 자동으로 인식하게 되고 프로젝트 내부에 숨겨져 있는 static func main() 메서드가 실제 Entry Point, 즉, 진입점이 되도록 한다.
그렇다면 main() 함수란??
main() 함수
일단 @main을 알기 위해선 main() 함수를 알아야하므로 이것 먼저 설명하겠다!
iOS 애플리케이션에서 main() 함수는
UIApplicationMain() 함수를 호출하고, 그리고 그 호출의 결과로 UIApplication객체를 생성하고 반환하는 역할을 한다.
근데 왜 main() 함수는 프로젝트 내에서 보이지 않는 걸까?
main() 함수는 UIKit 프레임워크 속에 숨어져있 때문. (AppDelegate 클래스에서 채택하는 UIApplicationDelegate 프로토콜의 extension 내부에 존재)
main() 함수가 이렇게 숨어있기 때문에 필요한 게 @main어노테이션이다.
위에서 한 번 언급했던 것처럼 Swift 컴파일러가 @main 어노테이션을 통해 AppDelegate에서 전역범위에 있는 코드를 자동으로 인식하게 되고 Entry Point가 지정되게 한다. 즉, 프로그램의 시작점을 알려주는 역할을 하는 것!
만약 @main 어노테이션을 지우고 빌드한다면?
Entry point (_main) undefined.라고 에러가 발생한다.
main() 함수가 호출하는 UIApplicationMain() 함수의 역할
UIApplicationMain() 함수는 App life cycle에서 매우 중요한 역할을 하는데
UIApplication객체와 Application 객체를 생성해 주며, UIApplication과 Application Delegate를 연결해 주는 역할을 하고 이벤트 사이클을 세팅해 주는 역할을 한다.
Creates the application object and the application delegate and sets up the event cycle.
UIApplicationMain()은 네 가지 파라미터를 가지는데 간단히 설명하면
- argc : argv의 개수
- argv : 앱을 실행할 때에 넘겨줄 변수 목록
- principalClassName : UIApplication 클래스 또는 하위 클래스의 이름. nil을 지정하면 UIApplication이 가정된다.
- delegateClassName : application delegate가 인스턴스화되는 클래스의 이름. PrincipalClassName이 UIApplication의 하위 클래스를 지정하는 경우 해당 하위 클래스를 대리자로 지정할 수 있습니다. 서브클래스 인스턴스는 애플리케이션 위임 메시지를 받습니다. 애플리케이션의 기본 nib 파일에서 위임 개체를 로드하는 경우 nil을 지정합니다.
delegateClassName는 아래에서 Application Delegate를 설명할 때 다시 한번 무엇을 의미하는지 언급하겠다!
UIApplication 객체와 Application Delegate
main() 함수가 UIApplicationMain()함수를 호출함으로써 생성/반환된다는 UIApplication 객체는 무엇일까?
앱의 본체라고 할 수 있는 객체이며, 이벤트 루프나, 앱 동작을 관리, iOS에서 실행되는 앱의 중앙 컨트롤 타워 격의 역할을 한다. 즉,, 매우 중요하다는 뜻!!
모든 iOS 앱에는 정확히 하나의 UIApplication 인스턴스(또는 매우 드물게 UIApplication의 하위 클래스)가 있고 앱이 시작되면 시스템이 UIApplicationMain(::::) 함수를 호출하면 shared를 사용하여 액세스 하는 싱글톤 UIApplication 객체를 생성하는 것이다.
앱의 애플리케이션 객체(UIApplication 객체)에 대해 공식문서에 있는 내용을 요약하자면,
- 들어오는 사용자 이벤트의 초기 라우팅을 처리
- 컨트롤 객체(UIControl 클래스의 인스턴스)에 의해 전달된 작업 메시지를 적절한 대상 객체로 전달
- UIApplication 클래스는 UIApplicationDelegate 프로토콜을 준수하는 대리자를 정의한다. 이 대리자는 UIApplicationDelegate 프로토콜을 준수하기 때문에 이 프로콜의 일부 메서드를 구현해야 한다. 애플리케이션 객체는 이를 통해 앱 실행, 메모리 부족 경고, 앱 종료 등 중요한 런타임 이벤트를 대리자에게 알리고 적절하게 응답할 수 있는 기회를 제공한다.
- UIApplication 클래스의 API를 사용하면 기기별 동작을 관리할 수도 있는데,
- 예를 들어, 원격 알림을 구현할 때 APNs에 푸시 알림 서비스를 등록하려면 싱글톤 UIApplication 객체의 registerForRemoteNotifications() 메서드를 사용해야 한다. ( UIApplication.shared.registerForRemoteNotifications() )
- undo-redo에 대한 UI를 트리거 하고 싶으면 applicationSupportsShakeToEdit 프로퍼티에 값을 지정해 주면 되는데 디폴트가 true로 설정되어 있기 때문에 사용자가 기기를 흔들 때 앱에서 실행 취소 및 다시 실행 버튼을 표시하지 않으려면 속성을 false로 설정하면 된다.
그럼 UIApplication 객체에 대해서는 이해했고, Application Delegate는 뭐길래 UIApplication 객체와 연결시킨다는 것인가?
일단 iOS 애플리케이션은 우리가 코드를 작성해서 커스텀할 수 있는 영역(커스텀 객체)과 우리가 건드릴 수 없는 시스템 프레임워크 영역(시스템 객체)으로 나뉩니다?!
시스템 프레임워크 즉, 우리가 코드로 커스텀할 수 없는 영역에서는 앱이 론칭되는 시점, 종료되는 시점 등의 상황이 일어나는데 이것을 알지 못하면 원하는 앱을 만들지 못합니다. 그러므로, 시스템 프레임워크에서 일어나는 것을 알아낼 수 있는 도구가 필요한데 이때 필요한 게 아까 언급했던 UIApplication과 앞으로 알아볼 Application Delegate에 대한 개념이다.
일단 UIApplication는 애플리케이션에서 한 개 의 객체를 가지고 있다고 했고, 이벤트 루프나, 앱 동작을 관리하고 iOS에서 실행되는 앱에 대한 중앙 집중식 제어 및 조정 지점이라고 했다. 이처럼 UIApplication 객체는 앱에서 매우 중요한 역할을 맡고 있다.
그렇기 때문에 UIApplication 객체에 직접 접근해서 시스템 프레임워크에서 일어나는 일을 알아내는 것은 위험하다고 볼 수 있다. 그래서 UIApplication은 Application Delegate라는 대리인으로 우리가 작성하는 코드와 상호작용을 한다.
그림에서 볼 수 있듯이 커스텀 객체인 Application Delegate가 UIApplication로부터 이벤트를 받고 다양한 요소들과 상호작용하고 있다. 즉, 우리는 Application Delegate를 통해 시스템프레임워크의 다양한 이벤트를 우리의 코드와 상호작용 시킬 수 있는 것이다.
Application Delegate를 들으니 생각나는 것이 있지 않은가?
바로 프로젝트 생성 시 기본으로 만들어지는 AppDelegate 파일과 AppDelegate클래스가 생각날 것이다.
자 그럼 아까 살펴봤던 UIApplicationMain()이라는 함수에서 delegateClassName라는 파라미터는 UIApplication 객체의 대리자인 Application Delegate가 인스턴스화되는 클래스 이름을 넣는 것이라고 했다.
그 클래스가 바로 프로젝트 생성시 기본으로 생성되어 있는 AppDelegate 파일에 있는 AppDelegate클래스인 것이다.
@main, @UIApplicationMain
자 다시 본론으로 돌아와서 앱에서는 시작 진입점을 지정하기 위해 어노테이션을 사용한다고 했다.
@main 또는 @UIApplicationMain 태그를 사용할 수 있다.
@main
//@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate { }
앞에서 설명하던 @main 말고 @UIApplicationMain는 또 무엇인가 하면~~
Swift 5.3 버전 이전까지 사용했던 거라고 보면 되는데, Application Delegate역할을 할 클래스에 @UIApplicationMain 라는 프로퍼티 래퍼를 작성해 iOS 시스템이 애플리케이션을 실행할 때 이 프로퍼티 래퍼를 찾아 해당 클래스를 Application Delegate로 지정했었다.
Swift 5.3부터는 @main이라는 attribute를 통해 Swift 프로그램의 진입점을 지정할 수 있게 되었다. class나 struct에 @main 이 명시된 경우 static func main() 메서드가 실제 진입점이 된다.
@UIApplicationMain
- @UIApplicationMain 이 표시된 클래스를 Application Delegate 대리자로 지정
- @UIApplicationMain 어노테이션은 UIApplicationMain 함수 호출 (UIApplication 객체 생성 & UIApplication 객체에 Application Delegate 대리자 지정 )
@main
- 컴파일러는 @main표기가 붙은 class나 struct를 자동으로 main 함수와 합성. static func main() 메서드가 실제 진입점이 됨.
- @main 어노테이션이 표시된 클래스가 app delegate로 지정된다.
- UIKit framework 가 main 함수를 관리해 주기 때문에 직접 개발자들이 main 함수에 코드를 작성할 필요가 없어졌습니다.
- @main은 프로그램 실행 시작 시 진입점으로 타입을 지정하기 위한 Swift 언어의 기능이다. 사용자는 탑 레벨의 코드를 작성하는 대신 @main단일 유형의 속성을 사용할 수 있다. 또한, @UIApplicationMain 는 클래스에서만 델리게이트를 인스턴스화 가능하지만, @main는 구조체와 클래스 모두 델리게이트를 인스턴스화 가능
@main와 @UIApplicationMain 어노테이션 없이 직접 시작 진입점을 지정하는 방법도 있을까?
직접 시작 진입점을 지정하려면 main.swift 파일을 생성하고 탑레벨 코드로 작성할 수 있다! → 이 경우에는 @main, @UIApplicationMain 어노테이션 지워도 동작함
//main.swift
import UIKit
UIApplicationMain(
CommandLine.argc,
CommandLine.unsafeArgv,
NSStringFromClass(UIApplication.self),
NSStringFromClass(AppDelegate.self)
)
@UIApplicationMain에서 @main으로 바뀐 이유?
Swift 5.3부터 @main으로 바뀌었다고 했는데 그럼 바뀐 이유는 뭘까? 뭔가 좋은 게 있어서 바뀌지 않았을까?
@UIApplicationMain는 앱델리게이트 클래스에만 국한되어 있지만 @main은 AppDelegate 외에 어떤 calss, struct 타입에도 시작점 지정할 수 있다.
이것만 봐서는 @main으로 바뀐 이유가 크게 있는 것 같지는 않아서 어떤 이점이 더 있는지는 모르겠지만 일단 지금까지 아는 바는 이게 끝이당...ㅎㅎ
@main에 대해 요약하자면 @main어노테이션을 통해 전역범위에 있는 코드를 자동으로 인식하게 하고 컴파일러는 @main표기가 붙은 class나 struct를 자동으로 main 함수와 합성하여 프로젝트의 실제 진입점인 main() 함수를 실행하도록 한다.
그 main() 함수에서 UIApplicationMain() 함수를 통해 UIApplication 객체와 delegate를 생성할 때 @main 어노테이션 했던 class나 struct가 app delegate로 지정된다.
이렇게 해서 UIApplication 객체와 Application Delegate가 만들어지고 앱이 시작되게 되는 것이다.
'iOS' 카테고리의 다른 글
[iOS] App Thinning 앱 씨닝과 Slicing, On-Demand Resource, Bitcode (4) | 2024.06.08 |
---|---|
[iOS] image asset의 크기 1x ,2x, 3x 사용하는 이유 | scale factor, 해상도(Resolution), pixel, point (0) | 2024.05.22 |
[iOS] Core Data (3) | codegen(generating code) 이해해보자! (0) | 2023.12.17 |
[iOS] Core Data (2) | CRUD (1) | 2023.12.10 |
[iOS] Core Data (1) | Persistence (1) | 2023.12.09 |