iphone - 正确使用@private变量/属性

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

我所有的研究都表明@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,然后从其中派生CarTwoDoorCar,然后使FourDoorCarSportsCar派生。在这种情况下,您可以将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/

相关文章:

iphone - 如何将本地视频文件嵌入到 Html 中以在 UIWebView 中播放?

ios - scrollToItemAtIndexPath 破坏 UICollectionViewCell 布局

iphone - 从 iOS 上的视频文件中剥离元数据

ios - IOS 中的索引路径嵌套 Tableview

ios - 自定义 UIWindow 不显示

ios - AudioServicesCreateSystemSoundID 和内存地址参数 - 如何传递属性?

iphone - NSURLConnection 在设备上不工作

java - 派生类中的实例变量与父类(super class)的私有(private)实例变量同名吗?

java - servlet如何工作?实例化, session ,共享变量和多线程

javascript - 如何使用 JavaScript 原型(prototype)在实例之间共享属性