iOS UIScrollView застревает и не отвечает с NSRunLoopCommonModes - PullRequest
4 голосов
/ 14 декабря 2011

В моем приложении для 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);
}

Эта операция выполняется в потоке main , как в [[NSOperationQueue mainQueue] addOperation:authOperation];.(возможно, это проблема, но я попытался запустить его в другом потоке, и он вылетает, поэтому библиотеке понадобится больше работы, чтобы сделать ее безопасной в фоновом режиме, поэтому я пока не могу доказать, что это решение ...)

До сих пор я пытался

  • изменить режим цикла выполнения на NSDefaultRunLoopMode - не помогло.
  • запуск операции в новой очереди операций, которую я создал (например, не в главном потоке), но библиотека, кажется, не готова к этому, так как она падает.

Я все еще чувствую, что стреляю в темноте ... help:)

спасибо!

Ответы [ 2 ]

2 голосов
/ 23 декабря 2011

Он перестает отвечать, поскольку выполняет цикл while в главном потоке.Это плохой дизайн шаблона.

Так как вы на iOS5, почему бы не использовать NSURLConnection + sendAsynchronousRequest: queue: завершениеHandler:?

Если вам нужно быть совместимым с 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
    }
}
0 голосов
/ 26 июня 2013

На самом деле вы должны запустить 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, использующий тот же метод.

...