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

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

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


至于我目前的发现。

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

这种方法有两个问题:

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

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


当前回答

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

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

静态函数对于特别敏感或速度关键的私有方法非常有用。

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

命名类别(例如@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方法的数量。

其他回答

如果你想避免顶部的@interface块,你可以总是把私有声明放在另一个文件myclasprivate .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

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

There isn't really a "private method" in Objective-C, if the runtime can work out which implementation to use it will do it. But that's not to say that there aren't methods which aren't part of the documented interface. For those methods I think that a category is fine. Rather than putting the @interface at the top of the .m file like your point 2, I'd put it into its own .h file. A convention I follow (and have seen elsewhere, I think it's an Apple convention as Xcode now gives automatic support for it) is to name such a file after its class and category with a + separating them, so @interface GLObject (PrivateMethods) can be found in GLObject+PrivateMethods.h. The reason for providing the header file is so that you can import it in your unit test classes :-).

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

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

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

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

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

你可以用积木?

@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

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

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

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