核心数据一对多关系和表视图

时间:2011-11-21 13:10:37

标签: objective-c ios core-data

好吧,我已经在这个问题上打了一段时间了,现在已经过了一个星期,我只是不知道自己哪里出错了。非常感谢您的帮助!!

这是应用程序的想法。我有两个以核心数据为模型的实体。大亨实体与Speech有一对多的关系,因此每个大亨都能够有多个演讲。 在Tycoon实体中,这种关系的标题是“TycoonToSpeech”,而在言语实体中,与Tycoon的关系名为'SpeechToTycoon'(这不是一对多关系)。

问题1:我认为我已经在appdelegate.m文件中正确创建了我的关系,但我不确定。以下是我的appdelegate中用于生成内容的代码。我应该用NSMutableSets做什么吗???

NSManagedObjectContext *context = [self managedObjectContext];


NSEntityDescription *tycoonEntity = [NSEntityDescription entityForName:@"Tycoon" inManagedObjectContext:context];

NSManagedObject *steve = [NSEntityDescription insertNewObjectForEntityForName:[tycoonEntity name] inManagedObjectContext:context];

[steve setValue:@"Steve Jobs" forKey:@"Name"];
int orgId = [steve hash];
[steve setValue:[NSNumber numberWithInt:orgId] forKey:@"Id"];

NSManagedObject *warren = [NSEntityDescription insertNewObjectForEntityForName:[tycoonEntity name] inManagedObjectContext:context];
[warren setValue:@"Warren Buffet" forKey:@"Name"];
int orgId2 = [warren hash];
[warren setValue:[NSNumber numberWithInt:orgId2] forKey:@"Id"];


NSEntityDescription *speechEntity = [NSEntityDescription entityForName:@"Speech" inManagedObjectContext:context];
NSManagedObject *stanford = [NSEntityDescription insertNewObjectForEntityForName:[speechEntity name] inManagedObjectContext:context];
[stanford setValue:[NSNumber numberWithInt:[stanford hash]] forKey:@"speechId"];
[stanford setValue:@"Stanford" forKey:@"speechName"];

NSManagedObject *wwdc = [NSEntityDescription insertNewObjectForEntityForName:[speechEntity name] inManagedObjectContext:context];
[wwdc setValue:[NSNumber numberWithInt:[wwdc hash]] forKey:@"speechId"];
[wwdc setValue:@"WWDC" forKey:@"speechName"];

NSManagedObject *shareHolder = [NSEntityDescription insertNewObjectForEntityForName:[speechEntity name] inManagedObjectContext:context];
[shareHolder setValue:[NSNumber numberWithInt:[shareHolder hash]] forKey:@"speechId"];
[shareHolder setValue:@"Shareholder's Meeting" forKey:@"speechName"];

NSManagedObject *columbia = [NSEntityDescription insertNewObjectForEntityForName:[speechEntity name] inManagedObjectContext:context];
[columbia setValue:[NSNumber numberWithInt:[columbia hash]] forKey:@"speechId"];
[columbia setValue:@"Columbia" forKey:@"speechName"];


NSMutableSet *jobsSpeeches = [steve mutableSetValueForKey:@"TycoonToSpeech"];
[jobsSpeeches addObject:stanford];
[jobsSpeeches addObject:wwdc];


NSMutableSet *warrenSpeeches = [warren mutableSetValueForKey:@"TycoonToSpeech"];
[warrenSpeeches addObject:shareHolder];
[warrenSpeeches addObject:columbia];

