我对财产重新申报表示怀疑
概述:
注意事项: - 内存管理= ARC(自动引用计数)
问题:
代码:(在单独的文件中)
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 = %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
B.h
#import"A.h"
@interface B : A
@property (readwrite) int n1;
@end
B.m
#import"B.h"
@implementation B
@synthesize n1 = _n1;
@end
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);
}
预期行为:
_n1 = 20
self.n1 = 20
实际行为:
_n1 = 0
self.n1 = 20
答案 0 :(得分:7)
有两种方法可以解决这个问题。在这两种情况下,您都不会在子类中调用@synthesize
。 我很惊讶为你编译。我希望像“属性'n1'这样的错误尝试使用在超类'A'中声明的ivar'_n1'。无论如何它肯定不是你真正可以做的事情,这就是你看到奇怪的原因行为。 (我记得为什么你没有看到这个错误;这是因为单独的编译单元。你只是用不同的ivars结束。)
首先,您需要了解@dyanmic
。这是一种告诉编译器的方法“是的,我知道你没有在这里看到所需方法的实现;我保证它会在运行时存在。”在子类中,您将使用@dynamic
让编译器知道可以继承n1
。
@implementation B
@dynamic n1;
@end
现在,您需要提供setN1:
方法。 IMO,子类不应该搞乱他们的超类的ivars,所以我赞同合成的ivars标记为@private
的事实。在一瞬间,我会告诉你如何解除这个问题,但现在让我们来处理我的首选解决方案:
setN1:
中将A
实施为私有方法。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
现在,有些人认为子类可以搞乱他们的超类的ivars。那些人是错的(好吧,恕我直言......)但是在ObjC中是可能的。你只需要声明ivar @protected
。当您直接在@interface
中声明ivars时,这是默认设置(这是您不应再这样做的众多原因之一)。它看起来像这样:
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
答案 1 :(得分:2)
我复制了您的代码并验证了您获得的行为。我可以解释它的机制,但不能解释它背后的逻辑。
以下是正在发生的事情:两个@synthesize
指令中的每一个都在其对应的类中生成隐藏变量_n
。此外,该指令在n1
中合成了A
的getter,在B
中合成了一个getter / setter对。 n1
中B
的吸气者会覆盖n1
中A
的吸气者;设定者没有,因为没有什么可以覆盖。
此时,A
中的_n1
B
成为孤儿:n1
的吸气者及其设定者都没有参考它。 setter引用B
的{{1}},而不是_n1
。这就是为什么您会看到A
display
方法中打印的不同值。将方法放在A
中的行为与您期望的一样。
编辑:
当然,接下来的问题是如何制作你想要的行为。结果很简单:不要在B
中合成属性,并在B
的实现文件中实现_n1
的setter(不将其放在接口中,以便它对您的界面的客户端保持只读。)
A