NSCopying in a Swift World
This crash happens because, behind the scenes, the Swift compiler synthesises overrides of a superclass’s designated initialisers. These overridden initialisers crash to prevent objects from being incorrectly initialised from Objective-C.
[…]
From a quick look on Stack Overflow, it seems
[self.class alloc]
is often a recommended way to create a copy in Objective-C. However, the problem is that the use ofself.class
dynamically looks up the subclassSocialDocument
, but the code here in our framework has no idea thatSocialDocument
has changed the initialisation requirements.[…]
If
Document
were a simpler type where all state that should be copied was public, then subclasses that required copying to create instances of the subclass could overridecopy(with:)
without callingsuper
[…][…]
There isn’t a nice way to make copying subclasses work while still adhering to Swift’s principle of reducing the amount of mutable state by using
let
to create read-only properties.
PSA: Don’t cast values to NSCopying in Swift or you risk a crash at runtime. Learned this the hard way. 🤠
[…]
Because all values that were originally bridged from Objective-C will pass the alone
NSCopying
cast, even when they don’t actually conform to the protocol. Such values immediately become instances of_SwiftValue
and, like trojans, they will sit there pretending to be innocent but will crash at runtime as soon as they’re accessed.
Previously:
- Swift Proposal: Init Accessors
- Marking Unused Required Swift Initializers As Unavailable
- Using Lazy Variables to Work Around Swift Initialization Rules
- “required” Swift Initializers and Decodable
- Mixing Swift Initializers
- Swift init()
- Failable Initializers, Revisited
- Strange Tales of Swift Initialization