iOS网络请求策略——NSURLConnection的基本使用

今天和大家一起来学习一下NSURLConnection的基本使用,有疏忽的地方,还望各位不吝赐教。


一、简单的网络术语解释

1、URL

  • URL全称是Uniform Resource Locator(统一资源定位符)
  • 通过一个URL,能找到互联网上唯一的一个资源
  • URL基本格式:协议://主机地址/路径
    • 协议:不同的协议,代表着不同的资源查找方式、资源传输方式
    • 主机地址:存放资源的服务器的IP地址(域名)
    • 路径:资源在服务器中的具体位置

2、URL中常见的协议 协议是为了规定服务器和客户端传递数据的格式

  • HTTP
  • 超文本传输协议,访问的是远程的网络资源,格式是http://
  • http协议是在网络中最常用的协议
  • file
  • 访问的是本地计算机上的资源,格式是file://(不用添加服务器的地址)
  • mailto
  • 访问的是电子邮件的地址,格式是mailto
  • ftp
  • 访问的是共享主机的文件资源,格式是ftp://

3、为什么选择HTTP协议

  • 简单快速——HTTP协议简单,所以HTTP服务器的程序规模小,通信速度很快
  • 灵活——HTTP允许传输各种各样的数据
  • 非持续性——限制每次连接只处理一个请求,服务器相应以后,马上断开连接,可以节省传输时间

4、HTTP基本通信过程

  • 请求,客户端向服务器索要数据
    HTTP规定,一个完整的HTTP请求中要包含以下内容。
    请求头:包含了对客户端环境描述、客户端请求信息等
    GET /1.png HTTP/1.1 包含了请求方法、请求资源路径,HTTP协议版本
    Host:120.120.120.120:8080 客户端想访问服务器的主机地址
    User-Agent:FireFox 客户端的类型,客户端的软件环境
    Accept:text/html 客户端所能接收的数据类型
    Accept-Language:zh-cn 客户端的语言环境
    Accept-Encoding:gzip 客户端支持的数据压缩格式
    请求体:客户端发给服务器的具体数据,显示文件数据,POST请求才会有这个。
  • 响应,服务器返回相应数据
    HTTP规定:一个完整的HTTP响应中要包含以下内容。
    响应头:包含了对服务器的描述,对返回数据的描述。
    HTTP/1.1 200 OK 包含了HTTP协议的版本、状态码、状态的英文名称
    Server:tomcat 服务器的类型
    Content-Type:iamge/png 返回数据的类型
    Content-Length:10000 返回数据的长度
    Date:2017 12:22:00 响应的时间
    响应体:服务器返回给客户端的具体数据,比如文件数据

5、HTTP请求的发送方法

  • GET、POST、OPTIONS、HEAD、PUT、DELETE、TRACE、CONNECT、PATCH
  • GET、POST最常用,其他的几乎用不到
  • PUT:增 GET:查 DELETE:删 POST:改

6、GET和POST

  • GET请求格式
    协议+主机地址+接口名称+?+参数1&参数2&参数3
    在请求URL后面以?的形式添加发送给服务器的参数,多个参数之间用&分隔开。
    由于浏览器和服务器对URL的长度有限制,因此在URL后面附带的参数是有限的,一般不超过1KB
  • POST请求格式
    协议+主机地址+接口名称
    发送请求时,参数都放在请求体中,虽然理论上传递的数据量没有限制,但是具体要根据服务器的实际情况。
  • 如何选择
    如果要传递大量数据,像是文件上传要用POST;
    如果包含机密信息,选用POST,因为GET安全性没有POST好;
    如果仅仅是索取数据,像是一些数据查询的操作,选用GET好一些。

二、iOS网络请求策略

1、苹果原生

  • NSURLConnection:用法简单,很古老、但是也是最经典直接的一种方案。
  • NSURLSession:功能比NSURLConnection强大,苹果目前推荐使用,iOS7.0开始使用。
  • CFNetwork:NSURL的底层,纯C语言。

2、第三方框架

  • ASIHttpRequest:功能很强大,但是已经不再更新了。
  • AFNetworking:简单易用,提供了基本的一些常用功能,维护和使用者众多。

三、NSURLConnection的基本使用

1、发送GET请求

同步请求

