Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/objective-c/27.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Objective c NSURLConnection与nsrunlopCommonModes的连接_Objective C_Ios_Concurrency_Nsurlconnection_Nsrunloop - Fatal编程技术网

Objective c NSURLConnection与nsrunlopCommonModes的连接

Objective c NSURLConnection与nsrunlopCommonModes的连接,objective-c,ios,concurrency,nsurlconnection,nsrunloop,Objective C,Ios,Concurrency,Nsurlconnection,Nsrunloop,我已经为我的iOS应用程序编写了自己的HTTPClient实现,以异步下载指定URL的内容。HTTPClient使用NSOperationQueue将NSURLConnection请求排队。我选择NSOperationQueue是因为我想在任何时间点取消任何或所有正在进行的NSURLConnection 我对如何实现我的HTTPClient做了大量研究,对于执行NSURLConnection,我有两种选择: 1) 在单独的辅助线程上执行每个排队的NSURLConnection。NSOperati

我已经为我的iOS应用程序编写了自己的HTTPClient实现,以异步下载指定URL的内容。HTTPClient使用NSOperationQueue将NSURLConnection请求排队。我选择NSOperationQueue是因为我想在任何时间点取消任何或所有正在进行的NSURLConnection

我对如何实现我的HTTPClient做了大量研究,对于执行NSURLConnection,我有两种选择:

1) 在单独的辅助线程上执行每个排队的NSURLConnection。NSOperationQueue在后台的次线程上执行每个排队操作,因此我不需要显式地执行任何操作来生成次线程,除了在NSOperation子类的重写的start方法中启动NSURLConnection并为生成的次线程运行runloop,直到ConnectionIDFinishLoading或调用connectionIDFailWitherRor。如下所示:

if (self.connection != nil) {
            do {
                [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
                                         beforeDate:[NSDate distantFuture]];
            } while (!self.isFinished);
}
NSRunLoop *runloop; //global

self.connection = [[NSURLConnection alloc] initWithRequest:self.urlRequest delegate:self startImmediately:NO];

[self.connection scheduleInRunLoop:runloop forMode:NSRunLoopCommonModes];

[self.connection start];
2) 在主线程上执行每个排队的NSURLConnection。为此,在start方法中,我使用performSelectorOnMainThread并在主线程上再次调用start方法。使用此方法,我将NSURLConnection与nsrunlopCommonModes的时间安排如下:

