【问题标题】:Check two NSArrays for containing each other's objects (NSManagedObject)检查两个 NSArray 是否包含彼此的对象 (NSManagedObject)
【发布时间】:2012-02-14 16:12:11
【问题描述】:

我已经被以下问题困扰了很长一段时间了: 我有两个 NSArray,都包含 NSManagedObject 子类对象。 它们由不同的来源提供,但其中的对象仍然具有相同的属性/值。 我现在要做的是检查数组 A 是否包含数组 B 中的对象,反之亦然。 不幸的是,NSArray 的 containsObject 方法似乎在这里不起作用。 我认为它使用 id-testing 对每个对象进行相等性检查,不是吗?

那么,有人知道要尝试什么吗?

我什至尝试将我的对象封装在 NSSets 中,使用 member: 作为我的比较方法,但这也没有奏效,特别是因为 NSManagedObject 子类的“你不能覆盖”isEqual 等。

这是一个代码sn-p:

//manufacturers is an array, parsed out of some xml here...

for(Manufacturer *manu in [fetchedResultsController fetchedObjects])
{
    if(![manufacturers containsObject:manu])
    {
        NSLog(@"Deleting %@", manu.name);
        [self.mContext deleteObject:manu];
    }
}

for(Manufacturer *manu in manufacturers)
{
    if(![[fetchedResultsController fetchedObjects] containsObject:manu])
    {
        NSLog(@"Adding %@", manu.name);
        [newArray addObject:manu];
    }
}

提前感谢任何提示;)

【问题讨论】:

    标签: iphone objective-c ios core-data


    【解决方案1】:

    我不确定这是否有效,但您可以尝试将您获得的字典与dictionaryWithValuesForKeys: 匹配。

    类似这样的:

    NSArray *keysToCompare = [NSArray arrayWithObjects:@"FooAttribute", @"BarAttribute", nil];
    
    // create an array with the dictionary representation of the managedObject
    NSMutableArray *fetchedObjectsDictionaries = [NSMutableArray arrayWithCapacity:[[fetchedResultsController fetchedObjects] count]];
    for (NSManagedObject *object in [fetchedResultsController fetchedObjects]) {
        NSDictionary *dictionaryRepresentation = [object dictionaryWithValuesForKeys:keysToCompare];
        [fetchedObjectsDictionaries addObject:dictionaryRepresentation];
    }
    
    // another array with dictionaries for managedObjects
    NSMutableArray *manufacturersDictionaries = [NSMutableArray arrayWithCapacity:[manufacturers count]];
    for (NSManagedObject *object in manufacturers) {
        NSDictionary *dictionaryRepresentation = [object dictionaryWithValuesForKeys:keysToCompare];
        [manufacturersDictionaries addObject:dictionaryRepresentation];
    }
    
    // compare those dictionaries
    for (NSInteger i = 0; i < [fetchedObjectsDictionaries count]; i++) {
        NSDictionary *dictionary = [fetchedObjectsDictionaries objectAtIndex:i];
        if (![manufacturersDictionaries containsObject:dictionary]) {
            // get the corresponding managedObject
            NSManagedObject *object = [[fetchedResultsController fetchedObjects] objectAtIndex:i];
            [newArray addObject:object];
        }
    }
    

    如果这不起作用,您可以编写自己的 isEqualToManufacturer: 方法并手动枚举数组。

    【讨论】:

    • 如果这些数组包含多个对象,这将非常昂贵且缓慢。
    【解决方案2】:

    您可以检查 3 种相等类型:相同的内存地址、托管对象 id 相等和值相等。您当前的代码已经检查对象是否共享相同的内存地址,这很可能不是您感兴趣的。这留下了两种可能的选择。使用托管对象 id 相等方法,您可以检查制造商是否指向数据库中的同一行。使用值相等,您可以根据共享值检查两个制造商是否相等。下面是一种检查 NSManagedObjectID 是否相等的方法。

    for(Manufacturer *manu in [fetchedResultsController fetchedObjects])
    {
        id databaseIDTest = ^(Manufacturer * checkManu, NSUInteger idx, BOOL *stop){
            return [[checkManu objectID] isEqual:[manu objectID]];
        };
    
        if([manufacturers indexOfObjectPassingTest:databaseIDTest] == NSIndexNotFound)
        {
            NSLog(@"Deleting %@", manu.name);
            [self.mContext deleteObject:manu];
        }
    }
    
    for(Manufacturer *manu in manufacturers)
    {
        id databaseIDTest = ^(Manufacturer * checkManu, NSUInteger idx, BOOL *stop){
            return [[checkManu objectID] isEqual:[manu objectID]];
        };
        NSArray * fetchedObjects = [fetchedResultsController fetchedObjects];
        if([fetchedObjects indexOfObjectPassingTest:databaseIDTest] == NSIndexNotFound)
        {
            NSLog(@"Adding %@", manu.name);
            [newArray addObject:manu];
        }
    }
    

    【讨论】:

    • 这并不完全正确。由于您处于相同的上下文中,因此比较两个指针(内存地址)和比较两个托管对象 ID 可以有效地做同样的事情,但比较两个指针要便宜 很多(数量级)。
    • 非常感谢您的回答,但这似乎也不起作用......认为我可能不得不手动迭代我的数组并计算出现次数。至少这是我能想到的以“我的方式”测试对象的唯一方法。
    【解决方案3】:

    您需要覆盖 -isEqual:,因为这是 -[NSArray containsObject:] 调用的内容:

    - (BOOL)isEqual:(id)other;
    {
        if (![other isKindOfClass:[Manufacturer class]]) {
            return NO;
        }
        Manufacturer *otherManufacturer = other;
        return ([self.name isEqual:otherManufacturer.name] &&
               ...
               );
    }
    

    检查 NSSet 中的包含成本更低(如果遇到性能问题,这可能是有意义的)。它只有在你有一个相对不错的-hash 实现时才有效,但它很容易像这样实现:

    - (NSUInteger)hash;
    {
        return [self.name hash] + [self.foo hash] + ...;
    }
    

    不要对哈希处理太多麻烦,只需使用 2 - 3 个最有可能的值来唯一标识对象。

    【讨论】:

    • 我希望能够简单地覆盖我的对象的比较方法,但由于我使用的是 NSManagedObject 子类,所以我不能。否则这肯定是最好的解决方案;)
    猜你喜欢
    • 1970-01-01
    • 2015-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-09
    • 1970-01-01
    • 1970-01-01
    • 2015-11-10
    相关资源
    最近更新 更多