Лучший способ определить частные методы для класса в 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

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