Sitemap
Junyoung

Junyoung

Static Framework vs Dynamic Framework(2)

12 min readJun 15, 2023

--

Static Framework와 Dynamic Framework 1편 보고오기
위의 게시물로는 완벽하게 이해하는데 부족함이 있어 2편을 작성했습니다!

Static Framework

  • Staitc Framework로 빌드하면 Framework파일을 포함한 App Excutable(앱실행파일)을 만든다
    - Static Framework가 함께 빌드되기 때문에 static framework를 많이 사용하면 할수록 링킹 타임이 오래걸리게 됨. -> 빌드타임 오래걸림
  • Xcode에서 Staitc Framework사용해보기!
    앱 패키지 내에서 앱 실행파일과 Framework파일들을 찾는 방법은
    naljin님의 블로그에 잘 소개되어 있다!
Press enter or click to view image in full size
Mach-O Type에서 원하는 프레임워크 종류를 선택한다(우리는 staticLibrary 로)!
Press enter or click to view image in full size
Embed 타입을 Do Not Embed로 설정 해야함

Do Not Embed의 뜻은 이 프레임 워크의 코드들을 최종 application package에 포함 시키지 않겠다는 의미.
why Do Not Embed? 이미 앱 실행 파일에 Staitc Framework 파일들이 포함되었기 때문에 중복해서 들어가지 않아도 된다.

먼저 Embed&Sign로 설정한 경우의 application package를 봐 보자!

Press enter or click to view image in full size
AppStore로 보이는 게 앱 실행 파일이고, Frameworks파일 안에 Repository.framework가 있는걸 볼 수 있다.

그렇다면 Do Not Embed타입으로 설정한 경우를 보자!

Frameworks파일 안에 Repository.framework가 없다!

Staitc Framework에 media bundle을 사용하는 경우에는 Embed&Sign로 설정해야 한다.

Dynamic FrameWork

  • Dynamic FrameWork는 앱 실행 파일에 포함되지 않고 프로그램에서 동일한 Framework 코드 사본을 공유한다.
  • Xcode에서 Dynamic FrameWork사용해보기!
    위에 절차는 동일하지만 Mach-O Type을 Dynamic으로 Embed타입을 Embed&Sign로 설정해서 최종 application package에 프레임워크 파일들을 포함시켜야 한다. (application package안에 Dynamic FrameWork 코드들이 포함되지 않으면 dyld: Library not loaded에러 발생)
    *dyld는 dynamic loader의 약자*
  • 다이나믹 프레임워크는 각자 실행파일을 만든다.
    그렇기 때문에 앱 실행파일 + 다이나믹 프레임워크 실행파일들 이 메모리에 올라감
  • Dynamic FrameWork를 사용하면 런치 타임이 오래 걸림.
    Apple문서 참고
Press enter or click to view image in full size

첫 번째, 두 번째 문단을 해석해 보면
코드를 실행하기 전에 시스템은 앱의 실행 파일과 앱이 의존하는 모든 라이브러리를 찾아 로드해야 한다.(Dynamic Framework는 최초 사용 시에만 로딩 오버헤드가 발생하고, 이후에는 메모리에 상주하므로 추가 로딩 비용이 없다.) 동적 로더(dyld)는 앱의 실행 파일을 로드하고, 실행 파일 내의 Mach 로드 명령을 검사하여 앱에 필요한 프레임워크와 동적 라이브러리를 찾는다. 그런 다음 각 프레임워크를 메모리에 로드하고, 실행 파일의 동적 심볼을 동적 라이브러리의 적절한 주소를 가리키도록 변환한다.(결국 링킹이 런치타임 때 발생함. 스태틱은 빌드 타임 때 링킹이 일어남)
*symbol은 변수명, 함수명, 클래스명 같은 걸 의미함*

즉, 이해하기 쉽게 다시 해석해 보면
동적라이브러리를 사용하면 dyld를 통해 앱실행파일에서 앱에서 사용되는 동적 프레임워크를 디스크에서 찾아 메모리에 로드 시키고, Framework 내부의 심볼들을 실제 메모리 주소와 연결(동적링킹), 동적 라이브러리의 적절한 주소를 가르키도록 앱 실행파일에서 dynamic symbols을 확인하는 작업들이 이뤄지기 때문에 다이나믹 라이브러리를 많이 사용하면 런치타임이 오래걸린다.

Get Junyoung’s stories in your inbox

Join Medium for free to get updates from this writer.

근데 Static framework도 런치타임에 메모리에 로드 될텐데 왜? Dynamic Framework가 더 부하가 클까?
- Static Framework는 앱 실행파일에 포함되어 있기 때문에 런치타임에 앱 실행파일 하나만 메모리에 로드하면 되기 때문에 Dynamic Framework처럼 사용중인 프레임워크들을 찾아서 메모리에 로드하지 않아도 되기 때문에 부하가 작음. Static Framework는 빌드타임 때 링킹이 일어나 모든 심볼의 메모리 주소가 확정되어 실행파일에 직접 기록되므로, 런치 타임에 심볼 바인딩 작업이 필요 없다. 반면 Dynamic Framework는 런치 타임에 dyld가 각 심볼을 찾아서 주소를 연결하는 바인딩 작업을 수행해야함.

// 1. static 
// 빌드 타임
func processData() {
let data = NetworkKit.fetchAPI()
// 링커: "fetchAPI는 0x00012340에 있음"
// → 코드에 0x00012340 직접 기록
}

// 런치 타임
// 그냥 실행하면 됨 ✅

// 2. dynamic
// 빌드 타임
func processData() {
let data = NetworkKit.fetchAPI()
// 링커: "fetchAPI는 나중에 찾기"
//→GOT 참조 코드 생성 GOT(Global Offset Table): DynamicFramework에서 사용하는 주소 저장 테이블
}

