5

这是我在 Stack Overflow 上的第一个问题,如果我违反了任何礼仪,请见谅。我对 Objective-C/app 创建也很陌生。

我一直在关注 CS193P 斯坦福课程,特别是 CoreData 讲座/演示。在 Paul Hegarty 的 Photomania 应用程序中,他从表格视图开始,并在后台填充数据,而不会中断 UI 流程。我一直在创建一个列出本地企业的应用程序(来自返回 JSON 数据的 api)。

我根据保罗的照片/摄影师课程创建了类别。类本身的创建不是问题,而是它们被创建的地方。

A simplified data structure:
- Section
    - Sub-section
        - business
        - business
        - business
    - business
    - business
    - business

我的应用程序从带有几个按钮的 UIViewController 开始,每个按钮都为相应的部分打开一个表格视图(这些都工作正常,我试图提供足够的信息,以便我的问题有意义)。我调用一个辅助方法来创建/打开 UIManagedDocument 的 URL,它基于这个问题。一旦应用程序运行,它就会被调用,并且加载速度很快。

我有一个非常类似于 Paul 的 fetchFlickrDataIntoDocument 的方法:

-(void)refreshBusinessesInDocument:(UIManagedDocument *)document
{
dispatch_queue_t refreshBusinessQ = dispatch_queue_create("Refresh Business Listing", NULL);
dispatch_async(refreshBusinessQ, ^{
    // Get latest business listing
    myFunctions *myFunctions = [[myFunctions alloc] init];
    NSArray *businesses = [myFunctions arrayOfBusinesses];

    // Run IN document's thread
    [document.managedObjectContext performBlock:^{

        // Loop through new businesses and insert
        for (NSDictionary *businessData in businesses) {
            [Business businessWithJSONInfo:businessData inManageObjectContext:document.managedObjectContext];
        }

        // Explicitly save the document.
        [document saveToURL:document.fileURL 
           forSaveOperation:UIDocumentSaveForOverwriting
          completionHandler:^(BOOL success){
              if (!success) {
                  NSLog(@"Document save failed");
              }
          }];
        NSLog(@"Inserted Businesses");
    }];
});
dispatch_release(refreshBusinessQ);
}

[myFunctions arrayOfBusinesses] 只是解析 JSON 数据并返回一个包含各个业务的 NSArray。

我在业务创建代码的开始和结束时使用 NSLog 运行代码。每个业务都分配一个版块,创建时间为 0.006 秒,其中有数百个。插入最终需要大约 2 秒。

辅助方法在这里:

// The following typedef has been defined in the .h file
// typedef void (^completion_block_t)(UIManagedDocument *document);

@implementation ManagedDocumentHelper

+(void)openDocument:(NSString *)documentName UsingBlock:(completion_block_t)completionBlock
{
    // Get URL for document -> "<Documents directory>/<documentName>"
    NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
    url = [url URLByAppendingPathComponent:documentName];

    // Attempt retrieval of existing document
    UIManagedDocument *doc = [managedDocumentDictionary objectForKey:documentName];

    // If no UIManagedDocument, create
    if (!doc) 
    {
        // Create with document at URL
        doc = [[UIManagedDocument alloc] initWithFileURL:url];

        // Save in managedDocumentDictionary
        [managedDocumentDictionary setObject:doc forKey:documentName];
    }

    // If the document exists on disk
    if ([[NSFileManager defaultManager] fileExistsAtPath:[url path]]) 
    {
        [doc openWithCompletionHandler:^(BOOL success)
         {
             // Run completion block
             completionBlock(doc);
         } ];
    }
    else
    {
        // Save temporary document to documents directory
        [doc saveToURL:url 
      forSaveOperation:UIDocumentSaveForCreating 
     completionHandler:^(BOOL success)
         {
             // Run compeltion block
             completionBlock(doc);
         }];
    }
}

并在 viewDidLoad 中调用:

if (!self.lgtbDatabase) {
    [ManagedDocumentHelper openDocument:@"DefaultLGTBDatabase" UsingBlock:^(UIManagedDocument *document){
        [self useDocument:document];
    }];
}

useDocument 只是将 self.document 设置为提供的文档。

我想更改此代码,以便将数据插入另一个线程,并且用户仍然可以单击按钮查看部分,而数据导入不会挂起 UI。

任何帮助将不胜感激我已经在这个问题上工作了几天但无法解决它,即使这里有其他类似的问题。如果您需要任何其他信息,请告诉我!

谢谢

编辑:

到目前为止,这个问题已经收到一票反对票。如果有办法我可以改进这个问题,或者有人知道我无法找到的问题,你能否评论一下如何或在哪里?如果还有其他原因您拒绝投票,请告诉我,因为我无法理解消极情绪,并且很想学习如何做出更好的贡献。

4

2 回答 2

11

有几种方法可以做到这一点。

由于您正在使用UIManagedDocument,您可以利用NSPrivateQueueConcurrencyTypefor 初始化一个新的NSManagedObjectContextperformBlock用来做您的事情。例如:

// create a context with a private queue so access happens on a separate thread.
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
// insert this context into the current context hierarchy
context.parentContext = parentContext;
// execute the block on the queue of the context
context.performBlock:^{

      // do your stuff (e.g. a long import operation)

      // save the context here
      // with parent/child contexts saving a context push the changes out of the current context
      NSError* error = nil;
      [context save:&error];
 }];

当您从上下文中保存时,私有上下文的数据被推送到当前上下文。保存仅在内存中可见,因此您需要访问主上下文(链接到的那个UIDocument)并在那里进行保存(看看dos-a-core-data-parent-managedobjectcontext-need-to-share -a-并发类型-wi )。

另一种方式(我最喜欢的方式)是创建一个NSOperation子类并在那里做一些事情。例如,声明一个NSOperation子类,如下所示:

//.h
@interface MyOperation : NSOperation

- (id)initWithDocument:(UIManagedDocument*)document;

@end

//.m
@interface MyOperation()

@property (nonatomic, weak) UIManagedDocument *document;

@end

- (id)initWithDocument:(UIManagedDocument*)doc;
{
  if (!(self = [super init])) return nil;

  [self setDocument:doc];

  return self;
}

- (void)main
{ 
  NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] init];
  [moc setParentContext:[[self document] managedObjectContext]];

  // do the long stuff here...

  NSError *error = nil;
  [moc save:&error];

  NSManagedObjectContext *mainMOC = [[self document] managedObjectContext];
  [mainMOC performBlock:^{
    NSError *error = nil;
    [mainMOC save:&error];
  }];

  // maybe you want to notify the main thread you have finished to import data, if you post a notification remember to deal with it in the main thread...
}

现在在主线程中,您可以将该操作提供给队列,如下所示:

MyOperation *op = [[MyOperation alloc] initWithDocument:[self document]];
[[self someQueue] addOperation:op];

PS 你不能在maina 的方法中启动异步操作NSOperation。完成main后,将不会调用与该操作关联的委托。说实话,你可以,但这涉及到处理运行循环或并发行为。

希望有帮助。

于 2012-06-12T10:53:35.080 回答
0

最初我只是想发表评论,但我想我没有权限。我只是想指出 UIDocument,超出了更改计数提供的范围 - (void)autosaveWithCompletionHandler:(void (^)(BOOL success))completionHandler

这不应该有我在等待“方便的时刻”时更新更改计数时遇到的延迟。

于 2012-07-27T02:09:04.870 回答