【问题标题】:NSArray @property backed by a NSMutableArray由 NSMutableArray 支持的 NSArray @property
【发布时间】:2012-01-14 11:44:30
【问题描述】:

我已经定义了一个类,我希望公共属性看起来好像由NSArray 支持。这很简单,但在我的情况下,实际的支持 ivar 是 NSMutableArray

@interface Foo
{
    NSMutableArray* array;
}
@property (nonatomic, retain) NSArray* array;

@end

在我的实现文件 (*.m) 中,我 @synthesize 属性但我立即遇到警告,因为使用 self.words 与尝试修改 NSArray 相同。

这样做的正确方法是什么?

谢谢!

【问题讨论】:

  • 你能在你的实现文件中发布负责警告的代码吗?
  • 创建属性readonly。并使用name1 = name2 版本的@synthesize

标签: objective-c ios nsarray mutable


【解决方案1】:

我会在您的标头中声明一个readonlyNSArray,并覆盖该数组的getter,以返回您的实现中声明的私有NSMutableArray 的副本。请考虑以下内容。

Foo.h

@interface Foo

@property (nonatomic, retain, readonly) NSArray *array;

@end

Foo.m

@interface Foo ()

@property (nonatomic, retain) NSMutableArray *mutableArray

@end

#pragma mark -

@implementation Foo

@synthesize mutableArray;

- (NSArray *)array
{
    return [[self.mutableArray copy] autorelease];
}

@end

【讨论】:

  • 此代码将可变数组复制到普通数组。这很好。
  • 使用非属性创建属性的问题在于,调用者可以轻松地在一段代码中编写像 aFoo.array 这样的代码 50 次,从而创建数组的许多副本 - 这会导致性能下降.
  • 在 ARC 时代这是如何工作的?我不确定是否应该将strongweak 用于NSArray*。由于它不受 ivar 支持,我认为它需要是 weak。对吗?
  • @darvids0n 我喜欢这个想法,但是当我尝试像[self.array addObject:object] 这样的东西时,编译器会抱怨数组是NSArray
  • @MichaelMior 好吧,你总是可以投射它:NSMutableArray *myInternalArray = (NSMutableArray *)self.array; [myInternalArray addObject:object]。虽然这是一件危险的事情,所以可能不要这样做。对于您引用self.array 的地方,您始终可以直接引用内部变量:[array addObject:object],这样就可以了。 self.array 在内部使用时应该给你一个 NSMutableArray 指针,如果你按照我建议的方式声明它..
【解决方案2】:

基本上,将 NSArray 属性放在头文件中的 category 中,将 NSMutableArray 属性放在实现文件中的 类扩展 中。就这样……

Foo.h:

@interface Foo
@end

@interface Foo (Collections)
@property (nonatomic, readonly, strong) NSArray *someArray;
@end

Foo.m

@interface Foo ()
@property (nonatomic, readwrite, strong) NSMutableArray *someArray;
@end

【讨论】:

  • 谢谢。博客文章的作者似乎删除了该文章。我删除了链接。
  • 在 .m 文件中调用 self.someArray 将使您能够访问 NSArray 并隐藏 NSMutableArray,这使得该解决方案不太理想
  • 它对我有用。但是,我想知道为什么它只有在只读不可变数组属于一个类别时才有效?为什么 Apple 不希望我们只将公共部分放在界面中?这似乎是一个简洁的设计。
【解决方案3】:

简单:

1) 不要使用不是一个属性的属性。

2) 代码简化为:

- (NSArray *)currentArray {
    return [NSArray arraywithArray:mutableArray]; // need the arrayWithArray -    otherwise the caller could be in for surprise when the supposedly unchanging array changes while he is using it. 
}

- (void)setArray:(NSArray *)array {
    [mutableArray setArray:array];
}

当对象被分配时创建数组,当它死亡时,释放数组。

仅使用“.”就会产生很大的影响运算符,它很容易忽略效率极低的代码。访问器就是这样。此外 - 如果有人调用 aFoo.array - 合同是为了访问 foo 的数组成员 - 但实际上它只是调用时的副本。这种差异是真实存在的,以至于它在此处发布的其他实现中引起了错误。

