Определение константы в target-c - PullRequest
16 голосов
/ 29 июня 2009

Я хочу определить константу в target-c.

Ранее у меня была следующая функция:

+(NSString *) getDocumentsDir {
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES);
    NSString *documentsDir = [paths objectAtIndex: 0];
    paths = nil;
    return documentsDir;
}

Я бы хотел определить константу «Documents_Dir» только один раз - при вызове функции и после этого для доступа к ранее созданному значению.

Я пробовал следующий код, который не работал:

#define getDocumentsDir \
{   \
#ifdef Documents_Dir    \
return Documents_Dir;   \
#else   \
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES);  \
NSString *documentsDir = [paths objectAtIndex: 0];  \
#define Documents_Dir [paths objectAtIndex: 0]; \
paths = nil;    \
return Documents_Dir;   \
#endif  \
}   \

Я не силен в директивах прекомпилятора, поэтому любая помощь будет оценена.

Ответы [ 3 ]

34 голосов
/ 29 июня 2009

Прелюдия: Понимает разницу между директивами прекомпилятора и истинными константами. #define просто выполняет замену текста, прежде чем компилятор создаст код. Это прекрасно работает для числовых констант и typedef, но не всегда является лучшей идеей для вызовов функций или методов. Я работаю в предположении, что вы действительно хотите истинную константу, то есть код для создания пути поиска должен выполняться только один раз.


В вашем файле MyClass.m определите переменную и заполните ее в методе +initialize следующим образом:

static NSArray *documentsDir;

@implementation MyClass

+ (void) initialize {
    if (documentsDir == nil) {
        documentsDir = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES) lastObject] retain];
    }
}

...

@end

Модификатор static делает его видимым только внутри блока компиляции, где он объявлен. Для простой константы это все, что вам нужно.

Если у класса есть подклассы, +initialize будет вызываться один раз для каждого подкласса (по умолчанию), поэтому вы должны проверить, является ли documentsDir nil, прежде чем присваивать ему, чтобы вы не пропустили объем памяти. (Или, как указывает Питер Льюис, вы можете проверить, является ли инициализируемый класс MyClass, используя либо ==, либо метод -isMemberOfClass:.) Если подклассам также необходим доступ к константе непосредственно, вам необходимо предварительно объявить переменную как extern в MyClass.h файле (который включает в себя дочерние классы):

extern NSArray *documentsDir;

@interface MyClass : NSObject
...
@end

Если вы предварительно объявили переменную как extern, вы должны удалить ключевое слово static 1035 * из определения, чтобы избежать ошибок компиляции. Это необходимо, чтобы переменная могла охватывать несколько единиц компиляции. (Ах, радости С ...)

Примечание: В коде Objective-C лучший способ объявить что-либо как extern - это использовать OBJC_EXPORT (#define, объявленное в <objc/objc-api.h>) который устанавливается в зависимости от того, используете ли вы C ++. Просто замените extern на OBJC_EXPORT и все готово.


Редактировать: Я только что натолкнулся на связанный вопрос SO .

12 голосов
/ 30 июня 2009

Самое простое решение - просто изменить пути на статическую переменную и вычислить ее только один раз, например так:

+(NSString *) getDocumentsDir {
    static NSString *documentsDir = nil;
    if ( !documentsDir ) {
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES);
        documentsDir = [paths objectAtIndex: 0];
    }
    return documentsDir;
}

«static» сообщает компилятору, что documentsDir - это глобальная переменная, доступная только внутри функции. Поэтому он инициализируется значением nil, и первый вызов getDocumentsDir оценивает его, а затем последующие вызовы возвращают предварительно оцененное значение.

1 голос
/ 06 января 2012

Небольшая оптимизация в отношении кода Питера N Льюиса:

-(NSString *) documentsDir {
    static NSString *documentsDir = nil;
    return documentsDir ?: (documentsDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]);
}
...