本篇文章總結一下自己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
-
Swift
協議如果想在OC
中使用,需要加@objc
修飾,。 - 但修飾後,這個協議在
Swift
中只能被類遵守。 -
@objc
修飾之後可以定義可選實現的方法。不要爲了實現協議可選方法而將協議改爲@objc
,Swift實現可選的方式是給協議寫擴展,擴展中提供默認實現。
@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() // 使對應的這個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中要實現調用分爲以下情況:
- Swift項目調用Pod庫中的C語言函數,可以直接調用,也可以起一個別名後調用。
- 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() {}
}