[self.connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
我选择了第二种方法并实现了它。根据我的研究,第二种方法似乎更好,因为它不会为每个NSURL连接启动单独的辅助线程。现在,在任何时候,应用程序中都可能有许多请求同时进行,使用第一种方法,这意味着将产生相同数量的辅助线程,并且在相关url请求完成之前不会返回池

我的印象是,我仍然在通过调度NSURLConnection和nsrunlopCommonModes与第二种方法并行运行。换句话说,对于这种方法,我认为我使用的是nsrunlopCommonModes,而不是多线程并发,因此NSURLConnection的观察者将尽快调用connectionIDFinishLaunching或connectionIDFailWitherRor,而不管主线程当时对UI做了什么

不幸的是,今天早上我的一位同事告诉我,在当前的实现中,NSURLConnection只有在其中一个视图控制器上的滚动视图停止滚动时才会返回,这证明了我所有的理解都是错误的。当滚动视图即将停止滚动时,NSURLRequest获取数据会启动,但即使在滚动视图停止调用之前完成,NSURLConnection也不会在滚动视图完全停止滚动之前回调ConnectionIDFinishLoading或ConnectionIDFailWitherRor。这意味着在主线程上使用nsrunlopCommonModes调度NSURLConnection以获得与UI操作(触摸/滚动)的真正并发性的整个想法被证明是错误的,NSURLConnection仍在等待,直到主线程忙于滚动滚动滚动视图

我试着切换到使用辅助线程的第一种方法,效果非常好。当滚动视图仍在滚动时,NSURLConnection仍调用其协议方法之一。这是清楚的,因为现在NSURLConnection没有在主线程上运行,所以它不会等待滚动视图停止滚动

我真的不想使用第一种方法,因为它由于多线程而很昂贵

如果我对第二种方法的理解不正确,有人能告诉我吗?如果正确,那么使用nsrunlopCommonModes调度NSURLConnection的原因可能是什么


如果答案更具描述性,我将不胜感激,因为它可以为我消除更多关于nsrunlop和NSRunLoopModes工作方式的疑问。我已经多次阅读了这方面的文档。

调度运行循环源不允许源的回调与其他源的回调同时运行

在网络通信的情况下,内核处理的事情,如接收和缓冲数据包,无论应用程序做什么,都会同时发生。然后,内核将套接字标记为可读或可写的,例如,如果线程在这样的调用中被阻塞,则可能会唤醒
select()
kevent()
调用。如果线程正在执行其他操作,比如处理滚动事件,那么在执行返回到运行循环之前,它不会注意到套接字的可读写性。只有这样,
NSURLConnection
的运行循环源才会调用其回调,让NSURLConnection处理套接字状态更改,并可能调用您的委托方法

下一个问题是,当一个运行循环有多个源并且有多个源准备就绪时会发生什么。例如,事件队列中有更多的滚动事件,并且套接字是可读或可写的。理想情况下,您可能需要一个公平的算法来服务运行循环源。实际上,GUI事件的优先级可能高于其他运行循环源。此外,运行循环源相对于其他源具有固有的优先级(“顺序”)

通常,即时维护
NSURLConnection
并不重要。通常允许它等待主线程的运行循环到达它是可以的。考虑到,同样的原因,<代码> NSURLCONNECT/<代码>的运行循环源不会在滚动时被服务,但是在后台线程上处理它不会有用户可见的效果。例如,它将如何影响应用程序的UI?它将使用
-performSelectorOnMainThread:…
或类似的方法来安排更新。但这和
NSURLConnection
run循环源代码一样可能会被饿死

然而,如果你绝对不能忍受这种可能的延迟,在sc和sc之间有一个中间地带
self.connection = [[NSURLConnection alloc] initWithRequest:self.urlRequest
                                                              delegate:self startImmediately:NO];

[self.connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];

[self.connection start];
NSRunLoop *runloop; //global

self.connection = [[NSURLConnection alloc] initWithRequest:self.urlRequest delegate:self startImmediately:NO];

[self.connection scheduleInRunLoop:runloop forMode:NSRunLoopCommonModes];

[self.connection start];
runloop = [NSRunLoop currentRunLoop];

while (!finished)
{
   [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
}
static NSThread *connectionProcessingThread;
static NSTimer *keepRunloopBusy;
static NSRunLoop *oauth2runLoop;

+ (void)initialize
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    connectionProcessingThread = [[NSThread alloc] initWithBlock:^{
        oauth2runLoop = [NSRunLoop currentRunLoop];
        keepRunloopBusy = [NSTimer timerWithTimeInterval:DBL_MAX repeats:YES block:^(NSTimer* timer) {
            NSLog(@"runloop is kept busy with this keepalive work");
        }];
        [oauth2runLoop addTimer:keepRunloopBusy forMode:NSRunLoopCommonModes];
        [oauth2runLoop run];
    }];
    [connectionProcessingThread start];
    atomic_thread_fence(memory_order_release);
});
}
NSURLConnection *aConnection = [[NSURLConnection alloc] initWithRequest:startRequest delegate:self startImmediately:NO];    // don't start yet
if( [NSRunLoop currentRunLoop] != [NSRunLoop mainRunLoop]) {
    atomic_thread_fence(memory_order_acquire);
    [aConnection scheduleInRunLoop:oauth2runLoop forMode:NSRunLoopCommonModes];
} else {
    [aConnection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; // let's first schedule it in the main runloop.
}
[aConnection start];    // now start