简体   繁体   English

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

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

I just started programming Objective-C and, having a background in Java, wonder how people writing Objective-C programs deal with private methods. 我刚刚开始编写Objective-C编程,并且具有Java背景,想知道编写Objective-C程序的人如何处理私有方法。

I understand there may be several conventions and habits and think about this question as an aggregator of the best techniques people use dealing with private methods in Objective-C. 我理解可能存在一些惯例和习惯,并将此问题视为人们在Objective-C中使用私有方法的最佳技术的聚合器。

Please include an argument for your approach when posting it. 请在发布时为您的方法添加一个参数。 Why is it good? 它为什么好? Which drawbacks does it have (that you know of) and how you deal with them? 它有哪些缺点(你知道)以及你如何处理它们?


As for my findings so far. 至于到目前为止我的发现。

It is possible to use categories [eg MyClass (Private)] defined in MyClass.m file to group private methods. 可以使用MyClass.m文件中定义的类别 [例如MyClass(Private)]来对私有方法进行分组。

This approach has 2 issues: 这种方法有两个问题:

  1. Xcode (and compiler?) does not check if you define all methods in private category in corresponding @implementation block Xcode(和编译器?)不检查您是否在相应的@implementation块中定义私有类别中的所有方法
  2. You have to put @interface declaring your private category in the begin of MyClass.m file, otherwise Xcode complains with a message like "self may not respond to message "privateFoo". 你必须把@interface声明你的私人类别在MyClass.m文件的开头,否则Xcode会抱怨“自己可能没有回复消息”privateFoo“。

The first issue can be worked around with empty category [eg MyClass ()]. 第一个问题可以解决空类 [例如MyClass()]。
The second one bothers me a lot. 第二个困扰我很多。 I'd like to see private methods implemented (and defined) near the end of the file; 我希望在文件末尾附近实现(和定义)私有方法; I do not know if that's possible. 我不知道这是否可能。

There isn't, as others have already said, such a thing as a private method in Objective-C. 正如其他人已经说过的那样,没有像Objective-C中的私有方法这样的东西。 However, starting in Objective-C 2.0 (meaning Mac OS X Leopard, iPhone OS 2.0, and later) you can create a category with an empty name (ie @interface MyClass () ) called Class Extension . 但是,从Objective-C 2.0(意味着Mac OS X Leopard,iPhone OS 2.0及更高版本)开始,您可以创建一个名为Class Extension的空名称(即@interface MyClass () )。 What's unique about a class extension is that the method implementations must go in the same @implementation MyClass as the public methods. 类扩展的独特之处在于方法实现必须与公共方法在同一个@implementation MyClass中。 So I structure my classes like this: 所以我像这样构建我的类:

In the .h file: 在.h文件中:

@interface MyClass {
    // My Instance Variables
}

- (void)myPublicMethod;

@end

And in the .m file: 在.m文件中:

@interface MyClass()

- (void)myPrivateMethod;

@end

@implementation MyClass

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

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

@end

I think the greatest advantage of this approach is that it allows you to group your method implementations by functionality, not by the (sometimes arbitrary) public/private distinction. 我认为这种方法的最大优点是它允许您按功能对方法实现进行分组,而不是通过(有时是任意的)公共/私有区分。

There isn't really a "private method" in Objective-C, if the runtime can work out which implementation to use it will do it. Objective-C中没有真正的“私有方法”,如果运行时可以解决使用它的实现将会做到这一点。 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. 而不是将@interface放在.m文件的顶部,就像你的第2点一样,我将它放入自己的.h文件中。 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 . 我遵循的惯例(并且已经在其他地方看到过,我认为这是一个Apple惯例,因为Xcode现在为它提供了自动支持)就是在它的类和类别之后命名这样一个文件,用+分隔它们,所以@interface GLObject (PrivateMethods)可以可以在GLObject+PrivateMethods.h找到。 The reason for providing the header file is so that you can import it in your unit test classes :-). 提供头文件的原因是您可以在单元测试类中导入它:-)。

