武汉ios培训
达内武汉中心

15271940953

热门课程

swift开发技巧之使用Swift反射

  • 时间:2016-05-10
  • 发布:武汉ios培训
  • 来源:达内新闻

    武汉达内ios培训专家讨论下Swift的反射功能,Swift的反射用了另一套API,实现机制也完全不一样,倒是和目前其他的面向对象语言有些相似,比如C#和Java。

iOS Runtime的问题

    基于Objc的Runtime是iOS开发的黑魔法,甚至可以是说奇技淫巧,比如神奇的Method Swizzle可以交换任何iOS的系统方法,在里面加上自己定义的一些功能。再比如消息转发机制,又比如说一些位于中的方法,比如class_copyIvarList等方法,可以动态获取一个类里面所有的方法和属性,还有就是动态给一个类添加属性和方法。Objc的Runtime是如此的强大,再加上KVC和KVO这两个利器,可以实现很多你根本就想不到的功能,给iOS开发带来极大的便捷。

    使用iOS Runtime好处非常多,但缺点也是显而易见的,主要有下面几个:

    基于Objc的Runtime不是类型安全的,需要开发者保证所有数据都是正确的类型。

    Runtime还是需要进行数据类型的检查,影响了执行效率。

    Apple推出全新的Swift语言后,单纯的Swift类型不再兼容原先的Objc的Runtime,

    其中前面两个问题影响不大,关键在于第三个。基于Swift作为一门静态语言,所有数据的类型都是在编译时就确定好了的,但是Apple为了让Swift兼容Objc,让Swift也使用了Runtime。这显然会拖累Swift的运行效率,和Apple所宣称Swift具有超越Objective-C的性能的观点完全不符。而Swift在将来是会慢慢替代 Objective-C的成为iOS或者OSX开发的主流语言,所以为了性能,我们应该尽量使用原生的Swift数据类型,避免让Runtime进行Swift类型->Objc类型的隐式转换。

    所以目前的问题是使用Swift原生的数据类型和想要使用Objc的Runtime有了冲突,那么Swift语言里有没有类似于Objc的Runtime的一套机制,让Swift数据类型也能实现Objc的Runtime的一些功能呢?

    答案是不能,Swift目前只有有限的反射功能,完全不能和Objc的Runtime相比。

什么是反射

    反射是一种计算机处理方式。是程序可以访问、检测和修改它本身状态或行为的一种能力。

    使用反射有什么用,看一些iOS Runtime的文章应该会很明白。下面再列举一下

    动态地创建对象和属性,

    动态地获取一个类里面所有的属性,方法。

    获取它的父类,或者是实现了什么样的接口(协议)

    获取这些类和属性的访问限制(Public 或者 Private)

    动态地获取运行中对象的属性值,同时也能给它赋值(KVC)

    动态调用实例方法或者类方法

    动态的给类添加方法或者属性,还可以交换方法(只限于Objective-C)

    上面的一系列功能的细节和计算机语言的不同而不同。对于Objective-C来说,位于中的一系列方法就是完成这些功能的,严格来说Runtime并不是反射。而Swift真正拥有了反射功能,但是功能非常弱,目前只能访问和检测它本身,还不能修改。

Swift的反射

    Swift的反射机制是基于一个叫Mirror的Stuct来实现的。具体的操作方式为:首先创建一个你想要反射的类的实例,再传给Mirror的构造器来实例化一个Mirror对象,最后使用这个Mirror来获取你想要的东西。

    首先我们来写一些测试用的类


protocol Drive{
    func run()
}


public  class Tire{ //轮胎
    var brand:String? //品牌
    var size:Float = 0 //大小
}

public class Vehicle:Drive{
    var carType:String?
    var tires:[Tire]?
    var host:String?// 主人
    var brand:String?//汽车品牌
    func run() {
        if let h = host{
            print("(h)Drive a (brand) (carType) car run")
        }
        else{
            print("this car is not selled")
        }
    }
}

public class Trunk:Vehicle{
    public var packintBox:String?
}

