Мне нужно скрыть (сделать личным) -init
метод моего класса в Objective-C.
Как я могу это сделать?
objective-c
Лайош
источник
источник
NS_UNAVAILABLE
. Как правило, я призываю вас использовать этот подход. Рассмотрит ли ФП пересмотр принятого ответа? Другие ответы здесь предоставляют много полезных деталей, но не являются предпочтительным методом достижения этого.NS_UNAVAILABLE
все еще позволяет вызывающей стороне вызыватьinit
косвенно черезnew
. Простое переопределениеinit
возвратаnil
будет обрабатывать оба случая.Ответы:
Objective-C, как и Smalltalk, не имеет понятия «частные» и «публичные» методы. Любое сообщение может быть отправлено любому объекту в любое время.
Что вы можете сделать, это бросить,
NSInternalInconsistencyException
если ваш-init
метод вызывается:Другая альтернатива - которая, вероятно, намного лучше на практике - это сделать
-init
что-то разумное для вашего класса, если это вообще возможно.Если вы пытаетесь сделать это, потому что вы пытаетесь «гарантировать» использование одноэлементного объекта, не беспокойтесь. В частности, не заморачиваться с «переопределение
+allocWithZone:
,-init
,-retain
,-release
» метод создания одиночек. Это практически всегда не нужно и просто добавляет усложнение без реального существенного преимущества.Вместо этого просто напишите свой код так, чтобы ваш
+sharedWhatever
метод был таким, каким вы получаете доступ к singleton, и запишите это как способ получить экземпляр singleton в своем заголовке. Это должно быть все, что вам нужно в подавляющем большинстве случаев.источник
alloc
и использоватьinit
свой код неправильно, потому что у него правильный класс, но неправильный экземпляр. Это суть принципа инкапсуляции в ОО. Вы скрываете в своих API то, что другим классам не нужно, или вам не нужен доступ. Вы не просто держите все публично и ожидаете, что люди будут следить за всем этим.NS_UNAVAILABLE
Это краткая версия недоступного атрибута. Впервые он появился в macOS 10.7 и iOS 5 . Это определяется в NSObjCRuntime.h как
#define NS_UNAVAILABLE UNAVAILABLE_ATTRIBUTE
.Существует версия, которая отключает метод только для клиентов Swift , но не для кода ObjC:
unavailable
Добавьте
unavailable
атрибут в заголовок, чтобы сгенерировать ошибку компилятора при любом вызове init.Если у вас нет причины, просто введите
__attribute__((unavailable))
или даже__unavailable
:doesNotRecognizeSelector:
Используйте,
doesNotRecognizeSelector:
чтобы вызвать NSInvalidArgumentException. «Система времени выполнения вызывает этот метод всякий раз, когда объект получает сообщение aSelector, на которое он не может ответить или переслать».NSAssert
Используйте,
NSAssert
чтобы бросить NSInternalInconsistencyException и показать сообщение:raise:format:
Используйте,
raise:format:
чтобы создать собственное исключение:[self release]
необходимо, потому что объект ужеalloc
съел. При использовании ARC его вызовет компилятор. В любом случае не о чем беспокоиться, когда вы собираетесь намеренно прекратить исполнение.objc_designated_initializer
Если вы намереваетесь отключить
init
принудительное использование назначенного инициализатора, для этого есть атрибут:Это генерирует предупреждение, если какой-либо другой метод инициализатора не вызывает
myOwnInit
внутренне. Подробности будут опубликованы в « Принятии Modern Objective-C» после следующего выпуска Xcode (я думаю).источник
init
. Так как, если этот метод недопустим, почему вы инициализируете объект? Кроме того, при возникновении исключения вы сможете указать какое-либо пользовательское сообщение, сообщающее разработчику правильныйinit*
метод, в то время как у вас нет такой опции в случаеdoesNotRecognizeSelector
.Apple начала использовать следующее в своих заголовочных файлах, чтобы отключить конструктор init:
Это правильно отображается как ошибка компилятора в XCode. В частности, это установлено в нескольких из их заголовочных файлов HealthKit (HKUnit является одним из них).
источник
Если вы говорите о методе по умолчанию -init, то вы не можете. Он унаследован от NSObject, и каждый класс ответит на него без предупреждений.
Вы можете создать новый метод, скажем -initMyClass, и поместить его в закрытую категорию, как предлагает Мэтт. Затем определите метод -init по умолчанию, чтобы вызвать исключение, если оно вызывается, или (лучше) вызвать ваш закрытый -initMyClass с некоторыми значениями по умолчанию.
Одна из основных причин, по которой люди, похоже, хотят скрыть init, - это одноэлементные объекты . Если это так, то вам не нужно скрывать -init, просто верните вместо этого объект-одиночка (или создайте его, если он еще не существует).
источник
Поместите это в заголовочный файл
источник
Вы можете объявить любой метод недоступным, используя
NS_UNAVAILABLE
.Таким образом, вы можете поместить эти строки ниже вашего @interface
Еще лучше определить макрос в заголовке вашего префикса
и
источник
Это зависит от того, что вы подразумеваете под «сделать частным». В Objective-C вызов метода для объекта лучше описать как отправку сообщения этому объекту. В языке нет ничего, что запрещало бы клиенту вызывать какой-либо метод на объекте; лучшее, что вы можете сделать, это не объявлять метод в заголовочном файле. Если клиент, тем не менее, вызывает «закрытый» метод с правильной подписью, он все равно будет выполняться во время выполнения.
Тем не менее, наиболее распространенный способ создания закрытого метода в Objective-C - это создание категории в файле реализации и объявление всех «скрытых» методов в нем. Помните, что это не помешает
init
запуску вызовов , но компилятор выдаст предупреждения, если кто-нибудь попытается это сделать.MyClass.m
На MacRumors.com есть достойная ветка на эту тему.
источник
проблема в том, что вы не можете сделать его «приватным / невидимым», потому что метод init отправляется на id (так как alloc возвращает id), а не на YourClass
Обратите внимание, что с точки зрения компилятора (средства проверки) идентификатор мог бы потенциально реагировать на все, что когда-либо вводилось (он не может проверить, что действительно входит в идентификатор во время выполнения), поэтому вы можете скрыть init только тогда, когда ничего не будет (publicly = in используйте метод init, чтобы компилятор знал, что id не может ответить на init, поскольку нигде нет init (в вашем источнике, всех библиотеках и т. д.)
поэтому вы не можете запретить пользователю проходить инициализацию и разбиваться компилятором ... но вы можете запретить пользователю получить реальный экземпляр, вызвав init
просто путем реализации init, который возвращает nil и имеет (приватный / невидимый) инициализатор, имя которого кто-то другой не получит (как initOnce, initWithSpecial ...)
Примечание: что кто-то может сделать это
и он фактически вернет новый экземпляр, но если initOnce нигде в нашем проекте не будет публично объявлен (в заголовке), он выдаст предупреждение (id может не отвечать ...), и в любом случае человеку, использующему это, потребуется точно знать, что настоящим инициализатором является initOnce
мы могли бы предотвратить это еще дальше, но в этом нет необходимости
источник
Я должен упомянуть, что размещение утверждений и поднятие исключений для методов скрытия в подклассе имеет неприятную ловушку для благонамеренного.
Я бы порекомендовал использовать,
__unavailable
как объяснил Яно для своего первого примера .Методы могут быть переопределены в подклассах. Это означает, что если метод в суперклассе использует метод, который просто вызывает исключение в подклассе, он, вероятно, не будет работать как задумано. Другими словами, вы просто сломали то, что раньше работало. Это верно и для методов инициализации. Вот пример такой довольно распространенной реализации:
Представьте, что происходит с -initWithLessParameters, если я делаю это в подклассе:
Это подразумевает, что вы должны стремиться использовать закрытые (скрытые) методы, особенно в методах инициализации, если только вы не планируете переопределять методы. Но это другая тема, так как вы не всегда имеете полный контроль над реализацией суперкласса. (Это заставляет меня усомниться в использовании __attribute ((objc_designated_initializer)) как плохой практике, хотя я не использовал его подробно.)
Это также подразумевает, что вы можете использовать утверждения и исключения в методах, которые должны быть переопределены в подклассах. («Абстрактные» методы как в Создание абстрактного класса в Objective-C )
И не забывайте о + новом методе класса.
источник