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

123

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

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

Роб Джонс
источник
15
Хороший вопрос, кстати.
bbum
5
К вашим правкам: да, принудительное исполнение было бы очень дорогим с очень небольшой выгодой. Принуждение во время компиляции было бы долгожданным дополнением к языку и вполне достижимо. Да, это можно было обойти во время выполнения, но это нормально. Программист не враг. Цель области - помочь защитить программиста от ошибок.
Роб Напье
1
Вы не можете «пропустить среду выполнения» - среда выполнения - это то, что отправляет сообщения.
Чак
«Там не было бы никакой возможности для частного метода к короткому замыканию этой проверки» вы имели в виду «обойти»? ;-)
Константино Царухас

Ответы:

103

Ответ ... ну ... простой. Фактически, простота и последовательность.

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

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

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

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

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

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

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

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

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

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

bbum
источник
49
+1 Objective-C - это (в некотором смысле) язык «большого мальчика», от которого ожидается, что программисты будут демонстрировать определенную дисциплину, вместо того, чтобы компилятор / среда выполнения на каждом шагу хлопали по руке. Я нахожу это освежающим. Для меня ограничение видимости метода во время компиляции / компоновки является достаточным и предпочтительным.
Куинн Тейлор
11
Более питон является "obj-c-ic" :). Гвидо активно поддерживал Python в системах NeXT, включая создание 1-й версии PyObjC. Таким образом, ObjC в некоторой степени повлиял на Python .
bbum
3
+1 за интересную историческую сказку о доброжелательном пожизненном диктаторе и его пересечениях с мифической системой NeXT.
pokstad
5
Спасибо. Python, Java и Objective-C имеют объектные модели времени выполнения, которые чертовски похожи на SmallTalk. Java произошла непосредственно от Objective-C. Python провел параллельный курс, более чем вдохновляющий, но Гвидо, безусловно, внимательно изучил NeXTSTEP.
bbum
1
За исключением того, что я не доверяю его лицензии и определенно предпочитаю clang вместо gcc. Но это наверняка хороший первый шаг.
Cthutu
18

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

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

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

  1. У класса AllPublicесть общедоступный метод экземпляраdoSomething

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

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

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

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

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

dreamlax
источник
1
Я согласен, что это будет связано с расходами. Возможно, это не то, что вам нужно на портативном устройстве. Сможете ли вы поддержать грандиозную квалификацию иначе? Будет ли иметь значение стоимость настольной системы?
Роб Джонс
1
Хотя нет, но, возможно, вы правы. Objective-C имеет отправку сообщений во время выполнения по сравнению с вызовом метода времени компиляции C ++, так что вы можете говорить о проверке во время компиляции по сравнению с проверкой во время выполнения.
Ремус Русану
Кажется действительно странным, что основная причина отсутствия поддержки частных методов связана с производительностью. Я думаю, что Apple просто не считала частные методы очень необходимыми, независимо от того, снизилась производительность или нет. В противном случае они должны были выбрать другой язык для своей многомиллиардной компании.
pokstad
1
Добавление частных методов не только усложнит реализацию среды выполнения, но и усложнит семантику языка. Контроль доступа - это простая концепция для статических языков, но она плохо подходит для динамической диспетчеризации, которая сопряжена с большими концептуальными накладными расходами и приведет ко многим, многим «почему это не работает?» вопросы.
Jens Ayton
7
Что на самом деле купили бы вам частные методы? В конце концов, Objective-C - это язык, основанный на C. Таким образом, если у кого-то есть код, запущенный в вашем процессе, он может легко настроить ваш процесс независимо от того, насколько частным или запутанным может быть любой API.
bbum
13

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

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

mipadi
источник
1
Проблема с этим подходом заключается в том, что кто-то может переопределить частные методы (даже неосознанно), когда они создают подкласс вашего класса. Я полагаю, по сути, вам нужно выбрать имена, которые вряд ли будут заменены.
dreamlax
3
Для меня это похоже на взлом поверх взлома.
Роб Джонс
1
@Mike: Apple заявила, что имена методов с ведущими символами подчеркивания явно зарезервированы только для Apple: developer.apple.com/mac/library/documentation/Cocoa/Conceptual/… . Рекомендуемая альтернатива - использовать в качестве префикса две заглавные буквы и затем подчеркивание.
dreamlax
8
Кроме того, укажите свой SSN после метода, чтобы другие программисты знали, кто его написал. = D Пространства имён, они нужны !!!
pokstad
2
Если вас беспокоит, что методы, которые вы определяете, будут отменены, вы не приняли должным образом присущие уровни многословия, которые поощряет Objective-C ...
Кендалл Хельмштеттер Гельнер
7

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

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

Cromulent
источник
Должны ли они быть определены вне раздела @implementation?
Роб Джонс,
@Rob - нет, они могут (и, скорее всего, должны быть) определены внутри вашей реализации классов.
Cromulent,
6

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

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

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

Huperniketes
источник
2
Изменение имени времени компиляции намного сложнее, чем можно было бы предположить, поскольку вам нужно изменить все селекторы методов, включая те, которые могут появляться в файлах XIB, и те, которые могут быть переданы таким вещам, как NSSelectorFromString (), а также KeyValueCoding и другим ...
bbum
1
Да, было бы более сложно, если бы вы хотели обеспечить поддержку частных методов в цепочке инструментов. Таблица символов компилятора должна быть сохранена и доступна через некоторый api инструментальной цепочки. Это будет не первый случай, когда Apple будет постепенно предоставлять разработчикам возможности, которые были достаточно важны, насколько позволяли время и стабильность. Однако что касается частных методов: сомнительно, что это достаточная победа, чтобы предпринять усилия. Еще более подозрительно, что они должны быть доступны за пределами самого класса с помощью этих других механизмов.
Huperniketes
1
Как принудительное применение только во время компиляции + изменение имен остановит кого-то, идущего по списку методов класса и обнаруживающего его там?
dreamlax
Это не так. Мой ответ касается только вопроса, поставленного ОП.
Huperniketes
Я подумал о том, чтобы попытаться добавить частные методы в Clang, одна из причин, по которой я думал, что частные методы были бы хороши, заключалась в том, что их можно было вызывать напрямую, как простые функции c, и тогда у вас могли бы происходить все обычные оптимизации функций C. Я никогда не беспокоился из-за времени, и вы уже можете сделать это, просто используя c.
Натан Дэй
4

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

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

цыпленок
источник
1

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

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

pokstad
источник
И если потребители вашей библиотеки могут отражать ваши частные методы, разве они не могут их вызвать?
Джаред Апдайк,
Если они знают, где искать, разве не так люди использовали недокументированные библиотеки на iPhone? Проблема с iPhone заключается в том, что вы рискуете не принять ваше приложение для использования какого-либо частного API.
pokstad
Если они обращаются к частным методам через отражение, они получают то, что заслуживают. Любые ошибки - это не ваша вина.
gnasher729
1

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

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

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

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

AlBlue
источник
0

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

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

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

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

gnasher729
источник
0

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

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

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