By the way, as far as implementing/defining methods near the end of the .m file is concerned, you can do that with a category by implementing the category at the bottom of the .m file: 顺便说一下,就.m文件末尾附近的实现/定义方法而言,可以通过实现.m文件底部的类别来实现类别:

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

or with a class extension (the thing you call an "empty category"), just define those methods last. 或者使用类扩展(你称之为“空类”的东西),只需最后定义这些方法。 Objective-C methods can be defined and used in any order in the implementation, so there's nothing to stop you putting the "private" methods at the end of the file. Objective-C方法可以在实现中以任何顺序定义和使用,因此没有什么可以阻止您将“私有”方法放在文件的末尾。

Even with class extensions I will often create a separate header ( GLObject+Extension.h ) so that I can use those methods if required, mimicking "friend" or "protected" visibility. 即使使用类扩展,我也经常会创建一个单独的标题( GLObject+Extension.h ),以便我可以根据需要使用这些方法,模仿“朋友”或“受保护”的可见性。

Since this answer was originally written, the clang compiler has started doing two passes for Objective-C methods. 由于这个答案是最初编写的,所以clang编译器已经开始为Objective-C方法进行两次传递。 This means you can avoid declaring your "private" methods completely, and whether they're above or below the calling site they'll be found by the compiler. 这意味着您可以完全避免声明“私有”方法,以及它们是否高于或低于调用站点,编译器将找到它们。

While I am no Objective-C expert, I personally just define the method in the implementation of my class. 虽然我不是Objective-C专家,但我个人只是在我的类的实现中定义方法。 Granted, it must be defined before (above) any methods calling it, but it definitely takes the least amount of work to do. 当然,它必须在调用它的任何方法之前(上面)定义,但它绝对需要做最少量的工作。

Defining your private methods in the @implementation block is ideal for most purposes. @implementation块中定义私有方法是大多数用途的理想选择。 Clang will see these within the @implementation , regardless of declaration order. 无论声明顺序如何,Clang都会在@implementation看到这些内容。 There is no need to declare them in a class continuation (aka class extension) or named category. 无需在类继续(也称为类扩展)或命名类别中声明它们。

In some cases, you will need to declare the method in the class continuation (eg if using the selector between the class continuation and the @implementation ). 在某些情况下,您需要在类continuation中声明该方法(例如,如果在类continuation和@implementation之间使用选择器)。

static functions are very good for particularly sensitive or speed critical private methods. static函数非常适合特别敏感或速度快的关键私有方法。

A convention for naming prefixes can help you avoid accidentally overriding private methods (I find the class name as a prefix safe). 命名前缀的约定可以帮助您避免意外覆盖私有方法(我发现类名作为前缀安全)。

Named categories (eg @interface MONObject (PrivateStuff) ) are not a particularly good idea because of potential naming collisions when loading. 命名类别(例如@interface MONObject (PrivateStuff) )并不是一个特别好的主意,因为加载时潜在的命名冲突。 They're really only useful for friend or protected methods (which are very rarely a good choice). 它们实际上只对朋友或受保护的方法有用(很少是一个很好的选择)。 To ensure you are warned of incomplete category implementations, you should actually implement it: 为了确保您收到不完整类别实现的警告,您应该实际实现它:

@implementation MONObject (PrivateStuff)
...HERE...
@end

Here's a little annotated cheat sheet: 这是一个带注释的备忘单:

MONObject.h 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 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

Another approach which may not be obvious: a C++ type can be both very fast and provide a much higher degree of control, while minimizing the number of exported and loaded objc methods. 另一种可能不明显的方法是:C ++类型既可以非常快速又可以提供更高程度的控制,同时最大限度地减少导出和加载的objc方法的数量。

You could try defining a static function below or above your implementation that takes a pointer to your instance. 您可以尝试在实现的下方或上方定义一个静态函数,该函数将指向您的实例。 It will be able to access any of your instances variables. 它将能够访问您的任何实例变量。

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

You could use blocks? 你可以使用积木吗?

