首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用dispatch_semaphore时泄漏

使用dispatch_semaphore时泄漏
EN

Stack Overflow用户
提问于 2013-11-22 15:53:40
回答 2查看 3.8K关注 0票数 1

我想在不同的队列中执行一些操作,所以我使用semaphore.But --当我用仪器检查它时,会出现泄漏。这个框架是dispatch_semaphore_create。我用ARC当我检查它的时候没有泄漏。代码看起来像:

代码语言:javascript
复制
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);
            }); 
     }
});

代码是这样的,我输入时使用WINDOWS计算机,所以有一些拼写错误,对不起。

附注:

我希望用动画逐个删除表视图中的单元格,如果我使用deleteRowsAtIndexpaths方法删除它们的索引路径数组,或者在主队列中循环删除它们,则它们将一起删除,而不是一个接一个地删除,所以我使用两个队列进行删除。守则是这样的:

代码语言:javascript
复制
dispatch_async(queue,^{

     dispatch_semaphore_t signal = dispatch_semaphore_create(1);

     for (int i=0;;i++)
     {
            dispatch_semaphore_wait(signal, DISPATCH_TIME_FOREVER);

            if (i == [indexPathsAry count])
            {
                 dispatch_semaphore_signal(finishAllSignal);
                 break;
            }

            dispatch_async(main_queue,^{
                NSIndexPath *indexPath = indexPathsAry[0];
                id item = items[indexPath.row];
                [items removeObject:item];

                [tableView beginUpdates];
                [tableView deleteRowsAtIndexPaths:indexPath withAnimation:...];
                [tableView endUpdates];
                dispatch_semaphore_signal(signal);
            }); 
     }
});

对不起,NSIndexPath *indexPath = indexPathsAryi;需要更改为NSIndexPath *indexPath = indexPathsAry;代码将逐个正确地删除单元格,而不是一起删除它们。它说在行dispatch_semaphore_t signal =dispatch_semaphore_create(1)中有漏洞;但是当我在一个小演示中测试这段代码时,它工作得很好,所以我不知道为什么。一开始我使用dispatch_sync而不是信号量,但它有时不能同步工作,这让我感到困惑。是因为我的项目中队列太多了吗?

EN

回答 2

Stack Overflow用户

发布于 2013-11-23 13:45:16

从您发布的代码来看,您泄漏的原因并不明显。也就是说,使用后台队列和信号量可能不是最好的方法。充其量,它会留下两个线程在序列的大部分时间内被阻塞(一个会周期性地唤醒以对下一个删除操作进行队列,另一个正在等待finishAllSignal)。有更好的方法。哪种方法是最好的,真正取决于你想要的确切效果。

例如,在常见的情况下,您的当前方法似乎允许(至少)在启动delete操作/动画之间允许(至少)一个主运行循环(请参见下面的解释)。我的第一个想法是使用一个计时器开始每删除一个或两个比前一个帧。看起来会是这样的:

代码语言:javascript
复制
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);

如果您确实需要保证运行循环的确切/只有一个自旋(信号量方法FWIW不能保证这一点),那么NSOperation可能是最简单的方法(因为主线程NSOperationQueue每个运行循环只提供一个操作)。如果您需要“完成”行为(即finishAllSignal),您可以使用NSOperation依赖关系来实现这一点。看起来可能是这样的:

代码语言:javascript
复制
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];

这两种方法都不应该有漏洞。此外,这两种方法中的任何一种都比让两个线程在信号量上等待时间更长。

更多详细信息:

下面是我认为发布的方法将做什么的解释:

  • 后台线程为主线程排队一个块,然后进入休眠状态,等待信号量。
  • 主线程运行循环被lib分派唤醒,并最终执行块。
  • 该块移除项,并在表视图上启动删除动画以删除该一行。
  • 然后向信号量发出信号,就完成了。主线程目前正在执行(即不休眠),并将继续处理主分派队列中的块,直到没有更多的线程为止,并且最终将继续运行循环,最终进入休眠状态,等待下一个事件/醒来。
  • 由于信号量信号,后台线程稍后会醒来。此时,这些步骤重复。

需要注意的一点是,主运行循环和后台线程之间没有强耦合。如果在您的块之后的主队列上有另一个不相关的块,那么后台队列可能会在不相关的块完成之前将下一个delete块放到主队列上。如果发生这种情况,这两个delete块可以在相同的runloop pass上执行,并且两个相应的项似乎同时被删除。在一般情况下,这种情况可能不会发生,但我的观点是,信号量方法不能保证单删除每运行循环的行为,但我相信NSOperation方法将保证这样做。

老实说,计时器方法可能是最好的,因为它在语义上捕捉到了您在这里想要的内容,即行之间有一些最小的延迟就可以一个接一个地删除。

票数 2
EN

Stack Overflow用户

发布于 2013-11-23 13:59:47

我看不出这是怎么回事,因为我觉得这段代码有很多问题:

  1. 我推断您的意图是将被删除的每个行分别动画化,但这并不能做到这一点,因为deleteRowsAtIndexPaths只启动动画。这段代码将使它们看起来几乎同时被删除。
  2. 我看不出这是如何工作的,因为在遍历该结构时,您正在改变结构(数组和表视图)。例如,当i == 0删除第一项时,一切都很好,但是到了i == 1时,数组中的第二项现在是数组中的第一项,所以当您引用indexPathsAry[i]时,您现在正在抓取原来的第三项,就像原来的第二项现在在indexPathsAry[0]上一样。
  3. deleteRowsAtIndexPaths方法接受一个索引路径数组,而不是一个索引路径。
  4. 这里完全没有必要使用信号量。一个更符合逻辑的模式是: dispatch_queue_create("com.company.app.tableupdate",队列= dispatch_queue_t 0;for (int i=0;i< count;i++) {dispatch_async(队列,^{ dispatch_sync(dispatch_get_main_queue(),^{ //对表}做一些事情);NSThread睡眠sleepForTimeInterval:0.1;//如果您希望在下一个}之前稍微延迟一点); 这将创建一个串行队列,向该队列中添加一组任务,这些任务本身在主队列上同步执行一些操作,然后在启动下一个队列之前等待。这达到了和我想的一样的效果。
  5. 您正在遍历整个表视图数据源,每次删除一个项。如果项目的数量不超过可见行的数量,那么这很好,但是我建议您确实希望(a)删除当前不可见的任何内容;(b)然后动画化删除可见的行。这将实现所需的视觉效果,同时不让用户等待非原始可见行的动画。

尽管如此,在这个代码示例中,我没有看到任何可能导致泄漏的东西(这是您最初的问题)。您应该共享哪些对象被报告为泄漏的信息。

顺便说一句,如果您想在从表中删除所有内容之前将可见单元格从表中移除,您还可以这样做:

代码语言:javascript
复制
NSInteger count = [self.tableView.indexPathsForVisibleRows count];

// first animate the cells flying off to the right

[self.tableView.indexPathsForVisibleRows enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) {
    [UIView animateWithDuration:0.25 delay:idx * 0.05 options:0 animations:^{
        UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
        cell.transform = CGAffineTransformMakeTranslation(self.view.frame.size.width, 0);
    } completion:^(BOOL finished) {

        // if it's the last one, empty the data source and refresh the table

        if (idx == (count - 1)) {
            [self emptyTableDataSource];
            [self.tableView reloadData];
        }
    }];
}];
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20149063

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档