在Objective-C中为类定义私有方法的最佳方法
我刚刚开始编程Objective-C,并且拥有Java的背景,不知道编写Objective-C程序的人是如何处理私有方法的。
我知道可能有几个约定和习惯,并将这个问题作为人们在Objective-C中处理私有方法的最佳技术的集合体来思考。
发布时请包括您的方法的论点。 为什么这是好的? 它有哪些缺点(你知道的)以及你如何处理它们?
至于我目前的发现。
可以使用MyClass.m文件中定义的类别 [例如MyClass(Private)]对私有方法进行分组。
这种方法有两个问题:
- Xcode(和编译器?)不会检查是否在相应的@implementation块中定义了私有类别中的所有方法
- 你必须把@interface声明你的私人类别在MyClass.m文件的开头,否则Xcode会抱怨一个消息,比如“self可能不回应消息”privateFoo“。
第一个问题可以用空类别 [例如MyClass()]解决。
第二个困扰我很多。 我希望在文件末尾附近实现(定义)私有方法; 我不知道这是可能的。
就像其他人已经说过的那样,Objective-C没有像私人方法那样的东西。 但是,从Objective-C 2.0开始(意思是Mac OS X Leopard,iPhone OS 2.0及更高版本),您可以创build一个名为空的类别(即@interface MyClass ()
),称为类扩展 。 类扩展的独特之处在于方法实现必须和公共方法一样在@implementation MyClass
中。 所以我像这样构build我的类:
在.h文件中:
@interface MyClass { // My Instance Variables } - (void)myPublicMethod; @end
在.m文件中:
@interface MyClass() - (void)myPrivateMethod; @end @implementation MyClass - (void)myPublicMethod { // Implementation goes here } - (void)myPrivateMethod { // Implementation goes here } @end
我认为这种方法的最大优点是它允许你按function分组你的方法实现,而不是(有时是任意的)公/私的区分。
Objective-C中没有真正的“私有方法”,如果运行时可以计算出使用它的实现。 但是,这并不是说没有方法不是文档界面的一部分。 对于这些方法,我认为一个类别是好的。 我没有把@interface
放在.m文件的顶部,而是把它放在自己的.h文件中。 我遵循的一个约定(我认为这是一个苹果惯例,因为Xcode现在给它提供了自动支持)就是在它的类和类别之后用+分隔它们来命名这样一个文件,所以@interface GLObject (PrivateMethods)
可以在GLObject+PrivateMethods.h
find。 提供头文件的原因是,你可以将它导入你的unit testing类:-)。
顺便说一下,就.m文件末尾的实现/定义方法而言,可以通过在.m文件底部实现类别来实现:
@implementation GLObject(PrivateMethods) - (void)secretFeature; @end
或者使用类扩展(你称之为“空类”),最后定义这些方法。 Objective-C方法可以在实现中以任何顺序定义和使用,所以没有什么能阻止你在文件末尾放置“private”方法。
即使使用类扩展,我也会经常创build一个单独的头文件( GLObject+Extension.h
),以便我可以根据需要使用这些方法,模仿“朋友”或“受保护的”可见性。
由于这个答案是最初编写的,所以clang编译器已经开始为Objective-C方法做两遍了。 这意味着您可以避免完全声明您的“私有”方法,以及它们是否位于编译器可以find的调用站点之上或之下。
虽然我不是Objective-C的专家,但我个人只是在实现我的课程时定义了这个方法。 当然,它必须在任何调用它的方法之前(之前)定义,但它绝对是最less量的工作。
在@implementation
块中定义私有方法对于大多数目的是理想的。 无论声明顺序如何,Clang都会在@implementation
看到这些内容。 没有必要在类继续(也称为类扩展)或命名类别中声明它们。
在某些情况下,您将需要在类继续中声明该方法(例如,如果在类继续和@implementation
之间使用select器)。
static
函数对于特别敏感或速度严格的私有方法非常有用。
命名前缀的约定可以帮助您避免意外覆盖私有方法(我发现类名称是前缀安全的)。
命名的类别(例如@interface MONObject (PrivateStuff)
)并不是一个特别好的想法,因为加载时可能存在命名冲突。 他们真的只对朋友或受保护的方法(这是非常不错的select)有用。 为了确保您对不完整的类别实现提出警告,您应该实际执行它:
@implementation MONObject (PrivateStuff) ...HERE... @end
这是一个有点注释的备忘单:
MONObject.h
@interface MONObject : NSObject // public declaration required for clients' visibility/use. @property (nonatomic, assign, readwrite) bool publicBool; // public declaration required for clients' visibility/use. - (void)publicMethod; @end
MONObject.m
@interface MONObject () @property (nonatomic, assign, readwrite) bool privateBool; // you can use a convention where the class name prefix is reserved // for private methods this can reduce accidental overriding: - (void)MONObject_privateMethod; @end // The potentially good thing about functions is that they are truly // inaccessible; They may not be overridden, accidentally used, // looked up via the objc runtime, and will often be eliminated from // backtraces. Unlike methods, they can also be inlined. If unused // (eg diagnostic omitted in release) or every use is inlined, // they may be removed from the binary: static void PrivateMethod(MONObject * pObject) { pObject.privateBool = true; } @implementation MONObject { bool anIvar; } static void AnotherPrivateMethod(MONObject * pObject) { if (0 == pObject) { assert(0 && "invalid parameter"); return; } // if declared in the @implementation scope, you *could* access the // private ivars directly (although you should rarely do this): pObject->anIvar = true; } - (void)publicMethod { // declared below -- but clang can see its declaration in this // translation: [self privateMethod]; } // no declaration required. - (void)privateMethod { } - (void)MONObject_privateMethod { } @end
另一种可能不明显的方法是:C ++types可以非常快,并提供更高程度的控制,同时最小化导出和加载的objc方法的数量。
你可以尝试在你的实现的下面或上面定义一个静态函数,它指向你的实例。 它将能够访问你的任何实例variables。
//.h file @interface MyClass : Object { int test; } - (void) someMethod: anArg; @end //.m file @implementation MyClass static void somePrivateMethod (MyClass *myClass, id anArg) { fprintf (stderr, "MyClass (%d) was passed %p", myClass->test, anArg); } - (void) someMethod: (id) anArg { somePrivateMethod (self, anArg); } @end
Objective C中的每个对象都符合NSObject协议,该协议保存在performSelector:方法中。 我之前也在寻找一种方法来创build一些我不需要公开的“帮手或私人”方法。 如果你想创build一个没有开销的私有方法,而不必在你的头文件中定义它,然后给这个镜头…
使用与下面的代码类似的签名来定义您的方法…
-(void)myHelperMethod: (id) sender{ // code here... }
那么当你需要引用该方法时,只需将其称为select器…
[self performSelector:@selector(myHelperMethod:)];
这行代码将会调用你创build的方法,并且不会在头文件中定义一个恼人的警告。
你可以使用块?
@implementation MyClass id (^createTheObject)() = ^(){ return [[NSObject alloc] init];}; NSInteger (^addEm)(NSInteger, NSInteger) = ^(NSInteger a, NSInteger b) { return a + b; }; //public methods, etc. - (NSObject) thePublicOne { return createTheObject(); } @end
我知道这是一个古老的问题,但这是我第一次find这个问题的答案。 我还没有看到任何其他地方讨论过这个解决scheme,所以让我知道如果这样做是愚蠢的。
如果你想避免顶部的@interface
块,你总是可以把私人声明放在另一个文件MyClassPrivate.h
不理想,但它不干扰实施。
MyClass.h
interface MyClass : NSObject { @private BOOL publicIvar_; BOOL privateIvar_; } @property (nonatomic, assign) BOOL publicIvar; //any other public methods. etc @end
MyClassPrivate.h
@interface MyClass () @property (nonatomic, assign) BOOL privateIvar; //any other private methods etc. @end
MyClass.m
#import "MyClass.h" #import "MyClassPrivate.h" @implementation MyClass @synthesize privateIvar = privateIvar_; @synthesize publicIvar = publicIvar_; @end
还有一件事,我没有看到在这里提到 – Xcode支持名称中带有“_private”的.h文件。 比方说你有一个类MyClass – 你有MyClass.m和MyClass.h,现在你也可以有MyClass_private.h。 Xcode会识别这个并将其包含在助手编辑器的“对应部件”列表中。
//MyClass.m #import "MyClass.h" #import "MyClass_private.h"
没有办法绕过问题#2。 这只是C编译器(以及Objective-C编译器)的工作方式。 如果使用XCode编辑器,那么popup的函数应该能够轻松地浏览文件中的@interface
和@implementation
块。
私人方法缺席有一个好处。 您可以将您要隐藏的逻辑移到单独的类中,并将其用作委托。 在这种情况下,您可以将委托对象标记为私有,并且不会从外部看到。 将逻辑移动到单独的类(可能有几个)可以更好地devise项目。 导致你的类变得更简单,你的方法被分组到具有正确名称的类中。