Swift-你可能會遇到的與OC混編問題

本篇文章總結一下自己Swift項目中遇到與OC混編的問題及解決辦法,文章儘量全面實用。

一、Swift屬性關聯

Swift中屬性關聯的寫法跟OC是比較類似的,看一個例子你應該就能懂。有興趣深入瞭解iOS-底層原理 18:關聯對象底層原理探索

    // 關聯的key使用 Void?類型時因爲它只佔1個字節
    private var BtnTitleKey: Void?
    /// 這裏使用屬性關聯來保存和訪問btnTitle
    var btnTitle: String? {
        get { objc_getAssociatedObject(self, &BtnTitleKey) as? String }
        set {  objc_setAssociatedObject(self, &BtnTitleKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) }
    }

二、Swift中實現方法交換

在Swift中方法交換的一個例子.

// let變量在Swift中只會初始化一遍,若是函數則可以反覆調用
static let swizzleMethods() -> Bool = {
    let originalSelector = #selector(UIApplication.sendEvent(_:))
    let swizzledSelector = #selector(UIApplication.my_sendEvent(_:))
    
    let originalMethod = class_getInstanceMethod(UIApplication.self, originalSelector)
    let swizzledMethod = class_getInstanceMethod(UIApplication.self, swizzledSelector)
    
    let didAddMethod = class_addMethod(UIApplication.self, originalSelector, method_getImplementation(swizzledMethod!), method_getTypeEncoding(swizzledMethod!))
    
    if didAddMethod {
        class_replaceMethod(UIApplication.self, swizzledSelector, method_getImplementation(originalMethod!), method_getTypeEncoding(originalMethod!))
    } else {
        method_exchangeImplementations(originalMethod!, swizzledMethod!)
    }

    return true
}()

三、Swift協議與OC

  1. Swift協議如果想在OC中使用,需要加@objc修飾,。
  2. 但修飾後,這個協議在Swift中只能被類遵守。
  3. @objc修飾之後可以定義可選實現的方法。不要爲了實現協議可選方法而將協議改爲@objc,Swift實現可選的方式是給協議寫擴展,擴展中提供默認實現。
@objc
protocol Runable {
    @objc optional fun run()
}

四、Swift中KVO、KVC的寫法

Swift中使用KVO、KVC的條件:

  1. 屬性所在類必須是遵守NSObject的類。
  2. 屬性需要使用@objc dynamic修飾,這樣纔會走OC的一套
func testKVO() {
    let aa = ZLKVO()
    print(aa)
    aa.age = 20
    aa.observation1?.invalidate() // 使對應的這個KVO監聽失效
    aa.age = 30
}

class ZLKVO: NSObject {
    @objc dynamic var age = 10
    // 如果age屬性沒有使用@objc dynamic,屬性變化不會走OC的方法調用流程,使用KVO\KVC都會奔潰
//    var age = 10
    
    var observation1: NSKeyValueObservation?
    
    init(age: Int = 10) {
        self.age = age
        super.init()
        // 方法1.OC樣式的KVO
        self.addObserver(self, forKeyPath: "age", options: .new, context: nil)
        
        // 方法2.閉包樣式的KVO
        self.observation1 = observe(\ZLKVO.age, options: .new) { kvo, change in
            print("kvo= ", kvo, "kvo.age= ", kvo.age) // age已經是新值了
            print("change = ", change, "change.newValue = ", change.newValue)
        }
    }

    // 方法一的監聽結果在這裏調用
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        print(change?[.newKey])
    }
    
    deinit {
        self.removeObserver(self, forKeyPath: "age")
    }
}

五、Swift繼承OC類

如果逼不得已要繼承OC裏面的類,這裏說一個注意點:
重寫方法時,要加@objc,不加的話你會發現,如果該方法是在OC中調用,會執行OC父類的那個方法,自己重寫的方法並沒有執行。

六、Swift擴展OC類

這裏也只說明一個注意點:
場景:Swift給OC類寫擴展時,OC中對一個屬性重寫了setter,我們不希望執行這個setter,所以需要覆蓋這個setter。
1.如果使用Swift擴展,我們不能重寫存儲屬性,所以做不到。
2.如果採用重寫setter方法,那麼我們會發現在setter中給該屬性賦值會導致循環調用,所以也行不通。
3.所以能採取的方式:①屬性關聯。②寫子類繼承,然後重寫屬性爲計算屬性,重寫set方法,在set中將值設置給子類的新存儲變量。

七、Swift調用C語言函數

C語言函數定義在OC文件中,Swift中要實現調用分爲以下情況:

  1. Swift項目調用Pod庫中的C語言函數,可以直接調用,也可以起一個別名後調用。
  2. Swift調用的地方跟C語言函數屬於同一個Target下(即在相同工程裏或相同Pod庫裏),那麼需要@_silgen_name起別名後調用
/// 1.ZLTest_sum是同在工程裏定義的C語言函數,起別名swift_sum
@_silgen_name("ZLTest_sum") func swift_sum(_ v1: Int32, _ v2: Int32) -> Int32
/// 2.kStatusBarHeight是Pod庫中定義的C語言函數,起別名swift_statusBarHeight
@_silgen_name("kStatusBarHeight") func swift_statusBarHeight() -> CGFloat
func testOC() {
    // 1.調用時只能使用swift_sum
    print(swift_sum(10, 20))
    
    // 2.swift中調用OC庫的C語言函數是可以直接調用的,也可以起別名後調用
    print(kStatusBarHeight())
    print(swift_statusBarHeight())
}

八、Swift調用方法走OC的runtime調用流程

Swift類中的方法使用dynamic修飾之後,方法的調用會走OC的runtime調用流程。

class Dog: NSObject {
    @objc dynamic fun run() {}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章