4

私の iPhone アプリでは、WebSockets ネットワーク用にサード パーティのライブラリ (libPusher) を使用しています。このライブラリが原因で、アプリ内のすべての UIScrollView コンポーネントが応答しなくなります。これには、UIScrollViews と UITableView が含まれます。

ユーザーがUIScrollViewコンポーネントの1つを指でスクロールし、ネットワーク操作が進行中のときにたまたま指でビューに触れてスライドし続けると、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 に答える