常用庫文件格式
-
.a
:靜態庫 -
.dylib
:動態庫 -
.framework
:動靜結合的庫 -
.xcframework
:特定架構下庫
庫(Library)說?了就是?段編譯好的?進制代碼,加上頭?件就可以供別
?使?。
什么時候會?到庫(Library)?
- 某些代碼需要給別?使?,但是我們不希望別?看到源碼,就需要以
庫的形式進?封裝,只暴露出頭?件。 - 對于某些不會進??的改動的代碼,我們想減少編譯的時間,就可以
把它打包成庫,因為庫是已經編譯好的?進制了,編譯的時候只需
要 Link ?下,不會浪費編譯時間。
什么是靜態庫?
靜態庫即靜態鏈接庫:可以簡單的看成?組?標?件的集合。即很多?
標?件經過壓縮打包后形成的?件。Windows 下的 .lib,Linux 和 Mac
下的 .a。Mac獨有的.framework。
缺點:
浪費內存和磁盤空間,模塊更新困難
什么是動態庫?
與靜態庫相反,動態庫在編譯時并不會被拷?到?標程序中,?標程序
中只會存儲指向動態庫的引?。等到程序運?時,動態庫才會被真正加
載進來。格式有:.framework、.dylib、.tdb。
缺點:
會導致?些性能損失。但是可以優化,?如延遲綁定(Lazy Binding)技術
什么是tdb格式?
tbd全稱是text-based stub libraries,本質上就是?個YAML描述的?本?
件。
他的作?是?于記錄動態庫的?些信息,包括導出的符號、動態庫的架構信息、動
態庫的依賴信息
?于避免在真機開發過程中直接使?傳統的dylib。
對于真機來說,由于動態庫都是在設備上,在Xcode上使?基于tbd格式的偽
framework可以??減少Xcode的??。
Framework
Mac OS/iOS 平臺還可以使? Framework。Framework 實際上是?種打包
?式,將庫的?進制?件,頭?件和有關的資源?件打包到?起,?便管
理和分發。
Framework 和系統的 UIKit.Framework 還是有很?區別。系統的
Framework 不需要拷?到?標程序中,我們??做出來的 Framework 哪怕
是動態的,最后也還是要拷?到 App 中(App 和 Extension 的 Bundle 是
共享的),因此蘋果?把這種 Framework 稱為 Embedded Framework。
Embedded Framework
開發中使?的動態庫會被放?到ipa下的framework?錄下,基于沙盒運?。
不同的App使?相同的動態庫,并不會只在系統中存在?份。?是會在多
個App中各?打包、簽名、加載?份。
Mach-o File Format
?個Mach-o?件有兩部分組成:header 和data。
header:代表了?件的映射,描述了?件的內容以及?件所有內容所在的位置。
data:緊跟header之后,由多個?進制組成,one by one。
Load Commands
進制?件加載進內存要執?的?些指令。
這?的指令主要在負責我們 APP 對應進程的創建和基本設置(分配虛擬內
存,創建主線程,處理代碼簽名/加密的?作),然后對動態鏈接庫(.dylib
系統庫和我們??創建的動態庫)進?庫加載和符號解析的?作。
Mach-o File Format
?個Mach-o?件有兩部分組成:header 和data。
header:包含三種類型。Mach header,segment,sections
header內的section描述了對應的?進制信息。
注意?:Mach header屬于header的?部分,它包含了整個?件的信息和segment信息。
Embedded Framework
開發中使?的Embedded Framework會被放?到ipa下的Frameworks?錄下,基
于沙盒運?。
不同的App使?相同的動態庫,并不會只在系統中存在?份。?是會在多
個App中各?打包、簽名、加載?份。
這里我們通過file
查看AFN的.a
文件,打開終端
file libAFNetworking.a
:查看文件的格式
libAFNetworking.a: current ar archive
ar -t libAFNetworking.a
查看.a文件的合集
__.SYMDEF
AFAutoPurgingImageCache.o
AFHTTPSessionManager.o
AFImageDownloader.o
AFNetworkActivityIndicatorManager.o
AFNetworking-dummy.o
AFNetworkReachabilityManager.o
AFSecurityPolicy.o
AFURLRequestSerialization.o
AFURLResponseSerialization.o
AFURLSessionManager.o
UIActivityIndicatorView+AFNetworking.o
UIButton+AFNetworking.o
UIImageView+AFNetworking.o
UIProgressView+AFNetworking.o
UIRefreshControl+AFNetworking.o
WKWebView+AFNetworking.o
clang
NAME
clang - the Clang C, C++, and Objective-C compiler
SYNOPSIS
clang [options] filename ...
DESCRIPTION
clang is a C, C++, and Objective-C compiler which encompasses prepro-
cessing, parsing, optimization, code generation, assembly, and linking.
Depending on which high-level mode setting is passed, Clang will stop
before doing a full link. While Clang is highly integrated, it is
important to understand the stages of compilation, to understand how to
invoke it. These stages are:
clang命令參數:
-x: 指定編譯文件語言類型
-g: 生成調試信息
-c: 生成目標文件,只運行preprocess,compile,assemble,不鏈接
-o: 輸出文件
-isysroot: 使用的SDK路徑
1. -I<directory> 在指定目錄尋找頭文件 header search path
2. -L<dir> 指定庫文件路徑(.a.dylib庫文件) library search path
3. -l<library_name> 指定鏈接的庫文件名稱(.a.dylib庫文件)other link flags -lAFNetworking
-F<directory> 在指定目錄尋找framework framework search path
-framework <framework_name> 指定鏈接的framework名稱 other link flags -framework AFNetworking
將.m編譯成.o文件
//-x 指定編譯語言
clang -x objective-c \
//-target 指定編譯平臺
-target x86_64-apple-macos10.15 \
//編譯成arc環境,10.6一下用-fno-objc-arc
-fobjc-arc \
//指定需要的sdk路徑 并找到自己xcode目錄下的sdk路徑,在這使用的是MacOSX,也可以換成對應的手機或者模擬器
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk \
//鏈接庫
-I./AFNetworking \
//將.m編譯成.o
-c test.m -o test.o
目標文件就可以通過重定位符號表,鏈接重定位。所以我們只需要導入頭文件即可
將.o編譯成可執行文件
clang -target x86_64-apple-macos10.5 \
//編譯成arc環境,10.6一下用-fno-objc-arc
-fobjc-arc \
//指定需要的sdk路徑 并找到自己xcode目錄下的sdk路徑,在這使用的是MacOSX,也可以換成對應的手機或者模擬器
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk \
//鏈接庫
-L./AFNetworking \
-lAFNetworking
//將.m編譯成.o
test.o -o test
靜態庫其實就是一個.o文件的合集
合并靜態庫
libtool
添加或更新靜態庫的目錄表
NAME
libtool - create libraries
ranlib - add or update the table of contents of archive libraries
libtool \
//代表要合并的是一個靜態庫
-static \
//輸出靜態庫的名稱
-o libCat.a \
//需要合并的靜態庫
libAFNetworking.a \
libSDWebImage.a
ar
ar
壓縮目標文件,并對其進行編號和索引,形成靜態庫。同時也可以解壓縮靜態庫,查看有哪些目標文件:
ar -rc a.a a.o
-r: 像a.a添加or替換文件
-c: 不輸出任何信息
-t: 列出包含的目標文件
生成workspace
添加文件之前需要xcode中所有的窗口
動態庫
//支持的架構
ld -dylib -arch x86_64 \
//支持最小的系統版本
-macosx_version_min 10.15 \
-syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk \
//system:dyld相關的鏈接器
-lsystem -framework Foundation \
//符號全部導出
-all_load \
libTestExample.a -o libTestExample.dylib
動態庫是.o文件鏈接過后的產物
什么是tdb格式?
tbd全稱是text-based stub libraries,本質上就是?個YAML描述的?本?
件。
他的作?是?于記錄動態庫的?些信息,包括導出的符號、動態庫的架構信息、動
態庫的依賴信息
?于避免在真機開發過程中直接使?傳統的dylib。
對于真機來說,由于動態庫都是在設備上,在Xcode上使?基于tbd格式的偽
framework可以??減少Xcode的??。
在向著上面的方式鏈接后,我們在鏈接動態庫運行時一直會image not found
我們通過otool -l test | grep 'DYLIB' -A 5
遇到DYLIB
向下查找5行(-B是向上)
cmd LC_LOAD_DYLIB
cmdsize 72
name TestExample (offset 24)
time stamp 2 Thu Jan 1 08:00:02 1970
current version 0.0.0
compatibility version 0.0.0
--
cmd LC_LOAD_DYLIB
cmdsize 96
name /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (offset 24)
time stamp 2 Thu Jan 1 08:00:02 1970
current version 1675.129.0
compatibility version 300.0.0
--
cmd LC_LOAD_DYLIB
cmdsize 56
name /usr/lib/libobjc.A.dylib (offset 24)
time stamp 2 Thu Jan 1 08:00:02 1970
current version 228.0.0
compatibility version 1.0.0
--
cmd LC_LOAD_DYLIB
cmdsize 56
name /usr/lib/libSystem.B.dylib (offset 24)
time stamp 2 Thu Jan 1 08:00:02 1970
current version 1281.100.1
compatibility version 1.0.0
--
cmd LC_LOAD_DYLIB
cmdsize 104
name /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (offset 24)
time stamp 2 Thu Jan 1 08:00:02 1970
current version 1675.129.0
compatibility version 150.0.0
我們可以看到其他的動態庫的name都是一個路徑TestExample
只有一個名字
動態庫的路徑是保存在自己的mach-o中的
install_name_tool
NAME
install_name_tool - change dynamic shared library install names
SYNOPSIS
install_name_tool [-change old new ] ... [-rpath old new ] ...
[-add_rpath new ] ... [-delete_rpath new ] ... [-id name] file
DESCRIPTION
Install_name_tool changes the dynamic shared library install names and
or adds, changes or deletes the rpaths recorded in a Mach-O binary.
For this tool to work when the install names or rpaths are larger the
binary should be built with the ld(1) -headerpad_max_install_names
option.
-change old new
Changes the dependent shared library install name old to new in
the specified Mach-O binary. More than one of these options can
be specified. If the Mach-O binary does not contain the old
install name in a specified -change option the option is
ignored.
-id name
Changes the shared library identification name of a dynamic
shared library to name. If the Mach-O binary is not a dynamic
shared library and the -id option is specified it is ignored.
-rpath old new
Changes the rpath path name old to new in the specified Mach-O
binary. More than one of these options can be specified. If
the Mach-O binary does not contain the old rpath path name in a
specified -rpath it is an error.
-add_rpath new
Adds the rpath path name new in the specified Mach-O binary.
More than one of these options can be specified. If the Mach-O
binary already contains the new rpath path name specified in
-add_rpath it is an error.
-delete_rpath old
deletes the rpath path name old in the specified Mach-O binary.
More than one of these options can be specified. If the Mach-O
binary does not contains the old rpath path name specified in
-delete_rpath it is an error.
執行Install_name_tool -id 路徑 目標文件
@rpath
Install_name_tool
命令中提供的路徑是絕對路徑,有什么可以比較方便的解決路徑問題呢, @rpath
@rpath
代表的誰鏈接我,誰提供路徑,這個是因為@rpath存在鏈接文件的mach-o中,鏈接時,傳遞給被鏈接的Mach-o中的@rpath
我們上面的命令可以改為Install_name_tool -id @rpath/Frameworks/TestExample.framework/TestExample TestExample
然后在需要鏈接的文件中Install_name_tool -add_rpath 路徑 目標文件
@executable_path
:表示可執?程序所在的?錄,解析為可執??件的絕對路徑。
@loader_path
:表示被加載的Mach-O
所在的?錄,每次加載時,都可能
被設置為不同的路徑,由上層指定。
XCFramework
XCFramework:是蘋果官?推薦的、?持的,可以更?便的表示?個多
個平臺和架構的分發?進制庫的格式。
需要Xcode11以上?持。
是為更好的?持Mac Catalyst和ARM芯?的macOS。
專?在2019年提出的framework的另?種先進格式。
iOS/iPad:arm64
iOS/iPad Simulator: x86_64 arm64
Mac Catalyst: x86_64 arm64
Mac: x86_64 arm64
和傳統的framework相?:
- 可以?單個.xcframework?件提供多個平臺的分發?進制?件;
- 與Fat Header相?,可以按照平臺劃分,可以包含相同架構的不同平
臺的?件; - 在使?時,不需要再通過腳本去剝離不需要的架構體系。
- xcodebuild
將文件進行編譯打包
xcodebuild archive -project 'SYTimer.xcodeproj' \
-scheme 'SYTimer' \
-configuration Release \
//指定編譯平臺
-destination 'generic/platform=iOS Simulator' \
//輸出路徑
-archivePath '../archives/SYTimer.framework-iphonesimulator.xcarchive' \
//將編譯的產物拷貝到目錄中
SKIP_INSTALL=NO
指定他的編譯環境進行打包
//編譯為真機環境下
xcodebuild archive -project 'SYTimer.xcodeproj' \
-scheme 'SYTimer' \
-configuration Release \
-destination 'generic/platform=iOS' \
-archivePath '../archives/SYTimer.framework-iphoneos.xcarchive' \
SKIP_INSTALL=NO
平常我們打包時都是多架構打包在一塊的
- lipo:創建或操作通用文件
- 胖二進制 :多個架構打包到一起
動態庫合并只能合并不同架構的
lipo -output
//指定輸出名稱
SYTimer
-create
//真機環境下的
../archives/SYTimer.framework-iphoneos.xcarchive/Products/Library/Frameworks/SYTimer.framework/SYTimer
//模擬器環境下的
../archives/SYTimer.framework-iphonesimulator.xcarchive/Products/Library/Frameworks/SYTimer.framework/SYTimer
提取出特定架構下的動態庫
lipo -output SYTimer-x86_64 -extract x86_64 ../archives/SYTimer.framework-iphonesimulator.xcarchive/Products/Library/Frameworks/SYTimer.framework/SYTimer
但是還需要前面,dSYM之類信息
xcframework
目前只能通過命令行生成
xcodebuild -create-xcframework \
-framework '../archives/SYTimer.framework-iphoneos.xcarchive/Products/Library/Frameworks/SYTimer.framework' \
-framework '../archives/SYTimer.framework-iphonesimulator.xcarchive/Products/Library/Frameworks/SYTimer.framework' \
-output 'SYTimer.xcframework'
就會根據生成不同平臺的framework
還需要把調試符添加進去
xcodebuild -create-xcframework \
-framework '../archives/SYTimer.framework-iphoneos.xcarchive/Products/Library/Frameworks/SYTimer.framework' \
-debug-symbols '/Users/xxx/xxx/archives/SYTimer.framework-iphoneos.xcarchive/BCSymbolMaps/6142AB7C-A46D-33C9-A32B-405B93A1D680.bcsymbolmap' \
-debug-symbols '/Users/xxx/xxx/archives/SYTimer.framework-iphoneos.xcarchive/BCSymbolMaps/314A0F21-A400-3D1F-ACBE-258D8A3C919F.bcsymbolmap' \
-framework '../archives/SYTimer.framework-iphonesimulator.xcarchive/Products/Library/Frameworks/SYTimer.framework' \
-debug-symbols '/Users/xxx/xxx/archives/SYTimer.framork-iphonesimulator.xcarchive/dSYMs/SYTimer.framework.dSYM' \
-output 'SYTimer.xcframework'
這里-debug-symbols
需要的是絕對路徑。
- weak_framework
我們在 xcconfig
中配置
// 2. -F: frmaework 所在的目錄
FRAMEWORK_SEARCH_PATHS = $(inherited) ${SRCROOT}
// 1. -I :頭文件
HEADER_SEARCH_PATHS = $(inherited) ${SRCROOT}/SYTimer.framework/Headers
// 路徑
LD_RUNPATH_SEARCH_PATHS = $(inherited)
// 3. 名稱
// null -》 runtime -〉 nil
// weak_import
// library
OTHER_LDFLAGS = $(inherited) -Xlinker -weak_framework -Xlinker "SYTimer"
設置了weak_framework
當沒有找到定義的符號是就會顯示為null。
鏈接兩個相同的靜態庫
OTHER_LDFLAGS = $(inherited) -l"AFNetworking" -l"AFNetworking2"
鏈接兩個相同的靜態庫,不同的名字,不會發生沖突,因為引入的時候默認是-noall_load
,會做個代碼剝離,如果找到了就不會進行鏈接。
動態庫鏈接動態庫
我們通過cocoapods
導入時只是生成鏈接器的參數,并不會真正導入。
- 導入動態庫的時候通過他給我們的腳本動態的拷貝到ipa包中。
我們在Podfile
中使用use_frameworks!
if [[ "$CONFIGURATION" == "Debug" ]]; then
install_framework "${BUILT_PRODUCTS_DIR}/AFNetworking/AFNetworking.framework"
fi
if [[ "$CONFIGURATION" == "Release" ]]; then
install_framework "${BUILT_PRODUCTS_DIR}/AFNetworking/AFNetworking.framework"
fi
- 在我們項目的target下再導入一次
如果動態庫想使用APP中的符號,我們可以OTHER_LDFLAGS
添加
OTHER_LDFLAGS = $(inherited) -framework "AFNetworking" -Xlinker -undefined -Xlinker dynamic_lookup
使用-undefined
會有風險,如果沒有定義的符號也會通過,我們也可以用
OTHER_LDFLAGS = $(inherited) -framework "AFNetworking" -Xlinker -U -Xlinker _OBJC_CLASS_$_LGAppObject
來指定特定的符號
動態庫鏈接靜態庫
在Podfile
中不使用use_frameworks!
可正常運行
如果想隱藏靜態庫的符號可使用-hidden-l
OTHER_LDFLAGS = $(inherited) -ObjC -Xlinker -hidden-l"AFNetworking"
靜態庫鏈接靜態庫
我們需要手動配置鏈接的靜態庫信息
LIBRARY_SEARCH_PATHS
、OTHER_LDFLAGS
靜態庫鏈接動態庫
app = app + 靜態庫 所以和動態鏈接動態差不多
cocoapods 即導入靜態庫又導入動態庫
platform :ios, '14.1'
target :'LGNetworkManager' do
use_frameworks!
# 靜態庫、動態庫
# 指定需要被編譯成static_framework的庫
$static_framework = ['AFNetworking']
pre_install do |installer|
installer.pod_targets.each do |pod|
if $static_framework.include?(pod.name)
def pod.build_type;
Pod::Target::BuildType.static_framework
end
end
end
end
pod 'SDWebImage'
end
Module
Module(模塊)-最小的代碼單元
一個Module
是機器代碼和數據的最小單元,可以獨立于其他代碼單元進行鏈接。
通常,Module
是通過編譯單個原文件生成的目標文件。例如,當前的test.m被編譯成目標文件test.o時,當前的目標文件就代表了一個Module。
AFN中的module
//framework module 名稱 AFNetworking
framework module AFNetworking {
umbrella header "AFNetworking-umbrella.h"
//將所有的頭文件重新導出
export *
//將上面子module全部導出
module * { export * }
}
umbrella
代表一個目錄代表傘柄, 這個目錄下所有.h代表傘骨
這一個文件里包含所有的頭文件
explicit
顯示指明子module名稱在文件中通過
@import
就可以直接導入模塊,例@import AFNetworking.AFHTTPSessionManager;
其實只要開啟了
module
,#include
和#import
最后都會轉換成@import
更多用法可參考官網
一般生成 framework
后會自動生成一個module
,如果要手動創建一個,在創建完.modulemap
, 還需要在build settings
中配置module file map
framework module LGOCFramework {
umbrella "Headers"
export *
module * {
export *
}
}
展開就是下面
framework module LGOCFramework {
// umbrella<目錄>
umbrella header "LGOCFramework.h"
explicit module LGTeacher {
header "LGTeacher.h"
export *
}
explicit module LGStudent {
header "LGStudent.h"
export *
}
}
在swift
中framework
是沒有橋接文件的,我們就需要module
,如果我們只想在framework
中使用,需要將其設置為私有的。
文件命名為xxx.private.modulemap
framework module LGSwiftFramework_Private {
module LGOCStudent {
header "LGOCStudent.h"
export *
}
}
最后在build settings
中配置Private Module file map
swift通過 libtool
合并靜態庫后在xcconfig
中配置
// OTHER_CFLAGS:傳遞給用來編譯C或者OC的編譯器,當前就是clang
OTHER_CFLAGS="-fmodule-map-file=${SRCROOT}/LGSwiftC/Public/LGSwiftA.framework/module.modulemap" "-fmodule-map-file=${SRCROOT}/LGSwiftC/Public/LGSwiftB.framework/module.modulemap"
// SWIFT_INCLUDE_PATHS: 傳遞給SwiftC編譯器,告訴他去下面的路徑中查找module.file
SWIFT_INCLUDE_PATHS="${SRCROOT}/LGSwiftC/Public/LGSwiftB.framework" "${SRCROOT}/LGSwiftC/Public/LGSwiftA.framework"
apinotes
我們在通過OC轉swift類時通過宏定義一個個轉
// 通過指定NS_SWIFT_NAME宏,我們可以添加一些詳細信息以使函數清晰可見,從而使其變得如下所示:
// 規范
- (nullable NSString *)teacherNameForIndex:(NSUInteger)index NS_SWIFT_NAME(teacherName(forIndex:));
// NS_REFINED_FOR_SWIFT從現在開始,Swift的Clang Importer將做一些額外的工作并將該方法導入為私有方法,并以雙下劃線字符開頭__,例如:
//- (BOOL)changeTeacherName:(nullable NSDictionary<NSString *, id> *)options;
- (BOOL)changeTeacherName:(nullable NSDictionary<NSString *, id> *)options NS_REFINED_FOR_SWIFT;
如果量太多我們就可以使用apinotes
,文件為SDK名稱.apinotes
文件放在SDK目錄下。
這個格式是yaml
格式
#yaml
---
Name: OCFramework
Classes:
- Name: LGToSwift
//Swift名
SwiftName: ToSwift
Methods:
- Selector: "changeTeacherName:"
Parameters:
- Position: 0
Nullability: O
MethodKind: Instance
SwiftPrivate: true
//設置不可以使用
Availability: nonswift
AvailabilityMsg: "這個不能用"
- Selector: "initWithName:"
MethodKind: Instance
DesignatedInit: true
具體信息可以通過官網查詢