将PList文件加载到字典中的Objective-C伪模板/抽象方法

将PList文件加载到字典中的Objective-C伪模板/抽象方法,objective-c,templates,nsdictionary,plist,abstract,Objective C,Templates,Nsdictionary,Plist,Abstract,我有一系列的字典存储在属性列表plist文件中,我想知道是否有一种方法可以使这段代码更高效,特别是在调试时 头文件包含如下部分: ////////////////////////////////////////////////////////////////////////////////////// // // dictionary objects retrieved from Property List ("plist") files... // - ( NSDictionary * )

我有一系列的字典存储在属性列表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;
//  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: &onceToken                       \
                                             dict: &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 )
NSDictionary* accessDictionaryWithKeyAndFilename (NSString* key, NSString* filename, dispatch_once_t* onceToken)
{
    ... everything from your macro here ...
}
在主代码部分,我有以下内容:

//////////////////////////////////////////////////////////////////////////////////////////////
//
//  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( &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 )
-(NSDictionary*)dictionaryAdvertising
{
    return accessDictionaryWithKeyAndFilename (CS_STRING_DICTIONARY_KEY_ADVERTISING, nil, &once_token);
}
它们都需要作为单独的方法创建,以便每个方法都有自己的一次性初始化令牌。由于这些对象实际上不是作为成员变量存储的,因此在父对象的正常清理过程中不会释放它们,因此它们将在父类的多个实例中持久化

我想做的是找到一个更优雅的解决方案来替换在主代码模块中定义的宏,特别是一个允许我在调试时逐步完成代码的解决方案

BTW,对于那些想要使用这个的人来说,在ObjtoE-C中创建/添加编译时成员访问方法是一个可行的过程,主要是所有C语言和C++语言的变体…伪单例代码确保成员访问方法只创建/分配/初始化一次

如果有帮助,下面是从plist文件加载dictionary对象的代码。此代码允许在用户的文档目录中使用覆盖文件。如果使用此代码,您可能希望删除该功能,或者至少要记住:

//  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;
}
如有任何帮助或意见,将不胜感激。另外,我不确定我是否为这个问题使用了正确的标签

=========================================================================

我根据给出的答案修改了代码,现在看起来是这样的:

//////////////////////////////////////////////////////////////////////////////////////
//
//  dictionary objects retrieved from Property List ("plist") files...
//

- ( NSDictionary * ) dictionaryAdvertising;
- ( NSDictionary * ) dictionaryBuildings;
- ( NSDictionary * ) dictionaryCampus;
- ( NSDictionary * ) dictionaryLockerItems;
- ( NSDictionary * ) dictionaryRandomScreenObjects;
- ( NSDictionary * ) dictionarySchools;
- ( NSDictionary * ) dictionarySounds;
- ( NSDictionary * ) dictionarySupport;
//  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: &onceToken                       \
                                             dict: &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 )
NSDictionary* accessDictionaryWithKeyAndFilename (NSString* key, NSString* filename, dispatch_once_t* onceToken)
{
    ... everything from your macro here ...
}
使用宏创建方法仍然有缺点,但是加载代码现在可以在调试模式下使用。我需要为每个dictionary/once_标记维护不同的静态值,这样我们就可以拥有父类的0->n个实例,但仍然可以在所有实例中保持加载的dictionary。即使父类的所有实例都被取消,并且随后创建了更多实例,原始的单字典加载仍然可用,从而简化了处理


谢谢

对我来说,最简单的方法就是将宏变成一个函数,然后编写8个非常小的包装器方法。因此,从这样一个自由函数开始:

//////////////////////////////////////////////////////////////////////////////////////
//
//  dictionary objects retrieved from Property List ("plist") files...
//

- ( NSDictionary * ) dictionaryAdvertising;
- ( NSDictionary * ) dictionaryBuildings;
- ( NSDictionary * ) dictionaryCampus;
- ( NSDictionary * ) dictionaryLockerItems;
- ( NSDictionary * ) dictionaryRandomScreenObjects;
- ( NSDictionary * ) dictionarySchools;
- ( NSDictionary * ) dictionarySounds;
- ( NSDictionary * ) dictionarySupport;
//  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: &onceToken                       \
                                             dict: &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 )
NSDictionary* accessDictionaryWithKeyAndFilename (NSString* key, NSString* filename, dispatch_once_t* onceToken)
{
    ... everything from your macro here ...
}
然后,对于要创建的任何给定方法,只需通过执行以下操作调用上述方法:

//////////////////////////////////////////////////////////////////////////////////////////////
//
//  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( &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 )
-(NSDictionary*)dictionaryAdvertising
{
    return accessDictionaryWithKeyAndFilename (CS_STRING_DICTIONARY_KEY_ADVERTISING, nil, &once_token);
}
等等。once_标记可以是方法中的静态标记,也可以是ivar。这更具可读性和可调试性。编写一行方法来访问函数并不比编写一行宏来自动创建方法困难。它的优点是将方法保留在类代码的其余部分,而不是在某些头文件或其他源文件中声明。当然,现在您可以进入每个方法并调试其中发生的任何事情


您的-loadDictionaryFromFile:方法似乎没有使用该类中的任何IVAR,因此它也可能成为一个自由函数。除非我错过了什么?

谢谢。我尝试使用这种方法实现它,但遇到了一些问题:当父类离开并重新创建时,字典会重新加载;在子函数中调用dispatch_once会使跟踪实际dict指针变得困难,实际dict指针需要在函数中保持静态,以允许在块内部进行赋值。我确实将loadDictionaryFromFile方法移动到了一个免费函数中,它仍然可以正常工作,谢谢!更新:修复了问题,加载方法现在在调试中很好,谢谢!