问题2:主要且非常烦人的问题,我不知道如何在我的根视图控制器中使用didSelectRowAtIndexPath工作,这样当我在表格视图中点击“史蒂夫·乔布斯”时,它只会显示他的演讲和当我点击Warren Buffet时,它只会提出他的演讲。为了做到这一点,我把头撞在了墙上。目前,当我点击史蒂夫乔布斯或沃伦巴菲特时,它会带我到一个新的桌面视图,显示'史蒂夫乔布斯'和'沃伦巴菲特',所以基本上与原始的表视图完全相同!这是我的didSelectRowAtIndexPath代码。

    // Create a new table view of this very same class.
    RootViewController *rootViewController = [[RootViewController alloc]
                                              initWithStyle:UITableViewStylePlain];

    // Pass the managed object context
    rootViewController.context = self.context;
    NSPredicate *predicate = nil;
    // Get the object the user selected from the array
    NSManagedObject *selectedObject = [entityArray objectAtIndex:indexPath.row];


    rootViewController.entityName = @"Speeches";

    // Create a query predicate to find all child objects of the selected object. 
    predicate = [NSPredicate predicateWithFormat:@"SpeechToTycoon == %@", selectedObject];


    [rootViewController setEntitySearchPredicate:predicate];

    //Push the new table view on the stack
    [self.navigationController pushViewController:rootViewController animated:YES];
    [rootViewController release];

此处新编辑* *

好的,这是我的SpeechViewController.m文件中的主要代码。 numberOfRowsInSection方法似乎正在工作,但真正的问题是cellForRowAtIndexPath是问题所在。我知道我可能做了一些非常愚蠢的事情,但我不知道它是什么。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
    cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
NSArray *speeches = (NSArray *)tycoon.TycoonToSpeech;
// Configure the cell...
cell.textLabel.text = [[speeches objectAtIndex:indexPath.row] retain];
return cell;

}

新语音视图控制器到语言点视图控制器

我在app委托中添加了这个,以便将对象添加到我的语言点实体。

NSEntityDescription *langPointEntity = [NSEntityDescription entityForName:@"LanguagePoints" inManagedObjectContext:context];

NSManagedObject *pointOne = [NSEntityDescription insertNewObjectForEntityForName:[langPointEntity name] inManagedObjectContext:context];
[pointOne setValue:[NSNumber numberWithInt:[pointOne hash]] forKey:@"LangId"];
[pointOne setValue:@"Drop out" forKey:@"LangPoint"];
[pointOne setValue:stanford forKey:@"LanguagePointsToSpeech"];

然后在我的speechViewController.m文件中创建了一个名为speechInfo的NSArray,然后这是我的didSelectRowAtIndexPath方法。

LangPointsViewController *langPointsView = [[LangPointsViewController alloc] initWithNibName:@"LangPointsViewController" bundle:[NSBundle mainBundle]];
Speech *speech = [speechInfo objectAtIndex:indexPath.row];
langPointsView.speech = speech;
[self.navigationController pushViewController:langPointsView animated:YES];
[langPointsView release];

我认为这是出错的地方,我不认为我在这里正确获取对象,因为我在LangPointsViewController中对speech.Name执行了一个NSLog,它出现了null,就好像我做了一个NSLog对于SpeechViewController中的tycoon.Name,它会打印我点击的人名。 NSArray是不正确的吗?

1 个答案:

答案 0 :(得分:9)

问题#1

首先你的关系名称不好。它们应该以小写字母开头,它们应该简单地描述关系另一方面的内容。你已经知道你在哪里。因此,请分别命名tycoonspeeches。其次,由于关系是双向的,您可以更轻松地构建数据,请参阅以下内容:

NSManagedObjectContext *context = [self managedObjectContext];

NSManagedObject *steve = [NSEntityDescription insertNewObjectForEntityForName:@"Tycoon" inManagedObjectContext:context];

[steve setValue:@"Steve Jobs" forKey:@"Name"];
int orgId = [steve hash];
[steve setValue:[NSNumber numberWithInt:orgId] forKey:@"Id"];

NSManagedObject *warren = [NSEntityDescription insertNewObjectForEntityForName:[tycoonEntity name] inManagedObjectContext:context];
[warren setValue:@"Warren Buffet" forKey:@"Name"];
int orgId2 = [warren hash];
[warren setValue:[NSNumber numberWithInt:orgId2] forKey:@"Id"];

NSManagedObject *stanford = [NSEntityDescription insertNewObjectForEntityForName:@"Speech" inManagedObjectContext:context];
[stanford setValue:[NSNumber numberWithInt:[stanford hash]] forKey:@"speechId"];
[stanford setValue:@"Stanford" forKey:@"speechName"];
[stanford setValue:warren forKey:@"tycoon"];