public struct TranGroup{ //货运集团
    private var trunks = {
        return [Trunk]()
    }()

    var country:String?
    var turnover:Float?
}

//一个中国的货运集团
var tranGroup = TranGroup(trunks: [Trunk](), country: "China", turnover: 123456789.111)
let trunk1 = Trunk()
trunk1.brand = "MAN"
trunk1.host = "Stan"
trunk1.packintBox = "Big And Long"
tranGroup.trunks.append(trunk1)
let mirrorTran = Mirror(reflecting: tranGroup)
print(tranGroup) //打印出  Mirror for TranGroup
print(mirrorTran.subjectType) //打印出  TranGroup
print(mirrorTran.displayStyle) //Optional(Swift.Mirror.DisplayStyle.Struct),是个Struct类型
print(mirrorTran.superclassMirror()) //nil,因为没有父类
for (key,value) in mirrorTran.children{
    print("(key) : (value)")
}
//打印结果
Optional("trunks") : [DemoConsole.Trunk]
Optional("country") : Optional("China")
Optional("turnover") : Optional(1.23457e+08)


    可以看出,和之前的文章类似,打印出个每个属性和其值。不同的是,对于自定义对象,不能自动打出里面的属性内容。

    在利用Swift的反射来改进BaseModel之前,让我们来看看Mirror里面都有什么东西吧


public init(reflecting subject: Any) //构造器

public typealias Child = (label: String?, value: Any)

public typealias Children = AnyForwardCollection

public enum DisplayStyle {
    case Struct
    case Class
    case Enum
    case Tuple
    case Optional
    case Collection
    case Dictionary
    case Set
}

public enum AncestorRepresentation {
    case Generated /// 为所有 ancestor class 生成默认 mirror。
    case Customized(() -> Mirror)/// 使用最近的 ancestor 的 customMirror() 实现来给它创建一个 mirror。
    case Suppressed /// 禁用所有 ancestor class 的行为。Mirror 的 superclassMirror() 返回值为 nil。
}

public let subjectType: Any.Type
public let children: Children
public let displayStyle: Mirror.DisplayStyle?
@warn_unused_result
public func superclassMirror() -> Mirror?


    第一个是构造器,它传入的参数类型是Any类型。说明Mirror支持对任意类型的反射。

    下面定义了两个typealias,分别是Child和Children,Child是个元组(label: String?, value: Any),label是指属性名,是个可空值,因为不是所有支持反射的数据结构都包含有名字的子节点。 struct 会以属性的名字做为 label,但是 Collection 只有下标,没有名字。Tuple 同样也可能没有给它们的条目指定名字。是Value是个Any,也就是说属性可以是任意类型。

    DisplayStyle是个enum,它会告诉你对象的类型。这里面其他囊括了所有Cocoa的类型,唯一的例外是个Closure,或者Block。

    AncestorRepresentation也是个enum,这个enum用来定义被反射的对象的父类应该如何被反射。也就是说,这只应用于 class 类型的对象。默认情况(正如你所见)下 Swift 会为每个父类生成额外的 mirror。然而,如果你需要做更复杂的操作,你可以使用 AncestorRepresentation enum 来定义父类被反射的细节。具体都有什么样的类型看上面的注释。

    subjectType是个Any.Type,从上面打印出来的东西可以看出,它应该是AnyClass的名称,不同的是AnyClass是AnyObject.Type.后面可以写代码验证

    children是个AnyForwardCollection类型,也就是个Child的集合。AnyForwardCollection是个什么玩意呢,Apple是这么说的
 

/// A type-erased wrapper over any collection with indices that
/// support forward traversal.
///
/// Forwards operations to an arbitrary underlying collection having the
/// same `Element` type, hiding the specifics of the underlying
/// `CollectionType`.


    这个确实有点难以理解:一个类型可擦除的包装器,适用于任何支持向前遍历的集合,前向遍历操作任意一个有着相同“元素”类型的底层集合,隐藏底层集合类型的细节。翻译过来还是不明白什么意思,也许需要大神指点,我只好把它看作一个可以遍历的普通集合算了。

    displayStyle是DisplayStyle enum,表示反射的对象属于什么样的类型

    superclassMirror()是获取父类的Mirror,如果没有父类,则为nil