/*  NSURLConnect负责发送请求,建立客户端和服务器的连接,发送数据给服务器,并且接收服务器的响应数据。
 * 使用NSURLConnect发送请求的步骤
 * 1、创建一个NSURL对象,设置请求路径
 * 2、通过传入的NSURL创建一个NSURLRequest对象,设置请求头和请求体
 * 3、使用NSURLConnection发送请求
 *
 * 请求:请求头(NSURLRequest默认包含)+请求体(GET没有)
 * 响应:响应头(真实类型-->NSHTTPURLResponse)+响应体(要解析的数据)
 */
  // 1、确定请求路径
    NSURL *url = [NSURL URLWithString:@"请求的URL"];
    // 2、创建请求对象 在做这一步转换的时候最好进行GET请求的中文转码,以免URL中带有中文
    // 默认请求方法为GET 所以没有请求体,请求头不需要设置(默认的请求头,除非有其他的需求)
    NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
    // 3、发送同步请求
    /* 这是一个同步请求
     * 第一个参数:请求对象
     * 第二个参数:响应头信息
     * 第三个参数:错误信息
     * 返回值:响应体信息
     */
    // 响应头信息的真实类型
    NSHTTPURLResponse *response = nil;
    // 同步网络请求会阻塞
    NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
    // 4、解析数据
    // 一般编码都用NSUTF8StringEncoding NSData转换为字符串
    NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);

异步请求——Block

    // 3、发送异步请求
    /* Block
     * 第一个参数:请求对象
     * 第二个参数:队列 决定代码执行在那个线程中
     * 第三个参数:回调block块 当请求完成时(成功|失败)完成回调
     * response 响应头
     * data 响应体
     * connectionError 错误信息
     */
    // 响应头信息的真是类型
    //    NSHTTPURLResponse *response = nil;
    // 发送异步请求
    [NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
        // 4、解析数据
        // 一般编码都用NSUTF8StringEncoding
        NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
        // 打印当前线程
        NSLog(@"%@",[NSThread currentThread]);
        // 打印状态码要事先转换
        NSHTTPURLResponse *res = (NSHTTPURLResponse *)response;
        NSLog(@"%ld",(long)res.statusCode);
    }];

异步请求——代理

/* 如果要下载大文件 就必须使用代理,因为数据如果一次性给,内存会爆掉
 * 先遵守协议NSURLConnectionDataDelegate
 * 设置代理的方式有三种
 * 类方法
     [NSURLConnection connectionWithRequest:request delegate:self];
 * 对象方法
     [[NSURLConnection alloc] initWithRequest:request delegate:self];
 */
    // 1、确定请求路径
    NSURL *url = [NSURL URLWithString:@"GET请求的URL""];
    // 2、创建请求对象
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    // 3、设置代理,发送请求
    //  startImmediately 是否发送请求 yes为发送 如果设置为no 要手动开启
    NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
    // 手动发送网络请求
    [connection start];
    // 取消发送请求方法
    //  [connection cancel];
/* 
 * 代理方法的说明
 */
// 1、当接收到服务器响应的时候调用
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(nonnull NSURLResponse *)response{
    
    NSLog(@"%s",__func__);
}

// 2、接收到服务器响应的时候响应 调用多次
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{

    NSLog(@"%s",__func__);
    // 拼接data
    [self.data appendData:data];
    
}
// 3、请求失败的时候调用
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
    
    NSLog(@"%s",__func__);
}
// 4、请求结束的时候调用
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{

    NSLog(@"%s",__func__);
    NSLog(@"%@",[[NSString alloc] initWithData:self.data encoding:NSUTF8StringEncoding]);
}

2、发送POST请求

    // 1、确定请求路径
    NSURL *url = [NSURL URLWithString:@"POST请求的URL"];
    
    // 2、创建可变请求对象 因为要修改请求方法 修改请求头信息
    // 请求头不需要设置(默认的请求头)
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
    
    // 3、修改请求方式 修改为POST的方式 必须大写
    request.HTTPMethod = @"POST";
    // 设置属性,请求超时
    request.timeoutInterval = 10.0;
    // 设置请求头信息
    [request setValue:@"iOS1000" forHTTPHeaderField:@"User-Agent"];
    
    // 4、设置请求体信息 --> NSData
    request.HTTPBody = [@"username=123&pwd=456&type=JSON" dataUsingEncoding:NSUTF8StringEncoding];
    
    // 5、发送请求 异步发送
    [NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
        // 6、数据解析
        // 一般编码都用NSUTF8StringEncoding
        NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
        NSLog(@"%@",[NSThread currentThread]);
        
    }];

3、小文件下载

