C++、Java、Objective-C、Swiftバイナリ互換テスト
4641 ワード
現在、ダイナミックライブラリがiOS Appで広く使用されていることを考慮すると、バイナリの互換性の問題は厄介な問題になる可能性があります.本文は主にC+、Java、Objecive-CとSwiftのバイナリ互換性の問題を比較する.
iOSエンドダイナミックライブラリの使用状況 iOS 8は、Appの動的ライブラリの使用をサポートし始めました. アップルは提出したAppの オープンソースライブラリはPodfileでソースコードを導入するしかなく、ソースコード依存でコンパイルが非常に遅い. の持続可能な構築には、Mac Pro/Mac Miniを使用して構築するなど、アップルベースの環境も必要です.Mac Proは高価で、Mac miniは性能が悪く、一度に構築するのに時間がかかります. 大型Appはコンパイル速度を速めるため、自分のプライベートウェアハウスを維持し、依存するライブラリをできるだけFrameworkにコンパイルし、コンパイル速度を速めることができます. Swiftは現在、ダイナミックライブラリに基づいて開発する必要があります. 動的ライブラリに基づいてAppを構築し、動的ライブラリをアップグレードするには依存ツリー全体をコンパイルする必要があります.特に、視覚コンポーネントの変更など、頻繁に変動する基礎コンポーネントは、一発で全身を動かす.
テスト環境
C++、Java、OC、SwiftはそれぞれFooというベースクラスを実現し、Barというサブクラスを実現し、mainはBarクラスを使用してメンバー変数の情報を印刷します.Fooクラスにメンバー変数:
コードアドレス:binary_compatibility_test.
LLDBは少し役に立つデバッグテクニックです.デバッグ機能の詳細については、The LLDB Debuggerを参照してください.
テスト結果 C++がずれますが、クラッシュはありません.バイナリも脆弱です. Javaは正常に動作します. OCは正常に動作します.OCは動的ライブラリベースのコンポーネント方式に非常に適している. Swift構造Barオブジェクトがクラッシュします.現状は私たちをとても悩ませている.
結果分析 C++の設計はバイナリ互換性の問題を考慮していないので互換性は普通です. Javaのバイナリ互換性は非常に完璧で、オブジェクトメンバーが変更され、方法が削除されても、バイナリ互換性の問題は簡単には発生しません.詳細は、Chapter 13.Binary Compatibilityを参照してください. OCの使用方法と属性はすべてメッセージの配布を使用して、方法を増加して削除して、方法の順序を移動して、すべて問題を招くことはできません;また、メンバー変数の変更をサポートしているので、バイナリ互換性は完璧です.
また,C++虚関数の順序変更が問題になるかどうかについても議論した.この問題について検証してみたが,C++虚関数テーブル内の関数の順序がヘッダファイルで宣言された関数の順序に完全に依存していることを確認した.
例えばFooにはfunc 1とfunc 2の2つの虚関数があり,func 1とfunc 2の順序を入れ替えmainを再コンパイルしない.mainでfunc 2を呼び出し、実際にfunc 1を呼び出します.
参考記事 C++ ABI Compliance Checker Objective-Cクラスメンバー変数深さプロファイリング Non Fragile ivars Objcソース Swiftライブラリバイナリインタフェース(ABI)互換性研究 最後の最後
Golangも新しい言語を称賛して、私はそれがバイナリ互換性に対してどのように考えているのかとても好奇心があって、だから広範な青年のためにGolangバージョンを補充することを歓迎します.
iOSエンドダイナミックライブラリの使用状況
__TEXT__
段の大きさに制限があり、多くのビッグマックAppがこの制限を超えやすい.iOS 9以前の各アーキテクチャの__TEXT__
セグメントは比較的小さく、iOS 9は500 MBに拡大された.詳細は、To submit an app for reviewを参照してください.テスト環境
C++、Java、OC、SwiftはそれぞれFooというベースクラスを実現し、Barというサブクラスを実現し、mainはBarクラスを使用してメンバー変数の情報を印刷します.Fooクラスにメンバー変数:
member0
を追加し、Foo(make foo&./main)を再コンパイルし、実行結果を観察します.コードアドレス:binary_compatibility_test.
LLDBは少し役に立つデバッグテクニックです.デバッグ機能の詳細については、The LLDB Debuggerを参照してください.
br set -f main.cpp -l 17 // main.m:17
br set -f main.cpp -n main // main.m:main
x/10a *(void**)bar // bar
テスト結果
warning: (x86_64) libBar.dylib unable to load swift module 'Bar' (failed to get module 'Bar' from AST context:
error: missing required module 'Foo'
)
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=2, address=0x1000a7a98)
* frame #0: 0x00007fff90419fd1 libobjc.A.dylib`realizeClass(objc_class*) + 1155
frame #1: 0x00007fff9041d26d libobjc.A.dylib`_class_getNonMetaClass + 127
frame #2: 0x00007fff9041d053 libobjc.A.dylib`lookUpImpOrForward + 232
frame #3: 0x00007fff9041cad4 libobjc.A.dylib`_objc_msgSend_uncached + 68
frame #4: 0x00000001000a4df5 libBar.dylib`type metadata accessor for Bar at Bar.swift:0
frame #5: 0x0000000100001584 main`main at main.swift:7
frame #6: 0x00007fff90d0f235 libdyld.dylib`start + 1
frame #7: 0x00007fff90d0f235 libdyld.dylib`start + 1
結果分析
,Swift , 。
また,C++虚関数の順序変更が問題になるかどうかについても議論した.この問題について検証してみたが,C++虚関数テーブル内の関数の順序がヘッダファイルで宣言された関数の順序に完全に依存していることを確認した.
例えばFooにはfunc 1とfunc 2の2つの虚関数があり,func 1とfunc 2の順序を入れ替えmainを再コンパイルしない.mainでfunc 2を呼び出し、実際にfunc 1を呼び出します.
#include "Foo.h"
using namespace std;
int main()
{
Foo *foo = new Foo();
foo->func2();
delete foo;
return 0;
}
$ lldb main
(lldb) target create "main"
Current executable set to 'main' (x86_64).
(lldb) br set -f main.cpp -l 17
Breakpoint 1: where = main`main + 65 at main.cpp:17, address = 0x0000000100000f11
(lldb) run
Process 11179 launched: '/Users/henshao/binary_compatibility_test/C++/main' (x86_64)
Process 11179 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x0000000100000f11 main`main at main.cpp:17
14 delete bar;*/
15
16 Foo *foo = new Foo();
-> 17 foo->func2();
18 delete foo;
19
20 return 0;
(lldb) x/10a *(void**)foo
0x1000900f0: 0x000000010008eff0 libfoo.dylib`Foo::func2() at Foo.cpp:10
0x1000900f8: 0x000000010008ee40 libfoo.dylib`Foo::func1() at Foo.cpp:6
0x100090100: 0x000000010008f050 libfoo.dylib`Foo::~Foo() at Foo.cpp:15
0x100090108: 0x000000010008f070 libfoo.dylib`Foo::~Foo() at Foo.cpp:15
0x100090110: 0x00007fff999b9bb8 libc++abi.dylib`vtable for __cxxabiv1::__class_type_info + 16
0x100090118: 0x000000010008ff00 libfoo.dylib`typeinfo name for Foo
0x100090120: 0x0000000000000000
0x100090128: 0x0000000000000000
0x100090130: 0x0000000000000000
0x100090138: 0x0000000000000000
参考記事
Golangも新しい言語を称賛して、私はそれがバイナリ互換性に対してどのように考えているのかとても好奇心があって、だから広範な青年のためにGolangバージョンを補充することを歓迎します.