在Objective-C中为类定义私有方法的最佳方法

我刚开始编写Objective-C,有Java背景,不知道人们写Objective-C程序是如何处理私有方法的。

我知道可能会有一些约定和习惯,把这个问题看作是人们在Objective-C中处理私有方法时使用的最佳技术的集合。

请包括一个论点,你的方法张贴时。为什么它是好的?你知道它有哪些缺点,你是如何处理它们的?


至于我目前的发现。

可以使用MyClass中定义的类别[例如MyClass (Private)]。M文件对私有方法进行分组。

这种方法有两个问题:

  1. Xcode(和编译器?)不会检查你是否在相应的@implementation块中定义了私有类别中的所有方法
  2. 你必须把@interface放在MyClass的开头声明你的私有类别。m文件,否则Xcode抱怨一个消息像“self可能不响应消息“privateFoo”。

第一个问题可以通过空的类别[例如MyClass()]来解决 第二个问题让我很困扰。我希望在文件末尾看到私有方法的实现(和定义);我不知道那是否可能。

185471 次浏览

虽然我不是Objective-C专家,但我个人只是在我的类的实现中定义方法。当然,它必须在任何调用它的方法之前定义,但它肯定需要最少的工作。

在Objective-C中并没有真正的“私有方法”,如果运行时能够找出使用它的实现的话。但这并不是说没有方法不是文档接口的一部分。对于这些方法,我认为分类就可以了。而不是像你的观点2那样把@interface放在.m文件的顶部,我会把它放在自己的.h文件中。我遵循的一个惯例(在其他地方见过,我认为这是苹果的惯例,因为Xcode现在自动支持它)是用类和类别来命名这样一个文件,用+分隔它们,所以@interface GLObject (PrivateMethods)可以在GLObject+PrivateMethods.h中找到。提供头文件的原因是你可以在你的单元测试类中导入它:-)。

顺便说一下,至于在.m文件末尾实现/定义方法,你可以通过在.m文件底部实现category来做到这一点:

@implementation GLObject(PrivateMethods)
- (void)secretFeature;
@end

或者使用类扩展(你称之为“空类别”的东西),最后定义这些方法。Objective-C方法可以在实现中以任何顺序定义和使用,因此没有什么可以阻止您将“私有”方法放在文件的末尾。

即使是类扩展,我也会经常创建一个单独的头文件(GLObject+Extension.h),以便在需要时使用这些方法,模仿“好友”或“受保护”可见性。

因为这个答案是最初编写的,clang编译器已经开始为Objective-C方法进行两次传递。这意味着你可以完全避免声明你的“私有”方法,无论它们是在调用站点的上面还是下面,它们都会被编译器找到。

没有办法绕过问题2。这就是C编译器(以及Objective-C编译器)的工作方式。如果你使用XCode编辑器,函数弹出应该可以很容易地导航文件中的@interface@implementation块。

您可以尝试在实现的下面或上面定义一个静态函数,该函数接受指向实例的指针。它将能够访问您的任何实例变量。

//.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中没有私有方法。然而,从Objective-C 2.0(意味着Mac OS X Leopard, iPhone OS 2.0和更高版本)开始,你可以创建一个名为类扩展的空名称(即@interface MyClass ())的类别。类扩展的独特之处在于方法实现必须与公共方法放在相同的@implementation MyClass中。所以我的课程结构是这样的:

在.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

我认为这种方法最大的优点是,它允许您根据功能对方法实现进行分组,而不是根据(有时是任意的)公共/私有区分进行分组。

如果你想避免顶部的@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

Objective C中的每个对象都遵循NSObject协议,该协议保留了performSelector:方法。我以前也在寻找一种方法来创建一些“辅助或私有”方法,我不需要在公共级别上公开这些方法。如果你想创建一个没有开销的私有方法,并且不需要在头文件中定义它,那么就试试这个…

用与下面代码相似的签名定义your方法…

-(void)myHelperMethod: (id) sender{
// code here...
}

然后,当你需要引用该方法时,只需将其作为选择器调用…

[self performSelector:@selector(myHelperMethod:)];

这行代码将调用您创建的方法,并且不会出现关于头文件中没有定义该方法的恼人警告。

没有私有方法是有好处的。您可以将打算隐藏的逻辑移动到单独的类中,并将其用作委托。在这种情况下,您可以将委托对象标记为私有,从外部将不可见。将逻辑移动到单独的类(可能是几个)可以更好地设计项目。因为你的类变得更简单,你的方法被分组在具有正确名称的类中。

你可以用积木?

@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

我知道这是一个古老的问题,但这是我在寻找这个问题答案时最先发现的问题之一。我还没有在其他地方看到过这个解决方案,所以如果这样做有什么愚蠢的地方,请告诉我。

还有一件事我在这里没有看到提到——Xcode支持名称为“_private”的。h文件。假设你有一个类。m和myclass。h现在你还可以有myclass_private。h。Xcode会识别它,并将其包含在助理编辑器的“对应项”列表中。

//MyClass.m
#import "MyClass.h"
#import "MyClass_private.h"

@implementation块中定义私有方法对于大多数目的来说是理想的。Clang将在@implementation中看到这些,不管声明顺序如何。不需要在类延续(又名类扩展)或命名类别中声明它们。

在某些情况下,你需要在类延续中声明方法(例如,如果在类延续和@implementation之间使用选择器)。

static函数非常适合特别敏感或速度关键的私有方法。

命名前缀的约定可以帮助您避免意外重写私有方法(我发现类名作为前缀是安全的)。

命名类别(例如@interface MONObject (PrivateStuff))不是一个特别好的主意,因为加载时可能会发生命名冲突。它们实际上只对朋友或受保护的方法有用(这很少是一个好的选择)。为了确保你被警告不完整的类别实现,你应该实际实现它:

@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
// (e.g. 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++类型既可以非常快,又可以提供更高程度的控制,同时最大限度地减少导出和加载objc方法的数量。

正如其他人所说,在@implementation块中定义私有方法对于大多数目的是OK的。

关于代码组织的话题——我喜欢把它们放在pragma mark private下面,以便在Xcode中更容易导航

@implementation MyClass
// .. public methods


# pragma mark private
// ...


@end