You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何利用反射简化ObjectMapper中多属性类的mapping函数实现

嘿,我完全懂你手动写ObjectMapper的mapping函数时的痛苦——属性一多简直是重复劳动!你的反射思路方向没错,但问题出在Swift反射的局限性和ObjectMapper<-操作符的工作逻辑上,我来帮你理清楚并给出可行的解决方案:

先说说你之前尝试的问题

你两次尝试都没成功,核心原因都是一样的:

  1. 第一次尝试:self.value(forKey: name)返回的是属性的值拷贝,而ObjectMapper的<-操作符需要的是属性的可变引用(inout参数)。修改这个拷贝根本不会影响原对象的属性,所以映射完全不会生效。
  2. 第二次尝试:Mirror的Child只是个元组类型别名(label: String?, value: Any),其中的value同样是属性的拷贝,既没法作为inout参数传给<-操作符,编译器也不允许直接对元组值使用这个操作符,所以写法本身就无法通过编译。

可行的自动映射方案

根据你的类是否继承自NSObject,有两种不同的实现方式:

方案1:继承自NSObject的类(借助OC运行时)

如果你的模型类继承自NSObject,可以利用Objective-C运行时直接获取并修改属性,实现完全自动的映射:

import ObjectMapper
import ObjectiveC

class BaseAutoMappable: NSObject, Mappable {
    required init?(map: Map) {}
    
    func mapping(map: Map) {
        var propertyCount: UInt32 = 0
        guard let properties = class_copyPropertyList(type(of: self), &propertyCount) else {
            return
        }
        
        for index in 0..<Int(propertyCount) {
            let property = properties[index]
            let propertyName = String(cString: property_getName(property))
            
            // 从map中解析值并通过KVC设置到属性上
            if let parsedValue = map[propertyName].value() {
                setValue(parsedValue, forKey: propertyName)
            }
        }
        
        free(properties)
    }
}

// 用法示例
class User: BaseAutoMappable {
    var username: String?
    var age: Int?
    var weight: Double?
    var array: [String]?
    var dictionary: [String: Any]?
    var bestFriend: User?
    var friends: [User]?
}

方案2:纯Swift类(借助KeyPath)

如果是不继承NSObject的纯Swift类,没法用OC运行时,我们可以通过KeyPath来实现半自动化的映射(比手动写每个<-简洁很多):

import ObjectMapper

protocol AutoMappable: Mappable {
    static var propertyKeyPaths: [PartialKeyPath<Self>] { get }
}

extension AutoMappable {
    func mapping(map: Map) {
        for keyPath in Self.propertyKeyPaths {
            // 处理可选类型属性
            if let writablePath = keyPath as? WritableKeyPath<Self, Any?> {
                self[keyPath: writablePath] <- map[keyPath._kvcKeyPathString!]
            }
            // 处理非可选类型属性
            else if let writablePath = keyPath as? WritableKeyPath<Self, Any> {
                self[keyPath: writablePath] <- map[keyPath._kvcKeyPathString!]
            }
        }
    }
}

// 用法示例
class User: AutoMappable {
    var username: String = ""
    var age: Int = 0
    var weight: Double = 0.0
    var array: [String] = []
    var dictionary: [String: Any] = [:]
    var bestFriend: User?
    var friends: [User] = []
    
    required init?(map: Map) {}
    
    static var propertyKeyPaths: [PartialKeyPath<User>] {
        return [\User.username, \User.age, \User.weight, \User.array, \User.dictionary, \User.bestFriend, \User.friends]
    }
}

注意事项

  • 如果JSON键和属性名不一致,这种自动映射就不适用了,你需要单独手动处理这些特殊属性。
  • 嵌套的Mappable对象(比如示例中的bestFriend)只要也实现了Mappable协议,ObjectMapper会自动完成嵌套解析。

内容的提问来源于stack exchange,提问作者Olcay Ertaşa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 09:52:05