6

我查看了 WWDC 2010 的一些演示文稿,还阅读了大部分关于块和并发的文档,并且有几个关于在 Grand Central Dispatch 中使用带有串行队列的块的问题。我有一个 iOS 4 项目,它有一个滚动视图和一个图像信息字典——图像的 URL 等等。我想使用 GCD 和块来下载图像并将它们放在我的滚动视图中,因此不会阻塞主线程。我编写了以下似乎有效的代码:

for (NSDictionary* dict in images)
{
     dispatch_async(image_queue, ^{

           NSString* urlString = [dict objectForKey:@"url"];
           NSURL* url = [NSURL URLWithString:urlString];
           NSData* imageData = [[NSData alloc] initWithContentsOfURL:url];
           UIImage* image = [UIImage imageWithData:imageData];
           UIImageView* imageView = // initialize imageView with image;      

           dispatch_async(dispatch_get_main_queue(), ^{
                [self.scrollView addSubview:imageView];
           });
           [imageData release];
      });
}

我有两个问题:

  1. 根据并发指南,我不应该从封闭范围中捕获非标量类型的变量 - 在我的代码中,我捕获 dict 这是一个 NSDictionary* 对象。如果我不允许捕获它,我应该如何编写代码?块是否仅捕获实际使用的封闭范围中的变量?

  2. 如果我在通过串行调度队列获取所有图像之前离开当前的 ViewController 会发生什么?我不认为他们知道创建它们的 ViewController 已经消失了,所以当他们执行完成处理程序时会发生什么,我将图像视图插入到主线程上的滚动视图中?它会导致错误还是什么?当我的 ViewController 消失时,如何取消串行队列上的任何剩余操作?

此致,

4

5 回答 5

10
  1. 虽然这是一个琐碎的问题,但这对于理解并发指南试图告诉您的内容很重要:指针标量类型。因此,您可以随心所欲地捕获块内的指针……但是您必须了解它们指向的内存的生命周期!NSDictionary * is-a-kind-of id,当你在一个块中引用一个 id 时,运行时负责在块被复制时保留 id(它是通过 dispatch_async()),然后在块时释放它本身被释放。是的,一个块只捕获其中引用的变量。

  2. 由于您现在知道异步块已经对自身进行了保留,因此应该清楚(呃)(模内存管理错误)您的 ViewController 在块完成之前不能“消失”。所以它不会崩溃——但你正确地注意到,当你实际上不打算再使用结果时,你真的想要一种取消这种异步工作的方法。一种简单但有效的模式是在异步块的开头进行测试,以检查工作是否仍应完成。

于 2010-08-10T16:01:49.947 回答
3

由于其他人已经回答了您的两个问题,我想对您的代码发表评论,并建议您不要将 GCD 用于图像等网络查询。它们的主要问题是它们都将同时运行。根据下载的数量,您可能会创建太多的同时连接,这可能会使蜂窝连接陷入困境,如果图像在争夺宝贵的网络时没有开始出现,用户最终可能会认为有问题。

尝试使用值为 2 或 3 的 an NSOperationQueuemaxConcurrentOperationCount这将允许您将可能无限数量的网络请求排队,但最多有几个并行执行。由于您可以访问设备的网络状态,因此您可以有条件地将其增加到 8 以进行 wifi 连接。

GCD 的第二个问题是取消挂起的操作有点麻烦。如果你的用户进入一个视图控制器然后推回,这取决于你如何编写代码,GCD 块将保留视图控制器,防止它被释放,实际上所有的网络操作都必须完成,直到控制器可以已释放(因此取消中的连接没有意义dealloc)。

我通过监控代理并快速进入视图并返回的艰难方法学到了这一点。看到挂起的连接如何对我原本不知道的应用程序造成大约 20 秒的额外网络带宽损害,真是令人震惊。

tl;dr 使用网络队列,GCD 用于图像处理/缩放/GUI 更新。

于 2011-09-27T10:56:35.440 回答
1

@Kaelin Colclasure:对于第一个问题,多线程应用程序中的共享状态问题似乎更有可能:对于整数类型,您有一个按值复制的副本(也适用于指针),但是当您使用指针引用的对象时,您'将遇到与缺少锁定相关的所有问题(您在此处提到的对象生命周期问题的一种变化)。

于 2011-09-26T15:32:22.927 回答
0

这是您的第一点的实际区别:

如果我将标量传递到 GCD 队列中使用的块中,那么在该块中我正在处理原始数据的副本。更改将在块外不可见。(对此有一些警告,__block例如修饰符,但总的来说这是正确的。)

例如,如果我将 an 传递NSMutableDictionary到块中,则对字典的更改在块外可见 - 这是因为您保留了对字典的引用并且没有进行深层复制。

无论哪种情况,都会为您执行内存管理,即复制标量变量或保留对象。

由于在初始化后您无法更改其内容NSDictionary,您可能会发现块会自动为您做正确的事情。

对于第二点,内存管理几乎是自动的,除非您需要处理可变对象的副本。

于 2011-09-26T16:37:26.270 回答
0

为 iOS 进行 BLE 开发时需要注意一些限制和要求。第一个涉及 iOS 模拟器。有趣的是,iOS 模拟器曾一度支持蓝牙开发(WWDC 2012 蓝牙 101 视频中提到了此功能),但在 WWDC 2013 上,Apple 宣布模拟器中将不再支持蓝牙。从表面上看,这似乎很不幸。但是,使用 BLE 在设备上进行开发是一种更好、更准确的体验。此外,由于在 iPhone 4s(2011 年)之后生产的所有 iPhone 都具有蓝牙 4.0(进而扩展为 BLE),因此大多数 iOS 开发人员已经拥有支持它的设备。我们几乎不需要再问这个问题了,但最好知道谁可以使用它,以防您确实有一组受限的设备类型可供您使用

在进行核心蓝牙开发时要记住的另一个重要要求是,Apple 将与 BLE 设备交互的大部分责任交给了应用程序开发人员。iOS 本身在管理蓝牙方面几乎没有管理和维护。操作系统中管理的一件事是显示在“设置”>“蓝牙”应用程序中的连接。

于 2018-11-28T07:01:06.510 回答