#objective-c #templates #nsdictionary #plist #abstract
#objective-c #шаблоны #nsdictionary #plist #аннотация
Вопрос:
У меня есть ряд словарей, хранящихся в файлах списка свойств («plist»), и мне было интересно, есть ли у меня способ сделать этот код более эффективным, особенно для целей отладки…
Заголовочный файл содержит раздел, подобный этому:
//////////////////////////////////////////////////////////////////////////////////////
//
// dictionary objects retrieved from Property List ("plist") files...
//
- ( NSDictionary * ) dictionaryAdvertising;
- ( NSDictionary * ) dictionaryBuildings;
- ( NSDictionary * ) dictionaryCampus;
- ( NSDictionary * ) dictionaryLockerItems;
- ( NSDictionary * ) dictionaryRandomScreenObjects;
- ( NSDictionary * ) dictionarySchools;
- ( NSDictionary * ) dictionarySounds;
- ( NSDictionary * ) dictionarySupport;
В разделе основного кода у меня есть следующее:
//////////////////////////////////////////////////////////////////////////////////////////////
//
// use a single macro to create multiple instances of the same basic method with subtle
// variations to avoid code replication (if there's an issue, fix it once instead of
// multiple cut/paste instances)...
//
#define __CREATE_DICTIONARY_ACCESS_METHOD( _method_, _key_, _filename_ )
- ( NSDictionary * ) _method_
{
/* actual pointer to the "singleton" instance of this dictionary... */
static NSDictionary * dict = nil;
/* once-only identifier for instantiating this "singleton" item... */
static dispatch_once_t onceToken = 0l;
/* allocate and load this object, if it hasn't been done already... */
dispatch_once( amp;onceToken
, ^{
dict = [ self loadDictionaryFromFile: _key_ ];
/* idiot check... */
NSAssert( ( dict )
, @"error allocating dictionary "%@""
, _key_
);
/* if there is a field that lists an image file... */
if ( _filename_ )
{
/* load it into the texture cache... */
[ self cacheFromDictionary: dict
name: _key_
key: _filename_
];
} /* end field name given for image file names */
} /* end run load process only once */
);
return dict;
}
__CREATE_DICTIONARY_ACCESS_METHOD( dictionaryAdvertising , CS_STRING_DICTIONARY_KEY_ADVERTISING , nil )
__CREATE_DICTIONARY_ACCESS_METHOD( dictionaryBuildings , CS_STRING_DICTIONARY_KEY_BUILDINGS , @"Filename" )
__CREATE_DICTIONARY_ACCESS_METHOD( dictionaryCampus , CS_STRING_DICTIONARY_KEY_CAMPUS , nil )
__CREATE_DICTIONARY_ACCESS_METHOD( dictionaryLockerItems , CS_STRING_DICTIONARY_KEY_LOCKER_ITEMS , @"Imagename" )
__CREATE_DICTIONARY_ACCESS_METHOD( dictionaryRandomScreenObjects, CS_STRING_DICTIONARY_KEY_RANDOM_OBJECTS, nil )
__CREATE_DICTIONARY_ACCESS_METHOD( dictionarySchools , CS_STRING_DICTIONARY_KEY_SCHOOLS , nil )
__CREATE_DICTIONARY_ACCESS_METHOD( dictionarySounds , CS_STRING_DICTIONARY_KEY_SOUNDS , nil )
__CREATE_DICTIONARY_ACCESS_METHOD( dictionarySupport , CS_STRING_DICTIONARY_KEY_SUPPORT , nil )
Каждый из них должен быть создан как отдельные методы, чтобы у каждого были свои собственные токены инициализации, доступные только один раз. Поскольку эти объекты фактически не хранятся как переменные-члены, они не будут освобождены во время обычной очистки родительских объектов, поэтому они будут сохраняться в нескольких экземплярах родительского класса.
Что я хотел бы сделать, так это найти более элегантное решение для замены макроса, определенного в основном модуле кода, особенно такого, который позволил бы мне пошагово выполнять код во время отладки.
Кстати, для тех из вас, кто хочет использовать это, это работоспособный процесс создания / добавления методов доступа к членам во время компиляции в Objective-C, в основном на всех языках C и C … Псевдо-одноэлементный код гарантирует, что метод доступа к члену создается / выделяется / инициализируется только один раз.
Если это поможет, вот код для загрузки объекта dictionary из файла plist (этот код допускает переопределение файла в пользовательском каталоге «documents», если вы используете это, вы можете удалить эту возможность или, по крайней мере, помнить об этом):
// load the given dictionary from the associated Property List ("plist") file...
- ( NSDictionary * ) loadDictionaryFromFile: ( NSString * const ) className
{
// assume that there is a problem...
NSDictionary * dict = nil;
// build the file name for the "plist" file...
NSString * const fullFileName = [ NSString stringWithFormat: @"%@.plist"
, className
];
// idiot check...
NSAssert1( ( fullFileName )
, @"error allocating object for file "%@""
, className
);
// get the path to the "plist" files for this application in the "documents" folder...
NSString * const rootPath = [ NSSearchPathForDirectoriesInDomains( NSDocumentDirectory
, NSUserDomainMask
, YES
) objectAtIndex: 0
];
// idiot check...
NSAssert( ( rootPath )
, @"error allocating object"
);
// build the fully-qualified path to the requested file...
NSString * plistPath = [ rootPath stringByAppendingPathComponent: fullFileName ];
// idiot check...
NSAssert1( ( plistPath )
, @"error allocating object for file "%@""
, fullFileName
);
// if the file doesn't exist in the "documents" folder...
if ( ! [ [ NSFileManager defaultManager ] fileExistsAtPath: plistPath ] )
{
// then pull it from the resources bundled with the application...
plistPath = [ [ NSBundle mainBundle ] pathForResource: className
ofType: @"plist"
];
// idiot check...
NSAssert1( ( plistPath )
, @"error allocating object for file "%@""
, className
);
} // end file not in "documents" folder
// read the "plist" file into a dictionary object (statically allocate it so that it
// doesn't get automatically dropped when moving between scenes)...
dict = [ [ NSDictionary alloc ] initWithContentsOfFile: plistPath ];
return dict;
}
Любая помощь или комментарии будут высоко оценены. Кроме того, я не уверен, использовал ли я правильные теги для этого вопроса….
=========================================================================
Я изменил код на основе данного ответа, и теперь он выглядит так:
// once-only method to access and load the contents of a Property List file into a dictionary...
- ( NSDictionary * ) accessDictionaryWithKeyAndFilename: ( NSString * const ) _key_
filename: ( NSString * const ) _filename_
token: ( dispatch_once_t * const ) onceToken
dict: ( NSDictionary * * const ) dict
{
// allocate and load this object, if it hasn't been done already...
dispatch_once( onceToken
, ^{
*dict = loadDictionaryFromFile( _key_ );
// idiot check...
NSAssert( ( *dict )
, @"error allocating dictionary "%@""
, _key_
);
// if there is a field that lists an image file...
if ( _filename_ )
{
// load it into the texture cache...
[ self cacheFromDictionary: *dict
name: _key_
key: _filename_
];
} /* end field name given for image file names */
} /* end run load process only once */
);
return *dict;
} // end accessDictionaryWithKeyAndFilename
//////////////////////////////////////////////////////////////////////////////////////////////
//
// use a single macro to create multiple instances of the same basic method with subtle
// variations to avoid code replication (if there's an issue, fix it once instead of
// multiple cut/paste instances)...
//
#define __CREATE_DICTIONARY_ACCESS_METHOD( _method_, _key_, _filename_ )
- ( NSDictionary * ) _method_
{
/* actual pointer to the "singleton" instance of this dictionary... */
static NSDictionary * dict = nil;
/* once-only identifier for instantiating this "singleton" item... */
static dispatch_once_t onceToken = 0l;
[ self accessDictionaryWithKeyAndFilename: _key_
filename: _filename_
token: amp;onceToken
dict: amp;dict
];
return dict;
}
__CREATE_DICTIONARY_ACCESS_METHOD( dictionaryAdvertising , CS_STRING_DICTIONARY_KEY_ADVERTISING , nil )
__CREATE_DICTIONARY_ACCESS_METHOD( dictionaryBuildings , CS_STRING_DICTIONARY_KEY_BUILDINGS , @"Filename" )
__CREATE_DICTIONARY_ACCESS_METHOD( dictionaryCampus , CS_STRING_DICTIONARY_KEY_CAMPUS , nil )
__CREATE_DICTIONARY_ACCESS_METHOD( dictionaryLockerItems , CS_STRING_DICTIONARY_KEY_LOCKER_ITEMS , @"Imagename" )
__CREATE_DICTIONARY_ACCESS_METHOD( dictionaryRandomScreenObjects, CS_STRING_DICTIONARY_KEY_RANDOM_OBJECTS, nil )
__CREATE_DICTIONARY_ACCESS_METHOD( dictionarySchools , CS_STRING_DICTIONARY_KEY_SCHOOLS , nil )
__CREATE_DICTIONARY_ACCESS_METHOD( dictionarySounds , CS_STRING_DICTIONARY_KEY_SOUNDS , nil )
__CREATE_DICTIONARY_ACCESS_METHOD( dictionarySupport , CS_STRING_DICTIONARY_KEY_SUPPORT , nil )
По-прежнему существует недостаток наличия макроса для создания методов, но код загрузки теперь доступен в режиме отладки. Мне нужно поддерживать отдельные статические значения для каждого словаря / once_token, чтобы у нас могло быть 0-> n экземпляров родительского класса, но при этом загруженный словарь сохранялся во всех экземплярах. Даже когда все экземпляры родительского класса отклоняются и позже создаются новые, исходная загрузка одного словаря по-прежнему доступна, что упрощает обработку.
Спасибо!
Ответ №1:
Для меня проще всего было бы просто сделать макрос функцией и написать 8 очень маленьких методов-оболочек. Итак, начните с бесплатной функции, подобной этой:
NSDictionary* accessDictionaryWithKeyAndFilename (NSString* key, NSString* filename, dispatch_once_t* onceToken)
{
... everything from your macro here ...
}
Затем для любого метода, который вы хотите создать, просто вызовите приведенный выше, выполнив следующие действия:
-(NSDictionary*)dictionaryAdvertising
{
return accessDictionaryWithKeyAndFilename (CS_STRING_DICTIONARY_KEY_ADVERTISING, nil, amp;once_token);
}
и так далее. ( once_token
Может быть либо статическим в методе, либо ivar.) Это гораздо более читабельно и гораздо более отладочно. Написать 1-строчный метод для доступа к функции не сложнее, чем написать 1-строчный макрос для автоматического создания метода. Преимущество заключается в том, что метод сохраняется вместе с остальной частью кода класса, а не объявляется в каком-либо заголовке или другом исходном файле. И, конечно, теперь вы можете перейти к каждому методу и отладить все, что происходит внутри него.
Похоже, что ваш -loadDictionaryFromFile:
метод не использует никаких ivar из класса, поэтому он также может стать бесплатной функцией. (Если я что-то не пропустил?)
Комментарии:
1. Спасибо. Я попытался реализовать его с использованием этой методологии, но столкнулся с несколькими проблемами: словари перезагружаются, когда родительский класс удаляется и создается заново; наличие вызова «dispatch_once» в подфункции затрудняет отслеживание фактического указателя «dict», который должен оставаться статичным вфункция, позволяющая выполнять присваивание внутри блока. Я переместил метод «loadDictionaryFromFile» в свободную функцию, и он по-прежнему работает нормально, спасибо!
2. Обновление: исправлена проблема, метод загрузки теперь работает при отладке, СПАСИБО!