5

iPhone アプリのコードを書いていますが、デフォルトのデータを正しくロードするのに問題があります。Ray Wenderlich 著の「Learning Cocos2d」の例に基づいてコードを作成しています。

アプリを完全に削除して新しいデータから開始しようとしても、アプリが矛盾してデータをロードしようとしないか、データがあると誤って考えて null をロードするようです。

containsValueForKey を使用して値が存在するかどうかを確認し、それをロードするか、デフォルト値をロードしますが、新規インストールでも、containsValueForKey はデータを検出し、デフォルトをロードしません。xcode のオーガナイザーで、デバイスのファイル構造を確認し、保存するように指定した Documents フォルダーにファイルが含まれていないように見えるため、何を取得しているのかわかりません。

私の推測では、問題は initWithCoder 関数に関係していると思います。時々不思議なことに機能を通過するようですが、常にではありません。もう 1 つの奇妙な点は[[GameManager sharedGameManager] save]、プレーヤーがハイスコアを取得したときに呼び出すことです (ここには示されていませんが、コードはこの ObjectiveList とまったく同じで、int のみです)。正しく保存されているように見えます。

そして今、コード:

GCDatabase.h

#import <Foundation/Foundation.h>
id loadData(NSString * filename);
void saveData(id theData, NSString *filename);

GCDatabase.m

#import "GCDatabase.h"
NSString * pathForFile(NSString *filename) {
// 1
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
// 2
NSString *documentsDirectory = [paths objectAtIndex:0];
// 3
return [documentsDirectory stringByAppendingPathComponent:filename];
}

id loadData(NSString * filename) {

NSString *filePath = pathForFile(filename);
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {

    NSData *data = [[[NSData alloc] initWithContentsOfFile:filePath] autorelease];
    NSKeyedUnarchiver *unarchiver = [[[NSKeyedUnarchiver alloc] initForReadingWithData:data] autorelease];
    id retval = [unarchiver decodeObjectForKey:@"Data"];
    [unarchiver finishDecoding];
    return retval;
}
return nil;
}

void saveData(id theData, NSString *filename) {

NSMutableData *data = [[[NSMutableData alloc] init] autorelease];
NSKeyedArchiver *archiver = [[[NSKeyedArchiver alloc] initForWritingWithMutableData:data] autorelease];
[archiver encodeObject:theData forKey:@"Data"];
[archiver finishEncoding];
[data writeToFile:pathForFile(filename) atomically:YES];
}

GameManager.h

@interface GameManager : NSObject <NSCoding>{
NSMutableArray *objectiveDescriptions;
}
@property (nonatomic, retain) NSMutableArray * objectiveDescriptions;
+(GameManager*)sharedGameManager;
-(void)save;
-(void)load;
-(void)encodeWithCoder:(NSCoder *)encoder;
-(id)initWithCoder:(NSCoder *)decoder;

@end

GameManager.m (強制的にロードしようとしてロード関数を追加しましたが、機能していないようです)

+(GameManager*)sharedGameManager {
@synchronized([GameManager class])
{
    if(!sharedGameManager) {
        sharedGameManager = [loadData(@"GameManager") retain];
        if (!sharedGameManager) {
            [[self alloc] init];
        }
    }
    return sharedGameManager;
}
return nil; 
}

+(id)alloc {
@synchronized([GameManager class]){
    NSAssert(sharedGameManager == nil, @"Attempted to allocate a second instance of the Game Manager singleton");
    sharedGameManager = [super alloc];
    return sharedGameManager;
}
return nil;
}

- (void)dealloc {
[objectiveList release];
[super dealloc];
}

- (void)save {
saveData(self, @"GameManager");
}

-(void)load {
loadData(@"GameManager");
}

- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:objectiveList forKey:@"objectiveList"];
}

- (id)initWithCoder:(NSCoder *)decoder  {
self = [super init];
if (self != nil) {
if ([decoder containsValueForKey:@"objectiveList"]) {
        objectiveList = [decoder decodeObjectForKey:@"objectiveList"];
    } else {
        [objectiveList addObjectsFromArray:[NSArray arrayWithObjects:@"1",@"2",@"3",@"4",@"5", nil]];
    }
}

return self;
}
@end
4

4 に答える 4

1

私は問題を見ているようです。コンストラクターが初めて呼び出されると、「initWithCoder」が呼び出されないため、objectiveList は作成されません。ObjectiveList 配列を構築するには、init メソッドもオーバーライドする必要があります。基本的に、init メソッドを呼び出すコードは次のとおりです。

