12. Swift 反射.png

反射 Swift.Reflection 就是可以动态获取类型、成员信息,在运行时可以调用属性、方法等行为的特性

KeyPath

也称 WritableKeyPath 添加引用标识键路径的功能,该路径指的是应用它的整个输入值。类似 OC 中的 KVC

  1. let keyPath = \Int.self
  2. print(keyPath) // Swift.WritableKeyPath<Swift.Int, Swift.Int>
  3. var a = 10
  4. print(a) // 10
  5. a[keyPath: keyPath] = 20
  6. print(a) // 20
  7. print(a[keyPath: keyPath]) // 20
  1. class Person {
  2. var name: String
  3. var age: Int
  4. init(name: String, age: Int) {
  5. self.name = name
  6. self.age = age
  7. }
  8. }
  9. let man = Person(name: "huangjian", age: 20)
  10. let nameKeyPath: KeyPath<Person, String> = \Person.name // Swift.ReferenceWritableKeyPath<swift_demo.Person, Swift.String>
  11. let nameCountKeyPath = \Person.name.count // Swift.KeyPath<swift_demo.Person, Swift.Int>
  12. man[keyPath: nameKeyPath] = "mr.huangjian"
  13. print(man[keyPath: nameKeyPath]) // "mr.huangjian"
  14. print(man[keyPath: nameCountKeyPath]) // 12

KVC / KVO

Swift 中可以使用 KVC \ KVO ,但有两个基本条件:

  • 使用 @objc 修饰对应的属性
  • 属性所在的类、监听器继承自 NSObject

此外:

  • KVO 监听的属性还需添加 dynamic,即 @objc dynamic
  • KVC 键值编码的属性可以为可选类型,在该可选类型可以在 OC 中表示时
  • KVC 可以通过 #keyPath 获取类的属性名称
  1. class Observer: NSObject {
  2. override func observeValue(forKeyPath keyPath: String?,
  3. of object: Any?,
  4. change: [NSKeyValueChangeKey : Any]?,
  5. context: UnsafeMutableRawPointer?) {
  6. if let newValue = change?[.newKey] {
  7. print("[observe]: ", newValue)
  8. }
  9. }
  10. }
  11. class Person: NSObject {
  12. @objc dynamic var age: Int = 0
  13. var observer = Observer()
  14. override init() {
  15. super.init()
  16. self.addObserver(observer, forKeyPath: "age", options: .new, context: nil)
  17. }
  18. deinit {
  19. self.removeObserver(observer, forKeyPath: "age")
  20. }
  21. }
  22. let person = Person()
  23. person.age = 20 // [observe]: 20
  24. person.age = 30 // [observe]: 30
  1. class Movie: NSObject {
  2. @objc dynamic var name: String = ""
  3. var observation: NSKeyValueObservation?
  4. override init() {
  5. super.init()
  6. // 必须接收返回值,不接收就不会收到监听结果...
  7. observation = observe(\Movie.name, options: .new) { (movie, change) in
  8. if let newValue = change.newValue {
  9. print(newValue)
  10. }
  11. }
  12. }
  13. }
  14. let movie = Movie()
  15. movie.name = "Home Alone"
  16. movie.name = "Home Alone 2nd."
  17. movie.setValue("Home Alone 3rd.", forKey: "name")
  1. import Foundation
  2. class MyClass: NSObject {
  3. /**
  4. 若 prop1 的数据类型改为 `Int?` 则会报错:
  5. 属性不能标记为@objc,因为它的类型不能在Objective-C中表示
  6. Property cannot be marked @objc because its type cannot be represented in Objective-C
  7. 可以理解为 Int(int, NSInteger) 无法为 nil,而 String(NSString) 可以为 nil.
  8. */
  9. @objc var prop1: String?
  10. @objc var prop2: Int = 0
  11. var prop3: Bool = true
  12. }
  13. let obj = MyClass()
  14. obj.prop2 = 10
  15. obj.setValue("hj", forKey: "prop1")
  16. let v1 = obj.value(forKey: "prop1")
  17. print(v1 as Any) // Optional(hj)
  1. import Foundation
  2. class Person: NSObject {
  3. @objc var name: String
  4. var age: Int
  5. init(name: String, age: Int) {
  6. self.name = name
  7. self.age = age
  8. }
  9. }
  10. print(#keyPath(Person.name)) // "name"

添加存储属性

默认情况下 extension 不可以新增存储属性,而借助关联对象却可以

  1. class Person {}
  2. extension Person {
  3. private static var _ageKey: Void?
  4. var age: Int {
  5. set {
  6. objc_setAssociatedObject(self, &Self._ageKey,
  7. newValue, .OBJC_ASSOCIATION_ASSIGN)
  8. }
  9. get {
  10. return objc_getAssociatedObject(self, &Self._ageKey) as? Int ?? 0
  11. }
  12. }
  13. }
  14. let p = Person()
  15. print(p.age) // 0
  16. p.age = 10
  17. print(p.age) // 10

指针类型

Swift 中也有专门的指针类型,这些都被定性为 Unsafe (不安全的),常见的有以下4种类型:

  1. UnsafePointer 类似于 const Pointee *
  2. UnsafeMutablePointer 类似于 Pointee *
  3. UnsafeRawPointer 类似于 const void *
  4. UnsafeMutableRawPointer 类似于 void *
  1. func test(_ ptr: UnsafeMutablePointer<Int>) {
  2. ptr.pointee += 10
  3. }
  4. var age = 10
  5. test(&age)
  6. print(age) // 20

获取对象的内存地址:

  1. func getObjAddress(_ object: AnyObject) -> Any {
  2. return Unmanaged.passUnretained(object).toOpaque()
  3. }

Mirror

Swift 的反射机制是基于一个叫 Mirror 的 struct 来实现的,其内部有如下属性和方法:

  1. children: Mirror.Children // 实例的子节点(属性)
  2. displayStyle: Mirror.DisplayStyle? // 实例的展示风格(eg. class/struct/enum/array...)
  3. subjectType: Any.Type // 实例的类型
  4. superclassMirror() -> Mirror? // 实例父类的 mirror

案例1:

  1. class User {
  2. var name: String = ""
  3. var nickname: String?
  4. var age: Int?
  5. var emails: [String]?
  6. }
  7. let user = User()
  8. user.name = "huangjian"
  9. user.age = 20
  10. user.emails = ["mr.huangjian@foxmail.com", "1776549643@qq.com"]
  11. let mirror = Mirror(reflecting: user)
  12. print(mirror.subjectType) // User
  13. print(mirror.displayStyle!) // class
  14. print(mirror.children.count) // 4
  15. for case let (label?, value) in mirror.children {
  16. print("\(label): \(type(of: value)) = \(value);")
  17. }

案例2:解包,不会有编译器的警告

  1. func unwrap(_ any: Any) -> Any {
  2. let mirror = Mirror(reflecting: any)
  3. if mirror.displayStyle != .optional {
  4. return any // Non optional
  5. }
  6. if mirror.children.count == 0 {
  7. return any // nil
  8. }
  9. return mirror.children.first!.value // optional
  10. }
  11. do {
  12. let name: String? = nil
  13. print(unwrap(name as Any)) // nil
  14. }
  15. do {
  16. let name: String? = "hj"
  17. print(unwrap(name as Any)) // hj
  18. }
  19. do {
  20. let name: String = "hj"
  21. print(unwrap(name)) // hj
  22. }

[备注]:使用反射将自定义对象数据序列化成JSON数据(字符串) Swift 进阶

Runtime