是否有覆盖属性的模式?

Din*_*rie 6 objective-c accessor mutability declared-property

Objective-C运行保持声明的属性作为元数据与一个类对象的列表.元数据包括属性名称,类型和属性.运行时库还提供了一些函数来检索这些信息.这意味着声明的属性不仅仅是一对存取方法(getter/setter).我的第一个问题是:为什么我们(或运行时)需要元数据?

众所周知,在子类中不能覆盖声明的属性(readwrite与readonly除外).但我有一个场景,保证需要:

@interface MyClass : MySuperClass <NSCopying, NSMutableCopying>

@property (nonatomic, copy, readonly) NSString *string;

- (id)initWithString:(NSString *)aString;

@end


@interface MyMutableClass : MyClass

@property (nonatomic, strong, readwrite) NSMutableString *string;

- (id)initWithString:(NSString *)aString;

@end
Run Code Online (Sandbox Code Playgroud)

当然,编译器不会让上面的代码通过.我的解决方案是使用一对存取方法替换声明的属性(使用readonly case,只是getter):

@interface MyClass : MySuperClass <NSCopying, NSMutableCopying> {
    NSString *_string;
}

- (id)initWithString:(NSString *)aString;

- (NSString *)string;

@end


@implementation MyClass

- (id)initWithString:(NSString *)aString {
    self = [super init...];
    if (self) {
        _string = [aString copy];
    }
    return self;
}

- (NSString *)string {
    return _string;
}

- (id)copyWithZone:(NSZone *)zone {
    return self;
}

- (id)mutableCopyWithZone:(NSZone *)zone {
    return [[MyMutableClass alloc] initWithString:self.string];
}

@end


@interface MyMutableClass : MyClass

- (id)initWithString:(NSString *)aString;

- (NSMutableString *)string;
- (void)setString:(NSMutableString *)aMutableString;

- (void)didMutateString;

@end


@implementation MyMutableClass

- (id)initWithString:(NSString *)aString {
    self = [super init...];
    if (self) {
        _string = [aString mutableCopy];
    }
    return self;
}

- (NSMutableString *)string {
    return (NSMutableString *)_string;
}

- (void)setString:(NSMutableString *)aMutableString {
    _string = aMutableString;

    // Inform other parts that `string` has been changed (as a whole).
    // ...
}

- (void)didMutateString {
    // The content of `string` has been changed through the interface of
    // NSMutableString, beneath the accessor method.
    // ...
}

- (id)copyWithZone:(NSZone *)zone {
    return [[MyClass alloc] initWithString:self.string];
}

@end
Run Code Online (Sandbox Code Playgroud)

属性string需要是可变的,因为它是逐步修改的,并且可能经常修改.我知道具有相同选择器的方法应该共享相同的返回和参数类型的约束.但我认为上述解决方案在语义和技术上都是合适的.对于语义方面,可变对象不可变对象.对于技术方面,编译器将所有对象编码为id.我的第二个问题是:上述解决方案是否有意义?或者它只是奇怪的?

我也可以采用混合方法,如下:

@interface MyClass : MySuperClass <NSCopying, NSMutableCopying> {
    NSString *_string;
}

@property (nonatomic, copy, readonly) NSString *string;

- (id)initWithString:(NSString *)aString;

@end


@interface MyMutableClass: MyClass

- (id)initWithString:(NSString *)aString;

- (NSMutableString *)string;
- (void)setString:(NSMutableString *)aMutableString;

- (void)didMutateString;

@end
Run Code Online (Sandbox Code Playgroud)

但是,当我使用点语法访问属性时myMutableObject.string,编译器会警告访问器方法的返回类型与声明的属性的类型不匹配.可以使用消息表单作为[myMutableObject string].这表明声明属性不仅仅是一对存取方法的另一个方面,即更多静态类型检查,尽管这里不需要.我的第三个问题是:当要在子类中重写时,使用getter/setter对而不是声明的属性是否常见?

gai*_*ige 3

我对此的看法会略有不同。对于@interfaceObjective-C 类,您需要声明该类与与其通信的所有类所使用的 API。通过用强属性替换NSString*复制属性NSMutableString*,您可能会造成可能发生意外副作用的情况。

特别是,NSString*复制属性预计会返回一个不可变的对象,这在许多情况下使用对象不会是安全的NSMutableString*(字典中的键、 an 中的元素名称NSXMLElement)。因此,您确实不想以这种方式替换它们。

如果您需要底层NSMutableString,我建议如下:

  • NSMutableString*除了字符串属性之外,添加一个属性,并将其命名-mutableString
  • 重写-setString:方法来创建NSMutableString并存储它
  • 重写该-string方法以返回可变字符串的不可变副本
  • 仔细评估是否可以用 NSMutableString 替换内部 ivar。如果您无权访问原始类并且不确定是否对类内部字符串的可变性做出了假设,这可能会成为问题

如果这样做,您将维护当前的界面,而不会中断该类的现有用户,同时扩展行为以适应您的新范例。

在可变对象和不可变对象之间进行更改的情况下,您确实需要小心,不要破坏该对象的 API 约定。