美文网首页
Swift-你可能会遇到的与OC混编问题

Swift-你可能会遇到的与OC混编问题

作者: Sweet丶 | 来源:发表于2022-12-19 19:58 被阅读0次

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

    相关文章

      网友评论

          本文标题:Swift-你可能会遇到的与OC混编问题

          本文链接:https://www.haomeiwen.com/subject/lvsjqdtx.html