2010-10-28 74 views
7

我想了解如何设置属性(访问器)的参数。初始化器,属性,访问器和复制/保留/只读

我从一个Kal日历的例子中拿了下面的代码。

// Holiday.h 

@interface Holiday : NSObject 
{ 
    NSDate *date; 
    NSString *name; 
    NSString *country; 
} 

@property (nonatomic, retain, readonly) NSDate *date; 
@property (nonatomic, retain, readonly) NSString *name; 
@property (nonatomic, retain, readonly) NSString *country; 

- (id)initWithName:(NSString *)name country:(NSString *)country date:(NSDate *)date; 

@end 

// Holiday.m 

#import "Holiday.h" 

@implementation Holiday 

@synthesize date, name, country; 

- (id)initWithName:(NSString *)aName country:(NSString *)aCountry date:(NSDate *)aDate 
{ 
    if ((self = [super init])) { 
    name = [aName copy]; 
    country = [aCountry copy]; 
    date = [aDate retain]; 
    } 
    return self; 
} 

- (void)dealloc 
{ 
    [date release]; 
    [name release]; 
    [country release]; 
    [super dealloc]; 
} 

@end 

1)属性设置为retain,但由于二传手不能使用retain使得这里没有任何意义。

2)另外,在initWithName方法中,值设置为copy。为什么不直接用copy定义属性并使用访问器方法?

@property (nonatomic, copy) NSString *name; 
// ... 
self.name = aName; 

3)我需要readonly吗?我不知道他们为什么在这里使用。如果我将copy与设置器一起使用,则readonly禁止我设置该值,因为没有设置器。

4)在initWithName方法中有时使用copy,有时使用retain。我建议在这里始终使用copy,因为这个值不应该在以后修改。

5)我能记住的是,它是确定在dealloc方法initWithNamereleasecopy/retain

那么你会如何建议在这个例子中使用retain,copyreadonly

回答

13

ETA: @DougW正确地指出,财产的所有权类型(assign/retain/copy)不影响吸气。它仍然影响着二传手。对于readonly类型,如果要在类扩展中覆盖声明的readonly部分,则这很重要,因此您可以在实现中使用setter。类扩展属性覆盖仅允许更改属性的状态,因此其余部分(即原子性和所有权类型)必须在标头中正确声明。即使你现在不覆盖这个属性,你也可能在将来,所以你也可以通过使用正确的选项来记录你想要为自己管理的内存。

自动引用计数(ARC)通过在自己的内存管理规则上覆盖传统的引用规则来更改运行时实现细节,但用于配置属性的规则和建议保持不变。


为什么要用retainreadonly如果标记属性作为retain,合成的访问做这样的事情:

/* getter for retain property */ 
- (NSString *)name { 
    return [[name retain] autorelease]; 
} 

现在,如果你发送的对象-name来,而你还在使用它改变了名称,调用代码仍会有对字符串的有效引用。如果你宣布它为assign,不过,这将是这样的:

/* getter for assign property */ 
- (NSString *)name { 
    return name; 
} 

现在,只要名字被更改的对象,它必须被释放,以避免泄漏,这将无效调用代码的参考。 retain/copy/assign确实陈述了一个内存管理策略:retain/copy说,“我保证我持有一个原始/我在这里提供的值的副本的参考,”而assign说,“我只是有价值并声称没有对此的参考。“

当该值不需要内存管理时,如普通的int,则assign是有意义的。当你故意不保留一个对象,例如代表,那么assign是有道理的。但是,在其他大多数情况下,您需要retaincopy

此外,实现文件只能覆盖属性声明的readwrite/readonly部分,而不是内存管理部分。至于宣布,该.m文件可以有:

用于重写的属性声明
@interface Holiday (/*class extension*/) 
@property(nonatomic, retain, readwrite) NSDate *date; 
/* override other properties to make them readwrite... */ 
@end 

非公有制制定者随后将与公众存取一起合成。

为什么不在-init期间使用setter/accessors?由于setter/accessor经常执行KVO通知,当您的对象未完全初始化时(即,在-init(当它在完全初始化的途中半初始化时)和-dealloc(当其初始化为半初始化时)完全未初始化的方式)。

为什么要用copyreadonly至于回应你的第一个问题:因为如果copyretain相比assign同时影响了制定者和获得者。副本的getter应该是这样的:

/* getter for copy property */ 
- (NSString *)name { 
    return [[name copy] autorelease]; 
} 

为何有时copy有时retaincopy通常与值对象(代表值的被动对象)一起使用; retain通常与其他对象一起使用。有时候,效率问题开始起作用(很可能过早......),并且您可能会选择使用retain,您通常会使用copy

您会如何在此处使用copy/retain以及readonly和他们一样。我会重写类扩展中的声明,以便我可以使用setter来更改-init-dealloc之外的属性值,其中我只使用直接实例变量访问。我也要nil了释放他们-dealloc,例如后的高德,

[name release], name = nil; 

这有助于避免将消息发送到或以其他方式引用一个已经释放的对象。

+0

** nonatomic **保留属性只是返回指针。他们**不**做保留,autorelease的事情。请参阅文档http://developer.apple的**原子性**部分。com/library/ios /#documentation/cocoa/Conceptual/ObjectiveC/Articles/ocProperties.html – JeremyP 2010-10-28 15:56:34

+0

@JeremyP:好的呼叫。不在非原子访问器中'[[保留] autorelease]'的决定是有道理的:如果你想要保持比当前runloop周期更长的值,你应该自己保留它。如果你使用'nonatomic',你基本上是说线程安全不是问题。如果你不必担心线程安全性,那么当你使用来自访问器的返回值时,除了你的代码之外,没有任何代码会被执行,所以访问器不需要执行'[[foo retain] autorelease] 。 – 2010-10-28 16:04:27

+0

@Jeremy:我甚至会说使用非原子意味着你说健壮不是一个问题 - 或者比性能更不重要。如果没有首先分析代码,将属性设置为非原子计数在我的书中算是过早的优化。 – JeremyP 2010-10-28 16:15:53