
反射 Swift.Reflection 就是可以动态获取类型、成员信息,在运行时可以调用属性、方法等行为的特性
KeyPath
也称 WritableKeyPath 添加引用标识键路径的功能,该路径指的是应用它的整个输入值。类似 OC 中的 KVC
let keyPath = \Int.selfprint(keyPath) // Swift.WritableKeyPath<Swift.Int, Swift.Int>var a = 10print(a) // 10a[keyPath: keyPath] = 20print(a) // 20print(a[keyPath: keyPath]) // 20
class Person {var name: Stringvar age: Intinit(name: String, age: Int) {self.name = nameself.age = age}}let man = Person(name: "huangjian", age: 20)let nameKeyPath: KeyPath<Person, String> = \Person.name // Swift.ReferenceWritableKeyPath<swift_demo.Person, Swift.String>let nameCountKeyPath = \Person.name.count // Swift.KeyPath<swift_demo.Person, Swift.Int>man[keyPath: nameKeyPath] = "mr.huangjian"print(man[keyPath: nameKeyPath]) // "mr.huangjian"print(man[keyPath: nameCountKeyPath]) // 12
KVC / KVO
Swift 中可以使用 KVC \ KVO ,但有两个基本条件:
- 使用 @objc 修饰对应的属性
- 属性所在的类、监听器继承自 NSObject
此外:
- KVO 监听的属性还需添加 dynamic,即 @objc dynamic
- KVC 键值编码的属性可以为可选类型,在该可选类型可以在 OC 中表示时
- KVC 可以通过 #keyPath 获取类的属性名称
class Observer: NSObject {override func observeValue(forKeyPath keyPath: String?,of object: Any?,change: [NSKeyValueChangeKey : Any]?,context: UnsafeMutableRawPointer?) {if let newValue = change?[.newKey] {print("[observe]: ", newValue)}}}class Person: NSObject {@objc dynamic var age: Int = 0var observer = Observer()override init() {super.init()self.addObserver(observer, forKeyPath: "age", options: .new, context: nil)}deinit {self.removeObserver(observer, forKeyPath: "age")}}let person = Person()person.age = 20 // [observe]: 20person.age = 30 // [observe]: 30
class Movie: NSObject {@objc dynamic var name: String = ""var observation: NSKeyValueObservation?override init() {super.init()// 必须接收返回值,不接收就不会收到监听结果...observation = observe(\Movie.name, options: .new) { (movie, change) inif let newValue = change.newValue {print(newValue)}}}}let movie = Movie()movie.name = "Home Alone"movie.name = "Home Alone 2nd."movie.setValue("Home Alone 3rd.", forKey: "name")
import Foundationclass MyClass: NSObject {/**若 prop1 的数据类型改为 `Int?` 则会报错:属性不能标记为@objc,因为它的类型不能在Objective-C中表示Property cannot be marked @objc because its type cannot be represented in Objective-C可以理解为 Int(int, NSInteger) 无法为 nil,而 String(NSString) 可以为 nil.*/@objc var prop1: String?@objc var prop2: Int = 0var prop3: Bool = true}let obj = MyClass()obj.prop2 = 10obj.setValue("hj", forKey: "prop1")let v1 = obj.value(forKey: "prop1")print(v1 as Any) // Optional(hj)
import Foundationclass Person: NSObject {@objc var name: Stringvar age: Intinit(name: String, age: Int) {self.name = nameself.age = age}}print(#keyPath(Person.name)) // "name"
添加存储属性
默认情况下 extension 不可以新增存储属性,而借助关联对象却可以
class Person {}extension Person {private static var _ageKey: Void?var age: Int {set {objc_setAssociatedObject(self, &Self._ageKey,newValue, .OBJC_ASSOCIATION_ASSIGN)}get {return objc_getAssociatedObject(self, &Self._ageKey) as? Int ?? 0}}}let p = Person()print(p.age) // 0p.age = 10print(p.age) // 10
指针类型
Swift 中也有专门的指针类型,这些都被定性为 Unsafe (不安全的),常见的有以下4种类型:
- UnsafePointer
类似于 const Pointee * - UnsafeMutablePointer
类似于 Pointee * - UnsafeRawPointer 类似于 const void *
- UnsafeMutableRawPointer 类似于 void *
func test(_ ptr: UnsafeMutablePointer<Int>) {ptr.pointee += 10}var age = 10test(&age)print(age) // 20
获取对象的内存地址:
func getObjAddress(_ object: AnyObject) -> Any {return Unmanaged.passUnretained(object).toOpaque()}
Mirror
Swift 的反射机制是基于一个叫 Mirror 的 struct 来实现的,其内部有如下属性和方法:
children: Mirror.Children // 实例的子节点(属性)displayStyle: Mirror.DisplayStyle? // 实例的展示风格(eg. class/struct/enum/array...)subjectType: Any.Type // 实例的类型superclassMirror() -> Mirror? // 实例父类的 mirror
案例1:
class User {var name: String = ""var nickname: String?var age: Int?var emails: [String]?}let user = User()user.name = "huangjian"user.age = 20user.emails = ["mr.huangjian@foxmail.com", "1776549643@qq.com"]let mirror = Mirror(reflecting: user)print(mirror.subjectType) // Userprint(mirror.displayStyle!) // classprint(mirror.children.count) // 4for case let (label?, value) in mirror.children {print("\(label): \(type(of: value)) = \(value);")}
案例2:解包,不会有编译器的警告
func unwrap(_ any: Any) -> Any {let mirror = Mirror(reflecting: any)if mirror.displayStyle != .optional {return any // Non optional}if mirror.children.count == 0 {return any // nil}return mirror.children.first!.value // optional}do {let name: String? = nilprint(unwrap(name as Any)) // nil}do {let name: String? = "hj"print(unwrap(name as Any)) // hj}do {let name: String = "hj"print(unwrap(name)) // hj}
[备注]:使用反射将自定义对象数据序列化成JSON数据(字符串) Swift 进阶
