美文网首页Swift编程
Swift 反射(2020.04.15更新)

Swift 反射(2020.04.15更新)

作者: coder_xiang | 来源:发表于2020-04-15 17:51 被阅读0次

与iOS Runtime不一样,Swift的反射用了另一套API,实现机制也完全不一样

1. iOS Runtime

  • 其实基于Objc的Runtime是iOS开发的黑魔法,比如神奇的Method Swizzle可以交换任何iOS的系统方法, 再比如讯息转发机制,又如class_copyIvarList等方法,可以动态获取一个类裡面所有的方法和属性, 还有就是动态给一个类新增属性和方法.
  • Objc的Runtime是如此的强大,再加上KVC和KVO这两个利器,可以实现很多你根本就想不到的功能,给iOS开发带来极大的便捷。

2. 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相比。

什么是反射

反射是一种计算机处理方式。是程式可以访问、检测和修改它本身状态或行为的一种能力。
上面的话来自百度百科。使用反射有什么用,看一些iOS Runtime的文章应该会很明白。下面再列举一下

  • 动态地建立物件和属性,
  • 动态地获取一个类裡面所有的属性,方法。
  • 获取它的父类,或者是实现了什么样的介面(协议)
  • 获取这些类和属性的访问限制(Public 或者 Private)
  • 动态地获取执行中物件的属性值,同时也能给它赋值(KVC)
  • 动态呼叫例项方法或者类方法
  • 动态的给类新增方法或者属性,还可以交换方法(只限于Objective-C)

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

Swift的反射

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

Mirror结构体常用属性:
subjectType:对象类型
children:反射对象的属性集合
displayStyle:反射对象展示类型

下面来简单介绍下Mirror的使用:

  1. 获取对象类型
  2. 获取一个类的属性名称和属性的值
        let p = Person()
        p.name = "刘伟湘"
        p.age = 22

        let mirror:Mirror = Mirror(reflecting: p)
        
        /*
         *  1. 获取对象类型
         */
        print("获取对象类型:\(mirror.subjectType)")
        //打印结果:  获取对象类型:Person
        
        /*
         *   2. 获取对象的所有属性名称和属性值
         */
        for property in mirror.children {
            let propertyNameStr = property.label!  // 属性名使用!,因为label是optional类型
            let propertyValue = property.value    // 属性的值
            print("\(propertyNameStr)的值为:\(propertyValue)")
            //打印结果: name的值为:Optional("刘伟湘")  age的值为:22
        }
        

swfit反射的应用场景现在还比较狭窄,因为功能还不够完善,比较常见的反射应用场景就是自定义类模型转字典

class NewViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        self.navigationItem.title = "new"
        self.view.backgroundColor = .white

        
        // 创建一个User实例对象模型
        let user1 = User()
        user1.name = "刘伟湘"
        user1.age = 100
        user1.emails = ["506299396.qq.com","111111111111.qq.com"]
        let tel1 = Telephone(title: "手机", number: "18711112222")
        let tel2 = Telephone(title: "公司座机", number: "2800034")
        user1.tels = [tel1, tel2]

        // 模型转字典
        if let model = user1.toJSONModel() {
            print(model)
        }
        /*
         *打印结果
         ["age": 100, "tels": ["item0": ["number": "18711112222", "title": "手机"], "item1": ["number": "2800034", "title": "公司座机"]], "emails": ["item0": "506299396.qq.com", "item1": "111111111111.qq.com"], "name": "刘伟湘"]
         *
         */
    }
}
class User {
    var name:String = ""
    var nickname:String?
    var age:Int?
    var emails:[String]?
    var tels:[Telephone]?
}

// 电话结构体
struct Telephone {
    var title:String   // 电话标题
    var number:String  // 电话号码
}

// 自定义一个JSON协议
protocol Sam_JSON {
    func toJSONModel() -> Any?
}

// 扩展协议方法,实现一个通用的toJSONModel方法(反射实现)
extension Sam_JSON {
    // 将模型数据转成可用的字典数据,Any表示任何类型,除了方法类型
    func toJSONModel() -> Any? {
        // 根据实例创建反射结构体Mirror
        let mirror = Mirror(reflecting: self)
        
        if mirror.children.count > 0  {
            // 创建一个空字典,用于后面添加键值对
            var result: [String:Any] = [:]

            for (idx, children) in mirror.children.enumerated() {
                let propertyNameString = children.label ?? "item\(idx)"
                let value = children.value
                // 判断value的类型是否遵循JSON协议,进行深度递归调用
                if let jsonValue = value as? Sam_JSON {
                    result[propertyNameString] = jsonValue.toJSONModel()
                }
            }
            return result
        }
        return self
    }
}

// 扩展可选类型,使其遵循JSON协议,可选类型值为nil时,不转化进字典中
extension Optional: Sam_JSON {
    func toJSONModel() -> Any? {
        if let x = self {
            if let value = x as? Sam_JSON {
                return value.toJSONModel()
            }
        }
        return nil
    }
}

// 扩展两个自定义类型,使其遵循JSON协议
extension User: Sam_JSON { }
extension Telephone: Sam_JSON { }

// 扩展Swift的基本数据类型,使其遵循JSON协议
extension String: Sam_JSON { }
extension Int: Sam_JSON { }
extension Bool: Sam_JSON { }
extension Dictionary: Sam_JSON { }
extension Array: Sam_JSON { }

相关文章

  • Swift 反射(2020.04.15更新)

    与iOS Runtime不一样,Swift的反射用了另一套API,实现机制也完全不一样 1. iOS Runtim...

  • 源码

    反射 Swift通过Mirror类型来提供简单的反射功能

  • Swift中的反射Mirror

    Swift中的反射Mirror [TOC] 前言 Mirror是Swift中的反射机制,对于C#和Java开发人员...

  • 校园里的白丁香和海棠花

    摄于2020.04.15

  • 思考 Swift 中的 MirrorType 协议

    Swift中的反射非常有限,仅允许以只读方式访问元数据的类型子集。或许 Swift 因有严格的类型检验而不需要反射...

  • Swift3.0 反射代码加载主类控制器

    Swift3.0 反射代码加载主类控制器 }

  • 2020.04.15

    能定而活动谋定而后动这是个不错的开始,昨天的在培训中有思想 遇到问题解决问题的过程就是一个好的开始,也是一种成长,...

  • 2020.04.15

    今天和顾客吵架了,对方要求先拿走定了五套的架子、但是对方又没有交定金、怎么可以让他拿走呢,最后生气走了、感觉是...

  • 2020.04.15

    姓名:蔡江燕 公司:海南蔚蓝时代实业有限公司 组别:365期谦虚3组学员 【日精进打卡第746天】 【知~学习】 ...

  • Swift - 反射Mirror

    在Swift中并不提倡使用Runtime,而是像其他语言一样使用反射(Reflect)。当然,目前Swift中的反...

网友评论

    本文标题:Swift 反射(2020.04.15更新)

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