NSManagedObject *wwdc = [NSEntityDescription insertNewObjectForEntityForName:[speechEntity name] inManagedObjectContext:context];
[wwdc setValue:[NSNumber numberWithInt:[wwdc hash]] forKey:@"speechId"];
[wwdc setValue:@"WWDC" forKey:@"speechName"];
[wwdc setValue:warren forKey:@"tycoon"];

NSManagedObject *shareHolder = [NSEntityDescription insertNewObjectForEntityForName:[speechEntity name] inManagedObjectContext:context];
[shareHolder setValue:[NSNumber numberWithInt:[shareHolder hash]] forKey:@"speechId"];
[shareHolder setValue:@"Shareholder's Meeting" forKey:@"speechName"];
[shareHolder setValue:warren forKey:@"tycoon"];

NSManagedObject *columbia = [NSEntityDescription insertNewObjectForEntityForName:[speechEntity name] inManagedObjectContext:context];
[columbia setValue:[NSNumber numberWithInt:[columbia hash]] forKey:@"speechId"];
[columbia setValue:@"Columbia" forKey:@"speechName"];
[columbia setValue:warren forKey:@"tycoon"];

请注意,我正在设置Speech方面的关系。核心数据将处理另一方。

问题#2

如果不查看整个根视图控制器,很难说你做错了什么。但是,我不会重复使用视图控制器来显示两种不同类型的对象。这是糟糕的形式,导致代码残骸。

相反,我会创建第二个“主”视图控制器,旨在处理语音。然后你可以传入大亨,你的SpeechViewController可以运行从关系派生的数组,或者它可以在内部构建其获取的结果控制器。即使更有价值的是能够自定义其单元格,标题等以适合它正在显示的数据。

问题#3

首先评论您添加的新代码:

SpeechViewController *speechView = [[SpeechViewController alloc] initWithNibName:@"SpeechViewController" bundle:[NSBundle mainBundle]];
Tycoon *tycoons = (Tycoon *)[fetchedResultsController objectAtIndexPath:indexPath];
speechView.tycoons = tycoons;
[self.navigationController pushViewController:speechView animated:YES];

ZERO 有理由退出-[NSFetchedResultsController objectAtIndexPath:]。任何返回id的方法都可以分配给任何指针。

Casting是编译器的谎言,应该不惜一切代价避免。

现在,把它放在一边,你的代码很好。我会将该物业命名为“大亨”而不是“大亨”,因为你传递了一个大亨实体。

SpeechViewController方面,您现在可以访问与该Tycoon实体相关联的演讲,并根据您的选择显示它们。

您看到的问题是什么?

问题#4

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (!cell) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
    }
    NSSet *speechesSet = [[self tycoon] valueForKey:@"TycoonToSpeech"];
    // Sort the speeches
    NSSortDescriptor *nameSort = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];
    NSArray *sortedSpeeches = [speechesSet sortedArrayUsingDescriptors:[NSArray arrayWithObject:nameSort]];
    // Configure the cell...
    id speech = [sortedSpeeches objectAtIndex:[indexPath row]];
    [[cell textLabel] setText:[speech valueForKey:@"name"]];

    return cell;
}

好的从顶部开始。首先,关系将返回NSSet而不是NSArray。关系的结果是无序的。因此,我们将结果作为NSSet抓取,然后对它们进行排序。我猜测你的Speech实体上有一个名称属性。

一旦我们对它们进行了排序,我们就可以抓住该行的项目。这将返回Speech实体。我将其分配给id,因为无论如何我们将使用KVC。

最后,我使用KVC从name实体获取Speech属性,并将其分配给单元格的textLabel

至于你的-tableView: numberOfRowsInSection:,那个更简单:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return [[[self tycoon] valueForKey:@"TycoonToSpeech"] count];
}

因为您只有一个部分,所以您只需要返回演讲次数。

我之前提到过你的属性名称需要一些工作。由于Tycoon只是一个对象而TycoonToSpeech只是该对象的一个​​属性,因此该属性应该被称为speeches,因为它不比对象上的任何其他属性更特殊。属性名称在代码中流动得更好,使代码更容易使用。此外,Objective-C命名约定要求属性aka属性以小写字母开头。