代码之家  ›  专栏  ›  技术社区  ›  Matthes

使用CoreData在iPhone上导入大型数据集

  •  5
  • Matthes  · 技术社区  · 16 年前

    根据“高效导入数据”一文,我使用了所有推荐的技术 http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Conceptual/CoreData/Articles/cdImporting.html 还有其他关于这个的文件,但是速度还是很慢。

    任何关于如何克服这些问题的建议都是非常感谢的! 谢谢

    9 回复  |  直到 16 年前
        1
  •  4
  •   Felix Lamouroux    16 年前

    1. 不需要在电话上输入

    如果总是替换整个内容,只需覆盖设备中的持久存储。否则,您可能希望将一个数组维护为plist,其中包含您下载的所有sqlite,然后使用此命令将所有存储添加到persistentstorecordinator。

    一句话:使用几个预编译的sqlite文件并将它们添加到persistentstorecordinator。

    您可以使用iPhone模拟器来创建CoreData SQLite商店,或者使用一个独立的Mac应用程序。你需要自己写这两个。

        2
  •  4
  •   Marcus S. Zarra    16 年前

    但是,如果你不能这样做,那么我会这样做:

    1. 数据下载后,将其分解为多个文件 导入。
    2. 在后台线程上导入,一次一个文件。
    3. 导入并保存文件后,删除导入文件。
    4. 在启动时,查找那些等待处理的文件,并从您离开的地方提取。

        3
  •  2
  •   epatel    16 年前

    这基本上是ViewControllers viewDidLoad

    - (void)viewDidLoad 
    {
        [super viewDidLoad];
    
        NSError *error = nil;
        if (![[self fetchedResultsController] performFetch:&error]) {
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        }
    
        // Only insert those not imported, here I know it should be 2006 entries
        if ([self tableView:nil numberOfRowsInSection:0] != 2006) {
    
            // Put up an alert with a progress bar, need to implement
            [self createProgressionAlertWithMessage:@"Initilizing database"];  
    
            // Spawn the insert thread making the app still "live" so it 
            // won't be killed by the OS
            [NSThread detachNewThreadSelector:@selector(loadInitialDatabase:) 
                                     toTarget:self 
                          withObject:[NSNumber numberWithInt:[self tableView:nil 
                                                    numberOfRowsInSection:0]]];
        }
    }
    

    - (void)loadInitialDatabase:(NSNumber*)number
    {
        NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    
        int done = [number intValue]+1; // How many done so far
    
        // I load from a textfile (csv) but imagine you should be able to 
        // understand the process and make it work for your data
        NSString *file = [NSString stringWithContentsOfFile:[[NSBundle mainBundle]
                                                    pathForResource:@"filename"
                                                             ofType:@"txt"] 
                                                   encoding:NSUTF8StringEncoding
                                                      error:nil];
    
        NSArray *lines = [file componentsSeparatedByString:@"\n"];
    
        float num = [lines count];
        float i = 0;
        int perc = 0;
    
        for (NSString *line in lines) {
            i += 1.0;
    
            if ((int)(i/(num*0.01)) != perc) {
                // This part updates the alert with a progress bar
                // setProgressValue: needs to be implemented 
                [self performSelectorOnMainThread:@selector(setProgressValue:) 
                                       withObject:[NSNumber numberWithFloat:i/num] 
                                    waitUntilDone:YES]; 
                perc = (int)(i/(num*0.01));
            }
    
            if (done < i) // keep track of how much done previously
                [self insertFromLine:line]; // Add to data storage...
    
        }
    
        progressView = nil;
        [progressAlert dismissWithClickedButtonIndex:0 animated:YES]; 
        [pool release];
    }
    

    这样有点粗糙,它试图从它离开的地方初始化数据存储,如果用户在前一次停止它。。。

        4
  •  1
  •   james    15 年前

    我在将许多对象导入CoreData时遇到了类似的问题。最初我在做 save 在我希望创建的每个对象之后的托管对象上下文中插入。

    您应该做的是创建/初始化要保存在CoreData中的每个对象,并在循环遍历所有远程数据+创建对象之后,执行托管对象上下文 节约 .

    我想您可以将此看作在SQLite数据库中执行事务:开始事务、执行大量插入/更新、结束事务。

    如果这仍然太长,只需执行这个该死的任务,并在完成之前阻止用户交互

        5
  •  0
  •   mr-sk    16 年前

    如果数据是时间敏感的,或者还没有准备好,或者由于任何原因您不能这样做,您可以在通过网络传送数据之前使用zlib压缩来压缩数据吗?

        6
  •  0
  •   Jared Stehler    16 年前

    我想你没有向客户展示所有的5K记录吧?我建议您在服务器上执行所需的所有聚合,然后只向手机发送必要的数据。即使这涉及到生成几个不同的数据视图,它仍然比在iPhone中发送(然后处理)所有这些行快几个数量级。

    您是否还在单独的(非事件/ui)线程中处理数据?

        7
  •  0
  •   georryan    16 年前

    有没有可能设置服务器端以公开RESTful web服务来处理数据?我也遇到了类似的问题,并且能够通过RESTful webservice公开我的信息。在iphone上有一些库可以让从这样的web服务中阅读变得非常容易。我选择从服务中请求JSON,并使用iphone上的SBJSON库快速获取得到的结果,并将其转换为字典,以便于使用。我使用ASIHTTP库来生成web请求,并将后续请求排队,并使它们在后台运行。

    REST的好处在于,它是一种内置的方式,可以让您获取成批的信息,这样您就不需要随意地找出如何分解要输入的文件。您只需设置要返回的记录数,然后在下一个请求中跳过这些记录。我不知道这是否是你的一个选择,所以我现在不想讨论很多代码示例,但如果可能的话,这可能是一个平滑的方法来处理它。

        8
  •  0
  •   Soft Dot IE    16 年前

    [self sQLdropIndex(OffendingIndexName)]
    [self breathInOverIP];
    [self breathOutToSQLLite];
    [self sQLAddIndex(OffendingIndexName)]
    

    这应该告诉你很多。

        9
  •  0
  •   Jacob    12 年前

    我在一个应用程序上工作,它必须定期处理10万个插入、删除和核心数据更新。如果它被5K的插入物阻塞,则需要进行一些优化。

    首先,创建一些NSOperation子类来处理数据。重写其-main方法以进行处理。但是,不能保证此方法在主线程上运行。实际上,它的目的是避免在主线程上执行代价高昂的代码,这会使主线程严重冻结,从而影响用户体验。因此,在-main方法中,需要创建另一个托管对象上下文,它是主线程的托管对象上下文的子上下文。

    - (void)main
    {
      NSManagedObjectContext *ctx = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
      [ctx setPersistentStoreCoordinator:mainManagedObjectContext.persistentStoreCoordinator];
      [ctx setUndoManager:nil];
      // Do your insertions here!
      NSError *error = nil;
      [ctx save:&error];
    }
    

    使用此上下文在-main方法中执行所有CRUD操作,然后保存该托管对象上下文。拥有主线程的托管对象上下文的任何对象都必须注册以响应名为nsmanagedobjectcontextdidsaverenotification的NSNotification。像这样注册:

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mocDidSaveNotification:) name:NSManagedObjectContextDidSaveNotification object:nil];
    

    - (void)mocDidSaveNotification:(NSNotification *)notification
    {
      NSManagedObjectContext *ctx = [notification object];
      if (ctx == mainManagedObjectContext) return;
      [mainManagedObjectContext mergeChangesFromContextDidSaveNotification:notification];
    }
    

    当所有这些结合在一起时,它将允许您在后台线程上执行长时间运行的操作,而不会阻塞UI线程。这种体系结构有几种变体,但中心主题是:在BG线程上处理、在主线程上合并、更新UI。还有一些事情要记住:(1)在处理过程中保持一个自动释放池,并每隔一段时间将其耗尽,以降低内存消耗。在我们的例子中,我们每1000个物体做一次。根据您的需要进行调整,但请记住,根据每个对象所需的内存量,消耗内存可能会非常昂贵,因此您不想经常这样做。(2) 尽量把你的数据缩减到你需要一个功能性应用程序的绝对最小值。通过减少要分析的数据量,可以减少保存数据所需的时间。(3) 通过使用这种多线程方法,您可以同时处理数据。因此,为NSOperation子类创建3-4个实例,每个实例只处理一部分数据,以便它们都并发运行,从而减少了解析数据集所需的实时时间。