안녕하세요 Noah입니다 :) 오늘은 iOS 어플리케이션의 진입점(Entry Point) 에 대해 알아보며,
기본적인 앱의 구조에 대해 살펴보도록 하겠습니다😃
프로그램 실행은 프로그래밍 언어나 운영체제 등에 의해 정의된 관용명을 통한,
지정된 이름으로 시작될 수 있다. 많은 언어에서(특히 C) 이 지정된 이름은main
이라고 불리는 함수이다.
결과적으로 엔트리 포인트는 main 함수라고 불린다. - 위키백과 -
C를 자주 접해 보신 분이라면 main()
함수가 익숙하실 텐데요,
프로그램의 시작 포인트가 되는 부분을 Entry Point(진입점)라고 합니다.
자바에서는 스태틱 메소드인 main()
이 Entry Point로 사용되고 있으며, react 프로젝트의 경우 src/index.js가 Entry Point로 사용되고 있습니다.
그럼 우리가 공부하고 있는 iOS 어플리케이션에서는 어디가 Entry Point일까요?
이를 알아보기에 앞서 이해를 돕기 위해 iOS 어플리케이션을 개발하기 위해 사용하는 언어
Swift 이전에 사용했던,
Objective-C의 예를 살펴보도록 하겠습니다.
Objective-C는 C언어에 뿌리를 두고 있기 때문에,
Objective-C로 만들어진 iOS 어플리케이션의 Entry Point 역시 main()
함수입니다.
Objective-C로 만들어진 iOS 어플리케이션에서 main()
함수가 하는 일은 다음과 같습니다.
main()
함수가 하는 일main()
함수는 AppDelegate 클래스를 이용해 UIApplicationMain()
함수를 호출하고, 결과로 UIApplication
객체를 반환합니다.
여기서 UIApplication
객체는 도대체 어떤일을 하는 객체일까요?
Every iOS app has exactly one instance of
UIApplication
(or, very rarely, a subclass ofUIApplication
).
When an app launches, the system calls theUIApplicationMain(_:_:_:_:)
function. Among its other tasks, this function creates a singletonUIApplication
object that you access usingshared
.
-apple developer documentation
모든 iOS app은 단 한 개의 UIApplication 인스턴스가 있습니다.
(또는 매우 드물게, UIApplication의 하위클래스)
앱이 시작되면 시스템은UIApplicationMain(_:_:_:_:)
함수를 호출합니다.
이 함수는 다른 task 중에서 singletonUIApplication
객체를 만들고,
shared
클래스 메소드를 이용해 접근합니다.
-애플 개발자 문서
이게 무슨 말일까요?
이름만 들어도 무시무시한 Gang of Four형님들의 디자인 패턴에 나오는 소프트웨어 디자인 패턴입니다.
Singleton Pattern singleton 패턴은 객체의 생성과 관련된 패턴으로서 특정 클래스의 객체가 오직 한 개만 존재하도록 보장한다. 즉 클래스의 객체를 하나로 제한한다. 프로그램에서 이런 개념이 필요할 때는 언제일까? 프린터 드라이버의 예를 들어보자.
여러 컴퓨터에서 프린터 한 대를 공유하는 경우, 한 대의 컴퓨터에서 프린트하고 있을 때 다른 컴퓨터가 프린트 명령을 내려도 현재 프린트하는 작업을 마치고 그다음 프린트를 해야지 두 작업이 섞여 나오면 문제가 될 것이다. 즉 여러 클라이언트(컴퓨터)가 동일 객체(공유 프린터)를 사용하지만 한 개의 객체(프린트 명령을 받은 출력물)가 유일하도록 상위 객체가 보장하지 못한다면 singleton 패턴을 적용해야 한다. 이처럼 동일한 자원이나 데이터를 처리하는 객체가 불필요하게 여러 개 만들어질 필요가 없는 경우에 주로 사용한다. 네이버 지식백과 singleton 패턴 (쉽게 배우는 소프트웨어 공학, 2015. 11. 30., 김치수)
모든 iOS 어플리케이션은 ‘단 한 개’의 UIApplication
인스턴스를 가지는데요,
(인스턴스를 편의상 객체로 표현하겠습니다.)
UIApplication 객체는 앱의 본체라고 할 수 있는 객체입니다.
또한 iOS 어플리케이션에서 생성되는 객체가 1개임을 보장받는 싱글톤 객체이며,
UIApplication
객체는 이벤트 루프나, 앱 동작을 관리할 뿐 아니라,
iOS에서 실행되는 앱의 중앙 컨트롤 타워 격의 역할을 합니다.
자 그러면 UIApplication
객체를 생성하는 UIApplciationMain(_:_:_:_)
함수에 대해 알아볼까요?
func UIApplicationMain(_ argc: Int32,
_ argv: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>,
_ principalClassName: String?,
_ delegateClassName: String?) -> Int
argc
The count of arguments inargv
; this usually is the corresponding parameter tomain
.
argv
A variable list of arguments; this usually is the corresponding parameter tomain
.
principalClassName
The name of theUIApplication
class or subclass.
If you specifynil
,UIApplication
is assumed.
delegateClassName
The name of the class from which the application delegate is instantiated. IfprincipalClassName
designates a subclass ofUIApplication
, you may designate the subclass as the delegate; the subclass instance receives the application-delegate messages. Specifynil
if you load the delegate object from your application’s main nib file.-apple developer documentation
UIApplciationMain(_:_:_:_)
함수는 총 4개의 파라미터를 가지는데요,
우리는 main에 해당하는 파라미터인 argc
, argv
보다는
principalClassName
, delegateClassName
에 집중해 살펴보도록 하겠습니다.
principalClassName
은 UIapplication
의 하위클래스입니다.
nil을 지정할 경우, UIApplication
으로 가정됩니다.
principalClassName
은 UIApplication
을 서브클래싱 하는 경우 지정해주는데요,
중대한 목적이 있다면 서브 클래싱을 하지만 하기도 어렵고,
굳이 할 필요성을 아직까진 느끼지 못해 필요성을 느끼게 된다면 포스팅을 통해 한번 알아보도록 하겠습니다.
delegateClassName
은 Application Delegate가 인스턴스화 되는 클래스의 이름입니다.
(밑에서 알아볼 테니 지금은 넘어가셔도 됩니다!)
principalClassName
이 UIApplication
의 하위 클래스를 지정하는 경우,
해당 하위 클래스를 delegate로 지정할 수 있습니다.
하위클래스 인스턴스는 application-delegate 메시지를 받습니다. 앱의 main nib file에서 delegate 객체를 load 하는 경우 nil을 지정합니다.
자 그럼 delegateClassName
는
Application Delegate가 인스턴스 화 되는 클래스의 이름이라고 하는데요,
iOS 어플리케이션은 기본적으로
우리가 건드릴 수 있는 영역과 우리가 건드릴 수 없는 시스템 프레임워크 영역으로 분리됩니다.
우리는 커스텀 가능한 영역에서 코드를 작성해
우리가 원하는 기능과 유저 인터페이스를 구현해 우리만의 어플리케이션을 만들 수 있습니다.
⇒ 이 부분이 바로 우리가 하는 작업이죠!
우리가 만들 앱은 다양한 기능이 있을 테지만
시스템에서 일어나는 일들을 알 수 없으면
우리가 원하는 기능을 구현할 수 없겠죠?
⇒ 예를 들면 앱이 launch 되는 시점, 종료되는 시점을 알 수 없다던가.. ,등등
시스템에서 일어나는 일들을 알 수 없다면 우리가 만들 앱에 상당한 제약이 많이 생기겠죠??
그럼 우리가 만들 앱에서 시스템에서 일어나는 일들을 알아내기 위해선 어떻게 해야 할까요?
뭔가 생각나지 않으시나요? 아까 위에서 우리 봤었죠..? 그죠..? 하하
다시 가져와 봤습니다 ^__^
UIApplication
객체는 이벤트 루프나, 앱 동작을 관리할 뿐 아니라, iOS에서 실행되는 앱의 중앙 컨트롤 타워 격의 역할을 합니다.
UIApplication
객체는 핵심적인 일을 담당하고 있어 하는 일이 많기도 하고,
아무 곳에나 잘못 접근하면 어플리케이션에 치명적인 문제가 생기겠죠?
그래서UIApplication
은 UIApplication
으로 부터 약간의 권한을 부여받은
Application Delegate라는 대리인을 내세워 커스텀 코드와 시스템과의 상호작용을 담당하게 합니다.
다음 그림을 보면 이해가 더 잘 가실 거예요!!
그림을 보면 커스텀 객체인 Application Delegate가 UIApplication
으로 부터 이벤트를 받고,
application 내의 다양한 요소들과 상호작용하는 모습을 볼 수 있네요!!
우리는 Application Delegate를 통해 시스템의 다양한 이벤트를 우리의 코드와 상호작용시킬 수 있습니다!!
우리 이제 Application Delegate라는 친구가 어떤 역할을 하는지 알았죠?
UIApplication
객체를 생성하는 UIApplciationMain(_:_:_:_)
함수에서
UIApplication
객체에서 Application Delegate가 인스턴스 화 되는 클래스의 이름을 delegateClassName
이라고 했는데요 ,
그 클래스가 바로
우리가 iOS 프로젝트를 생성할 때마다 있었던 바로
요 친구입니다. AppDelegate
main()
함수가 실행되어 UIApplciationMain(_:_:_:_)
함수를 호출해
UIApplication
객체를 생성할 때 “내가 권한을 위임받고 싶은 친구는 이친구야~” 하고
AppDelegate Class를 UIApplication
객체와 연결해주는 것입니다.
AppDelegate라고 되어있던 클래스의 이름을 Noah로 바꾸고,
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
print("내가 Application Delegate다!!!")
return true
}
이곳에 print("내가 Application Delegate다!!!")
를 추가해주었는데요,
이 함수는 앱의 실행 완료 직전에 호출되는 메소드인데요,
콘솔 부분에 정상적으로 내가 Application Delegate다!!!
라는 메시지가
정상적으로 호출되는 것을 확인할 수 있습니다.
여기서는 main()
함수가 실행되어 UIApplciationMain(_:_:_:_)
함수를 호출해
UIApplication
객체를 생성할 때 “내가 권한을 위임받고 싶은 친구는 Noah라는 친구야~” 하고
Noah Class를 Application Delegate로 UIApplication
객체와 연결해주는 것입니다.
자 정리해보면 다음과 같습니다.
main()
함수가 실행된다.main()
함수에서 UIKit의 UIApplciationMain(_:_:_:_)
함수를 호출한다.UIApplicationMain()
함수는 UIApplication
객체를 생성하는 역할을 한다.UIApplication
객체는 info.plist 파일을 바탕으로 앱에 필요한 데이터와 객체를 로드한다.UIApplication
객체와 연결한다.application(_:didFinishLaunchingWithOptions:)
메소드를 호출한다.자 그러면,
Swift는 main()
함수가 따로 없죠?
따라서 Swift 5.3 버전 이전까지는 Application Delegate역할을 할 클래스에
@UIApplicationMain
라는 프로퍼티 래퍼를 작성해
iOS 시스템이 어플리케이션을 실행할 때 이 프로퍼티 래퍼를 찾아
해당 클래스를 Application Delegate로 지정했었습니다.
이렇게 말이죠.
위의 iOS 어플리케이션의 실행과정 1~5번까지의 과정을 @UIApplicationMain
프로퍼티 래퍼 표기로 대체합니다.
@main
이라는 attribute가 생겼는데요,
원래 Swift로 만들어진 프로젝트는
@UIApplicationMain
혹은@NSApplicationMain
말고는 따로 진입점을 명시할 수 없고 최상단에 적힌 코드가 동작하는 방식이었습니다.
Swift 5.3부터는 @main
이라는 attribute를 통해 Swift 프로그램의 진입점을 지정할 수 있게 되었습니다.
class나 struct 에 @main
이 명시된 경우
static func main()
메서드가 실제 진입점이 되고,
(그렇다면 static func main()
을 작성해주어야겠죠?)
이와 마찬가지로
UIAppDelegate
내부에 들어가 보면
다음과 같이 static func main()
이 구현되어 있는 이곳이 진입점이 되어 iOS 어플리케이션의 실행과정 1~5번까지의 과정을 수행합니다.
iOS 13부터는
Scene Delegate라는 개념이 도입되었는데요,
이에 대해서는 Application Life-Cycle 관련 포스트에서 풀어보도록 하겠습니다!!
자, 여기까지 iOS 어플리케이션의 Entry Point에 대해 알아보았습니다!
다들 잘 이해가 가셨길 바라면서, 다음에는 Application의 Life-Cycle을 톺아보도록 하겠습니다!
날씨가 춥습니다, 다들 감기조심하세요!! 😷
혹시 제가 잘못 알고 있는 부분이 있거나, 오타 혹은 궁금한 점 있으시면 댓글로 알려주시면 감사하겠습니다!! 😎
이미지 출처
참고