swift Runtime

Runtime简称运行时。OC就是运行时机制,也就是在程序运行时候的一些机制,其中最主要的是消息机制。对于我们熟悉的C语言,函数的调用在编译的时候会决定调用哪个函数。但对于OC的函数,属于动态调用过程,在编译的时候并不能决定真正调用哪个函数,只有在真正运行的时候才会根据函数的名称找到对应的函数来调用。

 也就有了下面这两点结论:
 1、在编译阶段,OC可以调用任何函数,即使这个函数并未实现,只要声明过就不会报错。
 2、在编译阶段,C语言调用未实现的函数就会报错。

我们定义一个纯Swift的类 TestASwiftClass ,代码如下:

class TestASwiftClass{  
var aBoll :Bool = true
var aInt : Int = 0
func testReturnVoidWithaId(aId : UIView) {
    print("TestASwiftClass.testReturnVoidWithaId")
 }

再写一个继承自 UIViewController 的 ViewController

class ViewController: UIViewController{
let testStringOne  = "testStringOne"
let testStringTwo  = "testStringTwo"
let testStringThr  = "testStringThr"
var count:UInt32 = 0
override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.
    let  SwiftClass = TestASwiftClass()
    let  proList = class_copyPropertyList(object_getClass(SwiftClass),&count)
    for  i in 0..

}
上面的代码也很简单,我们在ViewController中添加了一些变量,然后通过Runtime的方法尝试着先来获取一下我们最上面定义的纯Swift类TestASwiftClass的属性,你运行上面代码你就会发现:

什么都没有!!!为什么??

下面我们先给出答案,用它来解释一下为什么我们通过上面Runtime的API没有获取到任何东西,然后再接着用OC来证明一下我们说的结论:

C 语言是在函数编译的时候决定调用那个函数,在编译阶段,C要是调用了没有实现的函数就会报错。
OC 的函数是属于动态调用,在编译的时候是不能决定真正去调用那个函数的,只有在运行的时候才能决定去调用哪一个函数 ,在编译阶段,OC可以调用任何的函数,即使这个函数没有实现,只要声明过也就不会报错。
纯Swift类的函数的调用已经不是OC的运行时发送消息,和C类似,在编译阶段就确定了调用哪一个函数,所以纯Swift的类我们是没办法通过运行时去获取到它的属性和方法的。
Swift 对于继承自OC的类,为了兼容OC,凡是继承与OC的都是保留了它的特性的,所以可以使用Runtime获取到它的属性和方法等等其他我们在OC中获得的东西。
针对上面给出的结论,我们看看Swift对于继承自OC的类是不是保留了OC所有的特性呢?再看下面代码,只是做一个简单的修改,把通过object_getClass方法获取的对象写成self:
let  proList = class_copyPropertyList(object_getClass(self),&count)
for  i in 0..

可以看到我们获取到了我们在ViewController中定义的变量。这样也就证明了的确是上面答案说的那样。

那这样就又衍生出一个问题
那Swiftw就没办法利用Runtime了吗?

想一想,要是真的Swift没办法利用Runtime,那是一件得多让人失望的事!答案也肯定是否定的,我们还是能让Swift用Runtime的。看下面的代码:

class TestASwiftClass{
    dynamic  var aBoll :Bool = true
    var aInt : Int = 0
    dynamic func testReturnVoidWithaId(aId : UIView) {
print("TestASwiftClass.testReturnVoidWithaId")
    }
}

嗯,我们利用了dynamic(英文单词动态的意思)关键字,在第一个变量和方法的定义前面我们添加了这个关键字,那添加了这个关键字之后又什么变化呢?我们再通过最开始我们获取纯Swift类的代码获取一下试试,看结果!

结果:

可以看到这里是获取到了变量了的。(这里是获取属性没有写获取方法代码所以是值拿到变量没有拿到方法)

aBoll 这个变量前面是添加了dynamic关键字的,我们获取到了。在aInt这个变量前面我们是没有添加的,所以可以看到我们是没有获取到这个变量的,那关键的就是我们要理解:dynamic 关键字的含义:

首先有 @objc 这个关键字,它是用来将Swift的API导出来给 Object-C 和 Runtime 使用的,如果你类继承自OC的类,这个标识符就会被自动加进去,加了这标识符的属性、方法无法保证都会被运行时调用,因为Swift会做静态优化,想要完全被声明成动态调用,必须使用 dynamic 标识符修饰,当然添加了 dynamic 的时候,它会自己在加上@objc这个标识符。

这样我们就理解了dynamic这个关键字,知道了它的作用,那我们接下来就是尝试着多使用一下 Swift Runtime。

Swift Runtime

1、获取方法:

