Inconsistent protocol Self problem

May I ask what your doing with this? Because I can't see any situation where using the initializer is clearer or shorter than let copy = initial.copy().

Version numbers of languages are not comparable, since the languages do not progress with same feature sets / roadmaps. C# is 18 years old language, swift is 4 years old...

If I'm understanding your intention with the playground example you provided, it seems you are trying to get value semantics behaviour for the Property class. If you defined "struct Property" instead of class, you wouldn't have to copy it manually.

For all NSObject based types (which have reference semantics) there is already copy() method, so for those you don't need to implement it yourself.

Within those limits, you'd be fine with:

struct Property<T>  {
  var value: T
  // init(value: T) is synthesized automatically
}

extension Property where T: NSObject {
  func copy() -> Property<T> {
    print("referencetype")
    return Property(value: value.copy() as! T)
  }
}

extension Property where T: Any {
  func copy() -> Property<T> {
    print("valuetype")
    return Property(value: value)
  }
}

let foo = Property(value: 12)
foo.copy()

let bar = Property(value: NSArray())
bar.copy()

I didn't realize the compiler was smart enough to choose the NSObject extension instead of the Any extension. That's really cool.

Note that the chosen overload is based on the static type of the value:
(using @Moximillian's definitions)

let arr: Any = NSArray()
let baz: Property<Any> = Property(value: arr)
baz.value as AnyObject === baz.copy().value as AnyObject // true

Also it doesn't work if you forget to make the class extend NSObject (or can't because you are on Linux), e.g.:

class Baz { // Not an NSObject and not a struct!
    var bazzer: Character
    init(_ c: Character) { bazzer = c }
}
var baz = Property1(value: Baz("A"))
let baz2 = baz.copy()
baz.value.bazzer = "B"
baz.value.bazzer // B :)
baz2.value.bazzer // B :(

There is s problem with this solution if you need to type-erase the Property type, as in holding a list of properties that are bound to differing types:

protocol PropertyProtocol
{
  func copy() -> Self
}

struct Property<T> : PropertyProtocol
{
  var value: T
}

extension Property where T : NSObject
{
  func copy() -> Property<T>
  {
    print("referencetype")
    
    return Property(value: value.copy() as! T)
  }
}

extension Property where T: Any
{
  func copy() -> Property<T>
  {
    print("valuetype")
    
    return Property(value: value)
  }
}

let foo: PropertyProtocol = Property(value: 12)

foo.copy()

let bar: PropertyProtocol = Property(value: NSArray())

bar.copy()

Now you get :

valuetype
valuetype

… mainly because anything that is NSObject is also Any :wink:

If you need type-erasing, then dynamic type checking is needed, like in your original code. Unfortunately with this approach, also Int ends up showing up as reference type due to implicit bridging(?)... But otherwise works fine.

struct Property<T>  {
  var value: T
}

struct Zoq {
  var stuff: Int = 0
}

extension Property {
  func copy() -> Property<T> {
    if let value = value as? NSObject {
      print("referencetype")
      return Property(value: value.copy() as! T)
    } else {
      print("valuetype")
      return Property(value: value)
    }
  }
}


let foo = Property(value: 12)
foo.copy()

let bar = Property(value: NSArray() as Any)
bar.copy()

let zoq = Property(value: Zoq())
zoq.copy()

Which is one reason why I am not using NSObject at all :sunglasses:

If you want (swift) classes that don't inherit from NSObject, then you don't get copy() for free, you have to implement it for each of those classes. So something like below. Using "Any" (same as NSObject.copy()) as the return type here, instead of "Self", to avoid writing workarounds.

protocol Copyable: AnyObject {
  func copy() -> Any
}

extension NSObject: Copyable {}

struct Property<T>  {
  var value: T
}

extension Property {
  func copy() -> Property<T> {
    if let value = value as? Copyable  {
      print("referencetype")
      return Property(value: value.copy() as! T)
    } else {
      print("valuetype")
      return Property(value: value)
    }
  }
}

/// pure value type
struct SwiftValueType {
  var stuff: Int = 0
}

/// swift class that does not inherit from NSObject
class SwiftClass {
  var stuff: Int

  required init(stuff: Int) {
    self.stuff = stuff
  }
}

extension SwiftClass: Copyable {
  func copy() -> Any {
    return SwiftClass(stuff: stuff)
  }
}

let foo = Property(value: 12)
foo.copy()

let bar = Property(value: NSArray())
bar.copy()

let zoq = Property(value: SwiftValueType())
zoq.copy()

let fot = Property(value: SwiftClass(stuff: 5))
fot.copy()

I agree, having copy return Any and using casts is a viable approach (Java does this) and it it probably what NSObject is doing (haven't looked and not very familiar with Obj-C, but Obj-C seems to use void * a lot!). If you don't want to use a hand written AnyCopyable then this is a good approach.