【讨论】:

  • 可能让 self.mutableArray 对 .m 文件保持私有可能是一个真正的胜利 - 例如,您可能希望稍后更改底层实现。
【解决方案4】:

更新:此答案不再有效。使用以下建议的解决方案之一。

这些天你可以做以下事情:

Foo.m:

@implementation Foo {
    NSMutableArray* _array;
}

@end

Foo.h:

@interface Foo

@property (readonly, strong) NSArray* array;

@end

您仍然可以通过 ivar 从实现内部寻址可变 _array,而外部则可以通过不可变属性访问它。不幸的是,这并不能保证其他人不能将其转换为 NSMutableArray 并进行修改。为了更好地防止白痴,您必须定义访问器方法并返回不可变副本,但是在某些情况下这可能非常昂贵。

我实际上同意上面的其中一个 cmets,如果您需要返回一些只读数据,最好使用简单的访问器方法,它肯定不会那么模棱两可。

【讨论】:

  • 现在这会产生警告:自动属性合成不会合成属性 [xxx]
【解决方案5】:

这是因为您的属性必须与实际 ivar 的类类型匹配。

可能的解决方案/解决方法:

//Foo.h:
@interface Foo
{
    NSMutableArray* mutableArray;
}
@property (readwrite, nonatomic, retain) NSArray* array;
//or manual accessor declarations, in case you're picky about wrapper-properties.
@end

//Foo.m:
@interface Foo ()
@property (readwrite, nonatomic, retain) NSMutableArray* mutableArray;
@end

@implementation

@synthesize mutableArray;
@dynamic array;

- (NSArray *)array {
    return [NSArray arrayWithArray:self.mutableArray];
}

- (void)setArray:(NSArray *)array {
    self.mutableArray = [NSMutableArray arrayWithArray:array];
}

@end

您将在类扩展中添加私有 mutableArray 属性,并将公共 array 简单地转发给您的私有可变属性。

对于 ObjC 的最新语言扩展,我倾向于删除

{
    NSMutableArray* mutableArray;
}

如果可能,完全阻止 ivar。

并通过合成定义 ivar,如下所示:

@synthesize mutableArray = _mutableArray;

这将为您生成一个NSMutableArray *_mutableArray; 实例。

【讨论】:

  • 删除 ivars 会严重影响您在大型代码库中调试和维护代码的能力。 “这是从哪里来的,为什么它没有出现在调试器中?”它的 C 代码。让我们直说吧。
  • 此代码 -(NSArray*)array 调用将可修改的 NSArray 返回给调用者。调用者或被调用者都可以在调用后修改该数组,这可能会在以后的代码中导致难以调试的问题。永远不要那样做。更糟糕的是,它看起来像是对属性的调用,因此调用者可以对会发生什么做出各种假设。
  • 它没有消失。它只是从公共接口中隐藏起来(应该是这样),而是移到了实现中。我需要知道的所有信息都可以从@property@synthesize 获得。此外,这也是 Apple 最近一直在接受的(如“Xcode 的默认行为”)。或者,您可以简单地将 ivar 块移动到 @implementation
  • @TomAndersen:关于可变 ivar 返回的公平点。固定的。但是,如果您将NSArray 转换为NSMutableArray,您需要这样做才能对其进行修改,那么无论如何您都做错了。
  • 对象可以修改数组本身——在将它提供给调用者之后,向调用者承诺一个非可变对象。现实世界中有很多这样的失败。
【解决方案6】:

最简单的答案:您的属性类型 (NSArray) 与您的实例变量类型 (NSMutableArray) 不匹配。

这是另一个很好的理由,您不应该定义自己的支持变量。让@synthesize 设置您的实例变量;不要手动操作。

【讨论】:

  • 人们还会@synthesize 变量吗?
  • 通常不会,不会 - 除非有动态属性和合成属性的组合
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-23
  • 1970-01-01
相关资源
最近更新 更多