/* 方式一
 * 以下载一张图片为例子
 * 耗时操作 无法监听进度 内存飙升 没办法释放内存 数据是一次性给的
 */
// 创建url
NSURL *url = [NSURL URLWithString:@""];
// 下载二进制数据
NSData *data = [NSData dataWithContentsOfURL:url];
 // 转换
 UIImage *iamge = [UIImage imageWithData:data];
/* 方式二
 * 以下载一张图片为例子
 * 无法监听进度 没办法释放内存 数据是一次性给的
 */
// 创建url
NSURL *url = [NSURL URLWithString:@""];
    
// 创建请求对象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
    
// 发送异步请求
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
        
        // 转换
        UIImage *image = [UIImage imageWithData:data];
        
    }];
/* 方式三
 * 以下载一张图片为例子
 * 使用代理 也会导致内存飙升
 */

/** 拼接下载数据的属性 */
@property (nonatomic, strong) NSMutableData *fileData;

/** totalSize 下载文件的总大小 */
@property (nonatomic, assign) NSInteger totalSize;

// 懒加载创建下载数据
- (NSMutableData *)fileData{

    if (!_fileData) {
        _fileData = [NSMutableData data];
    }
    
    return _fileData;
}

// 创建url
NSURL *url = [NSURL URLWithString:@""];
    
// 创建请求对象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
    
// 设置代理
[[NSURLConnection alloc] initWithRequest:request delegate:self];

// 代理方法实现 
// 当接收到服务器的时候响应
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{

    // 得到的文件总大小(本次请求的数据总大小)
    self.totalSize = response.expectedContentLength;
    
}

// 接收到服务器的时候响应 调用多次 计算进度下载在这个方法中进行实现
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{

    [self.fileData appendData:data];
    
    // 进度 = 已经下载的/文件总的大小
    NSLog(@"%f",1.0 * self.fileData.length / self.totalSize);
}

// 请求失败的时候调用
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{

    NSLog(@"%@",error);
}

// 请求结束的时候调用
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{

    // 将数据写到沙盒中去
    NSString *fullPath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"123.jpg"];
    [self.fileData writeToFile:fullPath atomically:YES];
    NSLog(@"%@",fullPath);
    
}

4、大文件下载


/** 沙盒路径属性 */
@property (nonatomic, strong) NSString *fullPath;

/** totalSize 文件总大小*/
@property (nonatomic, assign) NSInteger totalSize;

/** currentSize  当前下载大小*/
@property (nonatomic, assign) NSInteger currentSize;

/** 文件句柄 */
@property (nonatomic, strong) NSFileHandle *handle;

// 创建url
NSURL *url = [NSURL URLWithString:@""];
    
// 创建请求对象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
    
// 设置代理
[[NSURLConnection alloc] initWithRequest:request delegate:self];

// 代理方法实现 
// 接收到服务器响应的时候调用
// 为了保证内存不飙升 在下载到数据后就直接存储到沙盒中 在本方法中获得全路径
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{

    // 得到文件的总大小
    self.totalSize = response.expectedContentLength;

    // 获取全路径
    self.fullPath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"123.jpg"];
    NSLog(@"%@",self.fullPath);
    
    // 创建一个空的文件
    /*
     * 第一个参数:保存文件的路径
     * 第二个参数:创建文件的内容 此处创建的是一个空文件 所以直接传递nil
     * 第三个参数:设置文件属性
     */
    [[NSFileManager defaultManager] createFileAtPath:self.fullPath contents:nil attributes:nil];
    
    // 创建文件句柄 要写数据所以用此方法创建
    self.handle = [NSFileHandle fileHandleForWritingAtPath:self.fullPath];

   // 移动文件句柄到文件末尾
    [self.handle seekToEndOfFile];
    
}
// 接收到服务器数据的时候调用 会调用多次
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{

    // 但是这个方法是每次都在覆盖上一次保存的数据,所以此方法不能使用
    //    [data writeToFile:self.fullPath atomically:YES];

    // 使用文件管理者和文件句柄(指针),当进行拼接数据之前,先移动文件指针,然后再进行进行拼接
    // 写入数据
    [self.handle writeData:data];

    // 获得进度
    self.currentSize += data.length;
    NSLog(@"%f",1.0 * self.currentSize / self.totalSize);
}
// 请求失败的时候调用
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
}
// 请求结束的时候调用
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{

    // 关闭文件句柄
    [self.handle closeFile];
    self.handle = nil;

}

