1

我有一个关于管理对单例的访问的问题,此时它的初始化可能尚未完成,它是第一次使用。我创建了一个名为 StoriesModel 的单例,其中包含一些用于打开或创建其数据库文件的 Core Data 初始化代码。初始化后,这个单例使其 ManagedObjectContext 可用于应用程序中的各种屏幕,用于创建对象、显示对象表等。

我第一次打电话:

[StoriesModel sharedModel].context

它显然会失败。在 Objective C 中,解决这个问题的好模式是什么?我知道我可以在使用模型类之前对其进行第一次调用,但这并不总是有效。我以前的语言使用事件和数据绑定之类的东西来解决这个问题。我是多线程的新手。

这是标题的一些代码:

@interface StoriesModel : NSObject
+ (StoriesModel *)sharedModel;
@property (nonatomic,strong) NSManagedObjectContext *context;
@end

和实施:

@implementation StoriesModel

+ (StoriesModel *)sharedModel
{
    static StoriesModel *sharedSingleton;

    @synchronized(self)
    {
        if (!sharedSingleton)
        {
            sharedSingleton = [[StoriesModel alloc] init];
            [sharedSingleton doInit];
        }
        return sharedSingleton;
    }
}
- (void)doInit
{
    // do some stuff that results in the context property being set in a few seconds
}
@end

谢谢,

格里

-- 修改后的上下文获取器示例:

- (NSManagedObjectContext *)context
{
    if (!self.storyDatabase)
    {
        NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
        url = [url URLByAppendingPathComponent:@"StoryDatabase"];
        self.storyDatabase = [[UIManagedDocument alloc] initWithFileURL:url];

        // if file doesn't exist create it
        if (![[NSFileManager defaultManager] fileExistsAtPath:[self.storyDatabase.fileURL path]])
        {
            [self.storyDatabase saveToURL:_storyDatabase.fileURL forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success)
             {
                 _context = self.storyDatabase.managedObjectContext;
                 [self populateSampleStories];
             }];
        }
        // if file exists but is closed open it
        else if (self.storyDatabase.documentState == UIDocumentStateClosed)
        {
            [self.storyDatabase openWithCompletionHandler:^(BOOL success)
             {
                 _context = self.storyDatabase.managedObjectContext;
             }];
        }
        // if file exists and is open use it
        else if (self.storyDatabase.documentState == UIDocumentStateNormal)
        {
            _context = self.storyDatabase.managedObjectContext;
        }
    }
}
4

4 回答 4

4

您可以使用pthread_onceordispatch_once来确保您的单例初始化程序只执行一次。

dispatch_once示例:在 Objective C 中使用 GCD 的 dispatch_once 创建单例

于 2013-02-28T17:55:02.977 回答
2

Cocoa samurai 在他的博客中讨论了这个问题,这段代码应该可以快速创建单例,并适用于多线程应用程序。

  +(MyClass *)singleton {
  static dispatch_once_t pred;
  static MyClass *shared = nil;

  dispatch_once(&pred, ^{
    shared = [[MyClass alloc] init];
  });
  return shared;
  }

查看此链接
http://cocoasamurai.blogspot.com/2011/04/singletons-your-doing-them-wrong.html

于 2013-02-28T17:57:38.687 回答
0

这里的基本设计问题是:在创建单例 StoriesModel 时,你能做有用的事情吗?做这些会使你的代码复杂化吗?

我的偏好是

 [StoriesModel sharedModel].context

进行初始化并仅在准备好时返回。

 NSBlockOperation *op=[NSBlockOperationWithBlock:(^{
      StoryContext *context=[StoriesModel sharedModel].context;
      [context doSomething];
 };

将其转至另一个线程并继续执行其他任务。


例如,您的 getter 可能非常简单:

- (NSManagedObjectContext *)context
{
    if (self.storyDatabase) return self.storyDatabase;
    self.storyDatabase=[self spendSomeTimeAt: theLibrary];  // slow!
    return self.storyDatabase;
}

你的初始化代码可能是这样的:

  NSBlockOperation *op=[NSBlockOperationWithBlock:(^{
  StoryContext *context=[StoriesModel sharedModel].context;
        [context doSomething];
  };
  NSOperatorQueue *q=[self workQueue];
  [q addOperation: op];
  // proceed to do some other things while visiting the library
  [self initializeGraphics];
  [self cook: dinner];
  [self refactor: yourCode];

但是所有这些都带有一些过早的优化。如果您不确定第一次设置上下文是否会成为瓶颈,请以直接的方式进行,并在下雨的下午再回来。

于 2013-02-28T18:46:48.653 回答
0

这实际上取决于您是否可以阻止尝试访问单例的线程。如果你是,你可以尝试使用dispatch_semaphore. 基本上它可以让你说“直到完成一些工作,阻止这个调用”(还可以让你管理一组资源,但这与此无关)。或者只是有一个布尔值doneivar 和while(!done){}.

您还可以让您的 sharedModel 和 doInit 采用完成块或发布和NSNotification.

于 2013-02-28T20:56:01.550 回答