+(GameManager*)sharedGameManager {
@synchronized([GameManager class])
{
    if(!sharedGameManager) {
        sharedGameManager = [loadData(@"GameManager") retain];
        if (!sharedGameManager) {
            [[self alloc] init]; // GOES INTO INIT METHOD, NOT INITWITHCODER!
        }
    }
    return sharedGameManager;
}
return nil; 
}

余談ですが、そのシングルトンの実装には頭が痛くなりました。ただ言って。:)

于 2012-05-09T17:13:10.113 に答える
1

私はあなたの完全なコードを読んでいません..しかし、コードに問題が見つかりました....

メモリを配列に割り当てていませんobjectiveList..メモリを配列に割り当てない限り、オブジェクトは追加されません...

私は行くと思います

 objectiveList = [[NSMutableArray alloc] initWithArray:[NSArray arrayWithObjects:@"1",@"2",@"3",@"4",@"5", nil]];

それ以外の

    [objectiveList addObjectsFromArray:[NSArray arrayWithObjects:@"1",@"2",@"3",@"4",@"5", nil]];

構文を確認してください..配列にメモリを割り当てるのを忘れた過去にも私を悩ませたので、これが役立つことを願っています..そしてオブジェクトを追加し続けてnullになりました... :)問題、後でコードを完全に探します.. :)

于 2012-05-07T16:27:05.003 に答える
0

GameManager.m のメソッドは次のようになります。

- (id)initWithCoder:(NSCoder *)decoder  {
self = [super init];
if (self != nil) {
if ([decoder containsValueForKey:@"objectiveList"]) {
        objectiveList = [[decoder decodeObjectForKey:@"objectiveList"] retain];
    } else {
        objectiveList = [[NSMutableArray alloc] initWithObjects:@"1",@"2",@"3",@"4",@"5", nil];
    }
}

2 つのケースがありobjectiveListます。存在する場合は、以前にいくつかのデータを保存したことがあります。または、存在せず、デフォルト データ (1、2、3、4、5) を作成する必要があります。上記のコードでは、最初のケースを によって返される配列を保持するように変更しましたdecodeObjectForKey。Apple のドキュメントでは、このメソッドがautoreleaseオブジェクトを返すと述べているためです。アプリで後で作成される他のオブジェクトにメモリが再利用されるのを防ぐために、ここに保持する必要があります。を保持しないことによりobjectiveList、後でアクセスするときに、デコードしたばかりのものではなく、ガベージ結果 (つまり、ランダム メモリ) にアクセスしていた可能性があります。

同様に、objectiveListまだ存在していない 2 番目のケース (つまり、保存されたデータが存在しないアプリの新規インストールの場合) ではobjectiveList、オブジェクトを追加しようとする前に割り当てていません。この行を実際allocのオブジェクト (したがって必要なメモリ) に変更し、必要なinitデフォルト値に変更しました。以前、作成されていない配列にアイテムを追加しようとしていたため、そこから値にアクセスしようとすると、再びガベージ データが取得されます。NSMutableArrayhereを使用していると仮定していますが、 NSMutableSet.

于 2012-05-14T09:23:26.400 に答える
0

(提供されたコードからわかる限り) コードに論理的な欠陥があります。キーdecoderが含まれていない場合に何が起こるかを考えてみましょう。句は実行されますが、割り当てられobjectiveListていないため、呼び出しはサイレントに失敗します。elseobjectiveListaddObjectsFromArray:

この理論をテストするには、以下に示すようにコードを変更し、再実行します。アサーションが発生する場合、上記の理論は正しいです。そうでない場合は、もう少し調べる必要があります。


- (id)initWithCoder:(NSCoder *)decoder
{ self = [super init]; if (self != nil) { if ([decoder containsValueForKey:@"objectiveList"]) { objectiveList = [decoder decodeObjectForKey:@"objectiveList"]; } else { NSAssert(objectiveList, @"objectiveList must be non-nil to add objects."); [objectiveList addObjectsFromArray[NSArrayarrayWithObjects:@"1",@"2",@"3",@"4",@"5", nil]]; } } return self; }

ちなみに、objectiveListは ivar として宣言されることはありません...私はそれを想定していobjectiveListobjectiveDescriptions、同じであることを意図しています。

于 2012-05-12T08:30:01.267 に答える