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

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

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

Ответ 1

Ответ... ну... простой. Простота и согласованность.

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

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

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

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

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

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

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

     

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

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

Это выходит за пределы среды выполнения, так как в Cocoa существует множество механизмов, построенных на основе последовательного динамизма Objective-C. Например, как Key Value Coding, так и Key Value Observation будут либо очень сильно модифицированы для поддержки частных методов - скорее всего, путем создания доступных лазеек - или частных методов будет несовместимо.

Ответ 2

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

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

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

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

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

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

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

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

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

Ответ 3

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

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

Если вам нужны методы "private", вы также можете поместить это в файл реализации:

@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

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

Ответ 4

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

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

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

Ответ 5

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

Нет суеты вообще.

Ответ 6

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

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

Ответ 7

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

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

Ответ 8

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

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

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

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

Ответ 9

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

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

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

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

Ответ 10

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

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