iOS UIScrollView使用NSRunLoopCommonModes卡住并且没有响应

时间:2011-12-14 06:57:26

标签: ios uiscrollview nsrunloop

在我的iPhone应用程序中,我正在使用第三方库(libPusher)进行WebSockets网络,此库会导致我的应用程序中的每个UIScrollView组件无响应。这包括UIScrollViews和UITableView。

如果用户用手指滚动其中一个UIScrollView组件并且在网络操作正在进行的同时用手指触摸和滑动视图,那么会导致完全没有响应UIScrollView停止接受触摸事件(即使手指抬起,它也认为它一直处于拖动模式)并且没有适当减速。唯一的出路是销毁UIScrollView并重新创建一个新的。

我已经联系了图书馆的开发者,但遗憾的是到目前为止还没有收到回复。

从我读到的内容来看,这是在NSDefaultRunLoopMode等不合适的模式下运行运行循环时的常见问题,但是这个库似乎正在做正确的事情它在NSRunLoopCommonModes中运行其运行循环,所以我不清楚正确的解决方案是什么。

我尝试使用不同的模式(尝试NSDefaultRunLoopMode),但行为是相同的。

我正在使用iOS 5,它在模拟器和设备上的行为相同。

让我粘贴我认为在lib中存在问题的代码,并希望这是足够的范围让您帮我找到解决方案。

在NSOperation的子类中,我们有:

- (void)start 
{
  NSAssert(URLRequest, @"Cannot start URLRequestOperation without a NSURLRequest.");

  [self setExecuting:YES];

  URLConnection = [[NSURLConnection alloc] initWithRequest:URLRequest delegate:self startImmediately:NO];

  if (URLConnection == nil) {
    [self setFinished:YES]; 
  }

  // Common modes instead of default so it won't stall uiscrollview scrolling
  [URLConnection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
  [URLConnection start];

  do {
    [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
  } while (!_isFinished);
}

此操作在线程上运行,如[[NSOperationQueue mainQueue] addOperation:authOperation];中所示。 (也许这是问题所在,但我尝试在另一个线程中运行它崩溃所以库需要更多工作才能使后台线程安全,所以我无法证明这是解决方案...)

到目前为止,我试过

  • 将运行循环模式更改为NSDefaultRunLoopMode - 没有帮助。
  • 在我创建的新操作队列中运行操作(例如,不在主线程上)但是库在崩溃时似乎没有为此做好准备。

我仍然觉得我在黑暗中拍摄......帮助:)

谢谢!

2 个答案:

答案 0 :(得分:2)

它变得没有响应,因为它在主线程上执行了一个while循环。这是一个糟糕的设计模式。

由于您使用的是iOS5,为什么不使用NSURLConnection的+ sendAsynchronousRequest:queue:completionHandler:?

如果您需要兼容iOS4.x,我会删除运行循环内容并查看this blog post

基本上,我会做这样的事情:

- (void)start
{
    if (![NSThread isMainThread]) {
        return [self performSelectorOnMainThread:@selector(start) 
                                      withObject:nil
                                   waitUntilDone:NO];
    }

    [self willChangeValueForKey:@"isExecuting"];
    isExecuting = YES;
    [self didChangeValueForKey:@"isExecuting"];

    NSURLConnection *aConnection = [[NSURLConnection alloc] initWithRequest:request 
                                                                   delegate:self];

    self.connection = aConnection;
    [aConnection release];

    if (connection == nil) {
        // finish up here
    }
}

答案 1 :(得分:0)

实际上你应该在单个线程中启动NSURLConnection,如下所示:

+ (void) __attribute__((noreturn)) networkEntry:(id)__unused object
{
    do {
        @autoreleasepool
        {
            [[NSRunLoop currentRunLoop] run];
            NSLog(@"exit worker thread runloop");
        }
    } while (YES);
}

+ (NSThread *)networkThread
{
    static NSThread *_networkThread = nil;
    static dispatch_once_t oncePredicate;

    dispatch_once(&oncePredicate, ^{
        _networkThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkEntry:) object:nil];
        [_networkThread start];
    });

    return _networkThread;
}

- (void)start
{
    NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:self.URL
                                                            cachePolicy:NSURLCacheStorageNotAllowed
                                                        timeoutInterval:self.timeoutInterval];
    [request setHTTPMethod: @"GET"];

    self.connection =[[NSURLConnection alloc] initWithRequest:request
                                                     delegate:self
                                             startImmediately:NO];
    [self.connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
    [self.connection start];
}

一个例子是github。另一个很好的例子是AFNetworking,它使用相同的方法。