swift - Using reflection to set object properties without using setValue forKey -
in swift it's not possible use .setvalue(..., forkey: ...)
- nullable type fields
int
? - properties have
enum
it's type - an array of nullable objects
[myobject?]
there 1 workaround , overriding setvalue forundefinedkey method in object itself.
since i'm writing general object mapper based on reflection. see evreflection minimize kind of manual mapping as possible.
is there other way set properties automatically?
the workaround can found in unit test in library here code:
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 1") xctasserttrue(status.status == .notok, "the status should 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 list should have nullabletype 2") xctasserttrue(status.list[1]?.nullabletype == 3, "the second item in 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 = [] item in list { self.list.append(item as? testobject) } } default: nslog("---> setvalue key '\(key)' should handled.") } } }
i found way around when looking solve similar problem - kvo can't set value of pure swift protocol field. protocol has marked @objc, caused pain in code base. workaround ivar using objective c runtime, field offset, , set value using pointer. code works in playground in swift 2.2:
import foundation class myclass { var myint: int? } let instance = myclass() // ivar, , it's offset let ivar: ivar = class_getinstancevariable(instance.dynamictype, "myint") let fieldoffset = ivar_getoffset(ivar) // pointer arithmetic pointer field let pointertoinstance = unsafeaddressof(instance) let pointertofield = unsafemutablepointer<int?>(pointertoinstance + fieldoffset) // set value using pointer pointertofield.memory = 42 assert(instance.myint == 42)
notes:
- this pretty fragile, shouldn't use this.
- but maybe live in thoroughly tested , updated reflection library until swift gets proper reflection api.
- it's not far away mirror internally, see code in reflection.mm, around here: https://github.com/apple/swift/blob/swift-2.2-branch/stdlib/public/runtime/reflection.mm#l719
- the same technique applies other types kvo rejects, need careful use right unsafemutablepointer type. particularly protocol vars, 40 or 16 bytes, unlike simple class optional 8 bytes (64 bit). see mike ash on topic of swift memory layout: https://mikeash.com/pyblog/friday-qa-2014-08-01-exploring-swift-memory-layout-part-ii.html
Comments
Post a Comment