IOS UIScrollView is stuck and not responding with NSRunLoopCommonModes

In my iPhone application, I use a third-party library (libPusher) for the WebSockets network, and this library causes each UIScrollView component in my application to stop responding. This includes UIScrollViews and UITableView.

What happens if a user scrolls one of the UIScrollView components with his finger and, as a rule, continues to touch and move the view with his finger at the same time that the network operation is performed, this leads to a completely irresponsible UIScrollView that stops receiving touch events (he thinks that he is in drag mode all the time, even when lifting his finger) and does not slow down properly. The only way out is to destroy the UIScrollView and recreate a new one.

I contacted the developer of the library, but, unfortunately, still have not heard back.

From what I read, this is a common problem when starting a startup loop in unacceptable mode, like NSDefaultRunLoopMode , however this library seems to be doing the right thing and starting the execution loop in NSRunLoopCommonModes so I don't understand how it is right.

I tried playing in different modes (tried NSDefaultRunLoopMode ), but everything is the same.

I am using iOS 5 and this is the same behavior on the simulator as well as on the devices.

Let me paste some code that I think is problematic in lib, and hopefully this will be enough features so you can help me find a solution.

In the NSOperation subclass, we have:

 - (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); } 

This operation is performed in the main thread, as in [[NSOperationQueue mainQueue] addOperation:authOperation]; . (maybe this is a problem, but I tried to run it in a different thread, and it will work, so the library will need more work to make it background so that I cannot prove that this solution ...)

So far i tried

  • changing the run cycle mode to NSDefaultRunLoopMode - did not help.
  • starting the operation in a new work queue that I created (for example, not in the main thread), but the library does not seem to be ready for this, since it will work.

I still feel like shooting in the dark ... help :)

thanks!

+4
source share
2 answers

This becomes unresponsive as it executes a while loop in the main thread. This is a bad design.

Since you're on iOS5, why not use NSURLConnection + sendAsynchronousRequest: queue: completeHandler :?

If you need to be compatible with iOS4.x, I would share the loop stuff and see this blog post .

Basically, I would do something like this:

 - (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 } } 
+2
source

In fact, you should run NSURLConnection in a single-threaded stream, for example:

 + (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]; } 

Example: github . Another good example is AFNetworking, it uses the same method.

0
source

Source: https://habr.com/ru/post/1386247/


All Articles