Почему Objective-C не поддерживает частные методы? - PullRequest
122 голосов
/ 29 января 2010

Я видел несколько стратегий для объявления полуприватных методов в Objective-C , но, похоже, нет способа сделать действительно приватный метод. Я принимаю это. Но почему это так? Каждое объяснение, которое я, по сути, говорю: «Вы не можете сделать это, но вот близкое приближение».

Существует ряд ключевых слов, примененных к ivars (участникам), которые управляют их областью действия, например, @private, @public, @protected. Почему это нельзя сделать и для методов? Кажется, что-то, что среда выполнения должна поддерживать. Есть ли основополагающая философия, по которой я скучаю? Это умышленно?

Ответы [ 10 ]

102 голосов
/ 29 января 2010

Ответ ... ну ... просто. Простота и последовательность, на самом деле.

Objective-C является чисто динамическим в момент отправки метода. В частности, каждая отправка метода проходит через ту же точку разрешения динамического метода, что и любая другая отправка метода. Во время выполнения каждая реализация метода имеет одинаковую экспозицию, и все API, предоставляемые средой выполнения Objective C, которые работают с методами и селекторами, одинаково работают во всех методах.

Поскольку многие ответили (и здесь, и в других вопросах), поддерживаются частные методы времени компиляции; если класс не объявляет метод в своем общедоступном интерфейсе, то этот метод может также не существовать в том, что касается вашего кода. Другими словами, вы можете достичь всех различных комбинаций видимости, требуемых во время компиляции, правильно организовав свой проект.

Нет смысла дублировать ту же функциональность во время выполнения. Это добавило бы огромное количество сложности и накладных расходов. И даже при всей этой сложности это все равно не помешало бы всем, кроме самого случайного разработчика, использовать ваши предположительно «частные» методы.

РЕДАКТИРОВАТЬ: одно из предположений, которые я заметил, что личные сообщения должен пройти через время выполнения в результате чего потенциально большой накладные расходы. Это абсолютно верно?

Да, это так. Нет оснований предполагать, что разработчик класса не захочет использовать весь набор функций Objective-C в реализации, а это означает, что должна происходить динамическая диспетчеризация. Однако , нет особой причины, по которой частные методы не могли бы быть отправлены специальным вариантом objc_msgSend(), так как компилятор знал бы, что они были частными; это может быть достигнуто путем добавления таблицы методов только для частного использования в структуру Class.

Там не будет никакого способа для частного метод короткого замыкания этой проверки или пропустить время выполнения?

Невозможно пропустить среду выполнения, но среда выполнения не будет обязательно выполнять какую-либо проверку для частных методов.

Тем не менее, нет причины, по которой сторонняя сторона не могла бы намеренно вызывать objc_msgSendPrivate() на объекте вне реализации этого объекта, и некоторые вещи (например, KVO) должны были бы это сделать. По сути, это будет просто соглашение и немного лучше на практике, чем префикс селекторов частных методов или их отсутствие в заголовке интерфейса.

Однако сделать это подорвало бы чисто динамическую природу языка. Больше не будет отправка каждого метода через идентичный механизм отправки. Вместо этого вы окажетесь в ситуации, когда большинство методов ведут себя одинаково, а небольшая кучка просто отличается.

Это выходит за рамки времени выполнения, поскольку в Какао существует множество механизмов, построенных на основе согласованного динамизма Objective-C. Например, и кодирование значения ключа, и наблюдение значения ключа должны были бы быть либо сильно изменены, чтобы поддерживать частные методы - скорее всего, путем создания уязвимости, либо частные методы были бы несовместимы.

18 голосов
/ 29 января 2010

Время выполнения может поддержать это, но стоимость будет огромной. Каждый отправленный селектор должен быть проверен на то, является ли он закрытым или общедоступным для этого класса, или каждый класс должен управлять двумя отдельными таблицами отправки. Это не то же самое для переменных экземпляра, потому что этот уровень защиты выполняется во время компиляции.

Кроме того, среда выполнения должна проверить, что отправитель личного сообщения того же класса, что и получатель. Вы также можете обойти частные методы; если класс использует instanceMethodForSelector:, он может передать возвращаемый IMP любому другому классу, чтобы они могли напрямую вызывать закрытый метод.

Частные методы не смогли обойти отправку сообщения. Рассмотрим следующий сценарий:

  1. Класс AllPublic имеет метод открытого экземпляра doSomething

  2. Другой класс HasPrivate имеет метод частного экземпляра, также называемый doSomething

  3. Вы создаете массив, содержащий любое количество экземпляров как AllPublic, так и HasPrivate

  4. У вас есть следующий цикл:

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

    Если вы запустите этот цикл из AllPublic, среда выполнения будет вынуждена прекратить отправку doSomething в экземплярах HasPrivate, однако этот цикл можно будет использовать, если он находится внутри класса HasPrivate. *

13 голосов
/ 29 января 2010

Ответы, опубликованные до сих пор, дают хорошую возможность ответить на вопрос с философской точки зрения, поэтому я собираюсь сформулировать более прагматичную причину: что можно было бы получить, изменив семантику языка? Это достаточно просто, чтобы эффективно «скрыть» приватные методы. Например, представьте, что у вас есть класс, объявленный в заголовочном файле, например:

@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

