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

Why doesn't Objective-C support private methods?

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

有许多关键字应用于控制其范围的ivars成员,例如@private@public@protected。为什么不能对方法也这样做呢?这似乎是运行时应该能够支持的东西。有没有一个基本的哲学我错过了?这是故意的吗?


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

目标-C在方法调度时是纯动态的。特别是,每个方法调度都要经过与其他方法调度完全相同的动态方法解析点。在运行时,每个方法实现都有完全相同的暴露,由Objective-C运行时提供的所有用于方法和选择器的API在所有方法中工作相同。

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

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

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.

一个人是不可能的短路此检查的方法或跳过运行时?

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.

< /块引用>

然而,这样做会破坏语言的纯动态特性。每个方法调度不再会经历相同的调度机制。相反,您将处于这样一种情况:大多数方法的行为方式都是单一的,少数方法只是不同的。

这超出了运行时的范围,因为cocoa中有许多机制是建立在Objective-C的一致动态之上的。例如,键值编码和键值观察都必须进行非常大的修改以支持私有方法(最有可能是通过创建可利用的漏洞)或者私有方法将是inco马马虎虎的


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

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

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

  • AllPublic具有公共实例方法doSomething

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

  • 创建一个数组,其中包含AllPublicHasPrivate的任意数量的实例。

  • 您有以下循环:

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

    如果从AllPublic内运行该循环,运行时将不得不停止发送HasPrivate实例上的doSomething,但是如果该循环位于HasPrivate类内,则该循环将可用。


  • 到目前为止,这些答案都能很好地从哲学的角度回答这个问题,所以我要提出一个更加务实的理由:改变语言的语义会得到什么?它非常简单,可以有效地"隐藏"私有方法。举例来说,假设在头文件中声明了一个类,如下所示:

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

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

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

    当然,它与C++/Java私有方法不完全相同,但是它实际上足够接近,所以为什么要改变语言的语义,以及编译器、运行时等,来添加一个已经以可接受的方式仿真的特征?如其他答案所述,消息传递语义(以及它们对运行时反射的依赖)将使处理"私有"消息变得非常重要。


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

    别大惊小怪。


    是的,它可以通过使用编译器已经使用的用于处理C++的技术来完成运行,而不是影响运行时。

    它之所以没有完成,是因为还没有确定它将解决其他技术(如预混或抽芯)能够充分规避的编码问题空间中的一些相当大的困难。你需要更多的痛苦来克服根深蒂固的习惯。

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


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

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


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

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

    第二个答案,假设这是关于性能和内联的,是可能的,但是作为一个局部的C函数。如果您想要一个"private foo(NSString *arg方法),您可以执行void MyClass_foo(MyClass *self, NSString *arg)并将其称为c函数,就像MyClass_foo(self,arg)一样。语法是不同的,但是它与C++的私有方法的一种明智的性能特征相一致。

    虽然这回答了这个问题,但我应该指出,迄今为止,没有名字的类别是更常见的客观C方法。


    这是一个与Objtovi.C的运行环境有关的问题。C/C++编译成不可读取的机器代码,Objest-C仍然保留一些像字符串一样的方法可读属性。这使Objective-C具有执行反射功能的能力。

    编辑:如果没有严格的私有方法,那么作为一种反射性语言,Objective-C更像是"Python式的",因为您信任使用您的代码的其他人,而不是限制他们可以调用的方法。使用像双下划线这样的命名约定是为了将您的代码隐藏在一个临时的客户机编码人员面前,但不会阻止编码人员做更严肃的工作。


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

    在某种程度上,就像smalltalk一样,objective-c是面向成年人的程序员的。我们重视了解原始开发人员假定接口应该是什么,并负责处理在需要更改实现时产生的后果。所以是的,这是哲学,而不是实践。


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

    在C++中,每个方法必须在类的声明中可见。不能有包含头文件的人看不到的方法。因此,如果您希望实现之外的代码不应该使用的方法,您没有选择,编译器必须给您一些工具,这样您就可以告诉它不能使用该方法,即"private"关键字。

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

    例如,您以前必须在头文件中声明的变量(不再声明),@private、@public和@protected都可用。