【问题标题】:GCD and OSSpinLockLockGCD 和 OSSpinLockLock
【发布时间】:2015-01-21 14:29:34
【问题描述】:

我可能会以错误的方式问我的问题,并且有可能被 stackoverflow 完全阻止。我有亚斯伯格症,没有社交能力,所以很抱歉问了我最后一个 (?) 问题(因为像这样的系统只为没有障碍的人设计)。

我正在使用 GCD 从 Instagram 加载图像和视频。我在一个用户界面非常“忙碌”的应用程序中执行此操作,并且我希望保持其平稳运行,因此我在后台加载 Instagram 媒体。

下面的代码(我可能以错误的方式格式化,所以我提前道歉)工作正常,并且确实希望我想要它这样做。它在后台加载图像(为了简单起见,我把视频放在外面),我的主要 UI 是在图像加载时响应。我在加载之间显示它们,并且工作正常。

但是。

在 10 分钟后,有时是 20 分钟,有时是 30 分钟,甚至有时在两小时后,我的应用程序会出现 OSSpinLockLock 冻结。如果我删除下面的代码,我将看不到媒体,但应用程序永远不会冻结。

我已经在网上搜索了几天关于完成这项工作的替代方法并找到 OSSpinLockLock 的解释。没运气。我唯一发现的是,使用 GCD 不会导致 OSSpinLockLock。我已经使用了我所有的 Instruments 知识(我必须承认这比我想象的要有限),但我找不到错误。

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul);
    dispatch_async(queue, ^(void) {
        [[InstagramEngine sharedEngine] getMediaAtLocation:location count:kInstagramLocationBufferSize maxId:nil withSuccess:^(NSArray* media, InstagramPaginationInfo* paginationInfo) {
            if (media && media.count>0) {
                for (InstagramMedia* mediaObject in media) {
                    NSData* data = [[NSData alloc] initWithContentsOfURL:mediaObject.standardResolutionImageURL];
                    if (data) {
                        UIImage* img = [UIImage imageWithData:data];
                        if (img)
                            [self.locationBuffer addObject:img];
                        data = nil;
                    }
                }
            }
         } failure:^(NSError *error) {
             ;
         }];
    });

如果您查看此代码,您是否看到任何可能导致锁定的内容?因为我肯定不会。

self.locationBuffer 在 .h 中声明为

@property (nonatomic,strong) NSMutableArray* locationBuffer;

并且被正确分配和初始化(否则会很清楚问题是什么)。我也尝试不将 UIImage 而是 NSData 放入数组中,但这没有任何区别。

例如,在我的 iPad mini Retina 上,CPU 负载达到 195%,并且在很长一段时间内都保持在这个数字附近。最终,有时在几个小时后,应用程序就会崩溃。

非常欢迎任何建议。

编辑:正如我现在在 iPad 本身的 ips 文件上看到的(由于某种神秘的原因,我无法粘贴到此网页中(stackoverflow 是否仍处于实验阶段?))我看到 iPad 确实花了 16.000+ NSURLConnection 上的秒数...

【问题讨论】:

  • 不知道,但也许这会有所帮助:stackoverflow.com/questions/19570388/…
  • 您应该发布实际的回溯。仅仅知道你挂在OSSpinLockLock 上并没有给我们任何关于发生了什么的实质性线索——自旋锁在操作系统和框架中的所有地方都使用。
  • 好的。我很快就会发布 Instruments 的跟踪记录,也许你能比我得到更多。
  • 我还有第一台 iPad mini 型号,它刚刚完成了 5 小时的课程。相同的代码,完全没有问题。那台机器运行的是 iOS 7 而不是 8。这两个“参数”中的任何一个都可以与它有关吗?
  • 无法排除,但这里确实没有足够的信息。

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


【解决方案1】:

我的观点是某些超时或故障会阻塞您的 gcd 队列太长时间。尝试使用NSOperationQueue 重写该代码,这样您就可以在出现错误或视图/控制器消失时停止队列。

https://developer.apple.com/library/ios/documentation/Cocoa/Reference/NSOperationQueue_class/index.html

更新 1:

这是我对您的代码的试用,我添加了队列和日志,检查它们并检查超时值。这样,如果请求没有完成(很可能),您可以跟踪它。所有请求都是串行的,因此如果其中一个停止,您应该立即注意到它。 您可以创建多个队列并按顺序访问它们(循环)以同时处理更多请求。我不会使用超过 4 个队列,这也是大多数桌面互联网浏览器的默认设置。

// keep the same queue for all request (class member?), don't put it in the same block
NSOperationQueue* queue= [[NSOperationQueue alloc] init];

// keep this in another code block from queue
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul), ^(void) {
 [[InstagramEngine sharedEngine] getMediaAtLocation:location count:kInstagramLocationBufferSize maxId:nil withSuccess:^(NSArray* media, InstagramPaginationInfo* paginationInfo) {
     if (media && media.count>0) {
         for (InstagramMedia* mediaObject in media) {
             NSURL* url= mediaObject.standardResolutionImageURL;
             NSLog(@"Start loading from %@", url);
             NSURLRequest* req= [NSURLRequest requestWithURL:mediaObject.standardResolutionImageURL
                                                                                     cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                                             timeoutInterval:30]; // 30 seconds timeout
             [NSURLConnection sendAsynchronousRequest:req queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
                 NSLog(@"Stop loading from %@ %@ %@", url, response, connectionError);
                 if (data != nil && connectionError == nil) {
                     UIImage* img = [UIImage imageWithData:data];
                     if (img) {
                         // if you are triggering some ui update run on main thread
                         // [self.locationBuffer addObject:img];
                         [self.locationBuffer performSelectorOnMainThread:@selector(addObject:)
                                                                                                     withObject:img
                                                                                                waitUntilDone:NO];
                     }
                 }
             }];
         }
     }
 } failure:^(NSError *error) {
     NSLog(@"Error getting media list: %@", error);
 }];
});

【讨论】:

  • 感谢您的提示。我不熟悉它,但我找到了 Ray Wenderlich 的教程。谢谢。
  • 完成了它,虽然它是一种不同的方法,但它不能解释我的应用程序中发生的事情。无论如何,谢谢。
  • 我认为某些进程停止队列 (DISPATCH_QUEUE_PRIORITY_DEFAULT) 的时间过长,并且操作系统抛出了自旋锁。如果您使用新的NSOperationQueue,它只需创建一个仅用于该“操作队列”的新线程,并为您提供更多控制权。顺便说一句,为什么要投反对票?
  • 抱歉,我不知道我投了反对票。我不了解 NSOperationQueue (我的错,我知道)所以我尝试不再使用 [[NSData alloc] initWithContentsOfURL:mediaObject.standardResolutionImageURL] 而是使用 NSURLConnectionDelegate 。我不知道这是否会导致一个好的解决方案,但至少我了解这些东西。
  • Gianluca:看来你是对的。 initWithContentsOfURL 有时会阻塞。我现在使用NSOperationQueue 并每次分配一个新队列。我的前两个测试进展顺利,但 OSSpinLockLock 发生在最奇怪的时刻。我必须进行更多测试,看看它是否真的是解决方案。 [我没有投反对票,顺便说一句,我什至投了赞成票,但肯定有人反对,使余额为 0]。
【解决方案2】:

OSSpinLocks 在 iOS8.3+ 上有 bug:report
,这会导致这种冻结。
我认为您应该用其他东西替换 OSSpinLocks 以在您的应用程序中解决此问题(也许创建您自己的自旋锁实现 - 搜索 OSSpinLock)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多