SwiftとObjective-C混合プロジェクトのビルド速度改善方法
概要
古くからあるプロジェクトなど、まだSwift移行が完全にできていない場合など、SwiftとObjective-Cの両方のコードが存在するプロジェクトで有効なビルド速度改善方法を説明します。
Swift-Objective-Cのビルドプロセスに着目した、改善方法の原理も合わせて説明します。
無駄な再ビルドを防止する
まずはSwiftとObjective-Cの混合プロジェクトでビルドが遅くなる原因を説明します。デバッグビルドでは一度ビルドをした後に、再ビルドをする場合、修正箇所の影響範囲に応じて差分ビルドが実行されます。
ビルドを高速化するためには、この差分ビルドの影響範囲を狭め、なるべく多くのファイルを再ビルドさせないことが重要です。また、フルビルドの場合でもコンパイラが考慮すべき依存関係を減らすことで、高速化が見込めます。
ビルドプロセスに着目
では、差分ビルドの影響範囲を狭め、依存関係を減らすためにはどうすべきでしょうか。SwiftとObjective-Cの混合プロジェクトのビルドプロセスに着目してみます。
- Objecitve-Cのヘッダファイル(.h)をコンパイル
- Bridging Headerをコンパイル(Objecitve-CからSwiftに公開するもの)
- Swiftファイルをコンパイル
- Generated Headerをコンパイル(SwiftからObjective-Cに公開するもの)
- Objecitve-Cの実装ファイル(.m)をコンパイル
上記の通り、Objective-CとSwift間のインタフェースはBridging HeaderとGenerated Headerとなります。そのため、SwiftからはBridging Headerしか見えておらず、Objective-CからはGenerated Headerしか見えていないことになります。
つまり、修正の結果、Bridging HeaderとGenerated Headerに修正が入らなければ、再ビルドの必要はありません。結論としてBridging HeaderとGenerated Headerを必要最低限にし、修正がなるべく入らないようにする工夫が必要です。
Bridging Headerを最小限にする
Bridging Headerでのimportは最低限に
シンプルですが、Bridging HeaderにはSwiftへ公開するヘッダファイル以外は記載しないようにします。過去は使っていたが、もうSwift側で利用しなくなった場合など、Bridging Headerからの消し忘れがないようにします。
プロパティやメソッドの隠蔽
Bridging Headerでimportしているヘッダファイルの中身も最低限にします。
Objective-C側にも公開する必要のないプロパティやメソッドは実装ファイル側に隠蔽します。
プロパティなどはカテゴリにより実装ファイル側に記載できます。
#import "SomeClass.h"
@interface SomeClass ()
@property (nonatomic, assign) int hoge; // 非公開プロパティ
@end
カテゴリによるヘッダファイル分割
同一クラス内でSwiftに公開するものとそうでないものが混在する場合、ヘッダファイルの分割を検討します。以下のように、カテゴリによりヘッダファイルを分割することができます。
@interface SomeClass: NSObject
// ...
@end
#import "SomeClass.h"
@interface SomeClass ()
@property (nonatomic, strong) ObjCInternalClass *hoge; // ObjCのみで利用
@end
分割したヘッダファイルがSwift側に公開するもののみBridging Headerに記載します。
Generated Headerを最小限にする
Generated Headerを最小限にするには、Generated Headerを生成するコンパイラディレクティブである@objc
をなるべく付与しないようにする、もしくは付与したとしても隠蔽するようにします。
@objc
推論をオフに
Swift3まではNSObjectを継承したクラスのプロパティやメソッドなどには自動で@objc
が推論により付与されます。一見気づきにくいですが、UIViewControllerを継承したクラスなど、UIKitのクラスはNSObjectを継承していますので、さらにそれらのクラスを継承した場合でも同様です。
Swift4ではビルド設定で@objc
推論をオフ(OffまたはDefault)にすることができます。
推論を無効化し、必要最低限の@objc
のみ付与します。
privateの利用
@objc
を付与する必要があったとしても、Objective-C側のソースやその他のクラスに公開する必要のないプロパティやメソッドはprivateやfileprivateを付与し、外部から隠蔽します。
class Some {
@objc private var foo: Int
@objc private func bar() {
///
}
Swift4環境未満で@objc
が自動推論される場合でも同様です。
class Some: UIViewController {
private var foo: Int
private func bar() {
///
}
}
セレクタを利用しない
セレクタを利用する場合、Objective-Cの動的ディスパッチ機能が必要なため、@objc
を付与する必要が発生します。NotificationCenterのaddObserverメソッドやTimerのscheduledTimerメソッドなどではコールバック時の処理をセレクタではなくクロージャで実行するようにします。
// Bad
func addObserver(_ observer: Any,
selector aSelector: Selector,
name aName: NSNotification.Name?,
object anObject: Any?)
// Good
func addObserver(forName name: NSNotification.Name?,
object obj: Any?,
queue: OperationQueue?,
using block: @escaping (Notification) -> Void) -> NSObjectProtocol
参考
Author And Source
この問題について(SwiftとObjective-C混合プロジェクトのビルド速度改善方法), 我々は、より多くの情報をここで見つけました https://qiita.com/shtnkgm/items/72a7b2388eb09b344f10著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .