关于cocoa:在objective-c中为类定义私有方法的最佳方法

Best way to define private methods for a class in Objective-C

我刚开始编程ObjuleC,在Java中有一个背景,不知道人们编写ObjuleC程序是如何处理私有方法的。

我理解可能有一些惯例和习惯,并把这个问题当作人们在客观C中处理私有方法时使用的最佳技术的集合。

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


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

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

这种方法有两个问题:

  • Xcode(和编译器?)不检查是否在对应的@implementation块中定义私有类别中的所有方法
  • 您必须在myclass.m文件的开头放置@interface声明您的私有类别,否则xcode会发出类似"self may not response to message"privatefoo的消息。
  • 第一个问题可以用空类别解决[例如myclass()]。第二个很困扰我。我希望看到在文件的末尾实现(和定义)私有方法;我不知道这是否可行。


    正如其他人已经说过的,在Objective-C中没有私有方法。但是,从Objective-C 2.0(即Mac OS X Leopard、iPhone OS 2.0及更高版本)开始,您可以创建一个名为类扩展的空名称(即@interface MyClass ()的类别。类扩展的独特之处在于,方法实现必须与公共方法位于同一个@implementation MyClass中。所以我这样安排我的课程:

    在.h文件中:

    1
    2
    3
    4
    5
    6
    7
    @interface MyClass {
        // My Instance Variables
    }

    - (void)myPublicMethod;

    @end

    在.m文件中:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    @interface MyClass()

    - (void)myPrivateMethod;

    @end

    @implementation MyClass

    - (void)myPublicMethod {
        // Implementation goes here
    }

    - (void)myPrivateMethod {
        // Implementation goes here
    }

    @end

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


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

    顺便说一下,对于.m文件末尾附近的实现/定义方法,您可以通过实现.m文件底部的类别来对类别执行此操作:

    1
    2
    3
    @implementation GLObject(PrivateMethods)
    - (void)secretFeature;
    @end

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

    即使有了类扩展,我也常常会创建一个单独的头(GLObject+Extension.h),以便在需要时使用这些方法,模仿"friend"或"protected"可见性。

    因为这个答案最初是写的,所以clang编译器已经开始为objective-c方法做两次传递。这意味着您可以避免完全声明您的"私有"方法,无论它们在调用站点的上方还是下方,编译器都会找到它们。


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


    @implementation块中定义您的私有方法对于大多数目的都是理想的。Clang将在@implementation中看到这些,而不考虑申报单。不需要在类延续(即类扩展)或命名类别中声明它们。

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

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

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

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

    1
    2
    3
    @implementation MONObject (PrivateStuff)
    ...HERE...
    @end

    下面是一个带注释的小纸条:

    单目标

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @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

    M对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    @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方法的数量。


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    //.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


    你可以用积木吗?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    @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

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


    目标C中的每个对象都符合nsObject协议,该协议保留了performSelector:方法。我以前也在寻找一种方法来创建一些不需要在公共级别公开的"助手或私有"方法。如果您想创建一个没有开销的私有方法,并且不需要在头文件中定义它,那么就给它一个例子……

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

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

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

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

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


    还有一件事我在这里没有看到提到——xcode支持名为"private"的.h文件。假设您有一个类myclass—您有myclass.m和myclass.h,现在您也可以有myclass_private.h。xcode将识别这一点,并将其包含在助理编辑器的"副本"列表中。

    1
    2
    3
    //MyClass.m
    #import"MyClass.h"
    #import"MyClass_private.h"

    如果您希望避免顶部的@interface块,那么您可以始终将私有声明放在另一个文件MyClassPrivate.h中,这并不理想,但不会使实现混乱。

    MyClass

    1
    2
    3
    4
    5
    6
    7
    8
    9
    interface MyClass : NSObject {
     @private
      BOOL publicIvar_;
      BOOL privateIvar_;
    }

    @property (nonatomic, assign) BOOL publicIvar;
    //any other public methods. etc
    @end

    我的班级私人.h

    1
    2
    3
    4
    5
    @interface MyClass ()

    @property (nonatomic, assign) BOOL privateIvar;
    //any other private methods etc.
    @end

    MyClass

    1
    2
    3
    4
    5
    6
    7
    8
    #import"MyClass.h"
    #import"MyClassPrivate.h"
    @implementation MyClass

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

    @end

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


    没有办法解决问题2。这就是C编译器(因此也是Objective-C编译器)的工作方式。如果使用xcode编辑器,则弹出的函数应使导航文件中的@interface@implementation块变得容易。


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

    关于代码组织的主题-我喜欢把它们放在pragma mark private下,以便在xcode中导航。

    1
    2
    3
    4
    5
    6
    7
    @implementation MyClass
    // .. public methods

    # pragma mark private
    // ...

    @end