使用反射设置对象属性而不使用setValue forKey

Edw*_*eer 19 reflection setvalue swift

在Swift中,它不可能使用 .setValue(..., forKey: ...)

  • 可空类型字段如Int
  • 具有enum类型的属性
  • 一个可以为空的对象数组 [MyObject?]

这有一个解决方法,即通过覆盖对象本身中的setValue forUndefinedKey方法.

因为我正在编写一个基于反射的通用对象映射器.请参阅EVReflection我希望尽可能地减少这种手动映射.

还有其他方法可以自动设置这些属性吗?

解决方法:可以在我的库中的单元测试发现这里 这是代码:

class WorkaroundsTests: XCTestCase {
    func testWorkarounds() {
        let json:String = "{\"nullableType\": 1,\"status\": 0, \"list\": [ {\"nullableType\": 2}, {\"nullableType\": 3}] }"
        let status = Testobject(json: json)
        XCTAssertTrue(status.nullableType == 1, "the nullableType should be 1")
        XCTAssertTrue(status.status == .NotOK, "the status should be NotOK")
        XCTAssertTrue(status.list.count == 2, "the list should have 2 items")
        if status.list.count == 2 {
            XCTAssertTrue(status.list[0]?.nullableType == 2, "the first item in the list should have nullableType 2")
            XCTAssertTrue(status.list[1]?.nullableType == 3, "the second item in the list should have nullableType 3")
        }
    }
}

class Testobject: EVObject {
    enum StatusType: Int {
        case NotOK = 0
        case OK
    }

    var nullableType: Int?
    var status: StatusType = .OK
    var list: [Testobject?] = []

    override func setValue(value: AnyObject!, forUndefinedKey key: String) {
        switch key {
        case "nullableType":
            nullableType = value as? Int
        case "status":
            if let rawValue = value as? Int {
                status = StatusType(rawValue: rawValue)!
            }
        case "list":
            if let list = value as? NSArray {
                self.list = []
                for item in list {
                    self.list.append(item as? Testobject)
                }
            }
        default:
            NSLog("---> setValue for key '\(key)' should be handled.")
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Jon*_*n N 14

当我想要解决类似问题时,我找到了解决这个问题的方法--KVO无法设置纯Swift协议字段的值.该协议必须标记为@objc,这在我的代码库中造成了太多的痛苦.解决方法是使用目标C运行时查找Ivar,获取字段偏移量,并使用指针设置值.此代码适用于Swift 2.2中的操场:

import Foundation

class MyClass
{
    var myInt: Int?
}

let instance = MyClass()

// Look up the ivar, and it's offset
let ivar: Ivar = class_getInstanceVariable(instance.dynamicType, "myInt")
let fieldOffset = ivar_getOffset(ivar)

// Pointer arithmetic to get a pointer to the field
let pointerToInstance = unsafeAddressOf(instance)
let pointerToField = UnsafeMutablePointer<Int?>(pointerToInstance + fieldOffset)

// Set the value using the pointer
pointerToField.memory = 42

assert(instance.myInt == 42)
Run Code Online (Sandbox Code Playgroud)

笔记:

编辑:现在有一个名为Runtime的框架,位于https://github.com/wickwirew/Runtime,它提供了Swift 4+内存布局的纯Swift模型,允许它安全地计算ivar_getOffset的等价物,而无需调用Obj C运行时.这允许设置如下属性:

let info = try typeInfo(of: User.self)
let property = try info.property(named: "username")
try property.set(value: "newUsername", on: &user)
Run Code Online (Sandbox Code Playgroud)

这可能是一个很好的方法,直到等效功能成为Swift本身的一部分.