2012-08-29 74 views
0

我有一个singleton类,它将初始化它从Web服务的数据,然后将其自身保存到一个文件(使用NSCoding和NSKeyedUnarchiver),所以我可以只是初始化这个文件,而不是从网络上拉出所有数据再次。因为它是一个正在保存的单例实例,所以我希望能够像通常那样调用单例getter,检查是否存在已存档的副本,如果没有,请将数据拉下并存档。归档工作正常,但是当我尝试调用[[NSKeyedUnarchiver unarchiveObjectWithFile:filePath] retain]时,它会在sharedInstance初始化之前调用sharedInstance getter。这会导致init被调用,然后应用程序再次下载所有数据,随后被unarchiver覆盖。iOS NSKeyedUnarchiver调用单态getter

我在做我的设置有问题吗?还是有其他方式序列化这些数据?

这里的一些(简化)代码:

@implementation Helmets 
@synthesize helmetList, helmetListVersion; 
//Class Fields 
static Helmets *sharedInstance = nil; 

// Get the shared instance and create it if necessary. 
+ (Helmets *)sharedInstance { 
    //if(sharedInstance == nil){ 
     //[Helmets unarchive];    //This does not work! Calls sharedInstance() again (recursion) 
     if(sharedInstance == nil){ 
      sharedInstance = [[super allocWithZone:NULL] init]; //Pull from web service 
      [Helmets archive]; //Save instance 
     //} 
    //} 
    return sharedInstance; 
} 

- (id)init { 
    self = [super init]; 
    if (self) { 
      helmetList = [[NSMutableArray alloc]init]; 

      //Get our data from the web service and save it (excluded) 
      } 
    } 
    return self; 
} 

//This works! 
+(void)archive{ 
    if([NSKeyedArchiver archiveRootObject:sharedInstance toFile:[Helmets getFilePath]]){ 
     NSLog(@"Archiving Successful"); 
    } 
    else{ 
     NSLog(@"Archiving Failed"); 
    } 
} 

//This works, but calls getInstance causing data to be downloaded anyways! 
+(void)unarchive{ 
    // Check if the file already exists 
    NSFileManager *filemgr = [NSFileManager defaultManager]; 
    NSString *filePath = [Helmets getFilePath]; 
    if ([filemgr fileExistsAtPath: filePath]) 
    { 
     sharedInstance = [[NSKeyedUnarchiver unarchiveObjectWithFile: filePath] retain]; 
    } 
    [filemgr release]; 
} 

实例就像初始化:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ 
    ... 
    [Helmets unarchive];  //This calls sharedInstance() too soon! 
    [Helmets sharedInstance]; 
} 

类的.H实现NSCoding和覆盖的initWithCoder和encodeWithCoder(归档工作)。

在此先感谢!

+1

我没有看到'+ unarchive'调用'+ sharedInstance'你能详细说明吗? –

+0

对不起,'+ unarchive'不显式地调用'+ sharedInstance'。当[[[NSKeyedUnarchiver unarchiveObjectWithFile:filePath] retain]; '被调用(在'+ unarchive'中),那么'+ sharedInstance'被无意中调用。我没有想到NSKeyedUnarchiver的这种行为。 – javajavajavajavajava

+0

您是否重写'-retain'? –

回答

2

最后你需要一个私有方法来设置你的共享实例,除此之外你还需要一个不同的init,这对于实现来说又是私有的。

- (id)initAndFetch:(BOOL)fetch 
{ 
    if((self = [super init])) { 
     ... 

     if(fetch) { do the web fetch }; 
      ... 
    } 
} 

在+ sharedInstance方法中,您将传递YES。

那么你解码的样子:

- (id)initWithCoder:(NSCoder *)decoder 
{ 
    if((self = [self initAndFetch:NO])) { 
     title = [decoder decodeObjectForKey:@"title"]; 
     ... 
     sharedInstance = self; 
    } 
    return self; 
} 
+0

谢谢大卫。这将很好地工作。 – javajavajavajavajava

1

我得让它工作在以下方式。

-(id)initWithCoder:(NSCoder *)aDecoder 
{ 
    self = [super init]; 
    if (!self) { 
     return nil; 
    } 
    UserInfo *user =[UserInfo sharedInstance]; 
    return user; 
} 

-(void)encodeWithCoder:(NSCoder *)aCoder 
{ 
    UserInfo *user =[UserInfo sharedInstance]; 
    [aCoder encodeObject: user.phoneNumber forKey:@"phoneNumber"]; 
} 



-(void)save 
{ 
    [[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:[UserInfo sharedInstance]] forKey:@"USER_DATA"]; 
    [[NSUserDefaults standardUserDefaults]synchronize]; 

} 

-(UserInfo*)currentUser 
{ 
    NSData *data =[[NSUserDefaults standardUserDefaults] objectForKey:@"USER_DATA"]; 
    UserInfo *savedUser =[NSKeyedUnarchiver unarchiveObjectWithData:data]; 

    UserInfo *user =[UserInfo sharedInstance]; 
    return user; 
}