SwiftのRuntime

4500 ワード

SwiftアプリにObject-cのコードが1行もない場合でも、各アプリはObject-c runtimeで実行され、動的タスクの配布と実行時のオブジェクト関連に世界を開きます.より正確には、Swiftライブラリのみを使用する場合、Swift runtimeのみが実行される可能性があります.しかし、Objective-C runtimeをこんなに長く使っているので、私たちも彼に十分な役割を果たさせなければなりません.
以下では,Swiftの視点で関連オブジェクト(associated objects))とメソッドクロス(method swizzling)の2つの実行時の技術を観察する.
関連オブジェクト(Associated Objects)
Swift extensionは既存のCocoaクラスに極めて豊富な機能を追加することができ、具体的には(1)計算インスタンス属性(computed property)と計算クラス属性(computed property)を追加する
(2)インスタンスメソッドとクラスメソッドの定義
(3)新しいコンストラクタの提供
(4)サブスクリプトの定義
(5)新しいネストタイプの定義と使用
(6)あるインタフェースを遵守させる
それに比べて、Objective-Cのcategoryはずっと劣っています.たとえばObjective-Cのextensionでは既存のクラスに属性を追加できません.
幸いなことに、Objective-Cの関連オブジェクト(Associated Objects)はこの欠点を改善することができる.たとえば、プロジェクト内のすべてのview controllersにdescriptiveNameプロパティを追加するには、objc_get/setAssociatedObject()を使用してgetブロックとsetブロックを簡単に埋め込むことができます.
Swift

extension UIViewController {
    private struct AssociatedKeys {
        static var DescriptiveName = "nsh_DescriptiveName"
    }

    var descriptiveName: String? {
        get {
            return objc_getAssociatedObject(self, &AssociatedKeys.DescriptiveName) as? String
        }
        set {
            if let newValue = newValue {
                objc_setAssociatedObject(
                    self,
                    &AssociatedKeys.DescriptiveName,
                    newValue as NSString?,
                    UInt(OBJC_ASSOCIATION_RETAIN_NONATOMIC)
                )
            }
        }
    }

プライベートネストstructでstatic varを使用すると、必要な関連オブジェクトキーが生成されますが、ネーミングスペース全体が汚染されません.
メソッド交差(Method Swizzling)
便宜上、いくつかのフレームワーク内のバグを解決したり、他に方法がない場合、すでに存在するクラスのメソッドの動作を修正する必要がある場合もあります.方法の交差は2つの方法の交換を実現することができて、相当してあなたの自分で書いた方法で元の方法を再ロードして、しかも元の方法の行為が変わらないことを維持することができます.
次に、UIViewControllerのviewWillAppearメソッドを交差させ、画面に表示される各ビューを印刷する例を説明します.メソッド交差はinitializeクラスメソッド呼び出し時(以下のコードに示す)に発生する.代替の実装はnsh_viewWillAppearメソッド:
Swift
extension UIViewController {
    public override class func initialize() {
        struct Static {
            static var token: dispatch_once_t = 0
        }

        // make sure this isn't a subclass        
        if self !== UIViewController.self {
            return
        }

        dispatch_once(&Static.token) {
            let originalSelector = Selector("viewWillAppear:")
            let swizzledSelector = Selector("nsh_viewWillAppear:")

            let originalMethod = class_getInstanceMethod(self, originalSelector)
            let swizzledMethod = class_getInstanceMethod(self, swizzledSelector)

            let didAddMethod = class_addMethod(self, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))

            if didAddMethod {
                class_replaceMethod(self, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))
            } else {
                method_exchangeImplementations(originalMethod, swizzledMethod);
            }
        }
    }

    // MARK: - Method Swizzling

    func nsh_viewWillAppear(animated: Bool) {
        self.nsh_viewWillAppear(animated)
        if let name = self.descriptiveName {
            println("viewWillAppear: \(name)")
        } else {
            println("viewWillAppear: \(self)")
        }
    }
}

load vs.initialize(Swiftバージョン)
Objective-C runtimeは理論的にクラスのロードと初期化時に2つのクラスメソッド:load and initializeを呼び出す.method swizzlingを説明する原文では,セキュリティと一貫性の考慮から,メソッド交差過程はload()メソッドで永遠に行われると指摘している.各クラスは、ロード時にloadメソッドを1回しか呼び出さない.一方、1つのinitializeメソッドは、1つのクラスとそのすべてのサブクラスによって呼び出され得る.例えば、UIViewControllerのこのメソッドは、そのクラスが情報を伝達されていない場合、そのinitializeメソッドは永遠に呼び出されない.
異なることに、Swiftではloadクラスメソッドはruntimeに呼び出されないため、Method Swizzlingは実現できませんが、次の2つの方法があります.
1.initializeでメソッドの交差を実現するこの方法は安全で、関連するメソッドがdispatch_に交差することを確保する必要があります.onceの中でよかったです(これも一番お勧めの作り方です).
2.app delegateにおけるメソッド交差の実現は、上記のようにクラス拡張によるメソッド交差ではなく、単純にapp delegateのアプリケーション(:didFinishLaunchingWithOptions:)メソッド呼び出し時に関連コードを実行してもよい.クラスの変更に基づいて、この方法は、これらのコードが実行されることを保証するのに十分であるはずです.
最後に、やむを得ずObjective-C runtimeを使用することを注意してください.ベースフレームワークや使用するサードパーティコードを勝手に変更すると、プロジェクトに大きな影響を与えます.気をつけてくださいね.
出典:Swift&Object-c Runtime
備考:本文はすでに原作者の同意を得て、OneAPM技術ブログの転載を許可する
OneAPM Mobile Insightは実際のユーザー体験をメトリック基準としてCrash分析を行い、ネットワーク要求とネットワークエラーを監視し、ユーザーの保存を向上させる.OneAPM公式サイトにアクセスして、より多くのアプリケーション性能の最適化体験を体験し、より多くの技術記事を読みたい場合は、OneAPM公式技術ブログにアクセスしてください.