Objective-c 只读复制属性和 ivars

Objective-c readonly copy properties and ivars

我试图理解在 objective-c 中声明为复制和只读的属性,具体来说,我是否必须自己进行复制。在我的初始化方法中。证据表明我这样做:

@interface A : NSObject 
    @property(nonatomic, copy, readonly) NSData *test;
    - (instancetype)initWithData:(NSData *)data;
@end

@implementation A

- (instancetype)initWithData:(NSData *)data {
    if ((self = [super init]) != nil) {
        _test = data;
    }
    return self;
}

@end


int main (void) {
    NSData *d1 = [NSMutableData dataWithBytes:"1234" length:5];
    A *a = [[A alloc] initWithData:d1];

    NSLog(@"%lx", (unsigned long)d1);
    NSLog(@"%lx", (unsigned long)a.test);
    return 0;
}

我原以为我可以在我的 init 方法中做 self.test = data,但这是不允许的,因为它是只读的(并不意外)。当然,self.test = [data copy]保证了两个不同的对象。

所以:有没有一种方法可以在 objective-c 中创建一个只读的 属性 来复制传入的值,或者它是否足以使组合毫无意义的边缘情况,我必须做任何还是手动复制我自己?

@property 声明只是 shorthand 一些 accessor/mutator 方法声明,以及(在某些情况下)所述 accessor/mutator 方法的综合实现。

在您的例子中,@property(nonatomic, copy, readonly) NSData *test 声明扩展为以下等效代码:

@interface A : NSObject
{
    NSData* _test;
}
- (NSData*)test;
@end

@implementation A
- (NSData*)test
{
    return _test;
}
@end

没有setTest:修改器方法,因为属性被声明为readonly,所以copy属性没有效果。

您可以实现自己的修改器方法:

- (void)setTest:(NSData*)newValue
{
    _test = [newValue copy];
}

或者,您可以通过在实现文件的私有 class 扩展中声明 read/write 属性 让编译器为您合成一个修改器方法:

// A.m:

@interface A() 
@property (nonatomic, copy) NSData* test;
@end

这两种情况都允许您使用 test 增变器方法将值复制到 _test 实例变量:

- (instancetype)initWithData:(NSData *)data {
    if ((self = [super init]) != nil) {
        self.test = data;
    }
    return self;
}

最后的结果是:

@interface A : NSObject
@property(nonatomic, copy, readonly) NSData* test;
- (instancetype)initWithData:(NSData*)data;
@end

@interface A()
@property (nonatomic, copy) NSData* test;
@end

@implementation A
- (instancetype)initWithData:(NSData*)data {
    if ((self = [super init]) != nil) {
        self.test = data;
    }
    return self;
}
@end

除了 Darren 所说的之外,copy 属性还描述了属性 setter 具有的语义。在您的初始化程序中,您没有使用 setter,而是直接分配给实例变量。

可能有点难理解,但是实例变量和属性不是一回事。在这种情况下,它被用来 来实现 和 属性。但是,分配给实例变量与设置 属性.

不同

如果您希望您的初始化器也具有复制传入数据的语义,那是一个单独的设计决策(尽管使用 属性 的语义是个好主意)。您可以按照 Darren 的建议使用私有 setter 来实现它,但您也可以这样做:

    _test = [data copy];

在初始化程序中。