달력

4

« 2024/4 »

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

1. Intro

: 올해 초, 개발한 Library(*.framework 파일)를 다른 팀에 전달했는데 정상적으로 Build가 되지 않아서 고생을 했던 적이 있었다. 우리 팀에서 개발했던 Library를 사용하는 Project에서, Build Setting 항목 중 Other Linker Flags가 -ObjC인 경우 symbol 충돌이 발생하는 것이었다. symbol이 충돌하지 않도록 노가다...를 해서 해결했는데, -ObjC는 대체 왜 넣은 것일까? -ObjC는 대체 어떤 역할을 하는 것일까?

 

 

2. Objective-C로 작성된 Library

: -ObjC를 Other Linker Flags에 넣는 것은, Objective-C로 개발된 특별한? *.framework 파일을 사용할 때 발생하는 문제를 해결하기 위함이다. 문제를 발생시키기 위해 Sample Project를 만들었고, Sample Project의 구조는 다음과 같다.

 

그림1. Sample Project

 

 그림1에 명시된 Sample Project의 주요 특징을 정리해보자.

  1. LinkingTest라는 iOS Application은 SampleKit.framework를 link해서 쓰고 있다.
  2. SampleKit에는 구현한 코드가 Sample.m와 Sample+AdditionalFeature.m(Category에 선언된 Method를 구현함)로 나누어져 있다.
  3. SampleKit은 Static Library이고, Objective-C로 작성된 코드들이 있다.

 

  SampleKit의 헤더와 코드도 살펴보면 다음과 같다.

 

그림2. SampleKit.h
그림3. Sample.m
그림4. Sample+AdditionalFeature.m

 

 Objective-C로 개발된 특별한? *.framework 파일을 사용할 때, 발생할 수 있는 문제를 확인하는 것이 주 목적이므로 작성한 코드는 아주 간단하다. SampleKit을 LinkingTest에서 다음과 같이 사용했다.

 

그림4, Application에서 SampleKit을 사용하는 Code

 

 정말 단순해보이는 그림4의 코드를 실행시키면, Runtime에 Crash가 발생한다. Console에는 다음과 같은 메세지가 출력된다.

=> LinkingTest[49929:5165739] -[Sample outputDescription]: unrecognized selector sent to instance 0x600003d341a0

 

 outputDescription은 Sample+AdditionalFeature.m(category)에 선언된 코드인데 왜 unrecognized selector로 취급받는 것일까?

 

 

3. Crash가 발생하는 이유

: Crash가 발생하는 이유를 찾아본 결과, UNIX static library와 Objective-C의 Dynamic한 특성간 충돌이 있어서 static library에 있는 category method들이 Application에 Link가 되지 않는 이슈가 있다고 한다. 그래서 Runtime에 method를 찾지 못해서, unrecognized selector라는 메세지와 함께 Crash가 발생하는 것이다.

 

 Dynamic한 특성을 제공하기 위해, Method가 호출되기 전까지 method를 구현한 코드가 결정되지 않는다(symbol로만 표시해두고, 나중에 Runtime에 실행할 코드가 결정된다). 그리고 Objective-C는 method를 위한 linker symbol은 정의하지 않고 class를 위한 linker symbol만 정의한다. 예를 들어보면, sample.outputDescription()에서 Sample이라는 class에 대한 symbol은 있지만, outputDescription이라는 method에 대한 symbol은 없는 것이다.

 

 Objective-C의 category는 method들을 모아놓은 것이므로, category의 method들은 symbol을 생성하지 않는다. 그래서 class가 이미 정의된 경우(class는 정의되어 있고, 해당 class에 대한 category를 구현한 코드가 다른 파일인 경우), linker에서는 category에 정의된 것들을 모른다(load하지 않는다)는 것이다.

 

 -ObjC라는 Linker Flag는 linker가 static library에 있는 모든 Objective-C로 작성된 class와 category를 load하도록 한다. 그래서 이 Flag를 사용하면, 이 문제를 해결할 수 있다. 다음과 같이 Flag를 추가할 수 있고, 실행시켜보면 정상동작 하는 것을 확인할 수 있다.

 

그림5. static library를 사용하는 Project에서 Linker Flag에 -ObjC를 추가함

 

 static library에서만 발생하는 문제인 만큼, SampleKit의 Mach-O Type을 Dynamic Library로 변경하면 -ObjC Flag를 추가하지 않아도 문제가 해결된다. 아니면 Sample+AdditionalFeature.m에 구현된 코드들을 Sample.m으로 옮겨도(그러니까 category가 아닌 Sample class에 대한 implementation이 있는 파일과 category가 한 곳에 있을 때) 문제가 해결되지만, 한 파일에 너무 많은 코드가 담겨지게 될 것이다.

 

 

4. Reference

1) https://developer.apple.com/library/archive/qa/qa1490/_index.html

 

:
Posted by syjdev