iOS开发网络篇 一一 NSURLConnection和Runloop(面试题)

知识点:

NSURLConnection 设置代理的方式来发送网络请求所遇到的一些问题:

1. 使用NSURLConnection设置代理的方式来发送网络请求,代理方法默认是在主线程中执行的.

我们可以 使用 setDelegateQueue: 方法来设置代理方法在子线程中执行. 

[connect setDelegateQueue:[[NSOperationQueue alloc] init]];

注意: 不可以

[connect setDelegateQueue:[NSOperationQueue mainQueue]] 这样设置,否则代理方法不会被执行.


2. 设置代理的方式 本身是异步执行的.


3.  NSURLConnection设置代理后.理论上当connect变量出了大括号就被释放了.为什么还会调用代理方法\

    因为  the calling threads run loop must be operating in the default run loop mode.这个方法内部其实\

   会将connect对象当做source添加到runloop中,并设置运行模式默认模式.当所有代理方法都执行完/接收完服务器返回的数据后,\

    才会释放.(必须是默认模式)


4.当使用 initWithRequest: delegate :startImmediately 来发送请求时:

// startImmediately: YES 创建网络链接后会立刻发送网络请求 NO 不会发送网络请求. 除非调用 [connect start]方法
[[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];


5.发送网络请求的步骤也放入到子线程中去执行.此时代理方法不会被调用( startImmediately = YES ).

原因: connect 对象 当作sourse添加到runloop中了. 此时的connect再子线程中.子线程的runloop并没有自动开启, 需要我们自己来开启子线程的runloop.


6. 前提:网络请求在子线程中执行: 当startImmediately 为NO的时候, 调用 [connect start] 方法, 直接就可以调用代理方法.这是为什么呢?

第5条,startImmediately = YES, 就需要手动开启子线程runloop.

注意: 这里是因为 [connect start] 的start方法, start方法会将默认的 connect对象添加到runloop中并设置运行模式为默认, 如果当前的runloop

没有开启,那么start方法内部会自动获取当前线程的runloop对象并开启.


代码如下:

//  Created by 朝阳 on 2017/12/12.
//  Copyright © 2017年 sunny. All rights reserved.
//

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self delegate2];
}

// 异步的
- (void)delegate1
{
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://localhost:8080/MJServer/login?username=123&pwd=123&method=get&type=JSON"]];
    
    // 设置代理
    // 代理方法: 默认是在主线程中调用的
    // 给NSURLConnection 设置代理后. 理论上当connect变量出了 大括号就被释放了.为什么还会调用代理方法\
    因为  the calling thread’s run loop must be operating in the default run loop mode. 这个方法内部其实\
    会将connect对象 当做 source添加到runloop中,并设置运行模式 为 默认模式. 当所有代理方法都执行完/接收完服务器返回的数据后,\
    才会释放
    
    NSURLConnection *connect = [NSURLConnection connectionWithRequest:request delegate:self];
    
    // 设置代理方法在哪个线程中调用
    // [NSOperationQueue alloc] init] 如果是非主队列,就开 子线程
    // [NSOperationQueue mainQueue] 不能这样设置.没有任何反应
    // [connect setDelegateQueue:[NSOperationQueue mainQueue]];
    [connect setDelegateQueue:[[NSOperationQueue alloc] init]];
    
    // 先打印zy---- 证明 是异步执行的
    NSLog(@"zy-----");
    
}

- (void)delegate2
{
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://localhost:8080/MJServer/login?username=123&pwd=123&method=get&type=JSON"]];
    
    // 设置代理
    // 代理方法: 默认是在主线程中调用的
    // startImmediately: YES 创建网络链接后会立刻发送网络请求 NO 不会发送网络请求. 除非调用 [connect start]方法
    
    NSURLConnection *connect = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
    
    // 设置代理方法在哪个线程中调用
    [connect setDelegateQueue:[[NSOperationQueue alloc] init]];
    
    // 开始发送请求
    [connect start];
    
    // 先打印zy---- 证明 是异步执行的
    NSLog(@"zy-----");
    
}

- (void)newThreadDelegate1
{
    // 开子线程来发送网络请求
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        
        NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://localhost:8080/MJServer/login?username=123&pwd=123&method=get&type=JSON"]];
        
        // 设置代理
        // 代理方法: 默认是在主线程中调用的
        NSURLConnection *connect = [NSURLConnection connectionWithRequest:request delegate:self];
        
        // 设置代理方法在哪个线程中调用
        [connect setDelegateQueue:[[NSOperationQueue alloc] init]];
        
        NSLog(@"---%@---",[NSThread currentThread]);
        
        // 开启子线程的runloop(子线程的runloop默认是不开启的)
        [[NSRunLoop currentRunLoop] run];
        // 必须是默认模式
        // [[NSRunLoop currentRunLoop] runMode:UITrackingRunLoopMode beforeDate:[NSDate date]];
        
    });
}

- (void)newThreadDelegate2
{
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        
        NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://localhost:8080/MJServer/login?username=123&pwd=123&method=get&type=JSON"]];
        
        // 设置代理
        // 代理方法: 默认是在主线程中调用的
        // startImmediately: YES 创建网络链接后会立刻发送网络请求 NO 不会发送网络请求. 除非调用 [connect start]方法
        NSURLConnection *connect = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
        
        // 设置代理方法在哪个线程中调用
        [connect setDelegateQueue:[[NSOperationQueue alloc] init]];
        // 开始发送请求
        // If you don’t schedule the connection in a run loop or an operation queue before calling this method, the connection is scheduled in the current run loop in the default mode.\
        start 方法 会默认将 connect对象添加到runloop中并设置运行模式为默认.
        // 注意: 如果当前runloop没有开启,那么该方法内部会自动获取当前线程的runloop对象并开启
        [connect start];
        
        NSLog(@"---%@---",[NSThread currentThread]);
        
    });
    
}

#pragma -mark NSURLConnectionDataDelegate
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    NSLog(@"didReceiveResponse---%@",[NSThread currentThread]);
}

@end

你可能感兴趣的:(iOS-多线程网络,NSURLConnection,runloop)