Swift Runtime

作者: Stark_Dylan | 来源:发表于2016-03-31 13:24 被阅读6260次

深入了解过Objective-C这门语言的人一定知道,这是一门动态语言。在日常的开发中,也常常会使用到Runtime对自己的项目做一些小"手脚",比如说替换这些视图控制器的viewWillAppear方法让他们在启动的时候能在自己的事件日志中记录一下,或者是记录一下这个视图控制器的时间;或者是对某个模型做一些便利化的操作,比如说动态的获取到模型里边的属性名称,然后自动的encodedecode,将JSON自动的转为Model之类的操作。

Swift问世到现在已经有段时间了,从最开始的不稳定、BUG、各种各样的调整到现在稳定的版本Swift2.2,有一部分"喜新厌旧"的人已经正式的投入到了Swift开发之中,我认识的朋友也有几个把他们用到了真正的项目开发中,每次总是向我们这些OC的开发者在抱怨,这玩意编译好慢、这Swift好像有很多种写法啊,更像是面向接口开发,确实,当你看到一些BindRAC等库的时候,眼花缭乱的,可能需要细细的看才能知道这个方法的来龙去脉,而我感觉OCSwift两种语言对于开发者来说,若是想做一个不错的iOS开发者,OC一定是不能丢弃的,毕竟这门老语言已经成熟了很久,枝叶茂密,Swift任然是襁褓中的婴儿,有待培养、研究。

进入正题,为了便于大家的理解,我们这里的实验类有2个,一个是纯Swift类,另外一个继承于NSObject

测试类
  • Runtime,获取属性与方法
    按照我们在OC使用Runtime的方法,同样的写一遍就好了。
获取相关信息

Swift的基本语法知识这里就不说了-。-

然后调用看一下打印的结果

调用代码
  • TestSwiftClass (纯Swift的类),并不能获取到属性与方法
TestSwiftClass结果
  • ViewController (继承UIViewController的类,也就是继承NSObject), 可以打印, 但是大家仔细看一下,我们自己加入了returnIntreturnTuple的方法,returnInt有打印,但是tuple却没有。
ViewController结果

这是为什么?

  • Swift类的函数调用已经不再是OC的运行时发消息objc_msgsend,而是类似C++vtable,在编译时就确定了调用什么函数,所以runtime获取不到。
  • 继承于NSObject的类依然拥有动态性,所以可以拿的到。
    -OCruntime特性就是,所有的运行时方法都依赖TypeEncoding,他指定了方法的参数类型以及函数在调用时参数入栈所需要的内存空间,没有这个标识就无法动态的压入参数。返回类型Tuple,也就是元组,是Swift特有的,无法映射到OC的类型,也无法用OCTypeEncoding表示,所以也无法通过runtime获取。
m_type: Optional("q24@0:8q16") 

上边代码的解释在这里:
代码解释

Type Encoding扩展阅读1
Type Encoding扩展阅读2
Type Encoding官网文档

  • 方法的替换

OC项目中,想必大部分人都使用了这个Method Swizzling,替换一些系统类的方法为自己的方法,以提供便捷(Hook)。

  • 对于纯Swift类来说,我们上边的方法证实了无法通过runtime得到,所以不可以替换
  • 对于继承于NSObject的类来说,通过runtime可以获取到的方法可以完成替换,那些没有type Encoding的方法也就不能替换了。我们再次写代码来确认一下runtime拿到的方法可以进行替换,我们替换viewDidAppear:试一下。
替换方法 调用

确认可以交换。最后打印 des。 但是奇怪的事情出现了,这个时候我也想替换一下returnInt这个方法,但是却发现不可以了。所以我们加断点看一下调试信息。我们可以看到,在my_ViewDidAppear前,有@objc这样一个小玩意在,而大家单独调试returnInt却没有。

@objc

@objc是用来将Swift的API导出给OC与OC runtime使用的,如果你继承NSObject的类,将会被自动的加入这个标识。

这样一来,真相大白,我们给之前的纯Swift类加上这个标识看一下效果。

代码

这样,属性与方法名全部打印了出来。

dynamic

同时,还有这个,文档中有一句说明,加了@objc标识的方法、属性都无法保证都会被运行时调用,因为Swift会做静态优化。要想完全被动态调用就要使用dynamic修饰词了。使用这个标识也会隐形的加入@objc。这也就解释了为什么上边VC中的方法无法被替换了,被Swift优化成静态调用了,而ViewDidAppear本身为OC的方法,拥有动态特性,所以我们加入dynamic关键字

答案

这样,结果打印2喽,成功的动态替换。

今天文章就写到这里啦。

手机淘宝技术团队MTT今天发布了这篇Swift Runtime分析: 还像OC Runtime一样么文章,细读之后给更多的人分享出来。也希望读者在看到这篇文章的时候打开Xcode,新建一个工程来实际测试一下。

CopyRight@Dylan 2015-3-31 愚人节快乐。

相关文章

网友评论

  • 5ec1da87f063:看了两遍,还是不太明白,您说的是:swift仍有runtime.但是得使用@objc吗?
  • CeeYang:不明觉厉。
    Stark_Dylan:@_春风十里不如你_ 喔...
  • objcat:不明觉厉
  • 布袋的世界:请问下,不知道如何用swizzle,调换URL()的方法,让URL(string:String),自带一个判断是否为nil
  • 布袋的世界: self.returnInt(1)
    // Result of call to 'returnInt' is unused

    提示这个
  • 鱼香肉丝_我鱼呢:请教一下,swift如何想oc一样通过runtime 添加属性,
    var gender: Int {
    get {

    if let genderTemp = objc_getAssociatedObject(self, &genderKey) {

    return genderTemp as! Int
    }
    return 0
    }
    set {
    print(newValue)
    objc_setAssociatedObject(self, &genderKey, newValue, .OBJC_ASSOCIATION_ASSIGN)
    }
    }

    我尝试添加,如上,但是get的时候就崩了
    何解?
  • Caiflower:想问下,swift中如何扩展闭包属性,我使用一直报语法错误
    func setTapAction(block : () -> ()) 函数接收一个闭包参数,
    在这个函数内部调用下面这句,报语法错误
    objc_setAssociatedObject(self, UnsafeRawPointer("kDTActionHandlerTapBlockKey"), block, objc_AssociationPolicy.OBJC_ASSOCIATION_COPY)
    Stark_Dylan:@花菜ChrisCai
    static func setTappedBlock(_ tappedBlock: @escaping () -> ()) {
    objc_setAssociatedObject(self, "tappedBlock", tappedBlock, .OBJC_ASSOCIATION_COPY_NONATOMIC)
    }
  • 学习之路:用Xcode8 写你的代码 这里需要这样写 cls: ViewController.self 才不会报错 不支持只写类名 需要添加个后缀.self
    Stark_Dylan:@学习之路 谢谢提醒 文章近期没有更新, 抱歉了。
  • tztTzt:上次看一哥们说swift不支持运行时 哈哈看你的文章找到方法了
    Stark_Dylan:@wry如意 😋

本文标题:Swift Runtime

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