it-swarm.com.ru

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

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

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

122
Rob Jones

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

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

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

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

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

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

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

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

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

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

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

102
bbum

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

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

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

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

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

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

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

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

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

18
dreamlax

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

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

13
mipadi

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

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

7
Cromulent

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

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

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

6
Huperniketes

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

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

4
Chuck

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

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

1
pokstad

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

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

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

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

1
AlBlue

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

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

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

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

0
gnasher729

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

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

0
Stephan Eggermont