관리 메뉴

김종권의 iOS 앱 개발 알아가기

3[iOS - swift] 2. 모듈화 개념 - Binary File 개념 (Mach-O, CPU Architectures, Universal binary, lipo command) 본문

iOS 응용 (swift)

3[iOS - swift] 2. 모듈화 개념 - Binary File 개념 (Mach-O, CPU Architectures, Universal binary, lipo command)

jake-kim 2022. 6. 13. 23:57

Tuist로 모듈화 최신 포스팅 글 목록 > https://ios-development.tistory.com/1303


1. 모듈화 개념 - Library vs Framework (static library, dynamic library, static framework, dynamic framework)

2. 모듈화 개념 - Binary File 개념 (Mach-O, CPU Architectures, Universal binary, lipo command)

3. 모듈화 개념 - XCFramework 생성, 사용 방법

4. 모듈화 개념 - Tuist로 프로젝트 관리 방법

C언어에서의 Static Library - https://ios-development.tistory.com/1004

Xcode의 Execute File의 형태

  • Execute File은 Binary File형태로 존재
  • Mac OS에서 대표적인 bin파일 안을 보면 binary file로 존재
// terminal에서 확인 

$ cd
$ open /bin

  • 이 중 echo 파일의 정보를 확인
$ file /bin/echo

echo 바이너리 파일 정보

  • 위 파일 정보를 보면 다양한 단어(?)들이 존재
    • Mach-O
    • universal binary
    • architectures: [x86_64:Mach-O 64-bit arm64e]
  • x86은 인텔에서 만든 CPU 아키텍쳐, arm64는 애플에서 만든 모바일 CPU 아키텍쳐 (현재는 M1, M1x 처럼 노트북에도 탑재)
  • Mach-O와 Universal binary 개념은 아래에서 계속 

Execute File 포멧, Mach-O

  • Mach-O (Mach Object File Format)
    • Mach 기반의 커널 OS에서 사용하는 excuetable file,object file, library 파일 포멧을 의미
      * 커널: 프로그램이 OS 영역에 직접 접근을 못하지만 Kernel을 통해 OS를 제어

Mach-O 용어

  • File Types
    • Executable (앱 실행을 위한 main binary)
    • Dylib (Dynamic library)
    • Bundle (링크될 수 없는 Dylib을 의미하고 dlopen()으로 실행)
  • Image
    • 실행 가능한 dylib, bundle

CPU Architecture

  • Xcode > Project > Build Settings > Architectures
    • 아이폰은 기종에 따라 다른 아키텍쳐의 CPU를 사용
    • 빌드를 하면 Execute file이 생성(Binary File)되고 이 파일을 CPU에서 읽기 떄문에 CPU에 맞는 Architecture 생성이 필요

Architectures

  •  armv7
    • 아이폰: iPhone3S ~ iPhone5s
    • 아이패드: iPad2 ~ iPad4
  • arm64
    • 아이폰: iPhone5S ~ 최신아이폰
    • 아이패드: iPad5 ~ iPad9
  • x86_64
    • 64bit 기기에 대응하는 simulator, mac (32bit는 i386)

* 더 많은 정보는 이곳 참고

Build Active Architectures Only 옵션

  • Yes로 이 옵션을 활성화하면, 해당 기기에 맞는 아키텍쳐용 빌드만 생성 
  • No로 이 옵션을 비활성화하면, 모든 기기에 대응되는 아키텍쳐용 빌드를 생성
  • Debug환경에서는 Build Active Architecture Only를 Yes로하여 디버그 용도만 사용하는 아키텍쳐만 사용하게끔 처리 (default)
    • ex) 시뮬레이터 빌드 - 인텍 맥 일경우 x86_64, 실리콘 맥 일경우 arm64 아키텍쳐로 빌드

Yes인 경우, 모든 디바이스에 해당하는 Architecture를 생성하지 않음

  • Xcode13에서는 시뮬레이터 동작을 위해 인텔 맥(x86_64), 실리콘 맥 CPU(arm64) 모두 지원
$ open /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/SDKSettings.json

Universal Binary (= Universal App)

  • 두 개 이상의 아키텍쳐를 지원하는 excute file (바이너리 파일)
  • 위 Build Active Architecture Only 옵션에서 false로 지정하거나 아래처럼 info.plist에 universal app으로 등록
  • universal app - 하나의 프로젝트를 아이패드와 아이폰을 포함한 모든 버전의 iOS에서 실행되는 앱
  • Apple의 UIRequiredDeivceCapabilities 문서에 따르면, armv7으로 RequiredDeviceCapabilities 세팅을 시도하면 Universal 앱이 가능
  • UIRequiredDeviceCapabilities 값 설정은 Property List에서 사용
  • 프로젝트 폴더에서 Info.plist에 아래 코드를 추가
      <key>UIRequiredDeviceCapabilities</key>
      <array>
        <string>armv7</string>
      </array>​

    적용 완료

Universal Dynamic Framework

  • 이전 1번글에서 알아본대로, dynamic framework는 리소스와 library가 포함된 번들이고, Heap영역에 주소를 저장해놓고 stack영역에서 binary 파일을 사용
  • Universal이 붙은 dynamic framework는 말 그대로 프레임워크가 모든 기기에서 사용가능하도록 유지하는 방법

빌드하며, 프레임워크 바이너리 파일 확인

  • 예제) Cocoapods으로 RxSwift와 Firebase를 받아서 각각 바이너리 파일 정보 확인
