正确使用@private变量/属性

时间:2012-09-15 12:28:43

标签: iphone objective-c instance-variables private-members

我的所有研究表明, @private 指令没有 真正的 用法 - 所以我必须遗漏一些东西,需要你的专家来加入: - )

假设我们有两个类: Car 类和 SportsCar 类,其中 SportsCar Car的子类

这是 Car 类:

@interface Car : NSObject {
    NSString *make;
    NSString *model;

    @private
    int numberOfBackSeatPassengers;  // I'm making this a private iVar cause I'm just gonna
                // say that all Sportscars will be 2-seaters and therefore shouldn't
                // be able to set/get the number of back-seat passengers
}

@property (nonatomic, strong) NSString *make, *model;

// Now here's my first issue: if I also make "numberOfBackSeatPassengers" an @property 
// then it seems like all subclasses of this Car class *WILL* be able to access it as 
// well - even though I declared it as @private - but I'll do this anyway to make my point:

@property int numberOfBackSeatPassengers;

@end

实施如下:

@implementation Car

@synthesize make, model, numberOfBackSeatPassengers;

@end

现在这里是 Sportscar 课程:

#import "Car.h"

@interface Sportscar : Car

@property int turboEngineSize;

@end

及其实施:

#import "Sportscar.h"

@implementation Sportscar

@synthesize turboEngineSize;

@end

" main"我有这个:

    Car *car1 = [[Car alloc] init];
    [car1 setMake:@"Chevy"];
    [car1 setModel:@"Impala"];
    [car1 setNumberOfBackSeatPassengers:3];

    Sportscar *sports1 = [[Sportscar alloc] init];
    [sports1 setMake:@"Audi"];
    [sports1 setModel:@"tt"];
    [sports1 setNumberOfBackSeatPassengers:3];

显然,我可以在Sportscar上设置 NumberOfBackSeatPassengers - 即使iVar被声明为 @private - 但那是因为我在" Car.h"中成为 @property 这意味着它的合成getter和setter是Instance Methods,因此 所有 Car 的子类。

另一种选择是不将 numberOfBackSeatPassengers 声明为" Car.h"中的@property,只保留一个简单的iVar,而不是手动在" Car.m"的@implementation中为它创建一个Setter和Getter。像这样:

-(void) setNumberOfBackSeatPassengers:(int)numPassgeners {
    numberOfBackSeatPassengers = numPassgeners;
}

-(int)numberOfBackSeatPassengers {
    return numberOfBackSeatPassengers;
}

这会使 numberOfBackSeatPassengers 的getter和setter仅在&#34; Car.m&#34; - 我想这会使他们&#34;私人&#34; - 但是他们 私密:我永远不能从Main,或者来自&#34; Car.m&#34;以外的任何地方打电话给他们。而且,这是真正的观点:这样做意味着@private指令回到&#34; Car.h&#34;在任何一个方面都没有真正发挥作用。我的意思是我现在可以回到&#34; Car.h&#34;,在那里取出&#34; @ private&#34; 指令 - 以及我的手动setter和getter for < strong> numberOfBackSeatPassengers 仍然可以像现在一样工作,被认为是私有的 - 所以&#34; @ private&#34;?它是如何真正发挥作用的?

任何人都可以对此有所了解吗?

(是的,我知道我可以扩展我的Car类在&#34; Car.m&#34;文件的@interface部分 - 通过类别,或者 numberOfBackSeatPassengers 首先是readonly属性,然后将其更改为readwrite等等 - 但这些似乎都是解决方法或者#34; hacks&#34;制作&#34; @ private&#34; work。我只是#39;了解@private如何真正独立工作

=============================================== ==

编辑 - 回应下面的aroth的评论:

1)说完一个子类仍然理论上调用一个未在其父类的标题中声明的方法 - 通过使用{{1 }}。我说&#34;理论上&#34;,因为在我的情况下它不能正常工作正确:if - in&#34; main&#34; - 我打电话给

performSelector

然后我为[sportscar1 performSelector:@selector(setNumberOfBackSeatPassengers:)];插入了一些垃圾编号,因为在以这种方式调用方法时,我无法显式传入一个数字作为参数。

(问题:有解决方法吗?)

