0

いくつかの文字列フィールドといくつかの配列フィールドを持つモデルをセットアップしました。モデルは次のように保存されます。

- (void)saveLevel:(NSString*)level traps:(NSArray*)traps whirls:(NSArray*)whirls accels:(NSArray*)accels walls:(NSArray*)walls dest:(NSString*)dest jupiter:(NSString*)jupiter rating:(NSNumber*)pRating;
{   
    if (m_pMOC == nil) 
       // Retrieves the managed object context

    NSManagedObject* pNewLevel = [NSEntityDescription insertNewObjectForEntityForName:@"Level" inManagedObjectContext:m_pMOC];

    NSDate* pDate = [NSDate date];

    // Must have these four attributes
    [pNewLevel setValue:level forKey:@"Level_ID"];
    [pNewLevel setValue:jupiter forKey:@"Ball"];
    [pNewLevel setValue:pDate forKey:@"Creation_Date"];
    [pNewLevel setValue:dest forKey:@"Destination"];
    [pNewLevel setValue:pRating forKey:@"Rating"];

    // Optional attributes
    if ([traps count] != 0) 
        [pNewLevel setValue:traps forKey:@"Traps"];
    if ([whirls count] != 0) 
        [pNewLevel setValue:whirls forKey:@"Whirls"];
    if ([accels count] != 0) 
        [pNewLevel setValue:accels forKey:@"Accelerators"];
    if ([walls count] != 0) 
        [pNewLevel setValue:walls forKey:@"Walls"];

    NSError* pError;
    if (![m_pMOC save: &pError]) 
    // etc...
}

Core Data から/へのフェッチ/保存を処理する DataManager クラスがあり、Manager 内で、エンティティをフェッチすると配列がフェッチされることを確認しましたが、それを呼び出しているクラスに返されると、配列は nil です (一方、文字列は問題なく到着します)。フェッチ コードと、フェッチから返された値を解析するコードを次に示します。

- (NSArray*) getLevelWithID:(NSString*)level
{
    NSEntityDescription *pEntityDescription = [NSEntityDescription entityForName:@"Level" inManagedObjectContext:m_pMOC];
    NSFetchRequest *pRequest = [[[NSFetchRequest alloc] init] autorelease];
    [pRequest setEntity:pEntityDescription];
    NSPredicate* pPredicate = [NSPredicate predicateWithFormat:@"Level_ID == %@", level];
    [pRequest setPredicate: pPredicate];

    NSError* pError;
    NSArray* pLevels = [m_pMOC executeFetchRequest:pRequest error:&pError];
    if (pLevels == nil) 
    {
        NSLog(@"Could not find the level with Level_ID = %@",level);
        abort();
    }

    NSArray* pAccels = [pLevels valueForKey:@"Accelerators"];
    NSArray* pTraps = [pLevels valueForKey:@"Traps"];
    NSArray* pWalls = [pLevels valueForKey:@"Walls"];
    NSArray* pWhirls = [pLevels valueForKey:@"Whirls"];

    return pLevels;
}

最後の 4 つの配列にブレーク ポイントを設定すると、それらにはオブジェクトがありますが、それらを取得する関数 (以下に示す) では、それらは nil です。

- (void) initLevel:(NSArray*)pLevelObjects
{
    Level* pLevel = [pLevelObjects objectAtIndex:0];

    NSString* pJupiter = pLevel.Ball;
    NSString* pDest = pLevel.Destination;

    NSArray* pAccels = [pLevel valueForKey:@"Accelerators"];
    NSArray* pTraps = [pLevel valueForKey:@"Traps"];
    NSArray* pWalls = [pLevel valueForKey:@"Walls"];
    NSArray* pWhirls = [pLevel valueForKey:@"Whirls"];
    ... (other initialization of the level) ...
}

私はこれに困惑しています。文字列値はありますが、配列はありません。もともとドット表記 (NSArray* pAccels = pLevel.Accelerators など) を使用してみましたが、結果は同じでした。

アイデア?

編集: initLevel はビュー コントローラーから呼び出されます:

- (void) startGameWithLevelID:(NSString*)pLevelID
{
    NSLog(@"MyViewController - beginGameWithLevelID");
    NSArray* pLevel = [[DataManager getDataManager] getLevelWithID:pLevelID];
    if (pLevel == nil || [pLevel count] == 0) 
    {
        NSLog(@"Didn't retrieve any level with id %@", pLevelID);
        abort();
    }
    else
    {
        CGRect newFrame = makeScreen([UIScreen mainScreen].applicationFrame);
        GameBoard* pGB = [[GameBoard alloc] initWithFrame: newFrame];
        pGB.m_pMyVC = self;
        [pGB initLevel: pLevel];
        [self.view addSubview: (UIView*)pGB];

        [pGB release];
    }
}

編集: レベルとの対 1 関係を持つ個別のエンティティに配列を新たにリファクタリングしました。レベルは、これらのエンティティと対多の関係にあります。また、慣例によりよく従うように名前を変更しました。すべての属性と関係は小文字で始まるようになりました。属性と関係の前にそれぞれ a_ と r_ を付けました。「-[NSCFString _isKindOfEntity:]: 認識されないセレクターがインスタンス 0x4d83120 に送信されました」を保存するときにエラーが発生します。あまり役に立ちませんが、問題が見つかったらお知らせします。今のところ、保存のコードは次のようになります。

- (void)saveLevel:(NSString*)level traps:(NSArray*)traps whirls:(NSArray*)whirls accels:(NSArray*)accels walls:(NSArray*)walls dest:(NSString*)dest jupiter:(NSString*)jupiter rating:(NSNumber*)pRating;
{   
    if (m_pMOC == nil) 
    { // Code to get the managed object context from the delegate
    }
    NSManagedObject* pNewLevel = [NSEntityDescription insertNewObjectForEntityForName:@"Level" inManagedObjectContext:m_pMOC];
    NSManagedObject* pNewBall = [NSEntityDescription insertNewObjectForEntityForName:@"Ball" inManagedObjectContext:m_pMOC];
    [pNewBall setValue:jupiter forKey:@"a_Bounds"];
    NSManagedObject* pNewDest = [NSEntityDescription insertNewObjectForEntityForName:@"Dest" inManagedObjectContext:m_pMOC];
    [pNewDest setValue:dest forKey:@"a_Bounds"];

    NSDate* pDate = [NSDate date];

    // Must have these four attributes
    [pNewLevel setValue:level forKey:@"a_Level_ID"];
    [pNewLevel setValue:pDate forKey:@"a_Creation_Date"];
    [pNewLevel setValue:pRating forKey:@"a_Rating"];
    [pNewLevel setValue:@"Bob Dole" forKey:@"a_CreatedBy"];

    [pNewLevel setValue:pNewBall forKey:@"r_Ball"];
    [pNewLevel setValue:pNewDest forKey:@"r_Dest"];

    // Optional attributes
    if ([traps count] != 0) 
        [[pNewLevel mutableSetValueForKey: @"r_Trap"] addObjectsFromArray: traps];
    if ([whirls count] != 0) 
        [[pNewLevel mutableSetValueForKey: @"r_Whirl"] addObjectsFromArray: whirls];
    if ([accels count] != 0) 
        [[pNewLevel mutableSetValueForKey: @"r_Accel"] addObjectsFromArray: accels];
    if ([walls count] != 0) 
        [[pNewLevel mutableSetValueForKey: @"r_Wall"] addObjectsFromArray: walls];

    NSError* pError;
    if (![m_pMOC save: &pError]) 
    { // Error saving
    }
    else
    { // Successfully saved
    }
}

繰り返しになりますが、助けてくれてありがとう。長い投稿で申し訳ありませんが、これが他の Core Data 初心者 (私のような) が何かを学ぶのに役立つことを願っています。

最終編集:わかりました。リファクタリングの後、最終的に配列をオブジェクトとして保存する解決策を見つけました(ただし、それが最も効率的な方法かどうかはわかりません)。以下は、一連の「トラップ」を追加するためのコードです。関数に渡される配列は、格納されるトラップの境界です。次に、レベルとの関係が形成されます (トラップとレベルの両方で設定する必要はなく、いずれか一方だけで設定する必要はありません。コア データが別の方法で処理するためです)。

if ([traps count] != 0) 
    {
        for (NSString* pBounds in traps) 
        {
            NSManagedObject* pNewTrap = [NSEntityDescription insertNewObjectForEntityForName:@"Trap" inManagedObjectContext:m_pMOC];
            [pNewTrap setValue:pBounds forKey:@"a_Bounds"];
            [pNewTrap setValue:pNewLevel forKey:@"r_Level"];
        }
    }
4

1 に答える 1

2

アレイの誤用の可能性

最初のスニペットでは、 を呼び出しvalueForKey:NSArrayおり、結果は配列です。そのコードは を反復処理し、内部の各オブジェクトに引数をpLevels渡しvalueForKey:、値を結合するためです。

Key-Value Coding をNSArray実装 する方法です。

最初に配列から 1 つの pLevel をフェッチする 2 番目のコードでは、配列でvalueForKey:はなくエンティティに対してセレクターを実行します。結果は、その特定のオブジェクトにあるものです。

おそらく一部のエンティティにはこれらのフィールドがあるため、それらすべてを組み合わせた結果にはいくつかの値がありますが、一部の特定のものにはありません。

コアデータの誤用の可能性

Core Dataエンティティに配列を保持していると言います。どうやってそれをしますか?配列を保持する特別な型はないので、通常は でエンコードしてバイナリデータとして格納する必要があります[NSKeyedArchiver archivedDataWithRootObject:array]。配列を非配列フィールドに格納しようとしているため、格納とフェッチが正しくない可能性があります。

クラッシュも経験していますか?

于 2012-07-31T13:28:44.377 に答える