5、大文件断点下载——文件管理者


/** 沙盒路径属性 */
@property (nonatomic, strong) NSString *fullPath;

/** totalSize 文件总大小*/
@property (nonatomic, assign) NSInteger totalSize;

/** currentSize  当前下载大小*/
@property (nonatomic, assign) NSInteger currentSize;

/** 文件句柄 */
@property (nonatomic, strong) NSFileHandle *handle;

/** NSURLConnection对象属性 */
@property (nonatomic, strong) NSURLConnection *connect;

// 开始下载
// 创建URL
NSURL *url = [NSURL URLWithString:@""];
    
// 创建请求对象
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
/*
 * Range:bytes=0-499 表示头500个字节
 * Range:bytes=500-999 表示第二个500字节
 * Range:bytes=-500 表示最后500个字节
 * Range:bytes=500- 表示头500个字节之后的范围
 */
// 设置请求头中下载文件的部分
NSString *range = [NSString stringWithFormat:@"bytes=%zd-",self.currentSize];
[request setValue:range forHTTPHeaderField:@"Range"];
    
// 设置代理 发送请求
NSURLConnection *connect = [[NSURLConnection alloc] initWithRequest:request delegate:self];
    
// 设置全局的connect
self.connect = connect;

// 取消下载
[self.connect cancel];


// 代理方法实现 
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{

    // 得到本次请求的文件的总大小(期望大小)
    // 但是本次请求的文件大小和文件的总大小不同 假如一共有10个数据 本地已经下载了5个,那么下一次请求的数据期望大小就变成了5个,所以这里的文件总大小应该是当前下载的总大小加上文件期望大小才是文件总大小 如果这句话放在if后面就不需要加当前下载的文件总大小了,因为只会执行一次
    self.totalSize = response.expectedContentLength + self.currentSize;
    
    if (self.currentSize > 0) {
        
        return;

    }
    
    // 获取全路径
    self.fullPath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"123.jpg"];
    NSLog(@"%@",self.fullPath);
    
    // 创建一个空的文件
    /*
     * 第一个参数:保存文件的路径
     * 第二个参数:创建文件的内容 此处创建的是一个空文件 所以直接传递nil
     * 第三个参数:属性暂时先传递nil
     */
    [[NSFileManager defaultManager] createFileAtPath:self.fullPath contents:nil attributes:nil];
    
    // 创建文件句柄 要写数据所以用此方法创建
    self.handle = [NSFileHandle fileHandleForWritingAtPath:self.fullPath];

    // 移动文件句柄到文件末尾
    [self.handle seekToEndOfFile];

}
// 接收到服务器数据的时候调用 会调用多次
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
    
    // 使用文件管理者和文件句柄(指针),当进行拼接数据之前,先移动文件指针,然后再进行进行拼接
    [self.handle writeData:data];

    // 获得进度
    self.currentSize += data.length;
    NSLog(@"%f",1.0 * self.currentSize / self.totalSize);

}
// 请求失败的时候调用
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
    
}
// 请求结束的时候调用
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
    
    // 关闭文件句柄
    [self.handle closeFile];
    self.handle = nil;
    
}
/*
 * 在这里多一句嘴:采用多线程的方式下载文件的思路
 * 1、开启多条线程进行下载文件的部分(设置请求头中下载文件的部分)
 * 2、通过文件句柄去写入文件(这个会有一个offset的方法来进行特定位置的写入)
 * 3、通过判断是那个线程来进行控制写入,最终完成文件的多线程下载,节省时间
 * 鉴于以上无法使用输出流的方式下载,所以在此说明。
 */

6、大文件断点下载——输出流


/** 沙盒路径属性 */
@property (nonatomic, strong) NSString *fullPath;

/** totalSize 文件总大小*/
@property (nonatomic, assign) NSInteger totalSize;

/** currentSize  当前下载大小*/
@property (nonatomic, assign) NSInteger currentSize;

/** 输出流 */
@property (nonatomic, strong) NSOutputStream *stream;

/** NSURLConnection对象属性 */
@property (nonatomic, strong) NSURLConnection *connect;

// 开始下载
// 创建URL
NSURL *url = [NSURL URLWithString:@""];
    
// 创建请求对象
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
/*
 * Range:bytes=0-499 表示头500个字节
 * Range:bytes=500-999 表示第二个500字节
 * Range:bytes=-500 表示最后500个字节
 * Range:bytes=500- 表示头500个字节之后的范围
 */