2)在numberOfBackSeatPassengers我们可以简单地覆盖Sportscar课程的Car的二人制定者和获取者时,他们也绝对正确,并且拥有这些重写方法将其重置为0,或者给出错误等。但是虽然这是一个非常实用的解决方案并且似乎解决了这个特定的问题,但我觉得它并没有解决更大的问题numberOfBackSeatPassengers并没有真正表现出它应该做的事情。

3)重新设计逻辑以为@private创建一个类,为FourDoorCar重新设置另一个类,然后继续构建它是一个有趣的选项 - 但这几乎就像现在的Objective-C&#39 ; s语法是&#34;强制&#34;我自己的编程逻辑以及我如何构建我的项目 - 这感觉就像是一个强加。也许我错了,从中做出了太多 - 但无论如何这一切都是因为TwoDoorCar并没有做出它似乎承诺的事情......?感觉不对。

在一天结束时,我不断回到同一个问题:@private实际上对我们有什么好处?它有什么好处,它有什么好处&#34;购买&#34;我?似乎如果我想将iVar设为私有,我可以在&#34; .m&#34;中声明它。文件,并不是首先在Header文件中声明它。我的意思是我是对的吗?或者是否仍有一些实例,您希望在标题中将{iVar声明为@private,但在标题中为其声明一个setter和getter - 所以那些不会明确地明确子类 - 并且这一切都有意义吗?

我们能想到一个实际的例子吗?是否有某种Car属性我想在Header中声明为@private (而不是&#34; .m&#34;)不知何故让我受益? 我认为@private会是一个很好的例子,但在实际代码中,我还没有看到它是如何真正起作用的......

=============================================== ==========================

编辑#2 - 继续与@aroth对话: - )

@aroth - 我绝对同意它更好/更有条理地在Header中声明所有iVars而不是拆分,以便有些在Header中,有些在实现中。这造成了混乱,我真的不喜欢这种方法。 (我在原始问题中指出,我不想使用实施和/或类别方法来解决我的问题。) - 同样,是的,属性绝对不必总是由iVars支持。

- 正确地设计课程,我同意这当然是良好编程的关键。汽车/跑车的例子是我当场编写的,以便给我的问题提供一些背景信息,考虑到它的设计优点/缺陷,我没有时间投资。我想如果我们采取你的方法 - 这似乎是合乎逻辑的 - 并且使用Car类,FourDoorCar子类,TwoDoorCar子类等 - 我们可以解决很多问题 - 但它的仍然很可能迟早我们会遇到这样的情况,即我们 想要为我们的某个类想要一个@private iVar,而不想创建另一个类子类来处理它。 我的意思是,为了讨论,我们只是假设会发生这种情况。

所以,如果可能的话,我真的想为我们的Car课程考虑一个特定的iVar,因为 @private 是有意义的,在代码中显示如何使用它,并讨论其范围和限制。

我一直在想一个Car的某些属性的真实例子,我们只想让Car拥有 - 而且它的子类都不应该继承。 我真的认为numberOfBackSeatPassengers可以做到这一点 - 为了我们的讨论,它仍然可以,但是,我只是组成另一个并称之为numBackSeatPassengers: - )

所以:

phantomIVar

实施将是:

@interface Car : NSObject {

    @private
    //int numberOfBackSeatPassengers;  
    int phantomIVar;

}

@property (nonatomic, strong) NSString *make, *model;


@end

这几乎让我们回到了我们开始的地方: - )

至少我的感受如何。

我的意思是@implementation Car @synthesize make, model; -(void) setPhantomIVar:(int)i { phantomIVar = i; } -(int)phantomIVar { return phantomIVar; } @end 声明似乎购买我们的唯一因素是可读性。所以现在,任何看过Header的人都能看到@private是一辆iVar of Car,并且明白它是私有的。那就是它。

然而,就功能而言,它似乎并没有做太多。因为它不喜欢将phantomIVar放在@private前面,让我们仍然可以在Header中为它编写一个setter / getter,并且只能访问Car类对象而不是Car的子类。不,phantomIVar并不能帮到你。为了获得隐私,您必须进入实施文件并在那里编写您的setter和getter。最终在Objective-C中,没有私有方法。在Obj。 C.他们都是公开的。

如果我做对了,请告诉我 - 如果没有,我到底哪里出错了。

非常感谢: - )

1 个答案:

答案 0 :(得分:0)

您可以在readonly类本身中将该属性声明为Car,或仅在readonly类中将其重新声明为SportsCar

此外,@private与属性没有任何关系 - 它只会修改ivar本身的范围。