361

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

我知道可能有几种约定和习惯,并将这个问题视为人们在处理 Objective-C 中的私有方法时使用的最佳技术的聚合器。

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


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

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

这种方法有两个问题:

  1. Xcode(和编译器?)不会检查您是否在相应的 @implementation 块中定义了私有类别中的所有方法
  2. 您必须将 @interface 声明您的私有类别放在 MyClass.m 文件的开头,否则 Xcode 会抱怨“self may not respond to message”privateFoo”之类的消息。

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

4

12 回答 12

438

正如其他人已经说过的那样,Objective-C 中没有私有方法之类的东西。但是,从 Objective-C 2.0(意味着 Mac OS X Leopard、iPhone OS 2.0 和更高版本)开始,您可以创建一个名为Class Extension的具有空名称(即@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

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

于 2009-03-16T19:37:02.270 回答
54

Objective-C 中并没有真正的“私有方法”,如果运行时可以确定使用哪个实现,它就会做到这一点。但这并不是说没有不属于文档化接口的方法。对于那些方法,我认为一个类别很好。而不是@interface像您的第 2 点那样将 .m 文件放在顶部,而是将其放入自己的 .h 文件中。我遵循的一个约定(并且在其他地方看到过,我认为这是一个 Apple 约定,因为 Xcode 现在自动支持它)是在它的类和类别之后用 + 分隔它们来命名这样的文件,所以@interface GLObject (PrivateMethods)可以在GLObject+PrivateMethods.h. 提供头文件的原因是您可以将其导入单元测试类:-)。

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

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

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

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

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

于 2008-10-06T05:49:46.810 回答
37

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

于 2008-10-05T22:03:16.753 回答
23

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

在某些情况下,您需要在类延续中声明方法(例如,如果在类延续和 之间使用选择器@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 方法的数量。

于 2013-05-26T09:45:48.773 回答
14

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

//.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
于 2009-03-16T18:54:15.213 回答
3

Objective C 中的每个对象都符合 NSObject 协议,该协议包含performSelector:方法。我之前也在寻找一种方法来创建一些我不需要在公共级别公开的“帮助程序或私有”方法。如果您想创建一个没有开销且不必在头文件中定义它的私有方法,那么请试一试...

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

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

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

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

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

于 2010-11-19T16:21:21.230 回答
3

可以用积木吗?

@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

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

于 2012-06-17T04:35:28.227 回答
2

如果您想避免@interface顶部的块,您总是可以将私有声明放在另一个MyClassPrivate.h不理想的文件中,但它不会使实现混乱。

我的类.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

我的班级.m

#import "MyClass.h"
#import "MyClassPrivate.h"
@implementation MyClass

@synthesize privateIvar = privateIvar_;
@synthesize publicIvar = publicIvar_;

@end
于 2010-08-03T23:48:02.477 回答
2

我在这里没有看到的另一件事 - Xcode 支持名称中带有“_private”的 .h 文件。假设你有一个 MyClass 类——你有 MyClass.m 和 MyClass.h,现在你也可以有 MyClass_private.h。Xcode 将识别这一点并将其包含在助手编辑器的“对应部分”列表中。

//MyClass.m
#import "MyClass.h"
#import "MyClass_private.h"
于 2013-02-14T03:08:12.023 回答
1

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

于 2009-03-16T18:33:08.430 回答
1

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

于 2011-08-08T07:31:36.810 回答
0

正如其他人所说,在@implementation块中定义私有方法对于大多数用途都是可以的。

关于代码组织的主题- 我喜欢将它们放在一起pragma mark private以便在 Xcode 中更轻松地导航

@implementation MyClass 
// .. public methods

# pragma mark private 
// ...

@end
于 2018-10-24T23:32:19.940 回答