@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

I'm aware this is an old question, but it's one of the first I found when I was looking for an answer to this very question. 我知道这是一个老问题,但这是我在寻找这个问题答案时发现的第一个问题。 I haven't seen this solution discussed anywhere else, so let me know if there's something foolish about doing this. 我还没有在其他任何地方看到这个解决方案,所以让我知道这样做是否有些愚蠢。

every objects in Objective C conform to NSObject protocol, which holds onto the performSelector: method. Objective C中的每个对象都符合NSObject协议,该协议保存在performSelector:方法中。 I was also previously looking for a way to create some "helper or private" methods that I did not need exposed on a public level. 我之前也在寻找一种方法来创建一些我不需要在公共级别公开的“帮助或私有”方法。 If you want to create a private method with no overhead and not having to define it in your header file then give this a shot... 如果你想创建一个没有开销的私有方法而不必在你的头文件中定义它,那就给它一个镜头......

define the your method with a similar signature as the code below... 使用与下面的代码类似的签名定义您的方法...

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

then when you need to reference the method simply call it as a selector... 那么当你需要引用该方法时,只需将其称为选择器...

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

this line of code will invoke the method you created and not have an annoying warning about not having it defined in the header file. 这行代码将调用您创建的方法,并且没有关于未在头文件中定义它的恼人警告。

One more thing that I haven't seen mentioned here - Xcode supports .h files with "_private" in the name. 还有一件我在这里没有提到过的事情 - Xcode支持名称中带有“_private”的.h文件。 Let's say you have a class MyClass - you have MyClass.m and MyClass.h and now you can also have MyClass_private.h. 假设你有一个MyClass类 - 你有MyClass.m和MyClass.h,现在你也可以拥有MyClass_private.h。 Xcode will recognize this and include it in the list of "Counterparts" in the Assistant Editor. Xcode将识别它并将其包含在Assistant Editor的“Counterparts”列表中。

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

If you wanted to avoid the @interface block at the top you could always put the private declarations in another file MyClassPrivate.h not ideal but its not cluttering up the implementation. 如果你想避免顶部的@interface块,你总是可以将私有声明放在另一个文件MyClassPrivate.h并不理想,但它不会使实现混乱。

MyClass.h MyClass.h

interface MyClass : NSObject {
 @private
  BOOL publicIvar_;
  BOOL privateIvar_;
}

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

MyClassPrivate.h MyClassPrivate.h

@interface MyClass ()

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

MyClass.m MyClass.m

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

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

@end

There's no way of getting around issue #2. 没有办法解决问题#2。 That's just the way the C compiler (and hence the Objective-C compiler) work. 这就是C编译器(以及Objective-C编译器)的工作方式。 If you use the XCode editor, the function popup should make it easy to navigate the @interface and @implementation blocks in the file. 如果使用XCode编辑器,则弹出功能可以轻松导航文件中的@interface@implementation块。

There is a benefit of private methods absence. 私人方法缺席有一个好处。 You can move the logic that you intended to hide to the separate class and use it as delegate. 您可以将要隐藏的逻辑移动到单独的类中,并将其用作委托。 In this case you can mark delegate object as private and it will not be visible from outside. 在这种情况下,您可以将委托对象标记为私有,并且从外部看不到它。 Moving logic to the separate class (maybe several) makes better design of your project. 将逻辑移动到单独的类(可能是几个)可以更好地设计项目。 Cause your classes become simpler and your methods are grouped in classes with proper names. 导致您的类变得更简单,并且您的方法被分组在具有适当名称的类中。

As other people said defining private methods in the @implementation block is OK for most purposes. 正如其他人所说,在@implementation块中定义私有方法对于大多数目的来说是可以的。

On the topic of code organization - I like to keep them together under pragma mark private for easier navigation in Xcode 关于代码组织的主题 - 我喜欢将它们保存在pragma mark private ,以便在Xcode中更轻松地导航

@implementation MyClass 
// .. public methods

# pragma mark private 
// ...

@end

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM