2

End Goal: I have an object-graph made up of leaves, connected to other leaves. I need to traverse the object-graph and return all those leaves that are not wilted and that either 1) don't have sub-leaves or 2) All sub-leaves are wilted.

Situation: I have an NSFetchedResultsController and table view where I'd like to display the results.

What I've Tried: I started out trying to use an NSPredicate on the NSFetchRequest, but realized there was no way that I could see which could recursively run through a leaf's sub-leaves and all their sub-leaves, etc...

So I added an attribute to the Leaf object called "isFarthestNonWiltedLeaf" and created a custom get-accessor inside of a category on Leaf:

- (NSNumber*) isFarthestNonWiltedLeaf
{
    [self willAccessValueForKey:@"isFarthestNonWiltedLeaf"];
    NSNumber *returnValue = @([self.wilted boolValue] == NO && [[self allSubLeavesAreWilted] boolValue]);
    [self didAccessValueForKey:@"isFarthestNonWiltedLeaf"];

    return returnValue;
}

- (NSNumber*) allSubLeavesAreWilted
{
    for(Leaf *aLeaf in self.subLeaves)
    {
        if([aLeaf.wilted boolValue] == NO || ![[aLeaf allSubLeavesAreWilted] boolValue])
            return @NO;
    }
    return @YES;
}

Then, on the NSFetchedResultsController's fetch request, I set the following predicate:

[fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"isFarthestNonWiltedLeaf == YES"]];

This works great the first time that I open the application and started adding leaves and sub-leaves in a different view. However, the next time that I opened the app, the custom accessors method were not accessed the first time that the table view appeared! I have to go to each leaf and check/uncheck its "wilted" status, which then has the NSFetchedResultsController refresh that single Leaf... at that point it does call the custom isFarthestNonWiltedLeaf accessor and the leaf correctly appears in the list. I'd have to do this for each leaf for the entire tableview to be properly updated.

So my question is... how do I get the NSFetchRequest / NSFetchedResultsController to use the custom get accessor of the Leaf object each time? Thank you.

4

1 に答える 1

0

さて、私は自分の質問に答える必要があります...もう少し遊んで、問題を理解しましたが、その「理由」についてはまだわかりません.

CoreData で多くのキャッシュが行われていることに気付いたので、NSFetchedResultsController で cacheName を nil に設定していたにもかかわらず、そこに問題があるように感じました。また、get アクセサーを呼び出さずにプリミティブ属性にアクセスしていたようです。

つまり、プリミティブを保存する必要があります。そこで、値が計算された直後にget アクセサーに次の行を追加しました。

[self setPrimitiveValue:returnValue forKey:@"isFarthestNonWiltedLeaf"];

最初に KVO 通知コード (willChangeValueForKey と didChangeValueForKey) も追加しましたが、何らかの理由でアプリが完全に応答しなくなりました...循環参照の問題のようです。

したがって、最終的なコードは次のようになります。

- (NSNumber*) isFarthestNonWiltedLeaf
{
    [self willAccessValueForKey:@"isFarthestNonWiltedLeaf"];
    NSNumber *returnValue = @([self.wilted boolValue] == NO && [[self allSubLeavesAreWilted] boolValue]);
    [self didAccessValueForKey:@"isFarthestNonWiltedLeaf"];

    [self setPrimitiveValue:returnValue forKey:@"isFarthestNonWiltedLeaf"];

    return returnValue;
}

- (NSNumber*) allSubLeavesAreWilted
{
    for(Leaf *aLeaf in self.subLeaves)
    {
        if([aLeaf.wilted boolValue] == NO || ![[aLeaf allSubLeavesAreWilted] boolValue])
            return @NO;
    }
    return @YES;
}

... NSPredicate を次のように使用します。

[fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"isFarthestNonWiltedLeaf == YES"]];
于 2012-10-07T19:40:02.933 に答える