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

Swift中如何更简洁地实现NSCopying协议copy方法

NSCopying 简便实现方案

当存在大量遵循NSCopying的类、且类包含大量属性/多层继承关系时,不需要手动为每个属性编写赋值逻辑,可以通过Runtime遍历属性的方式,在基类统一实现自动拷贝逻辑,所有子类直接继承即可复用能力。

核心实现思路

  • 定义一个遵循NSCopying的基类,统一实现copy(with:)方法
  • 在copy方法中动态创建当前类的实例,保证继承场景下创建的是正确的子类类型,而非基类类型
  • 通过Runtime递归遍历当前类、以及所有父类(直到基类层级)的存储属性
  • 通过KVC为新实例的所有属性赋值,完成拷贝

可直接复用的代码

import Foundation

class AutoCopyNSObject: NSObject, NSCopying {
    required override init() {
        super.init()
    }
    
    func copy(with zone: NSZone? = nil) -> Any {
        // 创建当前实际类型的实例
        let copiedInstance = type(of: self).init()
        var targetClass: AnyClass? = type(of: self)
        
        // 递归遍历父类属性,直到离开AutoCopyNSObject的继承链
        while let cls = targetClass, cls is AutoCopyNSObject.Type {
            var propertyCount: UInt32 = 0
            // 获取当前类的属性列表
            guard let properties = class_copyPropertyList(cls, &propertyCount) else {
                targetClass = class_getSuperclass(cls)
                continue
            }
            
            // 遍历所有属性完成赋值
            for index in 0..<Int(propertyCount) {
                let property = properties[index]
                // 拿到属性名
                guard let cName = property_getName(property),
                      let propertyName = String(utf8String: cName) else {
                    continue
                }
                // 取值赋值,需要深拷贝的属性可在此处判断是否遵循NSCopying,调用copy方法后再赋值
                let propertyValue = self.value(forKey: propertyName)
                copiedInstance.setValue(propertyValue, forKey: propertyName)
            }
            
            free(properties)
            targetClass = class_getSuperclass(cls)
        }
        
        return copiedInstance
    }
}

使用方式

所有需要实现NSCopying的类直接继承该基类即可,不需要手动重写copy(with:)方法,也不需要逐个写属性赋值逻辑:

class TestA: AutoCopyNSObject {
    var a: CGFloat = 0
    var b: CGFloat = 0
}

class TestB: TestA {
    var c: CGFloat = 0
    var d: CGFloat = 0
    // 无需重写copy方法,基类会自动识别a/b/c/d四个属性完成赋值
}

注意事项

  • 上述实现默认是浅拷贝,如果需要对引用类型属性做深拷贝,只需要在属性赋值环节判断属性值是否遵循NSCopying,调用对应属性的copy()方法拿到拷贝值后,再赋值给新实例即可。
  • 如果类中存在只读属性、计算属性、标记为@nonobjc的Swift属性,无法通过KVC赋值,可以在遍历属性时通过property_getAttributes获取属性特征,过滤掉不需要拷贝的属性,避免运行时报错。
  • 你原有实现中TestB的copy方法存在两个问题:一是漏写了属性d的赋值,二是父类copy逻辑已经完成了b属性的赋值,子类重复写item.b = b属于冗余代码,自动拷贝逻辑可以完全避免这类手误。
  • 对于个别无法被Runtime识别、或者需要特殊拷贝逻辑的属性,可以在对应子类中重写copy(with:)方法,先调用super.copy拿到基类拷贝完成的实例,再单独为特殊属性赋值即可,不需要处理全量属性。

内容的提问来源于stack exchange,提问作者quan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 20:57:22