我所有的研究都表明@private指令没有真正的用途-因此,我必须缺少一些东西,并且需要您的专家来帮忙:-)
假设我们有2个类:Car类和SportCar类,其中SportsCar是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
现在是跑车课:
#import "Car.h"
@interface Sportscar : Car
@property int turboEngineSize;
@end
及其实现:
#import "Sportscar.h"
@implementation Sportscar
@synthesize turboEngineSize;
@end
在“主要”我有这个:
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。方法,从而可用于Car的所有子类。
另一个选择是不要在“ Car.h”中将numberOfBackSeatPassengers声明为@property,仅将其作为简单的iVar保留在那里,而应在“ Car.m”的@implementation中手动为其创建一个Setter和Getter。像这样:
-(void) setNumberOfBackSeatPassengers:(int)numPassgeners {
numberOfBackSeatPassengers = numPassgeners;
}
-(int)numberOfBackSeatPassengers {
return numberOfBackSeatPassengers;
}
这将使numberOfBackSeatPassengers的获取器和设置器仅在“ Car.m”中可用-我认为这会使它们“私有”-但它们太私有了:我永远不能从Main或其他地方调用它们“ Car.m”此外,这才是真正的要点:用这种方式进行操作意味着返回在“ Car.h”中的@private指令实际上根本没有发挥作用。我的意思是我现在可以回到“ Car.h”,在那里删除“ @private”指令,而我的numberOfBackSeatPassengers手动设置和获取器仍将与现在完全一样,应该是私有的,所以用“ @private”获得?它如何真正发挥作用?
谁能对此有真正的了解?
(是的,我知道我可以在Car.m文件的@interface部分中扩展我的Car类-通过类别,或者首先将numberOfBackSeatPassengers设置为只读属性,然后将其更改为readwrite等。-但是所有这些似乎是使“ @private”工作的解决方法或“ hacks”。我只是不了解@private如何真正独立工作。)
================================================== ===
编辑-针对以下Aroth的评论:
1)aroth绝对正确地说,子类仍然可以使用
performSelector
调用在父类的Header中未声明的方法。我说“从理论上讲”,原因是我的情况不太正常:如果-在“主要”中,我称之为[sportscar1 performSelector:@selector(setNumberOfBackSeatPassengers:)];
那么我会为
numberOfBackSeatPassengers
插入一些垃圾数字,因为在以这种方式调用方法时,我无法显式传递数字作为参数。(问题:有办法解决吗?)
2)aroth的说法也绝对正确:在
Sportscar
中,我们可以简单地覆盖Car
类的setter和getter for numberOfBackSeatPassengers
,并让这些替代方法将其重置为0,或给出错误,等等。是一个非常实用的解决方案,似乎可以解决此特定问题,我觉得它没有解决@private
的更大问题,实际上并没有真正做到应做的事情。3)重新设计逻辑,以便为
FourDoorCar
提供一个类,为TwoDoorCar
提供另一个类,然后继续构建该类,这是一个有趣的选择-但是几乎感觉像现在Objective-C的语法在我的编程中“强迫”了自己逻辑以及我如何构建自己的项目-这感觉像是一种强制。也许我错了,并且从中得到了太多东西-但无论哪种方式,都是因为@private
没有按照它的承诺去做...?感觉不对。归根结底,我不断回到同一个问题:
@private
实际上对我们有什么好处?它有什么好处,可以“购买”我什么?看来,如果我想将iVar设为私有,则可以在“ .m”文件中声明它,而不必再在Header文件中声明它。我的意思是对吗?还是在某些情况下,您想在Header中将iVar声明为@private
,而不在Header中为其声明setter和getter-因此那些子类将不能显式使用-并且具有都说得通吗?我们可以考虑一个实际的例子吗?我想在标题(而不是在“ .m”中)声明为某种
@private
的Car属性,以某种方式使我受益吗?我以为
numberOfBackSeatPassengers
是一个很好的例子,但是我没有看到它在实际代码中如何真正起作用。================================================== =======================
编辑#2-继续与@aroth对话:-)
@aroth-我完全同意,它更好/更有条理地在标头中声明所有iVar,而不是拆分内容,以便其中一些在标头中,而某些在实现中。这造成了混乱,我真的不喜欢这种方法。 (我在最初的问题中指出,我不想使用实现和/或类别方法来解决我的问题。)
-而且,是的,iVars绝对不必总是备份属性。
-关于适当地设计Class,我同意那当然是良好编程的关键。 “汽车/运动跑车”示例是我在现场整理的,目的是为我的问题提供一些背景信息,并且我没有花任何时间考虑其设计优点/缺点。我认为,如果我们采用您的方法-当然可以肯定是很合逻辑的-并选择Car类,FourDoorCar子类,TwoDoorCar子类等,我们可以解决很多问题-但仍然很有可能迟早我们会遇到这样的情况:我们可能希望为我们的一个类提供一个@private iVar,而不希望创建另一个子类来处理它。
我的意思是,为了讨论方便,让我们仅假设会发生这种情况。
因此,如果可能的话,我真的很想为我们的Car类设计一个特定的iVar,将其作为@private有意义,在代码中显示如何使用它,并讨论其范围和局限性。
我一直想着一个汽车的某些属性的真实示例,我们只希望汽车具有-并且它的任何子类都不应该继承。
我真的以为
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
这几乎使我们回到了起点:-)
至少我就是这样。
我的意思是
@private
声明似乎给我们带来的唯一好处是可读性。这样一来,任何查看Header的人都可以看到phantomIVar
是Car的iVar,并且可以理解其私密性。而已。但是在功能方面,它似乎并没有做太多事情。因为它不像是将
@private
放在phantomIVar
前面,这使我们得以腾出头来仍然可以在Header中为其编写一个setter / getter,并使它们只能由Car类对象而不是Car的子类访问。不,@private
不能帮助您。为了获得隐私,您必须进入实施文件并在其中写入您的设置程序和获取程序。最终在Objective-C中,没有私有方法。在Obj。 C.他们都是公开的。不好意思,请让我知道我是否正确-否则,我到底在哪里出错。
非常感谢 :-)
最佳答案
这将使
numberOfBackSeatPassengers仅在“ Car.m”内可用
不对。这些方法仍将存在于Car
的每个实例以及扩展Car
的每个对象的每个实例中,无论是否在头文件中声明它们。如果您尝试直接调用它们,编译器不会将它们视为公开可见的,并且会抱怨,但是您仍然可以使用Car
来调用performSelector:
的任何子类上的getter和setter方法。
无论如何,如果您有一个@property
,则在支持它的ivar上使用@private
是没有意义的(并且也没有明确的ivar支持它的意义,当您使用< cc>;但这是一个单独的主题)。我建议,如果@synthesize
是为了扩展SportsCar
而从不记录任何后座乘客,那么“标准”方法就是简单地将Car
中的getter / setter方法覆盖为如果尝试设置非零值,则始终设置/返回0或引发一些错误。
由于此属性不适用于所有SportsCar
实例,因此,另一种选择是将其完全从基类中移除。例如,您可以具有Car
,然后从其中派生Car
和TwoDoorCar
,然后使FourDoorCar
从SportsCar
派生。在这种情况下,您可以将TwoDoorCar
声明为numberOfBackSeatPassengers
的公共财产,因为每辆四门轿车都应能够将乘客容纳在后座。
回到最初的问题,在ivar上使用FourDoorCar
仅会影响该ivar的可见性。它不影响使用ivar的方法。因此,@private
的子类将无法看到Car
ivar本身。但是,由于您已经为此创建了一个公共的getter / setter,因此子类当然可以看到它们,并使用它们来修改ivar的值。
编辑
要简要回答更新的问题:
是的,您可以使用NSInvocation动态调用需要原始参数的方法。或者,您可以使用此处讨论的方法,该方法更加直接:Objective-C and use of SEL/IMP。或者,您可以使用numberOfBackSeatPassengers
代替NSNumber
,然后使用int
。
我不确定在这种情况下您要说的performSelector:withObject:
应该做什么。您认为使用@private
应该做什么?
我认为这与语法无关,而与面向对象设计的原理有关。如果某些汽车没有后座,那么为@private
超类赋予Car
属性并不是一个好的面向对象设计。这样做为对象提供了一个字段,该字段实际上并不适用于对象类型的每个实例。当您开始这样做时,您会碰到您在示例中描述的那种问题。超类的目的是包含其所有派生类型所共有的功能。如果它具有仅某些派生类型共有的功能,则通常是设计问题。无论如何,它与Objective-C语法或语义无关。
至于numberOfBackseatPassengers
能带给您什么呢,一方面简化班级的组织呢?是的,您可以在实现文件中声明一个ivar以实现类似的效果,但这真的和在标头中声明所有ivars一样方便吗?在一个相当复杂的项目中,如果仅在标头中声明了一些ivars,而在实现文件中则声明了其他一些ivar,其他开发人员是否能够轻松地遵循您的代码?
如果没有@private
/ @private
,则在标头中声明的每个ivar都将是公共的,由于Jonathan指出的所有原因,这在面向对象的环境中绝对不好。因此,这些访问修饰符可能首先存在,以解决此问题。
至于用例,带有getter / setter的属性可能不是最好的例子。实际上,getter / setter的目的始终是提供一个用于修改/查询属性值的公共接口,并且正如Objective-C所述,不必在任何范围内显式声明一个ivar来支持综合属性。
一个更好的例子可能是@protected
。您希望在标头中声明这些内容,以便XCode / Interface Builder可以找到它们,但您不希望它们在类实现之外或(通常)甚至在类的子类之外公开。因此,您可以在标头中声明它们,并且通常不会为这些ivars添加任何getter / setter方法。
编辑2
对于IBOutlet
有意义的特定示例,该如何处理:
@interface Car : NSObject {
@private
DataRecorder* blackBoxRecorder;
}
@property (nonatomic, strong) NSString *make, *model;
@end
我们知道,拟议的法规可能要求道路上的所有汽车都包括内置的黑匣子/数据记录仪。因此,每个
@private
必须有一个,并且Car
的任何子类都不能篡改Car
。在这种情况下,定义setter方法是没有意义的。您可以提供一个公共获取器,或者相反,您可以围绕
blackBoxRecorder
提供一个公共包装器API,子类可以使用该API记录数据。类似于DataRecorder
。因此,子类可以通过API使用-(void) logEventWithName:(NSString*)name andValue:(NSNumber*)value;
,但不能与支持ivar本身混淆,以禁用或修改强制性黑匣子/数据记录器的行为。但是无论如何,是的,我完全同意您的分析。
DataRecorder
主要影响代码的可读性/可维护性。要使Objective-C成功地成为一种面向对象的编程语言,就必须存在(如果默认情况下所有ivar都是公开的,并且没有办法对其进行修改,则该语言将是一团糟),但是它的作用是单纯的功能观点并不多。它更多是一种逻辑/组织工具。它有助于data hiding并允许您将所有ivars保留在头文件中,仅此而已。
关于iphone - 正确使用@private变量/属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12437379/