A menudo me gusta cómo, en los inicializadores de struct s, enum s y protocol s, puedo escribir algo como self = someValue . Esto es genial cuando tengo algunos valores predefinidos o estoy clonando un valor existente.
Sin embargo, esta sintaxis no funciona para class es. Realmente no puedo entender por qué, tampoco.
No se puede asignar al valor: 'self' es inmutable
Si el problema es la inicialización doble, el compilador Swift sabe si, cuándo y dónde llamo a los inicializadores super o self designados, por lo que sabe si terminé de inicializar esta instancia. Si la preocupación es que aún no he llamado a un inicializador designado, entonces debería estar bien porque solo estaría haciendo de esta instancia una referencia a la otra (2 vars 1 puntero). Si la preocupación es que el acceso concurrente podría haber causado que self ya se haya inicializado... bueno, eso no tiene sentido porque estamos en el inicializador y los inicializadores de Swift no son susceptibles a eso .
Y después de todo lo que descubrí, puedo solucionar esto con un protocolo de un solo uso:
class MyClass { let content: String init(content: String) { self.content = content } convenience init(from1 other: MyClass) { self = other // Cannot assign to value: 'self' is immutable } } protocol MyProto {} extension MyClass: MyProto {} extension MyProto { init(from2 other: Self) { self = other } } let foo = MyClass(content: "Foo") print(MyClass(from1: foo)) // Never would've compiled in the first place print(MyClass(from2: foo)) // Perfectly OK!Entonces, ¿por qué se niega esto en el uso común, pero se permite en las extensiones de protocolo?
Parece que esto actualmente se comporta como se esperaba.
Todo el problema se ha discutido en los foros rápidos: Asignación a uno mismo en extensiones de protocolo
La última vez que surgió esta peculiaridad en las discusiones internas, la idea de algunos de nosotros fue que podría valer la pena prohibir que las clases se ajusten a los protocolos con requisitos de mutación por completo. Si lo piensa, esto tiene cierto sentido: parece que sería bastante difícil escribir código que pueda operar tanto en valores mutables como en referencias mutables genéricamente, ya que estas últimas no tienen semántica de valor:
var x = y x.mutatingProtocolRequirement() // did y change too?Sin embargo, la discusión se apagó.
Hay un argumento para esta funcionalidad en el foro de Swift Evolution:
Permitir
self = xen inicializadores de conveniencia de clasehttps://forums.swift.org/t/allow-self-x-in-class-convenience-initializers/15924
El tono menciona cómo esta sintaxis ya funciona para los tipos de valor de estructura y en los inicializadores de protocolo como se menciona en esta pregunta:
La propia biblioteca estándar de Swift y la superposición de Foundation solucionan esta funcionalidad faltante haciendo que las clases se ajusten a protocolos ficticios y utilizando inicializadores de extensión de protocolo cuando sea necesario para implementar esta funcionalidad.
La propuesta parece tener un amplio apoyo, incluso de Chris Lattner:
+1, hace mucho tiempo!!
Sin embargo, el tono aún no se ha implementado. Una vez implementado, se puede revisar y agregar potencialmente al lenguaje Swift.