Ios 使用dispatch_信号量时发生泄漏
我想在不同的队列中执行一些操作,所以我使用信号量。但当我用仪器检查时,会出现泄漏。帧是dispatch\u semaphore\u create。我使用ARC,以前检查时没有泄漏。代码如下所示:Ios 使用dispatch_信号量时发生泄漏,ios,memory-leaks,grand-central-dispatch,Ios,Memory Leaks,Grand Central Dispatch,我想在不同的队列中执行一些操作,所以我使用信号量。但当我用仪器检查时,会出现泄漏。帧是dispatch\u semaphore\u create。我使用ARC,以前检查时没有泄漏。代码如下所示: dispatch_async(queue,^{ dispatch_semaphore_t signal = dispatch_semaphore_create(1); for (int i=0;i<100;i++) { dispatch_sem
dispatch_async(queue,^{
dispatch_semaphore_t signal = dispatch_semaphore_create(1);
for (int i=0;i<100;i++)
{
dispatch_semaphore_wait(signal, DISPATCH_TIME_FOREVER);
dispatch_async(queue,^{
/* some actions */
dispatch_semaphore_signal(signal);
});
}
});
附言。
对不起,nsindepath*indepath=indexPathsAry[i];需要更改为NSIndexPath*indexPath=IndeXPathry[0];代码将逐个正确删除单元格,而不是一起删除。它说在线路调度信号量=调度信号量创建(1)中存在泄漏;但当我在一个小的演示中测试这段代码时,它工作得很好,所以我不知道为什么。起初我用dispatch_sync代替信号量,但有时它不能同步工作,这让我很困惑。是因为我的项目中有太多的队列吗?从您发布的代码来看,您泄漏的原因并不明显。也就是说,使用后台队列和信号量可能不是最好的方法。充其量,它会使两个线程在序列的大部分时间内处于阻塞状态(一个定期唤醒以将下一个删除操作排入队列,另一个等待
完成lsignal
),有更好的方法。哪种方法最好实际上取决于你所追求的确切效果
例如,在常见情况下,您当前的方法看起来将允许(至少)在开始删除操作/动画之间旋转主运行循环一次(请参见下面的说明)。我的第一个想法是使用计时器在每次删除之前的一帧或两帧之后开始。它看起来像这样:
NSArray* indexPathsAry = @[ ... ];
const NSTimeInterval intervalInSeconds = 1.0 / 30.0;
__block NSUInteger i = 0;
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, (uint64_t)(intervalInSeconds * NSEC_PER_SEC), 0);
dispatch_source_set_event_handler(timer, ^{
NSIndexPath *indexPath = indexPathsAry[i++];
id item = items[indexPath.row];
[items removeObject:item];
[tableView beginUpdates];
[tableView deleteRowsAtIndexPaths:indexPath withAnimation:...];
[tableView endUpdates];
if (i >= indexPathsAry.count)
{
dispatch_source_cancel(timer);
}
});
dispatch_source_set_cancel_handler(timer, ^{
// whatever you want to happen when all the removes are done.
})
dispatch_resume(timer);
NSOperation* finishOp = [NSBlockOperation blockOperationWithBlock:^{
// ... whatever you want to have happen when all deletes have been processed.
}];
for (NSIndexPath* toDelete in indexPathsAry)
{
NSOperation* op = [NSBlockOperation blockOperationWithBlock:^{
id item = items[toDelete.row];
[items removeObject: item];
[tableView beginUpdates];
[tableView deleteRowsAtIndexPaths: @[toDelete] withAnimation:...];
[tableView endUpdates];
}];
[finishOp addDependency: op];
[[NSOperationQueue mainQueue] addOperation: op];
}
[[NSOperationQueue mainQueue] addOperation: finishOp];
如果确实需要保证运行循环的精确/仅一次旋转(信号量方法FWIW不能保证),那么NSOperation
可能是最简单的方法(因为主线程NSOperationQueue
只为每个运行循环旋转一次操作提供服务)(即finishAllSignal
)您可以使用NSOperation
依赖项来实现这一点。它可能如下所示:
NSArray* indexPathsAry = @[ ... ];
const NSTimeInterval intervalInSeconds = 1.0 / 30.0;
__block NSUInteger i = 0;
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, (uint64_t)(intervalInSeconds * NSEC_PER_SEC), 0);
dispatch_source_set_event_handler(timer, ^{
NSIndexPath *indexPath = indexPathsAry[i++];
id item = items[indexPath.row];
[items removeObject:item];
[tableView beginUpdates];
[tableView deleteRowsAtIndexPaths:indexPath withAnimation:...];
[tableView endUpdates];
if (i >= indexPathsAry.count)
{
dispatch_source_cancel(timer);
}
});
dispatch_source_set_cancel_handler(timer, ^{
// whatever you want to happen when all the removes are done.
})
dispatch_resume(timer);
NSOperation* finishOp = [NSBlockOperation blockOperationWithBlock:^{
// ... whatever you want to have happen when all deletes have been processed.
}];
for (NSIndexPath* toDelete in indexPathsAry)
{
NSOperation* op = [NSBlockOperation blockOperationWithBlock:^{
id item = items[toDelete.row];
[items removeObject: item];
[tableView beginUpdates];
[tableView deleteRowsAtIndexPaths: @[toDelete] withAnimation:...];
[tableView endUpdates];
}];
[finishOp addDependency: op];
[[NSOperationQueue mainQueue] addOperation: op];
}
[[NSOperationQueue mainQueue] addOperation: finishOp];
这两种方法都不应该出现泄漏。此外,这两种方法都比让两个线程等待信号量多长时间要好
更多详情:
以下是我认为发布的方法将要做的一个解释:
- 后台线程为主线程排队一个块,然后进入睡眠状态,等待信号量
- libdispatch唤醒主线程runloop并最终执行该块
- 该块将删除该项,并在表视图上启动“删除”动画以删除该行
- 主线程当前正在执行(即不休眠),将继续处理来自主调度队列的块,直到不再有块为止,并最终继续运行循环,最终进入休眠状态,等待下一个事件/唤醒
- 由于信号量信号,后台线程稍后会被唤醒
NSOperation
方法保证h
老实说,计时器方法可能是最好的,因为它在语义上捕获了您在这里想要的内容,即,行一行接一行地删除,其间的延迟最小。我不知道这怎么可能起作用,因为这段代码给我留下了一系列问题:
deleteRowsAtIndexPaths
仅启动动画。此代码将使它们看起来几乎同时被删除i==0
时,删除第一项后一切正常,但当i==1
时,数组中的第二项现在是数组中的第一项,因此当您引用索引路径[i]
,您现在正在抓取原来是第三项的内容,因为原来是第二项的内容现在位于indexpath[0]
deleteRowsAtIndexPaths
方法采用索引路径数组,而不是单个索引路径dispatch_queue_t queue = dispatch_queue_create("com.company.app.tableupdate", 0);
for (int i = 0; i < count; i++)
{
dispatch_async(queue, ^{
dispatch_sync(dispatch_get_main_queue(), ^{
// do some stuff to the table
});
[NSThread sleepForTimeInterval:0.1]; // if you want a little delay before the next one
});
}
完成所有操作后,
dispatch\u release(signal);
怎么样?dispatch对象是否表现为ObjC对象(并因此参与ARC)取决于项目的部署目标版本。IIRC,您需要iOS6或更高版本和/或OSX 10.8或更高版本才能获得该行为。否则,dispatch\u release
。如果这是在串行队列上完成的,则此代码将使该队列死锁,因为您正在发送信号并等待同一队列。结果会导致泄漏。此外,您说你正在处理不同队列之间的通信,所以也许你可以更新你的示例来说明你在做什么