本篇文章總結(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
-
Swift
協(xié)議如果想在OC
中使用,需要加@objc
修飾,。 - 但修飾后,這個協(xié)議在
Swift
中只能被類遵守。 -
@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的條件:
- 屬性所在類必須是遵守NSObject的類。
- 屬性需要使用
@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)用分為以下情況:
- Swift項目調(diào)用Pod庫中的C語言函數(shù),可以直接調(diào)用,也可以起一個別名后調(diào)用。
- 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() {}
}