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

发布于 2024-07-06 08:35:58 字数 855 浏览 11 评论 0原文

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

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

发布时请包含您的方法的论据。 为什么好呢? 它有哪些缺点(您知道)以及您如何处理它们?


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

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

这种方法有两个问题:

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

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

I just started programming Objective-C and, having a background in Java, wonder how people writing Objective-C programs deal with private methods.

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.

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 [e.g. MyClass (Private)] defined in MyClass.m file to group private methods.

This approach has 2 issues:

  1. Xcode (and compiler?) does not check if you define all methods in private category in corresponding @implementation block
  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".

The first issue can be worked around with empty category [e.g. 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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

沫雨熙 2024-07-13 08:35:58

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

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

There isn't, as others have already said, such a thing as a private method in 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 (i.e. @interface MyClass ()) called Class Extension. What's unique about a class extension is that the method implementations must go in the same @implementation MyClass as the public methods. So I structure my classes like this:

In the .h file:

@interface MyClass {
    // My Instance Variables
}

- (void)myPublicMethod;

@end

And in the .m file:

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

风尘浪孓 2024-07-13 08:35:58

Objective-C 中并不存在真正的“私有方法”,只要运行时能够确定使用哪个实现就可以做到这一点。 但这并不是说没有方法不属于已记录的接口的一部分。 对于这些方法,我认为类别就可以了。 我不会像第 2 点那样将 @interface 放在 .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 方法执行两次传递。 这意味着您可以完全避免声明“私有”方法,并且无论它们是在调用站点之上还是之下,编译器都会找到它们。

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 :-).

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:

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

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.

Since this answer was originally written, the clang compiler has started doing two passes for Objective-C methods. 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.

天邊彩虹 2024-07-13 08:35:58

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

While I am no Objective-C expert, I personally just define the method in the implementation of my class. Granted, it must be defined before (above) any methods calling it, but it definitely takes the least amount of work to do.

烂人 2024-07-13 08:35:58

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

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

Defining your private methods in the @implementation block is ideal for most purposes. Clang will see these within the @implementation, regardless of declaration order. 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 (e.g. if using the selector between the class continuation and the @implementation).

static functions are very good for particularly sensitive or speed critical private methods.

A convention for naming prefixes can help you avoid accidentally overriding private methods (I find the class name as a prefix safe).

Named categories (e.g. @interface MONObject (PrivateStuff)) are not a particularly good idea because of potential naming collisions when loading. 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

@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

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.

恏ㄋ傷疤忘ㄋ疼 2024-07-13 08:35:58

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

//.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 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
昔梦 2024-07-13 08:35:58

可以用积木吗?

@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

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

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.

若有似无的小暗淡 2024-07-13 08:35:58

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

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

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

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

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

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

every objects in Objective C conform to NSObject protocol, which holds onto the performSelector: method. 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.

剩余の解释 2024-07-13 08:35:58

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

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.

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
↙温凉少女 2024-07-13 08:35:58

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

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

One more thing that I haven't seen mentioned here - Xcode supports .h files with "_private" in the name. Let's say you have a class MyClass - you have MyClass.m and MyClass.h and now you can also have MyClass_private.h. Xcode will recognize this and include it in the list of "Counterparts" in the Assistant Editor.

//MyClass.m
#import "MyClass.h"
#import "MyClass_private.h"
暮光沉寂 2024-07-13 08:35:58

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

There's no way of getting around issue #2. That's just the way the C compiler (and hence the Objective-C compiler) work. If you use the XCode editor, the function popup should make it easy to navigate the @interface and @implementation blocks in the file.

放低过去 2024-07-13 08:35:58

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

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.

梨涡 2024-07-13 08:35:58

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

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

@implementation MyClass 
// .. public methods

# pragma mark private 
// ...

@end

As other people said defining private methods in the @implementation block is OK for most purposes.

On the topic of code organization - I like to keep them together under pragma mark private for easier navigation in Xcode

@implementation MyClass 
// .. public methods

# pragma mark private 
// ...

@end
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文