提问



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


我理解可能存在一些惯例和习惯,并将此问题视为人们在Objective-C中处理私有方法的最佳技术的聚合器。


请在发布时为您的方法添加一个参数。它为什么好?它有哪些缺点(你知道)以及你如何处理它们?





至于到目前为止我的发现。


可以使用类别[[例如MyClass(私有)]]在MyClass.m文件中定义,用于对私有方法进行分组。[30]


这种方法有两个问题:



  1. Xcode(和编译器?)不检查是否在相应的@implementation块中定义私有类别中的所有方法

  2. 你必须把@interface声明你的私人类别在MyClass.m文件的开头,否则Xcode会抱怨自己可能没有回复消息privateFoo。



第一个问题可以解决空类[[例如MyClass()]]。

第二个困扰我很多。我希望在文件末尾附近实现(和定义)私有方法;我不知道这是否可能。[31]

最佳参考


正如其他人已经说过的那样,没有像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


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

其它参考1


在Objective-C中没有真正的私有方法,如果运行时可以解决使用它的实现将会做到的事情。但这并不是说没有任何方法不属于记录的界面。对于那些方法,我认为一个类别是好的。而不是将@interface放在.m文件的顶部,就像你的第2点一样,我把它放到它自己的.h文件中。我遵循的一个约定(并且已经在其他地方看过,我认为它是一个苹果公约Xcode现在为它提供了自动支持)就是在它的类和类别之后命名这样一个文件,用+分隔它们,所以@interface GLObject (PrivateMethods)可以在GLObject+PrivateMethods.h中找到。提供头文件的原因是您可以在单元测试类中导入它:-)。


顺便说一下,就.m文件末尾附近的实现/定义方法而言,您可以通过实现.m文件底部的类别来实现类别:


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


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


即使有类扩展,我也会经常创建一个单独的标题(GLObject+Extension.h),以便我可以根据需要使用这些方法,模仿朋友或受保护的可见性。


由于这个答案是最初编写的,所以clang编译器已经开始为Objective-C方法进行两次传递。这意味着您可以避免完全声明您的私有方法,以及它们是否高于或低于调用站点,编译器将找到它们。

其它参考2


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

其它参考3


@implementation块中定义私有方法是大多数用途的理想选择。无论声明顺序如何,Clang都会在@implementation中看到这些内容。没有必要在类继续(也称为类扩展)或命名类别中声明它们。


在某些情况下,您需要在类continuation中声明该方法(例如,如果在类continuation和@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方法的数量。

其它参考4


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


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

其它参考5


Objective C中的每个对象都符合NSObject协议,该协议保留在 performSelector:方法中。我之前也在寻找一种方法来创建一些我不需要在公共级别公开的帮助或私有方法。如果你想创建一个没有开销的私有方法,而不必在头文件中定义它,那就给它一个镜头......


使用与下面的代码类似的签名定义您的方法...


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


那么当你需要引用该方法时,只需将其称为选择器...


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


这行代码将调用您创建的方法,并且没有关于未在头文件中定义它的恼人警告。

其它参考6


你可以使用积木吗?


@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


我知道这是一个老问题,但它是我在寻找这个问题的答案时发现的第一个问题。我还没有在其他任何地方看到这个解决方案,所以让我知道这样做是否有些愚蠢。

其它参考7


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

其它参考8


我还没有看到这里提到的另一件事 - Xcode在名称中支持带有_private的.h文件。让我们说你有一个MyClass类 - 你有MyClass.m和MyClass.h现在你也可以拥有MyClass_private.h。 Xcode将识别它并将其包含在Assistant Editor的Counterparts列表中。


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

其它参考9


没有办法绕过问题#2。这就是C编译器(以及Objective-C编译器)的工作方式。如果使用XCode编辑器,则弹出功能可以轻松浏览文件中的@interface@implementation块。

其它参考10


私人方法缺席有一个好处。您可以将要隐藏的逻辑移动到单独的类中,并将其用作委托。在这种情况下,您可以将委托对象标记为私有,并且从外部看不到它。将逻辑移动到单独的类(可能是几个)可以更好地设计项目。导致您的类变得更简单,并且您的方法被分组在具有适当名称的类中。