财产声明 - ivar和getter值不匹配

use*_*037 10 inheritance properties objective-c redeclaration

我对财产重新申报表示怀疑

概述:

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

注意事项: - 内存管理= ARC(自动引用计数)

题:

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

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

#import<Foundation/Foundation.h>

@interface A : NSObject

@property (readonly) int n1;

- (void) display;

@end
Run Code Online (Sandbox Code Playgroud)

上午

#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
Run Code Online (Sandbox Code Playgroud)

BH

#import"A.h"

@interface B : A

@property (readwrite) int n1;

@end
Run Code Online (Sandbox Code Playgroud)

BM

#import"B.h"

@implementation B

@synthesize n1 = _n1;

@end
Run Code Online (Sandbox Code Playgroud)

test.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);
}
Run Code Online (Sandbox Code Playgroud)

预期行为:

_n1     = 20
self.n1 = 20
Run Code Online (Sandbox Code Playgroud)

实际行为:

_n1     = 0
self.n1 = 20
Run Code Online (Sandbox Code Playgroud)

Rob*_*ier 7

有两种方法可以解决这个问题.在这两种情况下,您都不会调用@synthesize子类.我很惊讶为你编译.我希望像"属性'n1'尝试使用在超类'A'中声明的ivar'_n1'这样的错误".在任何情况下,它绝对不是你真正能做的事情,这就是为什么你会看到奇怪的行为.(我记得为什么你没有看到这个错误;这是因为单独的编译单元.你只是用不同的ivars结束.)

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

@implementation B
@dynamic n1;
@end
Run Code Online (Sandbox Code Playgroud)

现在,您需要提供该setN1:方法.IMO,子类不应该搞乱他们的超类的ivars,所以我赞成合成的ivars被标记的事实@private.在一瞬间,我会告诉你如何撤消,但现在让我们处理我的首选解决方案:

  • 实现setN1:作为私有方法A.
  • 暴露在外面B.

@interface A : NSObject
@property (readonly) int n1;
- (void) display;
@end
Run Code Online (Sandbox Code Playgroud)

上午

#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
Run Code Online (Sandbox Code Playgroud)

BH

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

BM

#import "B.h"
@implementation B
@dynamic n1; // Yes compiler, setN1: exists. I promise.
@end
Run Code Online (Sandbox Code Playgroud)

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

@interface A : NSObject {
  int _n1;
}
...
Run Code Online (Sandbox Code Playgroud)

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

Bh - 没有变化

BM

@implementation B
@dynamic n1;

- (void)setN1:(int)n1 {
  _n1 = n1;
}
@end
Run Code Online (Sandbox Code Playgroud)