为什么 Objective-C 不支持私有方法?

发布于 2024-08-19 16:55:24 字数 289 浏览 10 评论 0原文

我在Objective-C中看到了许多声明半私有方法的策略,但似乎没有一种方法可以创建真正的私有方法。我接受这一点。但是,为什么会这样呢?我的每一个解释本质上都是在说:“你做不到,但这是一个近似值。”

有许多关键字应用于ivars(成员)来控制其范围,例如@private@public@protected。为什么方法不能也这样做呢?这似乎是运行时应该能够支持的。我是否缺少一种潜在的哲学?这是故意的吗?

I've seen a number of strategies for declaring semi-private methods in Objective-C, but there does not seem to be a way to make a truly private method. I accept that. But, why is this so? Every explanation I've essentially says, "you can't do it, but here's a close approximation."

There are a number of keywords applied to ivars (members) that control their scope, e.g. @private, @public, @protected. Why can't this be done for methods as well? It seems like something the runtime should be able to support. Is there an underlying philosophy I'm missing? Is this deliberate?

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

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

发布评论

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

评论(10

秋千易 2024-08-26 16:55:24

答案是……嗯……很简单。事实上,简单性和一致性。

Objective-C 在方法分派时是纯粹动态的。特别是,每个方法分派都会经历与其他每个方法分派完全相同的动态方法解析点。在运行时,每个方法实现都具有完全相同的公开,并且 Objective-C 运行时提供的所有与方法和选择器一起使用的 API 在所有方法中都同样有效。

正如许多人已经回答的那样(在这里和其他问题中),支持编译时私有方法;如果一个类没有在其公开可用的接口中声明一个方法,那么就您的代码而言,该方法可能不存在。换句话说,您可以通过适当地组织项目来实现编译时所需的可见性的所有各种组合。

将相同的功能复制到运行时没有什么好处。这会增加大量的复杂性和开销。即使具有所有这些复杂性,它仍然不会阻止除了最随意的开发人员之外的所有人执行您所谓的“私有”方法。

编辑:我的假设之一
注意到私人消息会
必须经过运行时
从而产生潜在的大
开销。这绝对是真的吗?

<块引用>

是的,确实如此。没有理由认为类的实现者不想在实现中使用所有 Objective-C 功能集,这意味着必须进行动态分派。 但是,私有方法不能由 objc_msgSend() 的特殊变体分派并没有什么特别的原因,因为编译器会知道它们是私有的;即,这可以通过向 Class 结构添加私有方法表来实现。

私人是没有办法的
短路此检查的方法或
跳过运行时?

<块引用>

它无法跳过运行时,但运行时不一定必须对私有方法进行任何检查。

也就是说,第三方没有理由不能在该对象的实现之外以及某些事物(例如 KVO)故意对该对象调用 objc_msgSendPrivate()必须这样做。实际上,这只是一种约定,在实践中比给私有方法的选择器添加前缀或不在接口标头中提及它们好不了多少。

但这样做会破坏语言的纯粹动态性质。每个方法分派将不再经过相同的分派机制。相反,您将陷入这样一种情况:大多数方法都以一种方式运行,而少数方法只是不同。

这超出了运行时的范围,因为 Cocoa 中有许多机制构建在 Objective-C 的一致动态之上。例如,键值编码和键值观察要么必须进行大量修改才能支持私有方法(很可能是通过创建可利用的漏洞),要么私有方法将不兼容。

The answer is... well... simple. Simplicity and consistency, in fact.

Objective-C is purely dynamic at the moment of method dispatch. In particular, every method dispatch goes through the exact same dynamic method resolution point as every other method dispatch. At runtime, every method implementation has the exact same exposure and all of the APIs provided by the Objective-C runtime that work with methods and selectors work equally the same across all methods.

As many have answered (both here and in other questions), compile-time private methods are supported; if a class doesn't declare a method in its publicly available interface, then that method might as well not exist as far as your code is concerned. In other words, you can achieve all of the various combinations of visibility desired at compilation time by organizing your project appropriately.

There is little benefit to duplicating the same functionality into the runtime. It would add a tremendous amount of complexity and overhead. And even with all of that complexity, it still wouldn't prevent all but the most casual developer from executing your supposedly "private" methods.

EDIT: One of the assumptions I've
noticed is that private messages would
have to go through the runtime
resulting in a potentially large
overhead. Is this absolutely true?

Yes, it is. There's no reason to suppose that the implementor of a class would not want to use all of the Objective-C feature set in the implementation, and that means that dynamic dispatch must happen. However, there is no particular reason why private methods couldn't be dispatched by a special variant of objc_msgSend(), since the compiler would know that they were private; i.e. this could be achieved by adding a private-only method table to the Class structure.

There would be no way for a private
method to short-circuit this check or
skip the runtime?

It couldn't skip the runtime, but the runtime wouldn't necessarily have to do any checking for private methods.

That said, there's no reason that a third-party couldn't deliberately call objc_msgSendPrivate() on an object, outside of the implementation of that object, and some things (KVO, for example) would have to do that. In effect, it would just be a convention and little better in practice than prefixing private methods’ selectors or not mentioning them in the interface header.

To do so, though, would undermine the pure dynamic nature of the language. No longer would every method dispatch go through an identical dispatch mechanism. Instead, you would be left in a situation where most methods behave one way and a small handful are just different.

This extends beyond the runtime as there are many mechanisms in Cocoa built on top of the consistent dynamism of Objective-C. For example, both Key Value Coding and Key Value Observation would either have to be very heavily modified to support private methods — most likely by creating an exploitable loophole — or private methods would be incompatible.

云醉月微眠 2024-08-26 16:55:24

运行时可以支持它,但成本将是巨大的。发送的每个选择器都需要检查该类是私有的还是公共的,或者每个类需要管理两个单独的调度表。这与实例变量不同,因为这种级别的保护是在编译时完成的。

此外,运行时需要验证私有消息的发送者与接收者属于同一类。您还可以绕过私有方法;如果该类使用instanceMethodForSelector:,它可以将返回的IMP提供给任何其他类,以便它们直接调用私有方法。

私有方法无法绕过消息调度。考虑以下场景:

  1. AllPublic 有一个公共实例方法 doSomething

  2. 另一个类 HasPrivate 有一个私有实例方法,也称为 doSomething

  3. 您创建一个包含任意数量的 AllPublicHasPrivate 实例的数组

  4. 您有以下循环:

    for (id anObject in myArray)
        [一个对象做某事];
    

    如果您从 AllPublic 中运行该循环,则运行时必须阻止您在 HasPrivate 实例上发送 doSomething,但是此循环如果它位于 HasPrivate 类中,则可用。

The runtime could support it but the cost would be enormous. Every selector that is sent would need to be checked for whether it is private or public for that class, or each class would need to manage two separate dispatch tables. This isn't the same for instance variables because this level of protection is done at compile time.

Also, the runtime would need to verify that the sender of a private message is of the same class as the receiver. You could also bypass private methods; if the class used instanceMethodForSelector:, it could give the returned IMP to any other class for them to invoke the private method directly.

Private methods could not bypass the message dispatch. Consider the following scenario:

  1. A class AllPublic has a public instance method doSomething

  2. Another class HasPrivate has a private instance method also called doSomething

  3. You create an array containing any number of instances of both AllPublic and HasPrivate

  4. You have the following loop:

    for (id anObject in myArray)
        [anObject doSomething];
    

    If you ran that loop from within AllPublic, the runtime would have to stop you sending doSomething on the HasPrivate instances, however this loop would be usable if it was inside the HasPrivate class.

幸福不弃 2024-08-26 16:55:24

到目前为止发布的答案很好地从哲学角度回答了这个问题,所以我将提出一个更务实的原因:通过改变语言的语义会得到什么?它足够简单,可以有效地“隐藏”私有方法。举例来说,假设您在头文件中声明了一个类,如下所示:

@interface MyObject : NSObject {}
- (void) doSomething;
@end

如果您需要“私有”方法,您也可以将其放入实现文件中:

@interface MyObject (Private)
- (void) doSomeHelperThing;
@end

@implementation MyObject

- (void) doSomething
{
    // Do some stuff
    [self doSomeHelperThing];
    // Do some other stuff;
}

- (void) doSomeHelperThing
{
    // Do some helper stuff
}

@end

当然,这并不完全 em> 与 C++/Java 私有方法相同,但它实际上足够接近,那么为什么要改变语言的语义以及编译器、运行时等,以添加已经以可接受的方式模拟的功能呢?正如其他答案中所指出的,消息传递语义以及它们对运行时反射的依赖将使处理“私有”消息变得非常重要。

The answers posted thus far do a good job of answering the question from a philosophical perspective, so I'm going to posit a more pragmatic reason: what would be gained by changing the semantics of the language? It's simple enough to effectively "hide" private methods. By way of example, imagine you have a class declared in a header file, like so:

@interface MyObject : NSObject {}
- (void) doSomething;
@end

If you have a need for "private" methods, you can also put this in the implementation file:

@interface MyObject (Private)
- (void) doSomeHelperThing;
@end

@implementation MyObject

- (void) doSomething
{
    // Do some stuff
    [self doSomeHelperThing];
    // Do some other stuff;
}

- (void) doSomeHelperThing
{
    // Do some helper stuff
}

@end

Sure, it's not quite the same as C++/Java private methods, but it's effectively close enough, so why alter the semantics of the language, as well as the compiler, runtime, etc., to add a feature that's already emulated in an acceptable way? As noted in other answers, the message-passing semantics -- and their reliance on runtime reflection -- would make handling "private" messages non-trivial.

温折酒 2024-08-26 16:55:24

最简单的解决方案就是在 Objective-C 类中声明一些静态 C 函数。根据 static 关键字的 C 规则,它们仅具有文件范围,因此它们只能由该类中的方法使用。

完全不用大惊小怪。

The easiest solution is just to declare some static C functions in your Objective-C classes. These only have file scope as per the C rules for the static keyword and because of that they can only be used by methods in that class.

No fuss at all.

最偏执的依靠 2024-08-26 16:55:24

是的,通过利用编译器已经采用的用于处理 C++ 的技术:名称修改,可以在不影响运行时的情况下完成此操作。

它尚未完成,因为尚未确定它可以解决其他技术(例如,前缀或下划线)能够充分规避的编码问题空间中的一些相当大的困难。 IOW,你需要更多的痛苦来克服根深蒂固的习惯。

您可以向 clang 或 gcc 提供补丁,将私有方法添加到语法中,并生成它在编译期间单独识别的损坏名称(并立即忘记)。然后 Objective-C 社区中的其他人将能够确定它是否真的值得。这样可能比试图说服开发人员更快。

Yes, it can be done without affecting the runtime by utilizing a technique already employed by the compiler(s) for handling C++: name-mangling.

It hasn't been done because it hasn't been established that it would solve some considerable difficulty in the coding problem space that other techniques (e.g., prefixing or underscoring) are able to circumvent sufficiently. IOW, you need more pain to overcome ingrained habits.

You could contribute patches to clang or gcc that add private methods to the syntax and generated mangled names that it alone recognized during compilation (and promptly forgot). Then others in the Objective-C community would be able to determine whether it was actually worthwhile or not. It's likely to be faster that way than trying to convince the developers.

两仪 2024-08-26 16:55:24

本质上,它与 Objective-C 的方法调用的消息传递形式有关。任何消息都可以发送到任何对象,由对象选择如何响应消息。通常,它会通过执行以消息命名的方法进行响应,但它也可以通过许多其他方式进行响应。这并不意味着私有方法完全不可能——Ruby 使用类似的消息传递系统做到了这一点——但它确实让它们有些尴尬。

甚至 Ruby 的私有方法的实现也因为奇怪而让人们感到有点困惑(您可以向对象发送任何您喜欢的消息,除了这个列表中的消息!)。本质上,Ruby 通过禁止使用显式接收者调用私有方法来实现这一点。在 Objective-C 中,这需要更多的工作,因为 Objective-C 没有这个选项。

Essentially, it has to do with Objective-C's message-passing form of method calls. Any message can be sent to any object, and the object chooses how to respond to the message. Normally it will respond by executing the method named after the message, but it could respond in a number of other ways too. This doesn't make private methods completely impossible — Ruby does it with a similar message-passing system — but it does make them somewhat awkward.

Even Ruby's implementation of private methods is a bit confusing to people because of the strangeness (you can send the object any message you like, except for the ones on this list!). Essentially, Ruby makes it work by forbidding private methods to be called with an explicit receiver. In Objective-C it would require even more work since Objective-C doesn't have that option.

晚雾 2024-08-26 16:55:24

这是 Objective-C 运行环境的问题。 而 C/C++ 编译成不可读的机器代码, Objective-C 仍然保留了一些人类可读的属性,例如方法名称作为字符串。这使得 Objective-C 能够执行反射功能。

编辑: 作为一种没有严格私有方法的反射语言,Objective-C 更加“Pythonic”,因为您信任其他使用您代码的人,而不是限制他们可以调用哪些方法。使用双下划线等命名约定的目的是向临时客户端编码人员隐藏您的代码,但不会阻止编码人员需要做更认真的工作。

It's an issue with the runtime environment of Objective-C. While C/C++ compiles down into unreadable machine code, Objective-C still maintains some human-readable attributes like method names as strings. This gives Objective-C the ability to perform reflective features.

EDIT: Being a reflective language without strict private methods makes Objective-C more "pythonic" in that you trust other people that use your code rather than restrict what methods they can call. Using naming conventions like double underscores is meant to hide your code from a casual client coder, but won't stop coders needing to do more serious work.

错爱 2024-08-26 16:55:24

根据问题的解释,有两个答案。

第一种方法是从接口中隐藏方法实现。这通常与没有名称的类别一起使用(例如@interface Foo())。这允许对象发送这些消息,但不能发送其他消息 - 尽管仍然可能会意外(或以其他方式)覆盖。

第二个答案,假设这与性能和内联有关,是可能的,但作为本地 C 函数。如果您想要一个“private foo(NSString *arg)”方法,您可以执行 void MyClass_foo(MyClass *self, NSString *arg) 并将其作为 C 函数调用就像MyClass_foo(self,arg)。语法有所不同,但它具有与 C++ 私有方法相同的性能特征。

虽然这回答了问题,但我应该指出,无名类别是迄今为止更常见的 Objective-C 方法。

There are two answers depending on the interpretation of the question.

The first is by hiding the method implementation from the interface. This is used, typically with a category with no name (e.g. @interface Foo()). This permits the object to send those messages but not others - though one might still override accidentally (or otherwise).

The second answer, on the assumption that this is about performance and inlining, is made possible but as a local C function instead. If you wanted a ‘private foo(NSString *arg)‘ method, you would do void MyClass_foo(MyClass *self, NSString *arg) and call it as a C function like MyClass_foo(self,arg). The syntax is different, but it acts with the sane kind of performance characteristics of C++'s private methods.

Although this answers the question, I should point out that the no-name category is by far the more common Objective-C way of doing this.

胡大本事 2024-08-26 16:55:24

Objective-C 不支持私有方法,因为它不需要它们。

在 C++ 中,每个方法在类的声明中都必须可见。您不能拥有包括头文件在内的其他人看不到的方法。因此,如果您想要实现之外的代码不应该使用的方法,您别无选择,编译器必须为您提供一些工具,以便您可以告诉它不能使用该方法,即“private”关键字。

在 Objective-C 中,您可以拥有头文件中没有的方法。因此,通过不将该方法添加到头文件中,您可以很容易地实现相同的目的。不需要私有方法。 Objective-C 还有一个优点,即您不需要因为更改私有方法而重新编译类的每个用户。

对于实例变量,您过去必须在头文件中声明(不再是了),可以使用 @private、@public 和 @protected。

Objective-C doesn't support private methods because it doesn't need them.

In C++, every method must be visible in the declaration of the class. You can't have methods that someone including the header file cannot see. So if you want methods that code outside your implementation shouldn't use, you have no choice, the compiler must give you some tool so you can tell it that the method must not be used, that is the "private" keyword.

In Objective-C, you can have methods that are not in the header file. So you achieve the same purpose very easily by not adding the method to the header file. There's no need for private methods. Objective-C also has the advantage that you don't need to recompile every user of a class because you changed private methods.

For instance variables, that you used to have to declare in the header file (not anymore), @private, @public and @protected are available.

提笔书几行 2024-08-26 16:55:24

这里缺少的答案是:因为从可进化性的角度来看,私有方法是一个坏主意。在编写方法时将其设为私有似乎是个好主意,但它是早期绑定的一种形式。上下文可能会发生变化,并且以后的用户可能想要使用不同的实现。有点挑衅:“敏捷开发人员不使用私有方法”

在某种程度上,就像 Smalltalk 一样,Objective-C 适合成年程序员。我们重视了解原始开发人员假设的接口应该是什么,并在需要更改实现时承担处理后果的责任。所以是的,这是哲学,而不是实施。

A missing answer here is: because private methods are a bad idea from an evolvability point of view. It might seem a good idea to make a method private when writing it, but it is a form of early binding. The context might change, and a later user might want to use a different implementation. A bit provocative: "Agile developers don't use private methods"

In a way, just like Smalltalk, Objective-C is for grown-up programmers. We value knowing what the original developer assumed the interface should be, and take the responsibility to deal with the consequences if we need to change implementation. So yes, it is philosophy, not implementation.

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