Конечно, это не совсем то же самое, что частные методы C ++ / Java, но оно достаточно близко, поэтому зачем менять семантику языка, а также компилятор, среду выполнения и т. Д. добавить функцию, которая уже эмулируется приемлемым способом? Как отмечалось в других ответах, семантика передачи сообщений - и их зависимость от отражения во время выполнения - сделают обработку «частных» сообщений нетривиальной.

7 голосов
/ 04 февраля 2010

Самое простое решение - просто объявить некоторые статические функции C в ваших классах Objective-C. Они имеют только файловую область в соответствии с правилами C для статического ключевого слова и поэтому могут использоваться только методами в этом классе.

Никакой суеты.

6 голосов
/ 29 января 2010

Да, это можно сделать, не затрагивая среду выполнения, используя технику, уже использованную компилятором (ами) для обработки C ++: изменение имени.

Это не было сделано, потому что не было установлено, что это решило бы некоторую значительную трудность в пространстве проблем кодирования, которую другие методы (например, префикс или подчеркивание) способны обойти в достаточной степени. Тебе нужно больше боли, чтобы преодолеть укоренившиеся привычки.

Вы можете добавить патчи в clang или gcc, которые добавляют частные методы к синтаксису и генерируют искаженные имена, которые он один распознал во время компиляции (и быстро забыл). Тогда другие члены сообщества Objective-C смогут определить, действительно ли это того стоило или нет. Вероятно, так будет быстрее, чем пытаться убедить разработчиков.

4 голосов
/ 29 января 2010

По сути, это связано с формой вызовов методов в Objective-C. Любое сообщение может быть отправлено любому объекту, и объект выбирает, как ответить на сообщение. Обычно он отвечает, выполняя метод, названный в честь сообщения, но он также может отвечать и другими способами. Это не делает частные методы совершенно невозможными - Ruby делает это с аналогичной системой передачи сообщений - но делает их несколько неловкими.

Даже реализация частных методов в Ruby несколько смущает людей из-за странности (вы можете отправлять объекту любое сообщение, которое вам нравится, , за исключением сообщений в этом списке !) По сути, Ruby заставляет его работать, запрещая частным методам вызываться с явным получателем. В Objective-C это потребовало бы еще больше работы, поскольку Objective-C не имеет этой опции.

1 голос
/ 29 января 2010

Есть два ответа в зависимости от толкования вопроса.

Первый - это скрыть реализацию метода от интерфейса. Это используется, как правило, с категорией без имени (например, @interface Foo()). Это позволяет объекту отправлять эти сообщения, но не другие - хотя можно все равно случайно (или иным образом) переопределить.

Второй ответ, при условии, что речь идет о производительности и встраивании, возможен, но в качестве локальной функции Си. Если вам нужен метод ‘private foo (NSString *arg) do, вы должны выполнить void MyClass_foo(MyClass *self, NSString *arg) и вызвать его как функцию C, например MyClass_foo(self,arg). Синтаксис другой, но он действует с нормальными характеристиками производительности частных методов C ++.

Хотя это и отвечает на вопрос, я должен отметить, что категория без имен - гораздо более распространенный способ сделать это с помощью Objective-C.

1 голос
/ 29 января 2010

Это проблема со средой выполнения Objective-C. В то время как C / C ++ компилируется в нечитаемый машинный код, Objective-C по-прежнему поддерживает некоторые удобочитаемые атрибуты, такие как имена методов, в виде строк . Это дает Objective-C возможность выполнять отражающие функции.

РЕДАКТИРОВАТЬ: Быть рефлексивным языком без строгих частных методов делает Objective-C более "питоническим" в том смысле, что вы доверяете другим людям, которые используют ваш код, а не ограничивают методы, которые они могут вызывать. Использование соглашений об именах, таких как двойное подчеркивание, предназначено для того, чтобы скрыть ваш код от обычного клиентского кодера, но не остановит кодеров, которым требуется более серьезная работа.

0 голосов
/ 19 июля 2014

Здесь отсутствует ответ: частные методы - плохая идея с точки зрения эволюционируемости. Может показаться хорошей идеей сделать метод закрытым при его написании, но это форма раннего связывания. Контекст может измениться, и более поздний пользователь может захотеть использовать другую реализацию. Немного провокационно: «Гибкие разработчики не используют частные методы»

В некотором смысле, как и Smalltalk, Objective-C предназначен для взрослых программистов. Мы ценим знание того, каким первоначальный разработчик предполагал, что интерфейс должен быть, и берем на себя ответственность за последствия, если нам нужно изменить реализацию. Так что да, это философия, а не реализация.

0 голосов
/ 12 апреля 2014

Objective-C не поддерживает частные методы, потому что они им не нужны.

В C ++ каждый метод должен быть виден в объявлении класса. У вас не может быть методов, которые кто-то, включая файл заголовка, не может увидеть. Поэтому, если вы хотите, чтобы методы, которые не использовались в коде вне вашей реализации, у вас нет выбора, компилятор должен предоставить вам какой-то инструмент, чтобы вы могли сказать ему, что метод не должен использоваться, то есть ключевое слово «private».

В Objective-C вы можете иметь методы, которых нет в заголовочном файле. Таким образом, вы очень легко достигаете той же цели, не добавляя метод в заголовочный файл. Там нет необходимости в частных методах. Objective-C также имеет то преимущество, что вам не нужно перекомпилировать каждого пользователя класса, потому что вы изменили приватные методы.

Для переменных экземпляра, которые вы должны были объявить в заголовочном файле (больше нет), доступны @private, @public и @protected.

...