Swift runtime简解

作者: 柠檬草YF | 来源:发表于2017-08-14 18:58 被阅读1344次
    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的接口来实现

    相关文章

      网友评论

      • 骑着雅迪小毛驴上班的老瞿:楼主swift4.0中没有initialize()了.你怎么处理
        柠檬草YF:@小瞿简书 文章第三个问题最后一段,介绍了一种方法,可以参考下
      • sixthElement:不继承于NSObject的纯swift类应该还是没办法用runtime吧?加dynamic?楼主踩过坑了木有~?
        XIAODAO:不继承于NSObject的纯Swift类,在属性前面加dynamic修饰,可以使用运行时获取属性,亲测有效。个人猜测应该是当添加了 dynamic 时,它会自己再加上 @ObjC 这个标识符,也就是说这个纯Swift类继承自NSObject
        柠檬草YF:@sixthElement swift 中也有 NSObject 这个类,是可以用的,具体有没有不能用的类,还不太清楚呢

      本文标题:Swift runtime简解

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