声明属性时会合成什么等效代码?

3

当getter和setter方法自动合成后,它们的具体代码实现是什么样子的?

从官方文档中,我只找到了一些推荐的实现技巧,但并没有提到编译器在合成过程中使用的哪种方法:http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmAccessorMethods.html#//apple_ref/doc/uid/TP40003539-SW5

有些技巧建议实现包含autorelease消息的代码,这对于多线程编程来说并不十分安全。我想知道自动生成的代码是否遵循了其中某些推荐的实现方式。


例如:

.h

@interface AClass: NSObject{}
    @property (nonatomic, retain) AnotherClass *aProp;
@end

.m

@implementation AClass
    @synthesize aProp

-(id) init {
    if ((self = [super init])) {
        self.aProp = [[AnotherClass alloc] init];    // setter invocation
    }
    return self;
}

-(AnotherClass *) aMethod {
    return self.aProp;     // getter invocation
}
@end

编译器生成的aProp等效访问器代码片段是什么?

-(AnotherClass *) aProp {
    // getter body
}

-(void) setAProp: (AnotherClass *) {
    // setter body
}
2个回答

8

当将属性声明为nonatomic时,您将获得以下内容:

// .h
@property (nonatomic, retain) id ivar;

// .m
- (id)ivar {
    return ivar;
}

- (void)setIvar:(id)newValue {
    if (ivar != newValue) {  // this check is mandatory
        [ivar release];
        ivar = [newValue retain];
    }
}

请注意检查ivar != newValue。如果没有这个检查,ivar可能会在release之后被释放,接下来的retain将导致内存访问错误。
当您使用copy声明属性时,代码几乎相同,只是retain被替换为copy
对于assign,更加简单:
- (void)setIvar:(id)newValue {
    ivar = newValue;
}

现在,当你将属性声明为atomic时(这是默认设置),情况会稍微复杂一些。苹果公司的一名工程师在开发论坛上发布了类似于下面的片段:

- (id)ivar {
    @synchronized (self) {
        return [[self->ivar retain] autorelease];
    }
}

- (void)setIvar:(id)newValue {
    @synchronized (self) {
        if (newValue != self->ivar) {
            [self->ivar release];
            self->ivar = newValue;
            [self->ivar retain];
        }
    }
}

注意两个方法中的@synchronized块以及getter方法中额外的retain-autorelease。这些都确保在你尝试读取值时,如果某个线程更改了该值,则你将获得先前的值(被保留和自动释放)或新值。


嗨,你怎么知道编译器是否会基于技术3 生成实现呢? - Martin Babacaev
@Martin 编译器没有使用该文档中给出的任何技术。那些技术是为人类实现其自己的getter和setter而设计的。相反,编译器将直接将属性合成到对象代码中。因此,我答案中给出的代码只是一个高级模型,用于说明如果编译器生成ObjC代码作为文本,它可能会怎样合成属性。这对于理解合成属性的工作原理应该足够了。如果您想获得更明确的答案,可以查看例如LLVM编译器中代码生成的实现方式。 - Alexei Sholik
嗨,我该如何使用附件体实现(用C、汇编语言)获取这些中间结果?在概念层面上,我已经很熟悉了,但我实际上正在寻找的是编译器版本。 - Martin Babacaev
@Martin 查找clang的man页面。它有一个-S选项来生成汇编输出。 - Alexei Sholik
KVO的相关内容怎么样? - OrangeDog

0

这取决于您设置的属性,例如使用RETAIN属性...

- (void)setAProp:(AnotherClass *)value {
   [aProp release];
   aProp = value;
   [aProp retain];
}

对于ASSIGN属性...

- (void)setAProp:(AnotherClass *)value {
   aProp = value;
}

对于COPY属性(与NSString一起使用)...

- (void)setAProp:(AnotherClass *)value {
   aProp = [value copy];
}

2
是的,它取决于属性,但我们不知道苹果是如何实现它的,也不应该关心。 此外,您提供的保留示例存在错误。如果分配了已经分配的相同值,则对象将被释放,因为保留计数达到0。为避免这种情况,setProp通常会检查是否再次设置了相同的值。 您可以在此处查看更多详细信息: http://cocoawithlove.com/2010/06/assign-retain-copy-pitfalls-in-obj-c.html - Kobski
@Kobski,我们确实关心是否在主线程之外的线程中,因为两种推荐的技术都使用了autorelease:http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmAccessorMethods.html#//apple_ref/doc/uid/TP40003539-SW5 - Martin Babacaev
如果您不指定“nonatomic”,则getter和setter函数将是线程安全的。 - Kobski
原子属性仅提供数据完整性的基本保证。每个应用程序都必须提供自己的策略来保护在多个线程之间共享的数据。 - Alexei Sholik
你也没有指定任何关于 getters 的内容,所以我不会点赞。 - SilverSideDown
你的setAProp方法有问题。它没有检查要设置的对象是否与现有对象不同,如果它们是相同的对象,则在尝试保留它之前会释放它。 - occulus

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接