Лучший способ определить частные методы для класса в Objective-C
Я только начал программировать Objective-C и, имея фон в Java, задаюсь вопросом, как люди, пишущие программы Objective-C, имеют дело с частными методами.
Я понимаю, что может быть несколько условностей и привычек, и подумать об этом вопросе как совокупности лучших техник, которые используют методы, связанные с частными методами в Objective-C.
Пожалуйста, укажите аргумент для вашего подхода при его публикации. Почему это хорошо? Какие недостатки у него есть (что вы знаете) и как вы с ними справляетесь?
Что касается моих результатов до сих пор.
Можно использовать categories [например. MyClass (Private)], определенный в файле MyClass.m, чтобы группировать частные методы.
Этот подход имеет 2 проблемы:
- Xcode (и компилятор?) не проверяет, определяете ли вы все методы в частной категории в соответствующем блоке @implementation
- Вы должны поместить @interface объявление вашей частной категории в начале файла MyClass.m, иначе Xcode будет жаловаться на сообщение типа "я не могу ответить на сообщение" privateFoo ".
Первая проблема может быть решена с помощью пустой категории [например. MyClass()].
Второй беспокоит меня много. Я бы хотел, чтобы частные методы были реализованы (и определены) ближе к концу файла; Я не знаю, возможно ли это.
Ответы
Ответ 1
Нет, как уже говорили другие, такая вещь, как частный метод в Objective-C. Однако, начиная с Objective-C 2.0 (что означает Mac OS X Leopard, iPhone OS 2.0 и более поздние версии), вы можете создать категорию с пустым именем (т.е. @interface MyClass ()
), называемым расширением класса. Что уникально в расширении класса, так это то, что реализация метода должна идти в том же @implementation MyClass
, что и общедоступные методы. Поэтому я строю свои классы следующим образом:
В файле .h:
@interface MyClass {
// My Instance Variables
}
- (void)myPublicMethod;
@end
И в файле .m:
@interface MyClass()
- (void)myPrivateMethod;
@end
@implementation MyClass
- (void)myPublicMethod {
// Implementation goes here
}
- (void)myPrivateMethod {
// Implementation goes here
}
@end
Я думаю, что наибольшее преимущество этого подхода заключается в том, что он позволяет группировать реализации вашего метода по функциональности, а не по (иногда произвольному) общедоступному/частному различию.
Ответ 2
В Objective-C нет особого "частного метода", если среда выполнения может решить, какую реализацию использовать она сделает. Но это не означает, что нет методов, которые не являются частью документированного интерфейса. Для этих методов я считаю, что категория прекрасна. Вместо того, чтобы помещать @interface
вверху файла .m, как ваша точка 2, я бы поместил его в свой .h файл. Следующее соглашение (и я видел в другом месте, я думаю, что соглашение Apple, поскольку Xcode теперь дает ему автоматическую поддержку), следует назвать такой файл после его класса и категории с разделяющим их +, поэтому @interface GLObject (PrivateMethods)
можно найти в GLObject+PrivateMethods.h
. Причина предоставления файла заголовка заключается в том, что вы можете импортировать его в классы unit test: -).
Кстати, что касается методов реализации/определения в конце файла .m, вы можете сделать это с категорией, выполнив категорию внизу файла .m:
@implementation GLObject(PrivateMethods)
- (void)secretFeature;
@end
или с расширением класса (то, что вы называете "пустой категорией" ), просто определите эти методы последними. Objective-C методы могут быть определены и использованы в любом порядке реализации, поэтому вам нечего помещать методы "private" в конец файла.
Даже с расширениями класса я часто создаю отдельный заголовок (GLObject+Extension.h
), чтобы при необходимости использовать эти методы, имитируя видимость "друг" или "защищенный".
Поскольку этот ответ был изначально написан, компилятор clang начал выполнять два прохода для методов Objective-C. Это означает, что вы можете полностью отказаться от объявления методов "private" и вне зависимости от того, находятся ли они выше или ниже вызывающего узла, которые будут найдены компилятором.
Ответ 3
Пока я не эксперт Objective-C, я лично просто определяю метод в реализации моего класса. Разумеется, он должен быть определен до (выше) любых методов, вызывающих его, но он определенно требует наименьшего количества работы.
Ответ 4
Определение ваших частных методов в блоке @implementation
идеально подходит для большинства целей. Clang увидит их в пределах @implementation
, независимо от порядка объявления. Нет необходимости объявлять их в продолжении класса (например, расширение класса) или названной категории.
В некоторых случаях вам нужно будет объявить метод в продолжении класса (например, при использовании селектора между продолжением класса и @implementation
).
static
функции очень хороши для особо чувствительных или быстродействующих частных методов.
Соглашение об именах префиксов может помочь вам избежать случайного переопределения частных методов (я нахожу имя класса в качестве префикса безопасного).
Именованные категории (например, @interface MONObject (PrivateStuff)
) не являются особенно хорошей идеей из-за потенциальных конфликтов имен при загрузке. Они действительно полезны только для друзей или защищенных методов (что очень редко бывает хорошим выбором). Чтобы убедиться, что вас предупреждают о незавершенных реализациях категорий, вы должны их реализовать:
@implementation MONObject (PrivateStuff)
...HERE...
@end
Вот небольшой аннотированный чит-лист:
MONObject.h
@interface MONObject : NSObject
// public declaration required for clients' visibility/use.
@property (nonatomic, assign, readwrite) bool publicBool;
// public declaration required for clients' visibility/use.
- (void)publicMethod;
@end
MONObject.m
@interface MONObject ()
@property (nonatomic, assign, readwrite) bool privateBool;
// you can use a convention where the class name prefix is reserved
// for private methods this can reduce accidental overriding:
- (void)MONObject_privateMethod;
@end
// The potentially good thing about functions is that they are truly
// inaccessible; They may not be overridden, accidentally used,
// looked up via the objc runtime, and will often be eliminated from
// backtraces. Unlike methods, they can also be inlined. If unused
// (e.g. diagnostic omitted in release) or every use is inlined,
// they may be removed from the binary:
static void PrivateMethod(MONObject * pObject) {
pObject.privateBool = true;
}
@implementation MONObject
{
bool anIvar;
}
static void AnotherPrivateMethod(MONObject * pObject) {
if (0 == pObject) {
assert(0 && "invalid parameter");
return;
}
// if declared in the @implementation scope, you *could* access the
// private ivars directly (although you should rarely do this):
pObject->anIvar = true;
}
- (void)publicMethod
{
// declared below -- but clang can see its declaration in this
// translation:
[self privateMethod];
}
// no declaration required.
- (void)privateMethod
{
}
- (void)MONObject_privateMethod
{
}
@end
Другой подход, который может быть неочевидным: тип С++ может быть как очень быстрым, так и обеспечивать гораздо более высокую степень управления, минимизируя количество экспортированных и загруженных методов objc.
Ответ 5
Вы можете попытаться определить статическую функцию ниже или выше вашей реализации, которая принимает указатель на ваш экземпляр. Он сможет получить доступ к любой из ваших переменных экземпляра.
//.h file
@interface MyClass : Object
{
int test;
}
- (void) someMethod: anArg;
@end
//.m file
@implementation MyClass
static void somePrivateMethod (MyClass *myClass, id anArg)
{
fprintf (stderr, "MyClass (%d) was passed %p", myClass->test, anArg);
}
- (void) someMethod: (id) anArg
{
somePrivateMethod (self, anArg);
}
@end
Ответ 6
каждый объект Objective C соответствует протоколу NSObject, который хранится в методе performSelector:. Я также ранее искал способ создания некоторых "вспомогательных или частных" методов, которые мне не нужно было публиковать на публичном уровне. Если вы хотите создать частный метод без каких-либо накладных расходов и не указывая его в своем файле заголовка, тогда сделайте это...
определите свой метод с аналогичной сигнатурой, как код ниже...
-(void)myHelperMethod: (id) sender{
// code here...
}
тогда, когда вам нужно ссылаться на метод, просто назовите его как селектор...
[self performSelector:@selector(myHelperMethod:)];
эта строка кода вызовет метод, который вы создали, и не имеет раздражающего предупреждения о том, что он не указан в файле заголовка.
Ответ 7
Можно использовать блоки?
@implementation MyClass
id (^createTheObject)() = ^(){ return [[NSObject alloc] init];};
NSInteger (^addEm)(NSInteger, NSInteger) =
^(NSInteger a, NSInteger b)
{
return a + b;
};
//public methods, etc.
- (NSObject) thePublicOne
{
return createTheObject();
}
@end
Я знаю, что это старый вопрос, но это один из первых, который я нашел, когда искал ответ на этот самый вопрос. Я не видел, чтобы это решение обсуждалось где-то еще, поэтому дайте мне знать, если что-то нелепо об этом.
Ответ 8
Если вы хотите избежать блока @interface
вверху, вы всегда можете помещать частные объявления в другой файл MyClassPrivate.h
не идеальным, но не загромождать реализацию.
MyClass.h
interface MyClass : NSObject {
@private
BOOL publicIvar_;
BOOL privateIvar_;
}
@property (nonatomic, assign) BOOL publicIvar;
//any other public methods. etc
@end
MyClassPrivate.h
@interface MyClass ()
@property (nonatomic, assign) BOOL privateIvar;
//any other private methods etc.
@end
MyClass.m
#import "MyClass.h"
#import "MyClassPrivate.h"
@implementation MyClass
@synthesize privateIvar = privateIvar_;
@synthesize publicIvar = publicIvar_;
@end
Ответ 9
Еще одна вещь, о которой я не упоминал здесь, - Xcode поддерживает файлы .h с именем "_private" в названии. Скажем, у вас есть класс MyClass - у вас есть MyClass.m и MyClass.h, и теперь у вас также может быть MyClass_private.h. Xcode распознает это и включит его в список "Компоненты" в редакторе помощников.
//MyClass.m
#import "MyClass.h"
#import "MyClass_private.h"
Ответ 10
Невозможно обойти проблему №2. Это так, как работает компилятор C (и, следовательно, компилятор Objective-C). Если вы используете редактор XCode, всплывающее окно функции должно легко перемещаться по блокам @interface
и @implementation
в файле.
Ответ 11
Существует преимущество отсутствия частных методов. Вы можете переместить логику, которую вы намеревались скрыть, в отдельный класс и использовать ее в качестве делегата. В этом случае вы можете отметить объект делегирования как закрытый, и он не будет виден извне. Перемещение логики в отдельный класс (возможно, несколько) улучшает дизайн вашего проекта. Потому что ваши классы упрощаются, а ваши методы сгруппированы в классы с собственными именами.