target 'ExBinaryFile' do
  use_frameworks!

  pod 'RxSwift'
  pod 'Firebase'
end
  • pod install 후 빌드 (빌드를 안하면 프레임워크 바이너리 파일이 생성되지 않으므로 주의)
  • 프레임워크 위치 확인
    • Pods/Products에서 framework하나를 선택한 후 위치를 복사

  • file 명령어 사용하여 확인
    • architecture, framework 정보 획득 (Dynamic인지 Static인지)
    • 위 주소를 복사한 다음 끝에, /{프레임워크 이름} 을 붙여서 확인
file /Users/jake.k/Library/Developer/Xcode/DerivedData/ExBinaryFile-cusamfjziinzuwgokvoygiojkffz/Build/Products/Debug-iphoneos/FirebaseCore/FirebaseCore.framework/FirebaseCore

(결과) 

  • architecture는 armv7과 arm64를 지원
  • static이 아닌 dynamic 파일임을 확인 가능
/Users/jake.k/Library/Developer/Xcode/DerivedData/ExBinaryFile-cusamfjziinzuwgokvoygiojkffz/Build/Products/Debug-iphoneos/FirebaseCore/FirebaseCore.framework/FirebaseCore: Mach-O universal binary with 2 architectures: [arm_v7:Mach-O dynamically linked shared library arm_v7Mach-O dynamically linked shared library arm_v7] [arm64:Mach-O 64-bit dynamically linked shared library arm64Mach-O 64-bit dynamically linked shared library arm64]
/Users/jake.k/Library/Developer/Xcode/DerivedData/ExBinaryFile-cusamfjziinzuwgokvoygiojkffz/Build/Products/Debug-iphoneos/FirebaseCore/FirebaseCore.framework/FirebaseCore (for architecture armv7):	Mach-O dynamically linked shared library arm_v7
/Users/jake.k/Library/Developer/Xcode/DerivedData/ExBinaryFile-cusamfjziinzuwgokvoygiojkffz/Build/Products/Debug-iphoneos/FirebaseCore/FirebaseCore.framework/FirebaseCore (for architecture arm64):	Mach-O 64-bit dynamically linked shared library arm64

(RxSwift 프레임워크도 확인)

  • Firebase와 동일하게 지원
/Users/jake.k/Library/Developer/Xcode/DerivedData/ExBinaryFile-cusamfjziinzuwgokvoygiojkffz/Build/Products/Debug-iphoneos/RxSwift/RxSwift.framework/RxSwift: Mach-O universal binary with 2 architectures: [arm_v7:Mach-O dynamically linked shared library arm_v7Mach-O dynamically linked shared library arm_v7] [arm64]
/Users/jake.k/Library/Developer/Xcode/DerivedData/ExBinaryFile-cusamfjziinzuwgokvoygiojkffz/Build/Products/Debug-iphoneos/RxSwift/RxSwift.framework/RxSwift (for architecture armv7):	Mach-O dynamically linked shared library arm_v7
/Users/jake.k/Library/Developer/Xcode/DerivedData/ExBinaryFile-cusamfjziinzuwgokvoygiojkffz/Build/Products/Debug-iphoneos/RxSwift/RxSwift.framework/RxSwift (for architecture arm64):	Mach-O 64-bit dynamically linked shared library arm64

Xcode에서 simulator, device 설정에 따라 다른 바이너리 파일 생성

  • 위에서는 Any iOS Device (arm64)를 선택 > 빌드 > 바이너리 파일을 확인했을때의 결과

  • Simulator iPhone 13 Pro를 선택 > 빌드 한다면, Framework의 바이너리 파일이 변경되고 주소도 변경

  • file 명령어로 확인해보면, 예상대로 현재 빌드 환경이 인텔 맥이므로 x86_64 아키텍처로 바이너리 파일 생성
    • (만약 애플 실리콘 맥이면 arm64 바이너리 아키텍쳐로 나올 것)
/Users/jake.k/Library/Developer/Xcode/DerivedData/ExBinaryFile-cusamfjziinzuwgokvoygiojkffz/Build/Products/Debug-iphonesimulator/RxSwift/RxSwift.framework/RxSwift: Mach-O 64-bit dynamically linked shared library x86_64

Lipo 명령어

  • 프레임워크에 저장된 아키텍쳐를 분리하거나, 합칠 수 있는 명령어
  • 특정 Architecture를 제거할 수 있고 추가할 수 있는 명령어
  • 보통 보안 앱과 같은 곳에서 특정 라이브러리는 시뮬레이터만 지원하고(x86_64) 디바이스에는 지원하지 않는 경우가 있는데 이런 경우 앱 아카이빙 시 AppThnining에서 오류가 발생
  • lipo 명령어를 사용해서 Framework의 특정 Architecture를 지워서 해결이 가능
// https://stackoverflow.com/questions/42641806/check-and-remove-unsupported-architecture-x86-64-i386-in-ipa-archive
lipo -remove {architecture} {ProjectFramework_SDK} -o {ProjectFramework_SDK}

 

* 참고

https://stackoverflow.com/questions/42641806/check-and-remove-unsupported-architecture-x86-64-i386-in-ipa-archive

https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/MachOTopics/1-Articles/building_files.html

https://zamcom.tistory.com/286

https://www.innerfence.com/howto/apple-ios-devices-dates-versions-instruction-sets

https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPFrameworks/Frameworks.html

https://namu.wiki/w/Mach(%EC%BB%A4%EB%84%90)

https://ko.wikipedia.org/wiki/ARM_%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98

 

 

Comments