// 런치 타임
// 1. NetworkKit 로드
// 2. fetchAPI 주소 확인: 0x7fff12340
// 3. GOT 업데이트
// 4. 이제 실행 가능 ✅

Conclusion

어떤 문서에서 런치타임이 길어지는 것을 막기 위해Dynamic FrameWork 갯수를 제한하고 Staitc Framework와 섞어서 사용하라고 권장을 했는데.. 문서를 못찾겠다.. 찾으면 링크를 첨부해 두겠습니다!
Staitc Framework와 Dynamic FrameWork를 적절히 섞어서 잘 사용하자!

분배전략
그렇다면 어떤 방식으로 static과 dynamic을 분배하는 것이 좋을까?

  1. Static(빌드 타임 증가, 바이너리 크기 증가, 중복 링크(같은 Static Library가 여러 곳에 복사되는 현상) 가능성 있음)
  • 여러곳에서 사용하지 않고 해당 프레임워크를 의존하는 모듈이 1개인 경우(여러곳에서 사용하면 중복으로 포함됨).

2. dynamic(런치타임 로딩, 앱 시작 속도 영향 있음)

  • 여러 모듈에서 공유되는 라이브러리 (중복 방지)

3. 복사와 참조의 차이


Alamofire (외부 라이브러리)
↑
NetworkModule (Alamofire 사용)
↑
App (NetworkModule 사용)

Static Library인 경우

빌드 시:
1. Alamofire가 Static인 경우
┌─────────────────────────────┐
│ App.app │
│ ┌───────────────────────┐ │
│ │ NetworkModule │ │
│ │ ┌─────────────────┐ │ │
│ │ │ Alamofire │ │ │ ← 코드가 복사되어 들어감
│ │ └─────────────────┘ │ │
│ └───────────────────────┘ │
└─────────────────────────────┘
Alamofire 코드가 NetworkModule에 복사되고,
그게 다시 App에 복사됨. Alamofire가 바뀌면 복사된 코드를 모두 갱신해야 하니까 전부 재빌드.

2.Alamofire가 Dynamic인 경우
NetworkModule이 static이어도 Alamofire 코드가 복사된 게 아니라 심볼 참조만 들어있음.
Alamofire.framework만 교체하면 끝. 재빌드 불필요.
┌─────────────────┐
│ App.app │
│ ┌───────────┐ │
│ │ Network │ │ ──참조──→ Alamofire.framework
│ │ Module │ │ (별도 바이너리)
│ │ (static) │ │
│ └───────────┘ │
└─────────────────┘

Dynamic Library인 경우
빌드 시:
┌─────────────────┐
│ App.app │ ──참조──→ NetworkModule.framework
└─────────────────┘ │
참조
↓
Alamofire.framework
각각 별도 바이너리로 존재.
App은 NetworkModule을 런타임에 로드만 하니까,
NetworkModule 내부가 바뀌어도 인터페이스가 같으면 App 재빌드 불필요.
Press enter or click to view image in full size
// 외부 라이브러리 Static 참조
Alamofire (Static)
NetworkFeature (Static) → Alamofire
ImageFeature (Static) → Alamofire
CacheFeature (Static) → Alamofire

HostApp
├── NetworkFeature
├── ImageFeature
└── CacheFeature

결과:
MyApp (실행파일)
├── NetworkFeature 코드
├── Alamofire 코드 (from NetworkFeature)
├── ImageFeature 코드
├── Alamofire 코드 (from ImageFeature) ← 중복!
├── CacheFeature 코드
└── Alamofire 코드 (from CacheFeature) ← 중복!

Alamofire가 3번 중복!
앱 크기: +16MB (불필요)

// 외부 라이브러리 Dynamic 참조
Alamofire (Dynamic) ← 변경!
NetworkFeature (Static) → Alamofire (Dynamic 참조)
ImageFeature (Static) → Alamofire (Dynamic 참조)
CacheFeature (Static) → Alamofire (Dynamic 참조)

HostApp
├── NetworkFeature
├── ImageFeature
└── CacheFeature

결과:
MyApp.app/
├── MyApp (실행파일)
│ ├── HostApp 코드
│ ├── NetworkFeature 코드
│ ├── ImageFeature 코드
│ └── CacheFeature 코드
└── Frameworks/
└── Alamofire.framework ← 한 번만!

Alamofire는 중복 안됨 (물리적으로 파일 1개)
여러 모듈이 같은 Alamofire.framework 파일 참조
Alamofire 중복 없음!

Mergeable Libraries

  • Xcode 15이상부터 사용가능한 라이브러리 설정이다.
  • 디버그에서는 다이나믹, 릴리즈에서는 스태틱으로 동작한다.
  • 개발자의 입장에서는 빌드속도가 느린걸 참을 수 없으니 다이나믹을 사용하지만, 다이나믹을 많이 사용하다보면 느린 런치속도는 사용자 경험에 좋지 않다. 그렇기 때문에 런치 타임에 영향이 없도록 릴리즈에서는 스태틱라이브러리 형태로 동작하게 도와준다.
  • 설정 방법(릴리즈 빌드 시, 다이나믹 라이브러리들을 머지함)
    1. 머지 될 프레임 워크 > Build Settings → Build Mergeable Library → Yes
    2. 앱 타겟 > Build Settings → Create Merged Binary → Automatic 또는 Manual
    3. Manual vs Automatic
    Automatic: Build Settings에 Build Mergeable Binary = Yes 인 프레임 워크들을 xcode가 병합함
    Manual: 개발자가 명시적으로 지명한 머지에이블 프레임워크들만 메인 바이너리에 병합

참고

--

--