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


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

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

10 122

10 ответов:

ответ... что ж... простой. Простота и последовательность, на самом деле.

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

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

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

EDIT: одно из предположений, которые у меня есть заметил, что личные сообщения будут придется пройти через время выполнения в результате чего потенциально большой накладные расходы. Это абсолютно верно?

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@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, но это достаточно близко, поэтому зачем изменять семантику языка, а также компилятор, среду выполнения и т. д., чтобы добавить функцию, которая уже эмулируется приемлемым образом? Как отмечалось в других ответах, семантика передачи сообщений и их зависимость от отражения во время выполнения сделают обработку "частных" сообщений нетривиальной.

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

никакой суеты вообще.

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

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

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

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

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

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

EDIT: являясь рефлексивной язык без строгой частных методов делает цель-c подробнее "весть" в том, что Вы доверяете другим людям, что используйте свой код, а не ограничивать, какие методы они могут вызывать. Использование соглашений об именах, таких как двойные подчеркивания, предназначено для скрытия вашего кода от случайного клиентского кодера, но не остановит кодеров, нуждающихся в более серьезной работе.

есть два ответа в зависимости от интерпретации вопроса.

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

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

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

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

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

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

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

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

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