Objective-C中的ivars和属性之间有什么区别


82

这三种在Objective-C中使用ivars和属性的方式在语义上有什么区别?

1。

@class MyOtherObject; 
@interface MyObject {
}
@property (nonatomic, retain) MyOtherObject *otherObj;

2。

#import "MyOtherObject.h"
@interface MyObject {
    MyOtherObject *otherObj;
}
@property (nonatomic, retain) MyOtherObject *otherObj;

3。

#import "MyOtherObject.h"
@interface MyObject {
    MyOtherObject *otherObj;
}

Answers:


57

数字1与其他两个数字的不同之处在于,前向声明了MyOtherObject类,以最大程度地减少编译器和链接器看到的代码量,并有可能避免循环引用。如果这样做,请记住将#import放入.m文件。

通过声明一个@property(并在.m中匹配@synthesize)文件,您可以使用指定的内存语义自动生成访问器方法。大多数对象的经验法则是保留,但是NSStrings应该使用复制。而Singleton和Delegates通常应使用Assign。手写访问器既繁琐又容易出错,因此可以节省很多打字和愚蠢的错误。

同样,声明综合属性使您可以使用点符号来调用访问器方法,如下所示:

self.otherObj = someOtherNewObject; // set it  
MyOtherObject *thingee = self.otherObj; // get it 

而不是普通的消息传递方式:

[self setOtherObject:someOtherNewObject]; // set it
MyOtherObject *thingee = [self otherObj]; // get it 

在后台,您实际上是在调用一个如下所示的方法:

- (void) setOtherObj:(MyOtherObject *)anOtherObject {

    if (otherObject == anOtherObject) {
        return;  
    }

    MyOtherObject *oldOtherObject = otherObject; // keep a reference to the old value for a second
    otherObject = [anOtherObject retain]; // put the new value in  
    [oldOtherObject release]; // let go of the old object
} // set it

…或这个

- (MyOtherObject *) otherObject {  
    return otherObject;
} // get it

臀部完全疼痛,对。现在,对班级中的每个ivar都这样做。如果您做的不正确,则会导致内存泄漏。最好只是让编译器完成工作。

我看到1号没有ivar。假设这不是打字错误,那很好,因为@property / @synthesize指令还将在后台为您声明一个ivar。我相信这是Mac OS X的新功能-Snow Leopard和iOS4。

3号没有生成这些访问器,因此您必须自己编写它们。如果您希望访问器方法具有副作用,则可以执行标准的内存管理操作(如上所示),然后在访问器方法中执行所需的任何副作用。如果您合成一个属性并编写自己的属性,则您的版本具有优先权。

我涵盖了一切吗?


是的谢谢你!我想说明的一点是,如果您在#1中取出正向类编译指示,并用#import“ MyOtherObject”替换,则会出现编译时错误,但不确定为什么....
ennuikiller

与方法1相比,使用方法2有什么优势吗?
格雷格,

@Greg方法1将阻止循环引用。见stackoverflow.com/questions/7221174/...
willc2

3
不错的答案,除了点符号。您无需综合属性即可将其用于点表示法。实际上,您根本不需要声明属性。只要您有一个声明的setter和getter(例如setFoo:foo),就可以使用点表示法。
JeremyP 2012年

与此相关,如果使用ARC,则自动完成综合。
肖恩·拉金

17

在过去,您有ivars,如果您想让其他班级设置或阅读它们,则必须定义一个getter(即-(NSString *)foo)一个setter(即一个-(void)setFoo:(NSString *)aFoo;))。

什么属性给您的是设置程序和获取程序(几乎是免费的)以及一个ivar。因此,当您现在定义一个属性时,您可以设置原子性(例如,是否要允许来自多个线程的多个设置动作),以及分配/保留/复制语义(即,setter应该复制新值)或仅保存当前值-如果另一个类正试图使用​​可变的字符串设置您的字符串属性(稍后可能会更改),则很重要)。

这是做什么的@synthesize。很多人都将ivar名称保持不变,但是您可以在编写synthesize语句时更改它(即,@synthesize foo=_foo;意味着_foo为该属性命名一个ivar foo,因此,如果您要读取或编写此属性而您不使用self.foo,则可以必须使用_foo = ...-如果您只想通过setter和getter进行操作,它只会帮助您捕获对ivar的直接引用。

从Xcode 4.6开始,您不需要使用该@synthesize语句-编译器将自动执行该语句,并且默认情况下,将在ivar的名称前加上_


1
应该注意的是,属性的​​原子性不能保证线程安全
jscs 2012年

因此,如果我有一个原子的ivar,您的意思是,当setter在设置它或getter在获取它时,另一个线程将启动并尝试执行任一操作,那么这一切都会变得愚蠢吗?那么原子的意义是什么?我的理解是,原子至少可以确保如果您设置了一个ivar,它就会被设置,它的保留计数是适当的,等等。否则为什么要使用原子?[不是说它解决了所有问题,只是防止您变得愚蠢]
David H

2
您可以确保获得一个有效的整个对象-getter不会返回正在释放的对象-但如果另一个线程正在使用setter,则可以从之前或之后获取值。指定必须在getter和setter之外处理的内容。换句话说,getter或setter操作期间不会中断任何线程,但是未定义操作顺序(在此级别上不能定义为AFAIK)。
jscs 2012年

好吧,我认为您的原始评论被放错了地方-原子性得到尊重,只是通过线程访问会导致大量问题-因此,我曾经声明的每个ivar都是原子的,如果涉及线程,那么并发性在其他地方处理。
David H
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.