NSString *range = [NSString stringWithFormat:@"bytes=%zd-",self.currentSize];
[request setValue:range forHTTPHeaderField:@"Range"];
    
// 设置代理 发送请求
NSURLConnection *connect = [[NSURLConnection alloc] initWithRequest:request delegate:self];
    
// 设置全局的connect
self.connect = connect;

// 取消下载
[self.connect cancel];


// 代理方法实现 
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{

    // 得到本次请求的文件的总大小(期望大小)
    // 但是本次请求的文件大小和文件的总大小不同 假如一共有10个数据 本地已经下载了5个,那么下一次请求的数据期望大小就变成了5个,所以这里的文件总大小应该是当前下载的总大小加上文件期望大小才是文件总大小 如果这句话放在if后面就不需要加当前下载的文件总大小了,因为只会执行一次
    self.totalSize = response.expectedContentLength + self.currentSize;
    
    if (self.currentSize > 0) {
        
        return;

    }
    
    // 获取全路径
    self.fullPath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"123.jpg"];
    NSLog(@"%@",self.fullPath);
    
    /* 如果输出流指向的地址没有文件,会自动创建一个新的文件来接收写入的数据
     * 第一个参数:保存文件的路径
     * 第二个参数:是否追加数据 YES 追加 NO 不追加
     */
    // 创建输出流
    NSOutputStream *stream = [[NSOutputStream alloc] initToFileAtPath:self.fullPath append:YES];
    
    // 打开输出流
    [stream open];
    
    self.stream = stream;
}
// 接收到服务器数据的时候调用 会调用多次
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
    
    // 使用输出流写数据 注意第一个参数是写入的二进制数据 第二个参数是数据长度
    [self.stream write:data.bytes maxLength:data.length];

    // 获得进度
    self.currentSize += data.length;
    NSLog(@"%f",1.0 * self.currentSize / self.totalSize);

}
// 请求失败的时候调用
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
    
}
// 请求结束的时候调用
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
    
    // 关闭输出流
    [self.stream closeFile];
    self.stream = nil;

}

7、文件上传

// 文件上传步骤 再进行文件上传的时候如果需要压缩,使用ZipArchive可以进行压缩,具体使用交给你们了。
/*
 1.设置请求头
 Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryjv0UfA04ED44AhWx
 2.按照固定的格式拼接请求体的数据
 
 ------WebKitFormBoundaryjv0UfA04ED44AhWx
 Content-Disposition: form-data; name="file"; filename="Snip.png"
 Content-Type: image/png
 
 
 ------WebKitFormBoundaryjv0UfA04ED44AhWx
 Content-Disposition: form-data; name="username"
 
 123456
 ------WebKitFormBoundaryjv0UfA04ED44AhWx--
 
 */
