Псевдошаблон Objective-C / абстрактные методы для загрузки файла PList в словарь

#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. Обновление: исправлена проблема, метод загрузки теперь работает при отладке, СПАСИБО!