Swift runtime简解

0.694字数 164阅读 4794
swift有运行时特性吗?和OC的运行时有区别吗?runtime怎么用呢?Swift的extension(扩展)可以添加方法和属性吗?下面我们来一起回答下这几个问题,并且了解下Swift的特性。
1.swift有运行时特性吗?
Swift 没有运行时特性,它是一门静态语言。这就不合题意了,那我们该怎么介绍 swift runtime呢,莫急,且听我慢慢说....

Swift extensions(扩展) 在给已经存在cocoa系统类添加功能提供了巨大的灵活性,但是和它的OC兄弟一样存在着一样的问题:category的局限性;也就是说你不能通过扩展添加一个属性给已经存在的类,令人庆幸的是,OC的runtime关联属性的方法在解决category的局限性上起了巨大的作用,令人欣慰的是,OC可以和Swift混编,可以互用各自的方法和类,这时就有人会说 swift可以使用OC的runtime的接口吗? 答案是肯定的。我们再来回答第二个问题
2.和OC的运行时有区别吗
除了语法上的区别外,原理都是一样的,Swift是使用OC的runtime接口间接拥有了运行时的特性
3.Swift中runtime怎么用呢?
下面我们来看一个例子

给NSObject 类添加一个 personName的String类型

extension NSObject {
    private struct AssociatedKeys {
        static var personName = "yf_PersonName"
    }
    
    var personName: String? {
        get {
            return objc_getAssociatedObject(self, &AssociatedKeys.personName) as? String
        }
        
        set {
            if let newValue = newValue {
                objc_setAssociatedObject(
                    self,
                    &AssociatedKeys.personName,
                    newValue as NSString?,
                    .OBJC_ASSOCIATION_RETAIN_NONATOMIC
                )
            }
        }
    }
}
需要注意的是,一个私有的结构体中使用 静态变量(static var),此模式创建我们需要的静态关联对象key,但不会弄脏我们的命名空间

Method Swizzling

有时候我们会需要处理一个framework 的bug,或者需要需要修改一个存在的类中的方法,Method Swizzling 可以让你交换两个方法的实现,最厉害的是可以替换一个存在的方法和你自己实现的方法,并且保持原来的运行顺序,现在我们来看一个列子

extension UIViewController {
    public override class func initialize() {
        struct Static {
            static var token: dispatch_once_t = 0
        }
        
        // make sure this isn't a subclass
        if self !== UIViewController.self {
            return
        }
        
        dispatch_once(&Static.token) {
            let originalSelector = Selector("viewWillAppear:")
            let swizzledSelector = Selector("nsh_viewWillAppear:")
            
            let originalMethod = class_getInstanceMethod(self, originalSelector)
            let swizzledMethod = class_getInstanceMethod(self, swizzledSelector)
            
            let didAddMethod = class_addMethod(self, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))
            
            if didAddMethod {
                class_replaceMethod(self, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))
            } else {
                method_exchangeImplementations(originalMethod, swizzledMethod)
            }
        }
    }
    
    // MARK: - Method Swizzling
    
    func nsh_viewWillAppear(animated: Bool) {
        self.nsh_viewWillAppear(animated: animated)
        if let name = self.personName {
            print("viewWillAppear: \(name)")
        } else {
            print("viewWillAppear: \(self)")
        }
    }

这样我们就实现了在每个试图控制器 将要出现是打印 personName 这个属性的目的了,但是不幸的是 override class func initialize,这个再iOS3.0以后,苹果就不建议用了,因为这是OC的方法,不能保证swift以后的版本都会被调用,我们可以在 app delegate(_:didFinishLaunchingWithOptions:),也就是程序启动的时候,去掉用我们替换方法的方法,并且应该保证每次启动都会调用,就可以了

回答最后一个问题
4.Swift的extension(扩展)可以添加方法和属性吗?
局限性和OC的category一样,是不可以的,需要使用runtime的接口来实现

推荐阅读更多精彩内容