I'm allocating an NSMutableAttributedString, then assigning it to the attributedString property of an SKLabelNode. That property is an (NSAttributedString *), but I figured that I could cast it to an (NSMutableAttributedString *) since it was allocated as such. And then access its mutableString property, update it, and not have to do another allocation every time I want to change the string.
But after the cast, the object is immutable and an exception is thrown when I try to mutate it.
Is it true that I can't mutate an NSObject that was allocated as mutable just because it was referenced as immutable?
No, your general intuition here is correct. Ignoring for a second the concept of "mutable" and "immutable" in general, but focusing on the subclassing relationship between
NS<SomeType>andNSMutable<SomeType>: typically, Apple framework objects which have mutable/immutable counterparts have the mutable variant as a subtype of the immutable variant. Assigning a mutable variable to an immutable variable does not change anything about the stored variable, same as the following does not:You can see something similar with
NSMutableAttributedString(though it's a little more complicated becauseNSAttributedStringand subtypes form a class cluster:However: the key difference between assigning to a local variable like with
fandsabove, and assigning to anSKLabelNode'sattributedTextproperty lies in the property's definition:Specifically,
SKLabelNodeperforms a copy on assignment to itsattributedTextproperty, and performing a copy on anNSMutableAttributedStringproduces an immutable variant:So, when you assign to your
SKLabelNodein this way, it doesn't store your original instance, but a copy of its own — and it happens to be that this copy is immutable.Note that this is behavior is a confluence of two things:
SKLabelNodechooses to-copythe assigned variable; if it-retained it instead (e.g.@property(nonatomic, strong, nullable)), this would work as you expectedNSMutableAttributedStringreturns anNSAttributedStringfrom its-copymethod, but it doesn't have to. In fact, most types returninstancetypefrom-copy, butNSMutableAttributedStringchooses to return anNSAttributedStringfrom its-copymethod. (Well, that is the point of the class cluster:-copy→ immutable,-mutableCopy→ mutable)So in general, this need not be the case, but you will see this behavior for mutable/immutable class clusters which are implemented using these rules.
For comparison, with the
Fooexample above: