Чому Objective-C не підтримує приватні методи?


123

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

Є кілька ключових слова стосовно ivars(учасникам) , які контролюють сферу їх застосування, наприклад @private, @public, @protected. Чому цього не можна зробити і для методів? Здається, щось, що час виконання повинен бути здатний підтримувати. Чи є основна філософія, якої я бракую? Це навмисно?


15
Добре запитання, btw.
bbum

5
До ваших змін: так, виконання часу виконання буде дуже дорогим за дуже невелику користь. Час виконання компіляції було б бажаним доповненням до мови і є цілком досяжним. Так, його можна обійти під час виконання, але це добре. Програміст не ворог. Мета сфери - допомогти захистити програміста від помилок.
Роб Нап'єр

1
Ви не можете "пропустити час виконання" - це час виконання повідомлення.
Чак

"Не було б способу, щоб приватний метод коротко замикав цю перевірку" Ви мали на увазі "обійти"? ;-)
Константіно Царухас

Відповіді:


103

Відповідь ... ну ... проста. Простота і послідовність насправді.

Objective-C є чисто динамічним на момент відправки методу. Зокрема, кожна відправка методу проходить через таку ж точку динамічної роздільної здатності методу, що і кожна інша методика відправки. Під час виконання кожна реалізація методу має точно однакове опромінення, і всі API, передбачені програмою Objective-C, які працюють з методами та селекторами, працюють однаково для всіх методів.

Оскільки багато хто відповів (і тут, і в інших питаннях), підтримуються приватні методи компіляції за часом; якщо клас не оголошує метод у своєму загальнодоступному інтерфейсі, він може також не існувати, що стосується вашого коду. Іншими словами, ви можете досягти всіх різних комбінацій видимості, бажаних під час компіляції, організувавши свій проект належним чином.

Дублювати таку функціональність під час виконання мало користі. Це додало б величезної складності та витратних витрат. І навіть при всій цій складності це все-таки не завадить усім, крім самого випадкового розробника, виконувати ваші нібито "приватні" методи.

EDIT: Одне з припущень, які я помітив, - це те, що приватні повідомлення повинні пройти через час виконання, що призведе до потенційно великих витрат. Це абсолютно правда?

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

Не було б способу, щоб приватний метод коротко замикав цю перевірку або пропускав час виконання?

Він не міг пропустити час виконання, але час виконання не обов'язково повинен був перевіряти приватні методи.

Це означає, що немає жодних причин, щоб сторона не могла навмисно закликати objc_msgSendPrivate()об'єкт поза межами реалізації цього об'єкта, і деякі речі (наприклад, KVO) повинні були це зробити. Фактично, це було б просто умовою і мало кращою на практиці, ніж префіксація селекторів приватних методів або не згадування про них у заголовку інтерфейсу.

Однак це може підірвати чисту динамічну природу мови. Більше не кожен метод відправки проходитиме через ідентичний механізм диспетчеризації. Натомість ви опинилися б у ситуації, коли більшість методів веде себе в один бік, а невелика жменька - просто по-іншому.

Це виходить за рамки часу виконання, оскільки в Какао існує багато механізмів, побудованих на основі послідовної динамічності об'єктива-С. Наприклад, і кодування ключових значень, і спостереження ключових значень повинні або бути дуже сильно модифіковані для підтримки приватних методів - швидше за все, шляхом створення експлуатованої лазівки - або приватні методи будуть несумісними.


49
+1 Objective-C - це (певною мірою) мова "великого хлопця", в якій програмісти, як очікується, проявлятимуть певну дисципліну, а не отримують удари компілятором / робочим часом на кожному кроці. Я вважаю це освіжаючим. Для мене обмеження видимості методу під час компіляції / зв’язування є достатнім та бажаним.
Квінн Тейлор

11
Більше python будучи "obj-c-ic" :). Guido виявився досить активним у підтримці Python на NeXT системах, включаючи створення 1-ї версії PyObjC. Таким чином, ObjC дещо впливав на пітон .
bbum

3
+1 за цікаву історичну казку про доброзичливого диктатора життя та його схрещування з міфічною системою NeXT.
pokstad

5
Дякую. У Python, Java та Objective-C є об'єктні моделі, які досить схожі [на SmallTalk]. Java була цілком безпосередньо похідна від Objective-C. Пайтон пройшов паралельний курс більше, ніж натхненник, але Гвідо, безумовно, уважно вивчав 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класу.


1
Я погоджуюсь, що витрати будуть пов'язані. Можливо, це не те, що потрібно на портативному пристрої. Чи можете ви підтримати величезного класифікатора інакше? Чи дійсно вартість має значення в настільній системі?
Роб Джонс

1
Не хоч у цьому, але ти можеш мати рацію. Objective-C має розсилку повідомлень про виконання часу та виклик методу часу компіляції C ++, тому ви б говорили про перевірку часу компіляції проти перевірки часу виконання.
Рем Русану

Дуже дивно здається, що головна причина непідтримки приватних методів пов’язана з ефективністю. Я думаю, що Apple просто не вважала, що приватні методи є дуже потрібними, незалежно від того, чи не вдалося досягти ефективності. Інакше вони повинні обрати іншу мову для своєї багатомільярдної компанії.
pokstad

1
Додавання приватних методів не просто ускладнить реалізацію часу виконання, а й ускладнить семантику мови. Контроль доступу - це просте поняття в статичних мовах, але це погана відповідність для динамічної диспетчеризації, яка могла б мати багато концептуальних витрат і призвести до багатьох, багатьох "чому це не працює?" питання.
Йенс Айтон

7
Що б насправді придбали приватні методи? Зрештою, "Objective-C" - це мова, заснована на С. Таким чином, якщо хтось має код, який працює у вашому процесі, він може легко p0wnz вашого процесу незалежно від того, наскільки приватним чи прихованим може бути будь-який 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, але він ефективно досить близький, тому навіщо змінювати семантику мови, а також компілятор, час виконання тощо, щоб додати функцію, яка вже імітується у прийнятній шлях? Як зазначається в інших відповідях, семантика передачі повідомлень - і залежність від їх відображення - зробить обробку "приватними" повідомленнями нетривіальними.


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 для статичного ключового слова, і тому вони можуть використовуватися лише методами цього класу.

Ніякої суєти взагалі.


Чи потрібно їх визначати поза розділом @implementation?
Роб Джонс

@Rob - ні, їх не можна (і швидше за все, слід) визначити всередині ваших класів.
Кромулент

6

Так, це можна зробити, не впливаючи на час виконання, використовуючи техніку, вже використану компілятором для обробки C ++: управління іменами.

Це не було зроблено, оскільки не було встановлено, що це дозволило б вирішити значні труднощі в просторі проблем кодування, які інші методи (наприклад, префіксація або підкреслення) здатні достатньо обійти. IOW, вам потрібно більше болю, щоб подолати вроджені звички.

Ви можете внести виправлення до clang або gcc, які додають приватні методи до синтаксису та генерують розроблені імена, які він один розпізнавав під час компіляції (і негайно забув). Тоді інші в спільноті «Об'єктив-С» змогли б визначити, чи дійсно це варто чи ні. Це, швидше за все, буде швидше, ніж намагатися переконати розробників.


2
Скомпілювати час керування іменами набагато складніше, ніж можна було б припустити, оскільки вам доведеться обробляти всі селектори методів, включаючи ті, які можуть з’являтися у файлах XIB, і ті, які можуть бути передані таким речам, як NSSelectorFromString (), а також KeyValueCoding & друзі ...
bbum

1
Так, було б більше залучено, якби ви хотіли надати підтримку приватних методів у інструментальній мережі. Таблиця символів компілятора повинна бути збережена та доступна через деякий api ланцюжок інструментів. Це було б не вперше, коли Apple довелося поступово розгортати можливості для розробників, які були достатньо важливими, оскільки дозволяли час та стабільність. Однак, що стосується приватних методів: сумнівно, чи є достатньою виграшею, щоб докласти зусиль. Ще більш підозрілим є те, що вони повинні бути доступні поза самим класом цими іншими механізмами.
Huperniketes

1
Як би примусове виконання лише часу компіляції + керування іменами зупиняло когось ходити по списку методів класу та знаходити його там?
dreamlax

Це не буде. Моя відповідь стосується лише питання, поставленого ОП.
Huperniketes

Я думав про те, щоб піти, намагаючись додати приватні методи до Кланг, одна з причин, на яку я вважав, що приватні методи будуть хорошими, - це те, що їх можна викликати безпосередньо як прості функції c, і тоді у вас можуть відбуватися всі звичні оптимізації функції С. Я ніколи не турбувався через час, і ви вже можете це зробити, просто використовуючи c.
День Натана

4

По суті, це стосується форми передачі повідомлень Objective-C у викликах методів. Будь-яке повідомлення може бути надіслане будь-якому об’єкту, і об’єкт вибирає, як відповісти на повідомлення. Зазвичай він реагує, виконуючи метод, названий після повідомлення, але він також може відповісти рядом інших способів. Це не робить приватні методи абсолютно неможливими - Рубі робить це з подібною системою передачі повідомлень - але це робить їх дещо незручними.

Навіть реалізація приватних методів Рубі трохи збиває з пантелику людей через дивацтво (ви можете надіслати об’єкт будь-яке повідомлення, яке вам подобається, крім тих, що є у цьому списку !). По суті, Ruby змушує це працювати, забороняючи викликати приватні методи з явним приймачем. У Objective-C це вимагатиме ще більшої роботи, оскільки у Objective-C немає такої можливості.


1

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

EDIT: Будучи відображаючою мовою без суворих приватних методів, це робить «Objective-C» більш «пітонічним» тим, що ви довіряєте іншим людям, які використовують ваш код, а не обмежують, які методи вони можуть викликати. Використання таких імен, як подвійні підкреслення, має на меті приховати код від випадкового клієнтського кодера, але не зупинить кодери, які потребують більш серйозної роботи.


І якщо споживачі вашої лібби могли відобразити ваші приватні методи, чи не могли б вони їх також назвати?
Джаред Updike

Якщо вони знають, де їх шукати, чи не так люди користуються незадокументованими бібліотеками на iPhone? Проблема з iPhone полягає в тому, що ви ризикуєте не прийняти вашу програму для використання приватного API.
pokstad

Якщо вони отримують доступ до приватних методів через рефлексію, вони отримують те, що заслуговують. Будь-які помилки - не ваша вина.
gnasher729

1

Є дві відповіді залежно від інтерпретації питання.

Перший - це приховування реалізації методу від інтерфейсу. Застосовується, як правило, із категорією без імені (наприклад,@interface Foo() ). Це дозволяє об'єкту надсилати ці повідомлення, але не інші, хоча все-таки може бути переопрацьовано випадково (або іншим чином).

Друга відповідь, за припущенням, що це стосується продуктивності та вбудовування, стає можливою, але натомість як локальна функція C. Якщо ви хотіли «приватний Foo ( NSString *arg)», ви б void MyClass_foo(MyClass *self, NSString *arg)і назвати його як функцію C як MyClass_foo(self,arg). Синтаксис різний, але він відповідає нормальним характеристикам ефективності приватних методів C ++.

Хоча це відповідає на запитання, я мушу зазначити, що категорія без імені є набагато більш поширеним способом цього об'єктивного С.


0

Objective-C не підтримує приватні методи, оскільки вони їм не потрібні.

У C ++ кожен метод повинен бути видимим у декларації класу. У вас не може бути методів, які хтось із файлів заголовка не може бачити. Отже, якщо ви хочете, щоб методи, які не використовуються кодом поза вашою реалізацією, у вас немає вибору, компілятор повинен дати вам якийсь інструмент, щоб ви могли сказати йому, що метод не повинен використовуватися, тобто "приватне" ключове слово.

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

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


0

Тут відсутня відповідь: адже приватні методи - це погана ідея з точки зору еволюції. Можливо, здасться хорошою ідеєю зробити метод приватним при його написанні, але це форма раннього зв’язування. Контекст може змінитися, і пізніший користувач може захотіти використовувати іншу реалізацію. Трохи провокативно: "Agile розробники не використовують приватні методи"

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


Я проголосував за це, тому що він не заслуговує на голосування. Існує, як каже Стефан, аргумент, що "приватні" методи не потрібні, і це аналогічно аргументам щодо динамічного проти статичного введення тексту, в тому, що незалежно від вашого бажаного висновку, обидві сторони мають суть.
alastair
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.