標籤:運行 還需要 add ram 依賴 相對 size 變數 ios
在 OSX 上初次接觸到這些變數, 有些暈. 看了一些文檔之後, 覺得弄明白了. 做一個總結.
在編譯一個動態庫比如 libfoo.dylib 的時候, 你需要指定 INSTALL_PATH. 也就是它的安裝路徑.
一個可執行程式比如 bar.app 使用 libfoo.dylib, 那麼在編譯 bar.app 的時候, libfoo.dylib 的 INSTALL_PATH 會被記錄到 bar.app 中, 用來定位這個 dylib. 用如下命令可以查看:
$ otool -L bar.app/Contents/MacOS/bar
bar.app/Contents/MacOS/bar:
/usr/local/lib/libfoo.dylib (compatibility version 1.0.0, current version 1.0.0)
/System/Library/Frameworks/Cocoa.framework/Versions/A/Cocoa (compatibility version 1.0.0, current version 19.0.0)
/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (compatibility version 300.0.0, current version 945.0.0)
…
這裡的 /usr/local/lib 就是預設的 INSTALL_PATH. 要 bar.app 能正常運行, 必須先把 libfoo.dylib 拷貝到這個目錄. 如果 libfoo.dylib 只是被 bar.app 使用, 那麼拷貝到系統目錄可能是不合適的. 一個解決問題的辦法就是修改 libfoo.dylib 的 INSTALL_PATH, 使用相對路徑. 因而就需要用到下面這三個變數.
看 dyld 的 manual, 有這三個變數的解釋.
@executable_path 這個變數表示可執行程式所在的目錄. 比如 /path/bar.app/Contents/MacOS/ .
@loader_path 這個變數表示每一個被載入的 binary (包括可執行程式, dylib, framework 等) 所在的目錄. 在一個進程中, 對於每一個模組, @loader_path 會解析成不用的路徑, 而 @executable_path 總是被解析為同一個路徑(可執行程式所在目錄). 比如一個會被多個程式調用的 plugin, 位於 /path/Myfilter.plugin/Contents/MacOS/Myfilter, 依賴 /path/Myfilter.plugin/Contents/dylib/libfoo.dylib. 那麼 libfoo.dylib 的 INSTALL_PATH 可以設定為 @loader_path/../dylib, 這樣設定的話, 不論 Myfilter.plugin 目錄放到什麼位置, libfoo.dylib 都能正確的被載入.
@rpath 和前面兩個不同, 它只是一個儲存著一個或多個路徑的變數. 比如 libfoo.dylib 被兩個 .app 使用, 且被包含的路徑不同, 如下:
bar.app/
Contents/
MacOS/
bar
libfoo.dylib
baz.app
Contents/
MacOS/
baz
dylibs/
libfoo.dylib
將 libfoo.dylib 的 INSTALL_PATH 設定成 @loader_path/.. 或 @loader_path/../dylibs 都只能滿足其中一個 .app 的需求. 要解決這個問題, 就可以用 @rpath. 將 libfoo.dylib 的 INSTALL_PATH 設定成 @rpath, 然後在編譯 bar.app, baz.app 時分別指定 @rpath 為 @loader_path/.., @loader_path/../dylibs, 問題得到瞭解決. @rpath 的另一個優點是可以設定多個路徑. 如果 bar.app 還需要使用另一個 .framework (假設它的 INSTALL_PATH 也設定成了 @rpath), 位於 @loader_path/../frameworks, 把這個路徑加到 @rpath 即可.
道理說清楚了, 怎麼設定這些變數呢?
在 libfoo.dylib 的項目屬性中設定 INSTALL_PATH 設定為 @rpath
在 bar.app 中設定 @rpath 為 @loader_path/.. @loader_path/dylibs
對於一個編譯好的 binary, 有幾個命令列工具可以查看, 修改 rpath:
查看 @rpath
$ otool -l bar.app/Contents/MacOS/bar
...
Load command 19
cmd LC_RPATH
cmdsize 32
path @loader_path/.. (offset 12)
Load command 20
cmd LC_RPATH
cmdsize 40
path @loader_path/../dylibs (offset 12)
…
install_name_tool 還可以刪除/替換/添加 @rpath, 比如:
$ install_name_tool -add_rpath @loader_path/../frameworks bar.app/Contents/MacOS/bar
它還可以修改依賴的動態庫的載入路徑, 比如:
$ otool -L bar.app/Contents/MacOS/bar
bar.app/Contents/MacOS/bar:
@rpath/libfoo.dylib (compatibility version 1.0.0, current version 1.0.0)
…
$ install_name_tool -change @rpath/libfoo.dylib @loader_path/libfoo.dylib bar.app/Contents/MacOS/bar
$ otool -L bar.app/Contents/MacOS/bar
bar.app/Contents/MacOS/bar:
@loader_path/libfoo.dylib (compatibility version 1.0.0, current version 1.0.0)
...
看看 otool, install_name_tool 的協助瞭解這幾個命令的細節
iOS 編譯部署路徑