下面看看用各种类型来看看Mirror的各个属性可以打印出什么


extension Mirror{
    func printMirror(){
        print("mirror:(self) type:(self.subjectType) displayStyle (self.displayStyle) superClassMirror (self.superclassMirror()) ")
        for (key,value) in self.children{
            print("(key) : (value)")
        }
    }
}
var s = {(i:Int) -> Int in  return i + i }
let mir = Mirror(reflecting: s)
mir.printMirror()
typealias item = (key:String,value:AnyObject)
let a:item = ("111",222)
let mirty = Mirror(reflecting: a)
mirty.printMirror()
Mirror(reflecting: "1").printMirror()
Mirror(reflecting: 1.1).printMirror()
Mirror(reflecting: NSData()).printMirror()
Mirror(reflecting: NSNull()).printMirror()
enum week:Int{
    case Mon = 1,Thu,Wed,Tur,Fri,Sat,Sun
}
Mirror(reflecting: week.Fri).printMirror()

//打印结果
**mirror:Mirror for Int -> Int type:Int -> Int displayStyle nil superClassMirror nil
mirror:Mirror for (String, AnyObject) type:(String, AnyObject) displayStyle Optional(Swift.Mirror.DisplayStyle.Tuple) superClassMirror nil
Optional(".0") : 111
Optional(".1") : 222
mirror:Mirror for String type:String displayStyle nil superClassMirror nil
mirror:Mirror for Double type:Double displayStyle nil superClassMirror nil
mirror:Mirror for NSNull type:NSNull displayStyle Optional(Swift.Mirror.DisplayStyle.Class) superClassMirror Optional(Mirror for NSObject)
mirror:Mirror for _NSZeroData type:_NSZeroData displayStyle Optional(Swift.Mirror.DisplayStyle.Class) superClassMirror Optional(Mirror for NSData)
mirror:Mirror for week type:week displayStyle Optional(Swift.Mirror.DisplayStyle.Enum) superClassMirror nil**


    Colsure或者Block的displayStyle是nil,而typealias则转化成了正确的类型。其他所有类型都正确在获取并打印出来了。

    Mirror类还有一个些其他的构造器,还有一些扩展,可能一些特殊场合会用到,这里就省略了,总之来说,Swift的反射可以用在以下场景

遍历Turple

    对类做分析,获取属性和值

    运行时分析对象的一至性

    总体来说,因为功能比较弱,使用场景也比较窄。远远比不上Objc的Runtime,更别说Java和C#了。

    但是相对于Objc的Runtime,Swift的反射是可以获取全部属性的,而且API相对于Objc也简单许多。有时侯,可以用来代替Runtime中获取所有属性名的方法


    func getSelfPropertySwift()->[String]{
        var selfProperties = [String]()
        let mir = Mirror(reflecting: self.dynamicType.init())
        for (key,_) in mir.children{
            selfProperties.append(key!)//这里可以获取所有的属性,但是只有和Objc相容的类型才能做KVC操作
        }
        return selfProperties
    }


    可以用这个来重写description方法,代码看起来要简洁不少。但是—–这个根本就没法用。


 var dict = [String:AnyObject]()
     let mir = Mirror(reflecting: self.dynamicType.init())
     for (key,value) in mir.children{
         if let obj = value as? AnyObject{ //注意字典只能保存AnyObject类型。
           dict[key!] = obj
             }
     }
 return "(self.dynamicType):(dict)"


    Mirror中的children无法识别复杂类型,对于自定义类型,或者是没有赋值的可空类型,获取的value是nil。
上一篇:UIWebView中自定义菜单栏
下一篇:swift开发技巧之让Model实现自动归档

swift开发技巧之使用Swift反射

武汉ios培训:编程的四种境界

APP 开发必须避免的 5 个弊端!

学好ios技术,未来工作不用愁

选择城市和中心
贵州省

广西省

海南省