4

在我的 iPhone 应用程序中,我使用 3rd 方库 (libPusher) 进行 WebSockets 网络,并且该库导致我的应用程序中的每个 UIScrollView 组件都变得无响应。这包括 UIScrollViews 和 UITableView。

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

我已经联系了图书馆的开发人员,但不幸的是到目前为止还没有收到回复。

根据我的阅读,这是在不适当的模式下运行运行循环时的常见问题,例如NSDefaultRunLoopMode,但是这个库似乎在做正确的事情并且它运行它的运行循环NSRunLoopCommonModes所以我不清楚正确的解决方案是。

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

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

让我将我认为有问题的代码粘贴到库中,希望这足以让您帮助我找到解决方案。

在 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- 没有帮助。
  • 在我创建的新操作队列中运行操作(例如,不在主线程上),但库似乎还没有准备好,因为它崩溃了。

我仍然觉得我在黑暗中射击......帮助:)

谢谢!

4

2 回答 2

2

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

既然你在 iOS5 上,为什么不使用 NSURLConnection 的 +sendAsynchronousRequest:queue:completionHandler: 呢?

如果您需要与 iOS4.x 兼容,我会去掉运行循环的内容并查看这篇博文。

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

- (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
    }
}
于 2011-12-23T10:00:30.033 回答
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,它使用相同的方法。

于 2013-06-26T12:53:24.430 回答