本篇文章总结一下自己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() {}
}
网友评论