Лучший способ определить частные методы для класса в Objective-C - PullRequest
350 голосов
/ 06 октября 2008

Я только начал программировать Objective-C и, имея опыт работы с Java, удивляюсь, как люди, пишущие программы Objective-C, имеют дело с приватными методами.

Я понимаю, что может быть несколько условностей и привычек, и думаю, что этот вопрос объединяет лучшие методы, которые люди используют для работы с частными методами в Objective-C.

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

<Ч />

Что касается моих выводов.

Можно использовать категорий [например, MyClass (Private)], определенный в файле MyClass.m для группировки частных методов.

У этого подхода есть 2 проблемы:

  1. Xcode (и компилятор?) Не проверяет, определяете ли вы все методы в частной категории в соответствующем блоке @implementation
  2. Вы должны поместить @interface, объявляющий вашу личную категорию, в начало файла MyClass.m, в противном случае XCode жалуется на сообщение типа «self может не отвечать на сообщение« privateFoo ».

Первая проблема может быть решена с помощью пустой категории [например, MyClass ()]. ​​
Второй очень беспокоит меня. Я хотел бы видеть частные методы, реализованные (и определенные) в конце файла; Я не знаю, возможно ли это.

Ответы [ 12 ]

433 голосов
/ 16 марта 2009

В 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

Я думаю, что наибольшим преимуществом этого подхода является то, что он позволяет группировать реализации методов по функциональности, а не по (иногда произвольному) общему / частному различию.

50 голосов
/ 06 октября 2008

В Objective-C на самом деле не существует «частного метода», если среда выполнения может определить, какая реализация его использует. Но это не значит, что нет методов, которые не являются частью документированного интерфейса. Для этих методов я думаю, что категория хорошо. Вместо того, чтобы помещать @interface в верхнюю часть файла .m, как ваш пункт 2, я бы поместил его в свой собственный файл .h. Соглашение, которому я следую (и видел в другом месте, я думаю, что это соглашение Apple, поскольку XCode теперь обеспечивает его автоматическую поддержку), заключается в том, чтобы называть такой файл после его класса и категории с +, разделяющим их, так что @interface GLObject (PrivateMethods) можно найти в GLObject+PrivateMethods.h. Причина предоставления файла заголовка заключается в том, что вы можете импортировать его в свои классы модульного теста: -).

Кстати, что касается реализации / определения методов в конце файла .m, вы можете сделать это с категорией, реализовав категорию в нижней части файла .m:

@implementation GLObject(PrivateMethods)
- (void)secretFeature;
@end

или с расширением класса (то, что вы называете «пустой категорией»), просто определите эти методы последними. Методы Objective-C могут быть определены и использованы в любом порядке в реализации, поэтому ничто не мешает вам помещать «приватные» методы в конец файла.

Даже с расширениями классов я часто создаю отдельный заголовок (GLObject+Extension.h), чтобы при необходимости использовать эти методы, имитируя видимость «друг» или «защищенный».

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

37 голосов
/ 06 октября 2008

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

19 голосов
/ 26 мая 2013

Определение ваших личных методов в блоке @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

Другой подход, который может быть неочевиден: тип C ++ может быть очень быстрым и обеспечивать гораздо более высокую степень контроля при минимизации количества экспортируемых и загружаемых методов objc.

14 голосов
/ 16 марта 2009

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

//.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
3 голосов
/ 17 июня 2012

Вы могли бы использовать блоки?

@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

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

3 голосов
/ 19 ноября 2010

каждый объект в Objective C соответствует протоколу NSObject, который поддерживает метод executeSelector: . Я также ранее искал способ создания некоторых «вспомогательных или частных» методов, которые мне не нужны были бы доступны на публичном уровне. Если вы хотите создать приватный метод без накладных расходов и не определять его в заголовочном файле, попробуйте ...

определить ваш метод с такой же сигнатурой, как в коде ниже ...

-(void)myHelperMethod: (id) sender{
     // code here...
}

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

[self performSelector:@selector(myHelperMethod:)];

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

2 голосов
/ 14 февраля 2013

Еще одна вещь, о которой я не упомянул здесь, - Xcode поддерживает файлы .h с "_private" в названии. Допустим, у вас есть класс MyClass - у вас есть MyClass.m и MyClass.h, и теперь у вас также может быть MyClass_private.h. XCode распознает это и включит его в список «контрагентов» в помощнике редактора.

//MyClass.m
#import "MyClass.h"
#import "MyClass_private.h"
2 голосов
/ 04 августа 2010

Если вы хотите избежать блока @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
1 голос
/ 08 августа 2011

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

...