    let methodList = class_copyMethodList(object_getClass(SwiftClass), &count)
    for ind in 0..

2、属性成员变量:

    let IvarList = class_copyIvarList(object_getClass(SwiftClass),&count)
    for index in 0..

3、协议列表:

    let protocalList = class_copyProtocolList(object_getClass(self),&count)
    for index in 0..

4、方法交换

这个就是Runtime的一个重点了,仔细说一说。
OC的动态性最常用的其实就是方法的替换,将某个类的方法替换成自己定义的类,从而达到Hook的作用。(以前面试有人问过OC怎样Hook一个消息,那时候太懵懂,不知道怎么说!不知道大家有没有遇到过?)
对于纯粹的Swift类,由于前面的测试你知道无法拿到类的属性方法等,也就没办法进行方法的替换,但是对于继承自NSObject的类,由于集成了OC的所有特性,所以是可以利用Runtime的属性来进行方法替换,记得我们前面说的dynamic关键字。

func ChangeMethod() -> Void {
     
    // 获取交换之前的方法
    let originaMethodC = class_getInstanceMethod(object_getClass(self), #selector(self.originaMethod))
    // 获取交换之后的方法
    let swizzeMethodC  = class_getInstanceMethod(object_getClass(self), #selector(self.swizzeMethod))

     //替换类中已有方法的实现,如果该方法不存在添加该方法
     //获取方法的Type字符串(包含参数类型和返回值类型)
     //class_replaceMethod(object_getClass(self), #selector(self.swizzeMethod), method_getImplementation(originaMethodC), method_getTypeEncoding(originaMethodC))
     
     print("你交换两个方法的实现")
     method_exchangeImplementations(originaMethodC, swizzeMethodC)
}

dynamic func originaMethod() -> Void {
             
     print("我是交换之前的方法")
}
    
dynamic func swizzeMethod() -> Void {
             
     print("我是交换之后的方法")
}

5、关联属性

说上面的方法Hook比较重要的话,这个关联属性也是比较重要的,在前面我总结OC的Runtime的时候在方法的添加这里专门有提过一个Demo,我们把这个Demo重新整理一下,导航的渐变就是利用Runtime给导航添加属性来实现的。

extension UINavigationBar {
     
var navigationGradualChangeBackgroundView:UIView?{
 
    get{
        return objc_getAssociatedObject(self, &self.navigationGradualChangeBackgroundView) as? UIView;
    }
    set{
     
        objc_setAssociatedObject(self, &self.navigationGradualChangeBackgroundView, navigationGradualChangeBackgroundView, objc_AssociationPolicy.OBJC_ASSOCIATION_COPY_NONATOMIC)
    }
}
 
func setNavigationBackgroundColor (backgroundColor: UIColor) -> Void {
     
    if (self.navigationGradualChangeBackgroundView == nil) {
         
        self.setBackgroundImage(UIImage(), for: UIBarMetrics.default)
        self.navigationGradualChangeBackgroundView = UIView.init(frame: CGRect.init(x: 0, y: -20, width: SCREENWIDTH, height: self.bounds.size.height + 20))
        self.navigationGradualChangeBackgroundView!.isUserInteractionEnabled = false
        self.insertSubview(self.navigationGradualChangeBackgroundView!, at: 0)
    }
 
   self.navigationGradualChangeBackgroundView!.backgroundColor = backgroundColor
}
 
func removeNavigationBackgroundColor() -> Void {
     
   self.setBackgroundImage(nil, for: UIBarMetrics.default)
   self.navigationGradualChangeBackgroundView!.removeFromSuperview()
   self.navigationGradualChangeBackgroundView = nil
}
}

你可能感兴趣的:(swift Runtime)