【问题标题】:initializer, properties, accessors and copy/retain/readonly初始化器、属性、访问器和复制/保留/只读
【发布时间】:2010-10-28 15:02:31
【问题描述】:

我想了解如何设置属性(访问器)的参数。

我从 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 与setter 一起使用,readonly 将禁止我设置值,因为没有setter。

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

5) 我记得的是copy/retaininitWithNamereleasedealloc 方法中是可以的。

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

【问题讨论】:

    标签: iphone objective-c properties initialization accessor


    【解决方案1】:

    预计到达时间: @DougW 正确指出属性的所有权类型 (assign/retain/copy) 不会影响 getter。它仍然会影响二传手。对于readonly 类型,如果您要在类扩展中覆盖声明的readonly 部分,这很重要,因此您可以在实现中使用setter。类扩展属性覆盖只允许更改属性的readonly 状态,因此它的其余部分(即原子性和所有权类型)必须在标头中适当声明。即使您现在不覆盖该属性,但您将来可能会这样做,因此您不妨先使用正确的选项来记录您希望如何为自己管理内存。

    自动引用计数 (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 期间使用设置器/访问器? 因为设置器/访问器经常执行 KVO 通知,而您希望在对象未完全初始化时避免这种情况,即在 -init 期间(当它在完全初始化的过程中半初始化时)和-dealloc(当它在完全未初始化的过程中半初始化时)。

    为什么将copyreadonly 一起使用? 回答您的第一个问题:因为如果copyretainassign 对setter 和getter 都有影响。复制 getter 看起来像这样:

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

    为什么有时copy 有时retain copy 通常与值对象(表示值的被动对象)一起使用; retain 通常与其他对象一起使用。有时,效率问题开始发挥作用(很可能过早......),您可能会选择使用retain,而通常使用copy

    你会如何在这里使用copy/retainreadonly 和他们一样。我会覆盖类扩展中的声明,这样我就可以使用设置器来更改-init-dealloc 之外的属性值,我只会使用直接实例变量访问。在-dealloc 中发布它们后,我也会nil 将它们取出,例如,

    [name release], name = nil;
    

    这有助于避免向已发布的对象发送消息或以其他方式引用。

    【讨论】:

    • nonatomic 保留属性只是返回指针。他们做保留、自动释放的事情。请参阅文档developer.apple.com/library/ios/#documentation/cocoa/Conceptual/…atomicity 部分
    • @JeremyP:好电话。在非原子访问器中不使用[[foo retain] autorelease] 的决定确实是有道理的:如果您要保留该值的时间超过当前的运行循环周期,您应该自己保留它。如果您使用nonatomic,您基本上是在说明线程安全不是问题。如果您不必担心线程安全,那么当您使用访问器的返回值时,不会执行除您之外的任何代码,因此访问器无需执行[[foo retain] autorelease]
    • @Jeremy:我什至会说,使用非原子意味着您声明稳健性不是问题——或者与其说是性能问题,不如说是问题。在我的书中将属性设置为非原子而不首先分析您的代码算作过早的优化。
    • @Jeremy W. Sherman:这里有很多很好的提示。问题: 1)如果您覆盖属性声明,编译器如何知道要使用哪个访问器?通过类扩展? 2) 也分配给 NSInteger, BOOL, double, float, CLLocationCoordinate2D, ... ?
    • 因为int 不被引用计数。一个普通的旧 int 属性可以安全地在这里、那里和任何地方复制,而不用担心强引用或弱引用。哎呀,对它的读取和写入很可能甚至不需要被锁定,因为它足够小以至于它总是被底层指令集架构以原子方式读取/写入。 (这并不是说如果您不使用内存屏障,您就不会从另一个线程中获得陈旧的缓存值,但无论如何您都不会获得不一致的半设置值。)
    猜你喜欢
    • 2012-08-05
    • 2019-04-25
    • 2014-07-17
    • 1970-01-01
    • 2011-10-12
    • 2010-10-11
    • 2013-09-30
    • 1970-01-01
    • 2018-10-18
    相关资源
    最近更新 更多