3

我在使用属性时发现了一个奇怪的行为,该属性被继承为只读,而不是在继承的类中重新声明为 readwrite

@interface A : NSObject

@property (nonatomic, strong, readonly) NSObject * someProperty;

@end

Bh

@interface B : A

// no matter if here
// @property (nonatomic, strong, readwrite) NSObject * someProperty;

- (void)foo;

@end

Bm

@interface B()

// no matter if here
@property (nonatomic, strong, readwrite) NSObject * someProperty;

@end

@implementation B

- (void)foo {

    NSLog(@"%@", self.someProperty);

    // crash here with unrecognized selector setSomeProperty:
    self.someProperty = [NSObject new];
}

@end

打电话

self.someProperty = [NSObject new];

导致代码在无法识别的选择器“setSomeProperty:”上崩溃

调查显示,看起来 setter 没有被合成,即使声明为 readwrite

为什么会这样?编译器没有指出发生这种情况的任何警告,我也没有在任何记录此行为的地方找到

4

2 回答 2

2

我不能给你一个官方的参考,但这是我的经历:对于从超类继承的属性,编译器不会生成任何访问器方法。

在您的情况下,该属性readonly在类 A 中声明,因此编译器仅创建一个 getter 方法。类 B 中的重新声明不会创建任何额外的访问器方法。原因可能是 B 类不知道在 A 类中“如何”实现该属性(它不必是实例变量)。

因此,子类中的属性声明只是对编译器的“承诺”,即 getter 和 setter 函数将在运行时可用(类似于 @dynamic 声明)。如果没有设置器,那么您将获得运行时异常。

因此,在子类中重新声明属性的用例是,如果超类在公共接口中将属性声明为只读,但在(私有)类扩展中声明为读写:

// A.h
@interface A : NSObject
@property (nonatomic, strong, readonly) NSObject * someProperty;
@end

// A.m
@interface A()
@property (nonatomic, strong, readwrite) NSObject * someProperty;
@end

@implementation A
@end

在这种情况下,setter 和 getter 都在类 A 中创建,而类 B 可以在其实现中将属性重新声明为可读写。

于 2013-06-19T18:20:11.170 回答
2

向 Bm 文件添加@synthesize指令,崩溃就会消失:

@synthesize someProperty = _someProperty;

问题在于,由于在父类中您声明了该属性,因为readonly没有为它合成 setter。子类继承了这种行为。即使您将属性重新声明为readwrite子类。该@synthesize命令将指示编译器再次为 B 类生成访问器方法。

希望这可以帮助!

在此处输入图像描述

于 2013-06-19T18:14:38.260 回答