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

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

一、Swift屬性關(guān)聯(lián)

Swift中屬性關(guān)聯(lián)的寫法跟OC是比較類似的,看一個例子你應(yīng)該就能懂。有興趣深入了解iOS-底層原理 18:關(guān)聯(lián)對象底層原理探索

    // 關(guān)聯(lián)的key使用 Void?類型時因為它只占1個字節(jié)
    private var BtnTitleKey: Void?
    /// 這里使用屬性關(guān)聯(lián)來保存和訪問btnTitle
    var btnTitle: String? {
        get { objc_getAssociatedObject(self, &BtnTitleKey) as? String }
        set {  objc_setAssociatedObject(self, &BtnTitleKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) }
    }

二、Swift中實現(xiàn)方法交換

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

// let變量在Swift中只會初始化一遍,若是函數(shù)則可以反復(fù)調(diào)用
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協(xié)議與OC

  1. Swift協(xié)議如果想在OC中使用,需要加@objc修飾,。
  2. 但修飾后,這個協(xié)議在Swift中只能被類遵守。
  3. @objc修飾之后可以定義可選實現(xiàn)的方法。不要為了實現(xiàn)協(xié)議可選方法而將協(xié)議改為@objc,Swift實現(xiàn)可選的方式是給協(xié)議寫擴展,擴展中提供默認(rèn)實現(xiàn)。
@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() // 使對應(yīng)的這個KVO監(jiān)聽失效
    aa.age = 30
}

class ZLKVO: NSObject {
    @objc dynamic var age = 10
    // 如果age屬性沒有使用@objc dynamic,屬性變化不會走OC的方法調(diào)用流程,使用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已經(jīng)是新值了
            print("change = ", change, "change.newValue = ", change.newValue)
        }
    }

    // 方法一的監(jiān)聽結(jié)果在這里調(diào)用
    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,不加的話你會發(fā)現(xiàn),如果該方法是在OC中調(diào)用,會執(zhí)行OC父類的那個方法,自己重寫的方法并沒有執(zhí)行。

六、Swift擴展OC類

這里也只說明一個注意點:
場景:Swift給OC類寫擴展時,OC中對一個屬性重寫了setter,我們不希望執(zhí)行這個setter,所以需要覆蓋這個setter。
1.如果使用Swift擴展,我們不能重寫存儲屬性,所以做不到。
2.如果采用重寫setter方法,那么我們會發(fā)現(xiàn)在setter中給該屬性賦值會導(dǎo)致循環(huán)調(diào)用,所以也行不通。
3.所以能采取的方式:①屬性關(guān)聯(lián)。②寫子類繼承,然后重寫屬性為計算屬性,重寫set方法,在set中將值設(shè)置給子類的新存儲變量。

七、Swift調(diào)用C語言函數(shù)

C語言函數(shù)定義在OC文件中,Swift中要實現(xiàn)調(diào)用分為以下情況:

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

八、Swift調(diào)用方法走OC的runtime調(diào)用流程

Swift類中的方法使用dynamic修飾之后,方法的調(diào)用會走OC的runtime調(diào)用流程。

class Dog: NSObject {
    @objc dynamic fun run() {}
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容