10

我对财产重新申报有疑问

概述:

  • 类“A”是具有只读属性 int n1 的父类;
  • 类“B”是子类,它将属性重新声明为读写
  • 使用“B”类的设置器,属性值设置为 20
  • 当我使用 getter 和实例变量打印值时,我似乎得到了不同的值

注意点: - 内存管理 = ARC (Automatic Reference Counting)

问题:

  • 当我打印 self.n1 和 _n1 的值时,为什么会得到不同的值?
  • 为什么我的预期行为和实际行为不匹配(请向下滚动查看实际与预期)?

代码:(在单独的文件中)

#import<Foundation/Foundation.h>

@interface A : NSObject

@property (readonly) int n1;

- (void) display;

@end

#import "A.h"

@implementation A

@synthesize n1 = _n1;

- (void) display
{
    printf("_n1     = %i\n", _n1);                  //I expected _n1 and self.n1 to display the same value
    printf("self.n1 = %i\n\n", self.n1);            //but they seem to display different values
}

@end

溴化氢

#import"A.h"

@interface B : A

@property (readwrite) int n1;

@end

BM

#import"B.h"

@implementation B

@synthesize n1 = _n1;

@end

测试.m

#import"B.h"

int main()
{
    system("clear");

    B* b1 = [[B alloc] init];

    b1.n1 = 20;

    [b1 display];   //Doubt - my expected behavior is different from actual behavior


    return(0);
}

预期行为:

_n1     = 20
self.n1 = 20

实际行为:

_n1     = 0
self.n1 = 20
4

2 回答 2

7

有两种方法可以解决这个问题。在这两种情况下,您都不会调用@synthesize子类。我很惊讶为你编译。我预计会出现类似“属性'n1'试图使用超类'A'中声明的ivar'_n1'”的错误。无论如何,这绝对不是您真正可以做的事情,这就是您看到奇怪行为的原因。(我记得为什么你没有看到这个错误;这是因为单独的编译单元。你只是用不同的 ivars 结束。)

首先,您需要了解@dyanmic. 这是一种告诉编译器“是的,我知道你在这里看不到所需方法的实现;我保证它会在运行时出现”的一种方式。在子类中,您将使用@dynamic来让编译器知道可以继承n1.

@implementation B
@dynamic n1;
@end

现在,您需要提供setN1:方法。IMO,子类不应该弄乱他们超类的 ivars,所以我同意合成的 ivars 被标记的事实@private。稍后,我将告诉您如何撤消该操作,但现在让我们处理我首选的解决方案:

  • 实现setN1:为私有方法A
  • 将其暴露在B.

@interface A : NSObject
@property (readonly) int n1;
- (void) display;
@end

#import "A.h"

@interface A () // Private class extension, causes setN1: to be created but not exposed.
@property (readwrite) int n1;
@end
@implementation A

@synthesize n1 = _n1;

- (void) display {
   ...
}
@end

溴化氢

#import "A.h"    
@interface B : A
@property (readwrite) int n1; // Tell the world about setN1:
@end

BM

#import "B.h"
@implementation B
@dynamic n1; // Yes compiler, setN1: exists. I promise.
@end

现在,有些人认为子类可以与超类的 ivars 混淆。那些人是错的(好吧,恕我直言......),但在 ObjC 中是可能的。您只需要声明 ivar @protected。当您直接在 中声明 ivars 时,这是默认设置@interface(您不应再这样做的众多原因之一)。它看起来像这样:

@interface A : NSObject {
  int _n1;
}
...

Am -- 删除使 n1 在超类中可写的额外类扩展。

Bh——没有变化

BM

@implementation B
@dynamic n1;

- (void)setN1:(int)n1 {
  _n1 = n1;
}
@end
于 2011-12-07T04:07:31.300 回答
2

我复制了你的代码并验证了你得到的行为。我可以解释它的机制,但不能解释它背后的逻辑。

这是发生了什么:两个@synthesize指令中的每一个都_n在其相应的类中生成一个隐藏变量。此外,该指令综合了n1in的 getter 和 inA的 getter/setter 对Bn1in的getterB覆盖 in 的n1getter A;setter 没有,因为没有什么可以覆盖的。

在这一点上,A's _n1inB成为孤立的:无论是 gettern1还是它的 setter 都没有引用它。setter 引用B's _n1,而不是A's。这就是为什么您会看到displayA. 放入该方法的B行为符合您的预期。

编辑:

自然,下一个问题是如何做出你想要的行为。事实证明很简单:不要合成 in 属性,并实现in的实现文件B的 setter (不将其放入接口中,以便它对接口的客户端保持只读)。_n1A

// This goes in A.m without a declaration in A.h
- (void) setN1:(int)n1 {
    _n1 = n1;
}
于 2011-12-07T03:47:46.437 回答