为什么我不能改变分配为可变的 NSObject,引用为不可变的,然后转换回可变的?
Why can't I mutate an NSObject allocated as mutable, referenced as immutable, then cast back to mutable?
我正在分配一个 NSMutableAttributedString,然后将其分配给一个 SKLabelNode 的 attributedString 属性。 属性 是一个 (NSAttributedString *),但我想我可以将它转换为一个 (NSMutableAttributedString *),因为它是这样分配的。然后访问它的 mutableString 属性,更新它,而不必每次我想更改字符串时都进行另一次分配。
但是在转换之后,对象是不可变的,当我尝试改变它时抛出异常。
我真的不能改变一个被分配为可变的 NSObject 只是因为它被引用为不可变的吗?
Is it true that I can't mutate an NSObject that was allocated as mutable just because it was referenced as immutable?
不,您的一般直觉是正确的。暂时忽略一般“可变”和“不可变”的概念,但关注 subclassing NS<SomeType>
和 [=16= 之间的关系]:通常,具有mutable/immutable对应的Apple框架对象具有可变变体作为子类型 的不可变变体。将 mutable 变量分配给 immutable 变量不会改变存储变量的任何内容,与以下内容相同:
@interface Foo: NSObject @end
@implementation Foo @end
@interface Bar: Foo @end
@implementation Bar @end
Foo *f = [[Bar alloc] init];
NSLog(@"%@", f); // => <Bar: 0x6000014b0040>
你可以看到与 NSMutableAttributedString
类似的东西(虽然它有点复杂,因为 NSAttributedString
和子类型形成 class cluster:
NSAttributedString *s = [[NSMutableAttributedString alloc] initWithString:@"Hello"];
NSLog(@"%@", [s class]); // => NSConcreteMutableAttributedString
然而: 分配给 local 变量之间的主要区别,如上面的 f
和 s
,并分配给 SKLabelNode
的 attributedText
属性 在于 属性 的定义:
@property(nonatomic, copy, nullable) NSAttributedString *attributedText;
具体来说,SKLabelNode
在分配给它的 attributedText
属性 时执行 copy,并在 NSMutableAttributedString
上执行复制] 产生一个 不可变的 变体:
NSAttributedString *s = [[[NSMutableAttributedString alloc] initWithString:@"Hello"] copy];
NSLog(@"%@", [s class]); // => NSConcreteAttributedString
因此,当您以这种方式分配给您的 SKLabelNode
时,它不会存储您的原始实例,而是存储它自己的一个副本 — 碰巧这个副本是不可变的。
请注意,这是两件事的结合:
SKLabelNode
选择到-copy
赋值的变量;如果它 -retain
改为编辑它(例如 @property(nonatomic, strong, nullable)
),这将按您预期的那样工作
NSMutableAttributedString
return 是 -copy
方法中的 NSAttributedString
,但 没有 。事实上,大多数类型 return instancetype
来自 -copy
,但是 NSMutableAttributedString
选择 到 return 一个 NSAttributedString
从它的 -copy
方法。 (好吧,这就是 class 集群的重点:-copy
→ 不可变,-mutableCopy
→ 可变)
所以一般来说,情况不一定如此,但您会在使用这些规则实施的 mutable/immutable class 集群中看到这种行为。
为了与上面的 Foo
示例进行比较:
@interface Foo: NSObject @end
@implementation Foo
- (instancetype)copyWithZone:(NSZone *)zone {
// Expects to return a new Foo:
return [[[self class] alloc] init];
// OR:
// Not all types allow copying:
return self;
}
@end
@interface Bar: Foo @end
@implementation Bar @end
Foo *f = [[[Bar alloc] init] copy];
NSLog(@"%@", f); // => <Bar: 0x600001e7c1a0>
我正在分配一个 NSMutableAttributedString,然后将其分配给一个 SKLabelNode 的 attributedString 属性。 属性 是一个 (NSAttributedString *),但我想我可以将它转换为一个 (NSMutableAttributedString *),因为它是这样分配的。然后访问它的 mutableString 属性,更新它,而不必每次我想更改字符串时都进行另一次分配。
但是在转换之后,对象是不可变的,当我尝试改变它时抛出异常。
我真的不能改变一个被分配为可变的 NSObject 只是因为它被引用为不可变的吗?
Is it true that I can't mutate an NSObject that was allocated as mutable just because it was referenced as immutable?
不,您的一般直觉是正确的。暂时忽略一般“可变”和“不可变”的概念,但关注 subclassing NS<SomeType>
和 [=16= 之间的关系]:通常,具有mutable/immutable对应的Apple框架对象具有可变变体作为子类型 的不可变变体。将 mutable 变量分配给 immutable 变量不会改变存储变量的任何内容,与以下内容相同:
@interface Foo: NSObject @end
@implementation Foo @end
@interface Bar: Foo @end
@implementation Bar @end
Foo *f = [[Bar alloc] init];
NSLog(@"%@", f); // => <Bar: 0x6000014b0040>
你可以看到与 NSMutableAttributedString
类似的东西(虽然它有点复杂,因为 NSAttributedString
和子类型形成 class cluster:
NSAttributedString *s = [[NSMutableAttributedString alloc] initWithString:@"Hello"];
NSLog(@"%@", [s class]); // => NSConcreteMutableAttributedString
然而: 分配给 local 变量之间的主要区别,如上面的 f
和 s
,并分配给 SKLabelNode
的 attributedText
属性 在于 属性 的定义:
@property(nonatomic, copy, nullable) NSAttributedString *attributedText;
具体来说,SKLabelNode
在分配给它的 attributedText
属性 时执行 copy,并在 NSMutableAttributedString
上执行复制] 产生一个 不可变的 变体:
NSAttributedString *s = [[[NSMutableAttributedString alloc] initWithString:@"Hello"] copy];
NSLog(@"%@", [s class]); // => NSConcreteAttributedString
因此,当您以这种方式分配给您的 SKLabelNode
时,它不会存储您的原始实例,而是存储它自己的一个副本 — 碰巧这个副本是不可变的。
请注意,这是两件事的结合:
SKLabelNode
选择到-copy
赋值的变量;如果它-retain
改为编辑它(例如@property(nonatomic, strong, nullable)
),这将按您预期的那样工作NSMutableAttributedString
return 是-copy
方法中的NSAttributedString
,但 没有 。事实上,大多数类型 returninstancetype
来自-copy
,但是NSMutableAttributedString
选择 到 return 一个NSAttributedString
从它的-copy
方法。 (好吧,这就是 class 集群的重点:-copy
→ 不可变,-mutableCopy
→ 可变)
所以一般来说,情况不一定如此,但您会在使用这些规则实施的 mutable/immutable class 集群中看到这种行为。
为了与上面的 Foo
示例进行比较:
@interface Foo: NSObject @end
@implementation Foo
- (instancetype)copyWithZone:(NSZone *)zone {
// Expects to return a new Foo:
return [[[self class] alloc] init];
// OR:
// Not all types allow copying:
return self;
}
@end
@interface Bar: Foo @end
@implementation Bar @end
Foo *f = [[[Bar alloc] init] copy];
NSLog(@"%@", f); // => <Bar: 0x600001e7c1a0>