添加具有关系的实体 NSSet 后应用程序挂起/崩溃

发布于 2024-10-06 09:49:09 字数 3696 浏览 5 评论 0 原文

我必须解决我认为相关的主要问题,因为它们都发生在同一行代码上。

数据模型

注意:我已尽我所能简化了代码和模型。

我的核心数据模型中有 3 个实体。

  • Merchant(可以有多个Branch,可以有多个Sector
  • Sector(可以有多个商家
  • 分支(可以有一个商家

数据下载(以 JSON 格式)到应用程序。每个 Merchant 都会在提取的扇区上进行迭代,如果扇区存在,则将其提取并添加到 NSMutableArray 中。

...
//Iterating through Merchants
...
for(NSDictionary *sector in sectors) {
    NSLog(@"\tfetch sectors ID %@", [sector objectForKey:@"sector_id"]);
        
    NSPredicate *sectorPredicate = [NSPredicate predicateWithFormat:@"%K == %d", @"sectorID", [[sector objectForKey:@"sector_id"] integerValue]];
    [sectorRequest setPredicate:sectorPredicate];
                        
    NSArray *existingSector = [self.managedObjectContext executeFetchRequest:sectorRequest error:&error];
                
    if(!error && [existingSector count] == 1) {
        NSLog(@"\tfound sector");
    [merchantSectors addObject:[existingSector objectAtIndex:0]];
    }
    else {
        NSLog(@"\tcreate a new sector");
            
        //Create a new sector
        Sector *newSector = [[Sector alloc] initWithEntity:sectorEntity insertIntoManagedObjectContext:self.managedObjectContext];
        newSector.sectorID = [NSNumber numberWithInteger:[[sector objectForKey:@"sector_id"] integerValue]];
        newSector.name = [sector objectForKey:@"name"];
            
        [merchantSectors addObject:newSector];
            
        [newSector release]; newSector = nil;
    }
}
    
[sectorRequest release]; sectorRequest = nil;
    
NSLog(@"\tadd sectors to merchant");
[currentMerchant addSector:merchantSectors]; //<---- crash and hang

应用程序要么挂在:

 [currentMerchant addSector:merchantSectors];

要么有时抛出异常:

*** Terminating app due to uncaught exception \
'NSInternalInconsistencyException', reason: \ 
'-[__NSCFSet addObject:]: mutating method sent to immutable object'

Branch 解析代码几乎相同,但从来没有这些问题,或者应用程序会在成为问题之前挂起或崩溃(??)。

如果删除应用程序并重新安装代码将正常工作,是否可能是现有的相同关系导致了此问题?

编辑: JSON 的解析是使用 NSInitationOperation 调用的,因此当它挂起时,界面仍保持响应。崩溃版本会杀死该应用程序。

编辑2: Merchant.h 和 Merchant.m

Merchant.h

#import <CoreData/CoreData.h>

@class Branch;
@class Sector;

@interface Merchant :  NSManagedObject  
{
}

@property (nonatomic, retain) NSString * street;
@property (nonatomic, retain) NSString * locality;
@property (nonatomic, retain) NSString * city;
@property (nonatomic, retain) NSNumber * merchantID;
@property (nonatomic, retain) NSString * postcode;
@property (nonatomic, retain) NSString * property;
@property (nonatomic, retain) NSString * organisation;
@property (nonatomic, retain) NSDate * expires;
@property (nonatomic, retain) NSSet * Branch;
@property (nonatomic, retain) NSSet* Sector;

@end


@interface Merchant (CoreDataGeneratedAccessors)
- (void)addBranchObject:(Branch *)value;
- (void)removeBranchObject:(Branch *)value;
- (void)addBranch:(NSSet *)value;
- (void)removeBranch:(NSSet *)value;
- (void)addSectorObject:(Sector *)value;
- (void)removeSectorObject:(Sector *)value;
- (void)addSector:(NSSet *)value;
- (void)removeSector:(NSSet *)value;

@end

Merchant.m

#import "Merchant.h"
#import "Branch.h"

@implementation Merchant 

@dynamic street;
@dynamic locality;
@dynamic city;
@dynamic merchantID;
@dynamic postcode;
@dynamic property;
@dynamic organisation;
@dynamic expires;
@dynamic Branch;
@dynamic Sector;

@end

I have to main issues that I believe are related as they both occur on the same line of code.

Data Model

NB: I have simplified the code and model as best I can.

I have 3 entities in my Core data model.

  • Merchant (can have many Branches, can have many Sectors)
  • Sector (can have many Merchants)
  • Branch (can have one Merchant)

Data is downloaded (in JSON) to the app. Each Merchant is iterated over sectors are extracted, if the sector exists it is fetched and added to a NSMutableArray.

...
//Iterating through Merchants
...
for(NSDictionary *sector in sectors) {
    NSLog(@"\tfetch sectors ID %@", [sector objectForKey:@"sector_id"]);
        
    NSPredicate *sectorPredicate = [NSPredicate predicateWithFormat:@"%K == %d", @"sectorID", [[sector objectForKey:@"sector_id"] integerValue]];
    [sectorRequest setPredicate:sectorPredicate];
                        
    NSArray *existingSector = [self.managedObjectContext executeFetchRequest:sectorRequest error:&error];
                
    if(!error && [existingSector count] == 1) {
        NSLog(@"\tfound sector");
    [merchantSectors addObject:[existingSector objectAtIndex:0]];
    }
    else {
        NSLog(@"\tcreate a new sector");
            
        //Create a new sector
        Sector *newSector = [[Sector alloc] initWithEntity:sectorEntity insertIntoManagedObjectContext:self.managedObjectContext];
        newSector.sectorID = [NSNumber numberWithInteger:[[sector objectForKey:@"sector_id"] integerValue]];
        newSector.name = [sector objectForKey:@"name"];
            
        [merchantSectors addObject:newSector];
            
        [newSector release]; newSector = nil;
    }
}
    
[sectorRequest release]; sectorRequest = nil;
    
NSLog(@"\tadd sectors to merchant");
[currentMerchant addSector:merchantSectors]; //<---- crash and hang

The App will either hang at:

 [currentMerchant addSector:merchantSectors];

or sometimes throw an exception:

*** Terminating app due to uncaught exception \
'NSInternalInconsistencyException', reason: \ 
'-[__NSCFSet addObject:]: mutating method sent to immutable object'

The Branch parsing code is almost identical but never has these issues or the app will hang or crash before it becomes an issue (??).

If the App is deleted and reinstalled the code will work fine, is it possible that existing identical relationships are causing this problem?

Edit: The parsing of the JSON is called using an NSInvocationOperation, so when it hangs the interface stays responsive. The crash version kills the app.

Edit 2: Merchant.h and Merchant.m

Merchant.h

#import <CoreData/CoreData.h>

@class Branch;
@class Sector;

@interface Merchant :  NSManagedObject  
{
}

@property (nonatomic, retain) NSString * street;
@property (nonatomic, retain) NSString * locality;
@property (nonatomic, retain) NSString * city;
@property (nonatomic, retain) NSNumber * merchantID;
@property (nonatomic, retain) NSString * postcode;
@property (nonatomic, retain) NSString * property;
@property (nonatomic, retain) NSString * organisation;
@property (nonatomic, retain) NSDate * expires;
@property (nonatomic, retain) NSSet * Branch;
@property (nonatomic, retain) NSSet* Sector;

@end


@interface Merchant (CoreDataGeneratedAccessors)
- (void)addBranchObject:(Branch *)value;
- (void)removeBranchObject:(Branch *)value;
- (void)addBranch:(NSSet *)value;
- (void)removeBranch:(NSSet *)value;
- (void)addSectorObject:(Sector *)value;
- (void)removeSectorObject:(Sector *)value;
- (void)addSector:(NSSet *)value;
- (void)removeSector:(NSSet *)value;

@end

Merchant.m

#import "Merchant.h"
#import "Branch.h"

@implementation Merchant 

@dynamic street;
@dynamic locality;
@dynamic city;
@dynamic merchantID;
@dynamic postcode;
@dynamic property;
@dynamic organisation;
@dynamic expires;
@dynamic Branch;
@dynamic Sector;

@end

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

空城缀染半城烟沙 2024-10-13 09:49:09

尝试使用 CoreData addObject:removeObject: 自动生成的方法(如自定义到-许多关系访问器方法

for(NSDictionary *sector in sectors) {
    NSPredicate *sectorPredicate = [NSPredicate predicateWithFormat:@"%K == %d", @"sectorID", [[sector objectForKey:@"sector_id"] integerValue]];
    [sectorRequest setPredicate:sectorPredicate];

    NSArray *existingSector = [self.managedObjectContext executeFetchRequest:sectorRequest error:&error];

    if(!error && [existingSector count] == 1) 
    {
        [currentMerchant addSectorObject:[existingSector lastObject]];
    }
    else 
    {
        Sector *newSector = [[Sector alloc] initWithEntity:sectorEntity insertIntoManagedObjectContext:self.managedObjectContext];
        newSector.sectorID = [NSNumber numberWithInteger:[[sector objectForKey:@"sector_id"] integerValue]];
        newSector.name = [sector objectForKey:@"name"];

        [currentMerchant addSectorObject:newSector];

        [newSector release];
    }
}

或者您可以通过mutableSetValueForKey:检索包含当前商家扇区的可变代理对象并向其添加扇区:

NSMutableSet *merchantSectors = [currentMerchant mutableSetValueForKey:@"sector"];
for(NSDictionary *sector in sectors) {
    NSPredicate *sectorPredicate = [NSPredicate predicateWithFormat:@"%K == %d", @"sectorID", [[sector objectForKey:@"sector_id"] integerValue]];
    [sectorRequest setPredicate:sectorPredicate];

    NSArray *existingSector = [self.managedObjectContext executeFetchRequest:sectorRequest error:&error];

    if(!error && [existingSector count] == 1) 
    {
        [merchantSectors addObject:[existingSector lastObject]];
    }
    else 
    {
        Sector *newSector = [[Sector alloc] initWithEntity:sectorEntity insertIntoManagedObjectContext:self.managedObjectContext];
        newSector.sectorID = [NSNumber numberWithInteger:[[sector objectForKey:@"sector_id"] integerValue]];
        newSector.name = [sector objectForKey:@"name"];

        [merchantSectors addObject:newSector];

        [newSector release];
    }
}

无论如何,为了方便起见,最好使用小写sectors< /code> Mecrhant 实体的名称,用于与 Sector 实体的一对多关系:小写,以免与 Sector 类名和 s 混淆,并且可以肯定的是,该属性的 getter 方法返回多个对象。

Try to add Sectors to Merchant one by one using CoreData add<Key>Object: and remove<Key>Object: auto-generated methods (as described in Custom To-Many Relationship Accessor Methods)

for(NSDictionary *sector in sectors) {
    NSPredicate *sectorPredicate = [NSPredicate predicateWithFormat:@"%K == %d", @"sectorID", [[sector objectForKey:@"sector_id"] integerValue]];
    [sectorRequest setPredicate:sectorPredicate];

    NSArray *existingSector = [self.managedObjectContext executeFetchRequest:sectorRequest error:&error];

    if(!error && [existingSector count] == 1) 
    {
        [currentMerchant addSectorObject:[existingSector lastObject]];
    }
    else 
    {
        Sector *newSector = [[Sector alloc] initWithEntity:sectorEntity insertIntoManagedObjectContext:self.managedObjectContext];
        newSector.sectorID = [NSNumber numberWithInteger:[[sector objectForKey:@"sector_id"] integerValue]];
        newSector.name = [sector objectForKey:@"name"];

        [currentMerchant addSectorObject:newSector];

        [newSector release];
    }
}

Or you can retrieve mutable proxy object contains currentMerchants's sectors via mutableSetValueForKey: and add sectors to it:

NSMutableSet *merchantSectors = [currentMerchant mutableSetValueForKey:@"sector"];
for(NSDictionary *sector in sectors) {
    NSPredicate *sectorPredicate = [NSPredicate predicateWithFormat:@"%K == %d", @"sectorID", [[sector objectForKey:@"sector_id"] integerValue]];
    [sectorRequest setPredicate:sectorPredicate];

    NSArray *existingSector = [self.managedObjectContext executeFetchRequest:sectorRequest error:&error];

    if(!error && [existingSector count] == 1) 
    {
        [merchantSectors addObject:[existingSector lastObject]];
    }
    else 
    {
        Sector *newSector = [[Sector alloc] initWithEntity:sectorEntity insertIntoManagedObjectContext:self.managedObjectContext];
        newSector.sectorID = [NSNumber numberWithInteger:[[sector objectForKey:@"sector_id"] integerValue]];
        newSector.name = [sector objectForKey:@"name"];

        [merchantSectors addObject:newSector];

        [newSector release];
    }
}

Anyway, for convenience it's better to use lowercase sectors name for Mecrhant entity for to-many relationship with Sector entity: lowercase not to be ambiguous with Sector class name, and with s at and to be sure, that getter methods for this property return multiple objects.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文