GCD和可变对象

时间:2011-12-13 10:03:01

标签: objective-c ios grand-central-dispatch

我有一个单例数据管理器对象,它管理与Web服务的连接,处理数据,然后用对象更新可变字典。对这个字典的所有请求都是通过我创建的这个单例实例。我会一次又一次地遇到冲突,因为我显然是从很多线程编辑一个可变对象,所以偶尔会得到EXC_BAD_ACCESS。

我可以实现@synchronized方法或使用NSLock,但因为我已经广泛使用Grand Central Dispatch,我想知道是否有什么我可以通过同步强制它们在一个线程中完成所有事情?我也不确定上述方法是否有效以及如何实施。

以下示例代码。 getObjectArray方法可能会发生异常,因为我可以在translateToObjectFromDict方法中添加/更新子对象/删除。

NSMutableDictionary *staticDictionary;

-(NSArray *)getObjectArray{

    NSArray *returnArray = [[[NSArray alloc]init]autorelease];
    returnArray = [self.staticDictionary allValues];
    return returnArray;
}

-(void)translateToObjectFromDict:(NSDictionary *)sourceDictionary{



    dispatch_group_t taskGroup = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(0,0);


    dispatch_group_async(taskGroup,queue,^(){

    for (id key in sourceDictionary) {
        NSDictionary *subDictionary = [sourceDictionary objectForKey:key];
        for (id key in subDictionary) {

            NSArray *theObjData = [subDictionary objectForKey:key];
            if ([theObjData count] == 12) {

               theObj *temptheObj = [[PINKtheObj alloc] initWith......
                    NSString *unihex = temptheObj.unihex;

                    if ([self.staticDictionary objectForKey:unihex]){
                        PINKtheObj *localtheObj = [self.staticDictionary objectForKey:unihex];
                        if (temptheObj.time > localtheObj.time) {
                            [localtheObj updatetheObjWith.......
                        [temptheObj release];
                    }else{
                        [self.staticDictionary setObject:temptheObj forKey:unihex];
                    }
            }

        }
    }
    });

    dispatch_group_notify(taskGroup, queue, ^{
        NSDictionary *getMainDict = [self.mainDictionary objectForKey:@"theObjs"];

        for (id key in self.staticDictionary) {
            if (![getMainDict objectForKey:key]){
                [self.removeArray addObject:[self.staticDictionary objectForKey:key]];
            }
        }

        [[NSNotificationCenter defaultCenter]postNotificationName:@"updated" object:nil];
    });
    dispatch_release(taskGroup);

}

2 个答案:

答案 0 :(得分:3)

我认为您应该将所有电话(objectForKey:setObject:ForKey:等)提供给您自己的串行队列中的staticDictionary。当单身人士初始化并在任何地方使用时创建它 在头文件中创建ivar。

dispatch_queue_t queueDict;

在.m文件中只需添加到init

queueDict = dispatch_queue_create("com.yourid.yoursingleton.queueDict ", NULL);

当您想要访问或将数据放入字典时,只需制作

dispatch_async(queueDict, ^(){
[staticDict setObject:obj forKey:key]; //example
}

如果您希望任务等待返回值,可以使用dispatch_sync

__block id returnValue;
dispatch_sync(queueDict, ^(){
returnValue = [statictDict objectForKey:key]; //example
}
obj = returnValue;

答案 1 :(得分:0)

在NSBlockOperation或NSInvocation对象中封装每个访问字典的请求,然后将操作添加到NSOperationQueue,将最大并发操作数设置为1:

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue setMaxConcurrentOperationCount:1];
    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"Doing something...");
    }];
    
    [queue addOperation:operation];

如果存在一致的访问模式,例如使用Producer-Consumer设计模式的实现所固有的模式,则可以创建可重用的NSBlockOperation对象,隔离每个调用以在其自己的执行块中访问字典:

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"Doing something...");
    }];
    
    //you can add more blocks
    [operation addExecutionBlock:^{
        NSLog(@"Another block");
    }];
    
    [operation setCompletionBlock:^{
        NSLog(@"Doing something once the operation has finished...");
    }];
    
    [queue addOperation:operation];

操作队列提供自己的线程来运行其操作,因此不需要创建自己的操作。由于操作队列使用GCD,因此操作总是在单独的线程上执行,尽管它们各自的指定是异步和同步的。

这应该简化您的代码,特别是通过消除与调度队列相关的代码。真糟糕!

最后,通过将字典添加到AppDelegate来创建一个全局访问器方法:

  1. 强制执行单身

  2. 仅在方法或类首次引用字典后才执行初始化

  3. 仅适用于可变副本,之后创建新的可变字典以替换之前的字典
  4. 这是Apple在开发者文档中提出的另一个选项(我直接从Concurrency Programming Guide复制了这个选项):

      

    使用Dispatch信号量来规范有限资源的使用If   您提交给调度队列的任务访问一些有限的   资源,你可能想用一个调度信号量来规范   同时访问该资源的任务数。发货   信号量就像常规信号量一样,只有一个例外。什么时候   资源可用,获取调度所需的时间更少   信号量比获取传统系统信号量要好。这个   是因为Grand Central Dispatch没有调用内核   对于这个特殊情况。它调用内核的唯一时间是   当资源不可用且系统需要停放您的资源时   线程,直到信号量发出信号。使用a的语义   调度信号量如下:   1.创建信号量时(使用dispatch_semaphore_create函数),可以指定一个正整数,表示数字   资源可用。   2.在每个任务中,调用dispatch_semaphore_wait来等待信号量。   3.当等待呼叫返回时,获取资源并完成您的工作。   4.完成资源后,通过调用dispatch_semaphore_signal函数释放它并发信号通知信号量。为   这些步骤如何工作的示例,请考虑使用文件描述符   在系统上。每个应用程序都有一个有限数量的文件   要使用的描述符。如果您有一个处理大量的任务   文件,您不希望一次打开这么多文件   超出文件描述符。相反,您可以使用信号量来限制   您的任何时候使用的文件描述符的数量   文件处理代码。您将合并的基本代码段   进入你的任务如下://创建信号量,指定   初始池大小dispatch_semaphore_t fd_sema =   dispatch_semaphore_create(getdtablesize()/ 2); //等一下免费   文件描述符dispatch_semaphore_wait(fd_sema,   DISPATCH_TIME_FOREVER); fd = open(“/ etc / services”,O_RDONLY); //   完成后释放文件描述符(fd);   dispatch_semaphore_signal(fd_sema);当你创建信号量时,你   指定可用资源的数量。这个值就变成了   信号量的初始计数变量。每次你等待   信号量,dispatch_semaphore_wait函数减少该计数   变量1.如果结果值为负,则函数告知   内核阻止你的线程。在另一端,   dispatch_semaphore_signalfunction将count变量递增1   表示资源已被释放。如果有任务   阻止并等待资源,其中一个随后   畅通无阻,并允许其工作。