skip to Main Content

I have created multiple classes, all of which need to implement the NSCopying protocol, but there are a lot of properties in my classes, is there an easier way?
Below is my current way:

    class TestA: NSObject, NSCopying {
    var a: CGFloat = 0
    var b: CGFloat = 0
    
    required override init() {
         
    }
    
    func copy(with zone: NSZone? = nil) -> Any {
        let item = type(of: self).init()
        item.a = a
        item.b = b
        return item
    }
}

class TestB: TestA {
    var c: CGFloat = 0
    var d: CGFloat = 0
    
    override func copy(with zone: NSZone? = nil) -> Any {
        let item = super.copy(with: zone) as! TestB
        item.c = c
        item.b = b
        return item
    }
}

My thought is, can we take all the properties of the class, automatically create a new object, assign values to the new object?

3

Answers


  1. Chosen as BEST ANSWER

    I think I've found a solution, but I'm not sure if this will have any ill effects. Can someone tell me what's wrong with this。Thanks!

      @objcMembers class TestA: NSObject, NSCopying {
        var a: CGFloat = 0
        var b: CGFloat = 0
        var c: CGFloat = 0
        
        required override init() {
            
        }
        
        func copy(with zone: NSZone? = nil) -> Any {
            let item = type(of: self).init()
            for property in getAllPropertys() {
                let value = self.value(forKey: property)
                item.setValue(value, forKey: property)
            }
            return item
        }
    
        func getAllPropertys()->[String]{
            var result = [String]()
            var count:UInt32 = 0
            let proList = class_copyPropertyList(object_getClass(self),&count)
            for i in 0..<numericCast(count) {
                let property = property_getName((proList?[i])!);
                let proper = String.init(cString: property)
                result.append(proper)
              }
              return result
        }
    }
    

  2. Use the initializer.

    class TestA: NSObject, NSCopying {
        var a: CGFloat = 0
        var b: CGFloat = 0
    
        required override init() {}
        
        convenience init(a: CGFloat, b: CGFloat) {
            self.init()
            self.a = a
            self.b = b 
        }
        
        func copy(with zone: NSZone? = nil) -> Any {
            let item = TestA(a: a, b: b)
            return item
        }
    }
    

    Doing it this way doesn’t really save code since you still need an initializer that takes values for all properties, but you do get a simplified copy method and another initializer that might be useful in other situations too.

    Login or Signup to reply.
  3. You can look at KeyValueCoding package with KeyValueCoding protocol which implements enumeration of all properties of an object and setting values by key paths for pure swift classes and structs.

    Based on it you can implement Copying protocol:

    protocol Copying: KeyValueCoding {
        init()
    }
    
    extension Copying {
        func makeCopy() -> Self {
            var item = Self()
            var _self = self
            metadata.properties.forEach {
                item[$0.name] = _self[$0.name]
            }
            return item
        }
    }
    

    How it works:

    class TestA: Copying {
        var a: CGFloat = 1
        var b: Int = 2
        
        required init() {}
    }
    
    class TestB: TestA {
        let c: String = "Hello Copy!"
        let d: Date = Date(timeIntervalSince1970: 123456789)
    }
    
    let objectA = TestA()
    objectA.a = 100
    objectA.b = 200
    let copiedA = objectA.makeCopy()
    print(copiedA.a) // "100.0"
    print(copiedA.b) // "200"
    
    let objectB = TestB()
    objectB.a = 100
    objectB.b = 200
    let copiedB = objectB.makeCopy()
    print(copiedB.a) // "100.0"
    print(copiedB.b) // "200"
    print(copiedB.c) // "Hello Copy!"
    print(copiedB.d.timeIntervalSince1970) // "123456789.0"
    

    So as you can see this approach works with inherited properties as well.

    Moreover it works with structs:

    struct MyStruct: Copying {
        let a = 1.0
        let b = 2
        let c = "c"
    }
    
    let myStruct = MyStruct()
    let copied = myStruct.makeCopy()
    print(copied) // MyStruct(a: 1.0, b: 2, c: "c")
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search