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
相关产品推荐
相关产品推荐

