Swift 中的 Runtime - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
OneAPM
V2EX    Swift

Swift 中的Runtime

  •  
  •   OneAPM
    oneapm 2015-12-18 15:35:32 +08:00 3553 次点击
    这是一个创建于 3660 天前的主题,其中的信息可能已经有所发展或是发生改变。

    即使在 Swift APP 中没有一行 Object-c 的代码,每个 APP 也都会在 Object-c runtime 中运行,为动态任务分发和运行时对象关联开启了一个世界。更确切地说,可能在仅使用 Swift 库的时候只运行 Swift runtime 。但是使用 Objective-C runtime 这么长时间,我们也应该让他充分发挥其作用。

    下面我们将以 Swift 的视角来观察关联对象(associated objects])和方法交叉(method swizzling) 这两个在运行时的技术。

    关联对象(Associated Objects)

    Swift extension 可以给已经存在 Cocoa 类添加极为丰富的功能,具体有:
    ( 1 )添加计算实例属性 ( computed property) 和计算类属性

    ( 2 )定义实例方法和类方法

    ( 3 )提供新的构造器

    ( 4 )定义下标( subscript )

    ( 5 )定义和使用新的嵌套类型

    ( 6 )使一个遵守某个接口

    相比之下, Objective-C 的 category 就逊色多了。比如说 Objective-C 中的 extension 就无法向既有类添加属性。

    庆幸的是 Objective-C 的 关联对象(Associated Objects) 可以改善这个缺憾。例如要向一个工程里所有的 view controllers 中添加一个 descriptiveName 属性,我们可以简单的使用 objc_get/setAssociatedObject()来填充其 get 和 set 块:
    ```
    Swift

    extension UIViewController {
    private struct AssociatedKeys {
    static var DescriptiveName = "nsh_DescriptiveName"
    }

    var descriptiveName: String? { get { return objc_getAssociatedObject(self, &AssociatedKeys.DescriptiveName) as? String } set { if let newValue = newValue { objc_setAssociatedObject( self, &AssociatedKeys.DescriptiveName, newValue as NSString?, UInt(OBJC_ASSOCIATION_RETAIN_NONATOMIC) ) } } } 
    注意,在私有嵌套 struct 中使用 static var ,这样会生成我们所需的关联对象键,但不会污染整个命名空间。 #####方法交叉(Method Swizzling) 有时为了方便,也有可能是解决某些框架内的 bug ,或者别无他法时,需要修改一个已经存在类的方法的行为。方法交叉可以实现两个方法的交换,相当于是用你自己写的方法来重载原有方法,并且还能够是原有方法的行为保持不变。 下面,我们说一个例子,在这个例子中我们交叉 UIViewController 的 viewWillAppear 方法,然后打印出每一个在屏幕上显示的 view 。方法交叉发生在 initialize 类方法调用时(如下代码所示);替代的实现在 nsh_viewWillAppear 方法中: 

    Swift
    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) if let name = self.descriptiveName { println("viewWillAppear: \(name)") } else { println("viewWillAppear: \(self)") } } 

    }
    ```
    load vs. initialize (Swift 版本)

    Objective-C runtime 理论上会在加载和初始化类的时候调用两个类方法: load and initialize 。在讲解 method swizzling 的原文中曾指出出于安全性和一致性的考虑,方法交叉过程 永远 会在 load() 方法中进行。每一个类在加载时只会调用一次 load 方法。另一方面,一个 initialize 方法可以被一个类和它所有的子类调用,比如说 UIViewController 的该方法,如果那个类没有被传递信息,那么它的 initialize 方法就永远不会被调用了。

    可不同的是,在 Swift 中 load 类方法是不会被 runtime 调用,因此 Method Swizzling 就没有办法来实现,但是,我们有如下两个方法可以来解决:

    1.在 initialize 中实现方法交叉 这种做法很安全,你只需要确保相关的方法交叉在一个 dispatch_once 中就好了(这也是最推荐的做法)。

    2.在 app delegate 中实现方法交叉 不像上面通过类扩展进行方法交叉,而是简单地在 app delegate 的 application(_:didFinishLaunchingWithOptions:) 方法调用时中执行相关代码也是可以的。基于对类的修改,这种方法应该就足够确保这些代码会被执行到。

    最后,提醒大家,在不得已的情况下才去使用 Objective-C runtime 。随便修改基础框架或所使用的三方代码会给项目造成很大的影响。请务必要小心哦。

    文章来源:Swift&Object-c Runtime

    备注:本文已经得到原作者的同意,授权 OneAPM 技术博客进行转载

    OneAPM Mobile Insight 以真实用户体验为度量标准进行 Crash 分析,监控网络请求及网络错误,提升用户留存。访问 OneAPM 官方网站感受更多应用性能优化体验,想阅读更多技术文章,请访问 OneAPM 官方技术博客

    2 条回复    2015-12-21 20:21:18 +08:00
    canautumn
        1
    canautumn  
       2015-12-19 00:37:27 +08:00
    小学生翻译的吧?
    lawder
        2
    lawder  
       2015-12-21 20:21:18 +08:00
    这翻译也是醉了
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2784 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 23ms UTC 14:51 PVG 22:51 LAX 06:51 JFK 09:51
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86