属性声明 - ivar 和 getter 值不匹配



我对属性重新声明有疑问

概述:

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

注意:-内存管理=ARC(自动参考计数(

问题:

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

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

A.h

#import<Foundation/Foundation.h>
@interface A : NSObject
@property (readonly) int n1;
- (void) display;
@end

A.m

#import "A.h"
@implementation A
@synthesize n1 = _n1;
- (void) display
{
    printf("_n1     = %in", _n1);                  //I expected _n1 and self.n1 to display the same value
    printf("self.n1 = %inn", self.n1);            //but they seem to display different values
}
@end

B.h

#import"A.h"
@interface B : A
@property (readwrite) int n1;
@end

B.m

#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

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

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

@implementation B
@dynamic n1;
@end

现在,您需要提供setN1:方法。IMO,子类不应该干扰其超类的ivar,所以我赞成合成的ivar标记为@private的事实。稍后,我将告诉您如何撤消它,但现在让我们来处理我喜欢的解决方案:

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

A.h

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

A.m

#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

B.h

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

B.m

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

现在,有些人认为子类可以扰乱其超类的ivar。这些人错了(好吧,IMHO…(,但在ObjC中这是可能的。您只需要声明ivar @protected即可。当您直接在@interface中声明ivar时,这是默认的(这是您不应该再这样做的众多原因之一(。它看起来是这样的:

A.h

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

A.m——删除使n1在超类中可写的额外类扩展。

B.h——无变化

B.m

@implementation B
@dynamic n1;
- (void)setN1:(int)n1 {
  _n1 = n1;
}
@end

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

发生的情况如下:两个@synthesize指令中的每一个都在其相应的类中生成一个隐藏变量_n。此外,该指令合成了An1的getter和B中的getter/setter对。Bn1的getter覆盖An1的getter;setter没有,因为没有什么可重写的。

此时,BA_n1变为孤立:n1的getter和它的setter都没有引用它。setter引用B_n1,而不是A的。这就是为什么在Adisplay方法中打印出不同的值。将该方法放入B中的行为与您所期望的一样。

编辑:

自然,下一个问题是如何做出你想要的行为。结果很简单:不合成B中的属性,而是在A的实现文件中实现_n1的setter(不将其放在接口中,这样它对接口的客户端保持只读(。

// This goes in A.m without a declaration in A.h
- (void) setN1:(int)n1 {
    _n1 = n1;
}

相关内容

  • 没有找到相关文章

最新更新