即使在 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 官方技术博客。

Swift 中的 Runtime的更多相关文章

  1. xamarin.ios – 没有找到ViewController ::.ctor(System.IntPtr)的构造函数

    我有一个问题,我的Monotouch应用程序有时在收到内存警告后才会崩溃.请参见下面的堆栈跟踪.堆栈跟踪是正确的,因为指定的类缺少构造函数获取IntPtr参数.但是这是有意的,因为我在应用程序中根本不使用InterfaceBuilder.那为什么会这样呢?

  2. iOS8 / Swift和MobileVLCKit构建失败

    我正在努力用MobileVLCKit和cocoapods构建我的项目.在不添加任何VLCKit代码的情况下,我在模拟器或设备上构建应用程序时会出错.Pod文件:这是我得到的错误我的部署目标是8.0.我在Debug中将“BuildActiveArchitectureOnly”设置为YES.非常感谢你的帮助!解决方法除了做MukeshThawani所说的话;ChangeC++StandardLibra

  3. swift runtime type

    varv=1.0;println(v.dynamicType);//Swift.Double

  4. runTime(二)

    我们前面已经讲过一篇runtime原理,现在这篇文章主要介绍的是runtime是什么以及怎么用!首先,第一个问题,1》runtime实现的机制是什么,怎么用,一般用于干嘛?在我们平时编写的OC代码中,程序运行过程时,其实最终都是转成了runtime的C语言代码,runtime算是OC的幕后工作者比如说,下面一个创建对象的方法中,举例:OC:第二个问题runtime用来干什么呢??..这是我们学习runtime必须知道的函数!

  5. swift之用runtime实现字典转模型

    6、未完成的事情当类的属性与字典里的key值不一定的时候,出现的情况:字典里面的key是关键字的时候当类的属性是数组,并且数组里面要放自定义类的时候接着完成未完成的事情,首先当字典里的key值与属性不一致的时候,我弄了个映射一、解决类的属性与字典里的key值不一定的情况二、首先我们添加一个方法,这个方法的作用是把字典数组转成模型数组。

  6. Swift & the Objective-C Runtime

    Swift&theObjective-CRuntimeWrittenbyNateCook—January26th,2015EvenwhenwrittenwithoutasinglelineofObjective-Ccode,everySwiftappexecutesinsidetheObjective-Cruntime,openingupaworldofdynamicdispatchandasso

  7. Swift 调用 objc/runtime OBJC_ASSOCIATION_RETAIN

    我扩展一些类在Swift2.0工作与ReactiveCocoa3.0,但遇到了一些问题。我跟随科林·艾伯哈特的教程,和有复制粘贴一些他UIKit扩展逻辑结束对我的OSX应用程序。它所有的编译很好,除了此属性:UInt,这给了我以下的编译器错误。使用的未解析的标识符如何访问此属性?我一直对importObjectiveC和#import头文件,但没有什么似乎工作。解决方法1:这是实际上现在导入Swift作为枚举命名为objc_AssociationPolicy。或与枚举速记语法。

  8. Swift 中的 Runtime

    即使在SwiftAPP中没有一行Object-c的代码,每个APP也都会在Object-cruntime中运行,为动态任务分发和运行时对象关联开启了一个世界。更确切地说,可能在仅使用Swift库的时候只运行Swiftruntime。但是使用Objective-Cruntime这么长时间,我们也应该让他充分发挥其作用。方法交叉发生在initialize类方法调用时;替代的实现在nsh_viewWillAppear方法中:loadvs.initializeObjective-Cruntime理论上会在加载和初

  9. Swift是否和OC一样有runtime机制

    Swift是否和OC一样有runtime机制OC语言最大的特性无疑是其的动态性,可以利用OC的动态性能够获得一个类的方法和属性,从而实现灵活的程序,但Swift是否也包含了runtime机制呢?对于TestSwiftClass来说除testReturnTuple、testReturnVoidWithaCharacter两个方法外,其他的都获取成功了。可以知道@objc是用来将Swift的API导出给Objective-C和Objective-Cruntime使用的,如果你的类继承自Objective-c的

  10. Swift Runtime分析:还像OC Runtime一样吗?

    本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

随机推荐

  1. Swift UITextField,UITextView,UISegmentedControl,UISwitch

    下面我们通过一个demo来简单的实现下这些控件的功能.首先,我们拖将这几个控件拖到storyboard,并关联上相应的属性和动作.如图:关联上属性和动作后,看看实现的代码:

  2. swift UISlider,UIStepper

    我们用两个label来显示slider和stepper的值.再用张图片来显示改变stepper值的效果.首先,这三个控件需要全局变量声明如下然后,我们对所有的控件做个简单的布局:最后,当slider的值改变时,我们用一个label来显示值的变化,同样,用另一个label来显示stepper值的变化,并改变图片的大小:实现效果如下:

  3. preferredFontForTextStyle字体设置之更改

    即:

  4. Swift没有异常处理,遇到功能性错误怎么办?

    本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

  5. 字典实战和UIKit初探

    ios中数组和字典的应用Applicationschedule类别子项类别名称优先级数据包contactsentertainment接触UIKit学习用Swift调用CocoaTouchimportUIKitletcolors=[]varbackView=UIView(frame:CGRectMake(0.0,0.0,320.0,CGFloat(colors.count*50)))backView

  6. swift语言IOS8开发战记21 Core Data2

    上一话中我们简单地介绍了一些coredata的基本知识,这一话我们通过编程来实现coredata的使用。还记得我们在coredata中定义的那个Model么,上面这段代码会加载这个Model。定义完方法之后,我们对coredata的准备都已经完成了。最后强调一点,coredata并不是数据库,它只是一个框架,协助我们进行数据库操作,它并不关心我们把数据存到哪里。

  7. swift语言IOS8开发战记22 Core Data3

    上一话我们定义了与coredata有关的变量和方法,做足了准备工作,这一话我们来试试能不能成功。首先打开上一话中生成的Info类,在其中引用头文件的地方添加一个@objc,不然后面会报错,我也不知道为什么。

  8. swift实战小程序1天气预报

    在有一定swift基础的情况下,让我们来做一些小程序练练手,今天来试试做一个简单地天气预报。然后在btnpressed方法中依旧增加loadWeather方法.在loadWeather方法中加上信息的显示语句:运行一下看看效果,如图:虽然显示出来了,但是我们的text是可编辑状态的,在storyboard中勾选Editable,再次运行:大功告成,而且现在每次单击按钮,就会重新请求天气情况,大家也来试试吧。

  9. 【iOS学习01】swift ? and !  的学习

    如果不初始化就会报错。

  10. swift语言IOS8开发战记23 Core Data4

    接着我们需要把我们的Rest类变成一个被coredata管理的类,点开Rest类,作如下修改:关键字@NSManaged的作用是与实体中对应的属性通信,BinaryData对应的类型是NSData,CoreData没有布尔属性,只能用0和1来区分。进行如下操作,输入类名:建立好之后因为我们之前写的代码有些地方并不适用于coredata,所以编译器会报错,现在来一一解决。

返回
顶部