// 拼接请求体的数据格式
/*
 拼接请求体
 分隔符:----WebKitFormBoundaryjv0UfA04ED44AhWx
 1)文件参数
 --分隔符
 Content-Disposition: form-data; name="file"; filename="Snip.png"
 Content-Type: image/png(MIMEType:大类型/小类型)
 空行
 文件参数
 2)非文件参数
 --分隔符
 Content-Disposition: form-data; name="username"
 空行
 123456
 3)结尾标识
 --分隔符--
 */
   // 宏定义
   #define kBoundary @"---------------------------67949934127389441772834429"
   #define kNewLine [@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]
    // 创建URL对象
    NSURL *url = [NSURL URLWithString:@""];
    // 创建请求对象
    NSMutableURLRequest *requset = [NSMutableURLRequest requestWithURL:url];
    
    // 设置请求方法
    requset.HTTPMethod = @"POST";
    
    // 设置而请求头
    NSString *contentTypeValueStr = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",kBoundary];
    [requset setValue:contentTypeValueStr forHTTPHeaderField:@"Content-Type"];
    
    // 拼接请求体数据
    NSMutableData *fileData = [NSMutableData data];

    /*
     --分隔符
     Content-Disposition: form-data; name="file"; filename="Snip.png"
     Content-Type: image/png(MIMEType:大类型/小类型)
     空行
     文件参数
     */
    //  name 服务器给我们的
    //  filename 文件名
    //  MIMEType 
    [fileData appendData: [[NSString stringWithFormat:@"--%@",kBoundary] dataUsingEncoding:NSUTF8StringEncoding]];
    [fileData appendData:kNewLine];
    [fileData appendData:[@"Content-Disposition: form-data; name=\"file\"; filename=\"icon.png\"" dataUsingEncoding:NSUTF8StringEncoding]];
    [fileData appendData:kNewLine];
    [fileData appendData:[@"Content-Type: image/png" dataUsingEncoding:NSUTF8StringEncoding]];
    [fileData appendData:kNewLine];
    [fileData appendData:kNewLine];
    
    UIImage *image = [UIImage imageNamed:@"icon"];
    NSData *imageData = UIImagePNGRepresentation(image);
    [fileData appendData:imageData];
    [fileData appendData:kNewLine];

    /*
    2)非文件参数
    --分隔符
    Content-Disposition: form-data; name="username"
    空行
    123456
    */
    [fileData appendData: [[NSString stringWithFormat:@"--%@",kBoundary] dataUsingEncoding:NSUTF8StringEncoding]];
    [fileData appendData:kNewLine];
    [fileData appendData:[@"Content-Disposition: form-data; name=\"username\"" dataUsingEncoding:NSUTF8StringEncoding]];
    [fileData appendData:kNewLine];
    [fileData appendData:kNewLine];
    [fileData appendData:[@"123" dataUsingEncoding:NSUTF8StringEncoding]];
    [fileData appendData:kNewLine];

    /*
     * 3)结尾标识
     --分隔符--
     */
    [fileData appendData: [[NSString stringWithFormat:@"--%@--",kBoundary] dataUsingEncoding:NSUTF8StringEncoding]];
    
    // 设置请求体
    requset.HTTPBody = fileData;
    
    // 发送请求
    
    [NSURLConnection sendAsynchronousRequest:requset queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
        
        if (connectionError) {
            
            NSLog(@"%@",connectionError);
            
            return ;
            
        }
        
        NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
    }];

三、NSURLConnection和RunLoop

// 创建URL
    NSURL *url = [NSURL URLWithString:@""];
    
    // 创建请求对象
    NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
    
    // 设置代理
    
    NSURLConnection *connect = [[NSURLConnection alloc] initWithRequest:request delegate:self];
    
    // 代理方法在哪个线程中调用 不能设置在主队列中进行调用
//    [connect setDelegateQueue:[NSOperationQueue mainQueue]];
    [connect setDelegateQueue:[[NSOperationQueue alloc] init]]; // 开子线程
dispatch_async(dispatch_get_global_queue(0, 0), ^{
       
        // 创建URL
        NSURL *url = [NSURL URLWithString:@""];
        
        // 创建请求对象
        NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
        
        // 设置代理
        // 如果在子线程中直接这样设置代理,代理方法是不会执行的,为啥呢?
        /*
         * 因为你定义的connect对象是一个局部变量,当出了这个方法之后就会被系统释放
         * 然后自然不会去调用代理方法,那为啥放在主线程里就行呢
         * 因为主线程中的存在运行循环的原因,默认情况下,connect正常使用,
         * 调用线程的运行循环必须操作在默认运行循环模式,
         * 因为默认在子线程中是没有运行循环的,所以不执行。
         * 可以使用scheduleInRunLoop:forMode:改变运行循环和模式。
         */
       // 这个方法系统会把connect对象当做一个Source加入当前的RunLoop中,指定运行模式为默认
        NSURLConnection *connect = [[NSURLConnection alloc] initWithRequest:request delegate:self];
        
        // 代理方法在哪个线程中调用 不能设置在主队列中进行调用
        [connect setDelegateQueue:[[NSOperationQueue alloc] init]];
        
        [[NSRunLoop currentRunLoop] run];
    });

dispatch_async(dispatch_get_global_queue(0, 0), ^{

        // 创建URL
        NSURL *url = [NSURL URLWithString:@""];
        
        // 创建请求对象
        NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
        
        // 设置代理
        NSURLConnection *connect = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
        
        // 代理方法在哪个线程中调用 不能设置在主队列中进行调用
        [connect setDelegateQueue:[[NSOperationQueue alloc] init]];
        
        // 如果你不在创建connect对象的时候就发送请求,而是手动开启,就不需要向子线程中添加运行循环了
        // 如果你不添加connect运行循环,该方法connect将添加运行循环,并且设置为默认模式。
        [connect start];

    });

写在最后的话:关于NSURLConnection的基本使用今天就分享到这里,关于NSURLConnection使用方面的问题欢迎大家和我交流,共同进步,谢谢各位。

你可能感兴趣的:(iOS网络请求策略——NSURLConnection的基本使用)