[iOS学习笔记]自学过程中积累的知识点(五)

41. 多线程

iOS里线程可以使用四种方法,其优缺点以及介绍如下:

技术方案 简介 语言 线程管理 使用频率
pthread 一套通用的多线程API
适用于Unix\Linux\Windows等系统
跨平台\可移植
使用难度大
C 程序员管理 几乎不用
NSThread 使用更加面向对象
简单易用,可直接操作线程对象
OC 程序员管理 偶尔使用
GCD 旨在替代NSThread等线程技术
充分利用设备的多核
C 自动管理 经常使用
NSOperation 基于GCD(底层是GCD)
比GCD多了一些更简单实用的功能
使用更加面向对象
OC 自动管理 经常使用

41.1 NSThread

41.1.1 NSThread的创建

  1. NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];[thread start]; // 线程一启动,就会在线程thread中执行self的run方法
  2. [NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil]; //创建线程后自动启动线程
  3. [self performSelectorInBackground:@selector(run) withObject:nil]; //隐式创建并启动线程

后2种创建线程方式简单快捷但是无法对线程进行更详细的设置

41.1.2 NSThread的其他方法

  • 主线程相关用法
    +(NSThread *)mainThread; // 获得主线程
    -(BOOL)isMainThread; // 是否为主线程
    +(BOOL)isMainThread; // 是否为主线程

  • 获得当前线程
    NSThread *current = [NSThread currentThread];

  • 线程的调度优先级
    +(double)threadPriority;
    +(BOOL)setThreadPriority:(double)p;
    -(double)threadPriority;
    -(BOOL)setThreadPriority:(double)p;
    调度优先级的取值范围是0.0 ~ 1.0,默认0.5,值越大,优先级越高

  • 线程的名字
    -(void)setName:(NSString *)n;
    -(NSString *)name;

41.1.3 线程状态

线程状态一般分为:新建、就绪、运行、阻塞、死亡。

  • 启动线程
    - (void)start; // 进入就绪状态 -> 运行状态。当线程任务执行完毕,自动进入死亡状态

  • 阻塞(暂停)线程
    + (void)sleepUntilDate:(NSDate *)date;
    + (void)sleepForTimeInterval:(NSTimeInterval)ti; // 进入阻塞状态

  • 强制停止线程
    + (void)exit; // 进入死亡状态

注意:一旦线程停止(死亡)了,就不能再次开启任务

41.1.4 线程安全

使用锁可以避免多条线程抢夺同一块资源
@synchronized(锁对象)~~~~ {// 需要锁定的代码 }
一般都是锁self

41.1.5 线程间通信

  • - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
  • - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;

41.2 CGD(Grand Central Dispatch)

GCD中有2个核心概念:任务:执行什么操作,队列:用来存放任务。

GCD的使用的2个步骤:定制任务(确定想做的事情),将任务添加到队列中(GCD会自动将队列中的任务取出,放到对应的线程中执行,任务的取出遵循队列的FIFO原则:先进先出,后进后出)

41.2.1 执行任务

GCD中有2个用来执行任务的函数(同步和异步)

  • 用同步的方式执行任务
    dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
    queue:队列
    block:任务

  • 用异步的方式执行任务
    dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

  • 同步、异步、并发、串行

    • 同步和异步决定了要不要开启新的线程
      同步:在当前线程中执行任务,不具备开启新线程的能力
      异步:在新的线程中执行任务,具备开启新线程的能力

    • 并发和串行决定了任务的执行方式
      并发:多个任务并发(同时)执行
      串行:一个任务执行完毕后,再执行下一个任务

41.2.2 创建队列

GCD的队列可以分为2大类型

  • 并发队列(Concurrent Dispatch Queue)
    可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)
    并发功能只有在异步(dispatch_async)函数下才有效

  • 串行队列(Serial Dispatch Queue)
    让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)

    1. 并发队列的创建
      GCD默认已经提供了全局的并发队列,供整个应用使用,不需要手动创建

      • 使用dispatch_get_global_queue函数获得全局的并发队列
        dispatch_queue_t dispatch_get_global_queue(
        dispatch_queue_priority_t priority, // 队列的优先级
        unsigned long flags); // 此参数暂时无用,用0即可
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 获得全局并发队列

      • 全局并发队列的优先级
        #define DISPATCH_QUEUE_PRIORITY_HIGH 2 // 高
        #define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 默认(中)
        #define DISPATCH_QUEUE_PRIORITY_LOW (-2) // 低
        #define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // 后台

    2. 串行队列的创建
      GCD中获得串行有2种途径

      • 使用dispatch_queue_create函数创建串行队列
        dispatch_queue_t
        dispatch_queue_create(const char *label, // 队列名称
        dispatch_queue_attr_t attr); // 队列属性,一般用NULL即可
        dispatch_queue_t queue = dispatch_queue_create("cn.itcast.queue", NULL); // 创建
        dispatch_release(queue); // 非ARC需要释放手动创建的队列

      • 使用主队列(跟主线程相关联的队列)
        dispatch_queue_t queue = dispatch_get_main_queue();
        主队列是GCD自带的一种特殊的串行队列
        放在主队列中的任务,都会放到主线程中执行

各种队列的执行效果

全局并发队列 手动创建串行队列 主队列
同步(sync) 没有开启新线程
串行执行任务
没有开启新线程
串行执行任务
异步(async) 有开启新线程
并发执行任务
有开启新线程
串行执行任务

线程间通信

// 从子线程回到主线程
dispatch_async(
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // 执行耗时的异步操作...
        dispatch_async(dispatch_get_main_queue(), ^{
        // 回到主线程,执行UI刷新操作
        });
});

GCD的其他用法

  • 执行延时

    • 调用NSObject的方法
      [self performSelector:@selector(run) withObject:nil afterDelay:2.0];
      // 2秒后再调用self的run方法

    • 使用GCD函数
      dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ // 2秒后异步执行这里的代码...});

  • 一次性代码
    使用dispatch_once函数能保证某段代码在程序运行过程中只被执行1次

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    // 只执行1次的代码(这里面默认是线程安全的)
});
  • 队列组
    有这么1种需求
    首先:分别异步执行2个耗时的操作
    其次:等2个异步操作都执行完毕后,再回到主线程执行操作
    如果想要快速高效地实现上述需求,可以考虑用队列组
dispatch_group_t group =  dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // 执行1个耗时的异步操作
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
0), ^{
    // 执行1个耗时的异步操作
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    // 等前面的异步操作都执行完毕后,回到主线程...
});

ARC/MRC下单例模式的写法

可以用宏判断是否为ARC环境
#if __has_feature(objc_arc)
// ARC
#else
// MRC
#endif

ARC下:

// 在.m中保留一个全局的static的实例
static id _instance;

// 重写allocWithZone:方法,在这里创建唯一的实例(注意线程安全)
+ (id)allocWithZone:(struct _NSZone *)zone
{
    @synchronized(self) {
        if (!_instance) {
        _instance = [super allocWithZone:zone];
        }
    }
    return _instance;
}

// 提供1个类方法让外界访问唯一的实例
+ (instancetype)sharedSoundTool
{
    @synchronized(self) {
        if (!_instance) {
            _instance = [[self alloc] init];
            }
        }
    return _instance;
}

MRC下(比ARC多了几个步骤):

// 实现copyWithZone:方法
+ (id)copyWithZone:(struct _NSZone *)zone
{
    return _instance;
}

// 实现内存管理方法
- (id)retain { return self; }
- (NSUInteger)retainCount { return 1; }
- (oneway void)release {}
- (id)autorelease { return self; }

41.3 NSOperation

  • NSOperation的作用
    配合使用NSOperation和NSOperationQueue也能实现多线程编程

  • NSOperation和NSOperationQueue实现多线程的具体步骤
    先将需要执行的操作封装到一个NSOperation对象中
    然后将NSOperation对象添加到NSOperationQueue中
    系统会自动将NSOperation中封装的操作放到一条新线程中执行

  • NSOperation是个抽象类,并不具备封装操作的能力,必须使用它的子类
    使用NSOperation子类的方式有3种
    NSInvocationOperation
    NSBlockOperation
    自定义子类继承NSOperation,实现内部相应的方法

41.3.1 NSInvocationOperation

  • 创建NSInvocationOperation对象
    - (id)initWithTarget:(id)target selector:(SEL)sel object:(id)arg;

  • 调用start方法开始执行操作
    - (void)start;
    一旦执行操作,就会调用target的sel方法

  • 注意
    默认情况下,调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作
    只有将NSOperation放到一个NSOperationQueue中,才会异步执行操作

41.3.2 NSBlockOperation

  • 创建NSBlockOperation对象
    + (id)blockOperationWithBlock:(void (^)(void))block;

  • 通过addExecutionBlock:方法添加更多的操作
    - (void)addExecutionBlock:(void (^)(void))block;

  • 注意:只要NSBlockOperation封装的操作数 > 1,就会异步执行操作

40.3.3 NSOperationQueue

  • NSOperationQueue的作用
    NSOperation可以调用start方法来执行任务,但默认是同步执行的
    如果将NSOperation添加到NSOperationQueue(操作队列)中,系统会自动异步执行NSOperation中的操作

  • 添加操作到NSOperationQueue中
    - (void)addOperation:(NSOperation *)op;
    - (void)addOperationWithBlock:(void (^)(void))block;

  • 最大并发数的相关方法
    - (NSInteger)maxConcurrentOperationCount;
    - (void)setMaxConcurrentOperationCount:(NSInteger)cnt;

  • 队列的取消、暂停、恢复
    - (void)cancelAllOperations;
    提示:也可以调用NSOperation的- (void)cancel方法取消单个操作
    - (void)setSuspended:(BOOL)b; // YES代表暂停队列,NO代表恢复队列
    - (BOOL)isSuspended;

41.3.4 NSOperation的其他操作

  • 设置NSOperation在queue中的优先级,可以改变操作的执行顺序
    - (NSOperationQueuePriority)queuePriority;
    - (void)setQueuePriority:(NSOperationQueuePriority)p;

    • 优先级的取值(优先级越高,越先执行)
      NSOperationQueuePriorityVeryLow = -8L,
      NSOperationQueuePriorityLow = -4L,
      NSOperationQueuePriorityNormal = 0,
      NSOperationQueuePriorityHigh = 4,
      NSOperationQueuePriorityVeryHigh = 8
  • NSOperation之间可以设置依赖来保证执行顺序
    比如一定要让操作A执行完后,才能执行操作B,可以这么写
    [operationB addDependency:operationA]; // 操作B依赖于操作A
    可以在不同queue的NSOperation之间创建依赖关系,但不能相互依赖

  • 自定义NSOperation
    重写- (void)main方法,在里面实现想执行的任务

    • 重写- (void)main方法的注意点
      自己创建自动释放池(因为如果是异步操作,无法访问主线程的自动释放池)
      经常通过- (BOOL)isCancelled方法检测操作是否被取消,对取消做出响应

42 HTTP协议

HTTP请求:请求行、请求头、请求体
HTTP响应:响应行、响应头、响应体
在iOS中发送请求时不需要程序员写请求行和请求头,有需要时只需要写请求体

HTTP请求方式主要有两种:GET请求和POST请求
还有一种HEAD请求,Head请求可用于在文件下载时查询欲下载的大小

发送请求时的主要过程:urlStr –> NSURL –> NSMutableURLRequest –> NSURLConnection

42.1 GET请求与POST请求发送时的区别

POST过程和GET一致,只是需要设置NSMutableURLRequest的HTTPBody以及HTTPMethod,另外url与GET方式不一致.

GET方式

NSString *urlStr = [NSString stringWithFormat:@"http://192.168.1.200:8080/MJServer/login?username=%@&pwd=%@", username, pwd];
urlStr = [urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [NSURL URLWithString:urlStr];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; // 默认就是GET请求
request.timeoutInterval = 5; // 设置请求超时
NSOperationQueue *queue = [NSOperationQueue mainQueue];
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {  // 当请求结束的时候调用
   // connectionError与data只有一个有值
}];

POST方式

NSURL *url = [NSURL URLWithString:@"http://192.168.1.200:8080/MJServer/login"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; request.timeoutInterval = 5; // 设置请求超时
request.HTTPMethod = @"POST"; // 设置为POST请求
// 通过请求头告诉服务器客户端的类型
[request setValue:@"ios" forHTTPHeaderField:@"User-Agent"];
// 设置请求体
NSString *param = [NSString stringWithFormat:@"username=%@&pwd=%@", username, pwd];
request.HTTPBody = [param dataUsingEncoding:NSUTF8StringEncoding];
NSOperationQueue *queue = [NSOperationQueue mainQueue];
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {  // 当请求结束的时候调用

}];

HEAD方式查询欲下载文件大小

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"HEAD";
NSURLResponse *response = nil;
[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
totalLength = response.expectedContentLength;

43 NSURLConnection

ASI、AFN框架的使用见第46、47点
配置以及其他信息点击这里

43.1 发送请求

请求分为同步请求和异步请求,区别在于会不会阻塞主线程

43.1.1 同步请求

  • + (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error;

43.1.2 异步请求

  • + (void)sendAsynchronousRequest:(NSURLRequest*) request
    queue:(NSOperationQueue*) queue
    completionHandler:(void (^)(NSURLResponse* response, NSData* data, NSError* connectionError)) handler NS_AVAILABLE(10_7, 5_0);
    data与connectionError只会一个有值.

  • + (NSURLConnection*)connectionWithRequest:(NSURLRequest *)request delegate:(id)delegate;

  • - (instancetype)initWithRequest:(NSURLRequest *)request delegate:(id)delegate;

  • - (instancetype)initWithRequest:(NSURLRequest *)request delegate:(id)delegate startImmediately:(BOOL)startImmediately NS_AVAILABLE(10_5, 2_0);
    如果此函数最后一个参数传入NO,则不会马上开始请求。需要开始时要调用start函数.

43.1.3 NSURLConnectionDataDelegate

  • - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;
    请求错误(失败)的时候调用(请求超时\断网\没有网, 一般指客户端错误)

  • - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
    当接受到服务器的响应(连通了服务器)就会调用

  • - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
    当接受到服务器的数据就会调用(可能会被调用多次, 每次调用只会传递部分数据)

  • - (void)connectionDidFinishLoading:(NSURLConnection *)connection;
    当服务器的数据接受完毕后就会调用

e.g. 代理方法的运用

#pragma mark - NSURLConnectionDataDelegate 代理方法
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    [MBProgressHUD showError:@"网络繁忙, 请稍后再试"];
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    // 初始化数据
    self.responseData = [NSMutableData data];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    // 拼接(收集)数据
    [self.responseData appendData:data];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    // 解析服务器返回的数据
    NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:self.responseData options:NSJSONReadingMutableLeaves error:nil];
    NSString *error =  dict[@"error"];
    if (error) { // 登录失败
        [MBProgressHUD showError:error];
    } else { // 登录成功
        NSString *success =  dict[@"success"];
        [MBProgressHUD showSuccess:success];
    }
}

43.2 请求缓存

// 1.创建请求
NSURL *url = [NSURL URLWithString:@"http://localhost:8080/MJServer/video"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];

// 2.设置缓存策略(有缓存就用缓存,没有缓存就重新请求)
request.cachePolicy = NSURLRequestReturnCacheDataElseLoad;

// 获得全局的缓存对象
NSURLCache *cache = [NSURLCache sharedURLCache];

// 定期处理缓存
// if (缓存没有达到7天) {
// request.cachePolicy = NSURLRequestReturnCacheDataElseLoad;
// }
// if (缓存达到7天) {
// [cache removeCachedResponseForRequest:request];
// }


NSCachedURLResponse *response = [cache cachedResponseForRequest:request];
if (response) {
    NSLog(@"---这个请求已经存在缓存");
} else {
    NSLog(@"---这个请求没有缓存");
}

44 数据解析

44.1 JSON的解析

JSON OC
{} NSDictionary
[] NSArray
“” NSString
数字 NSNumber

在iOS中,JSON的常见解析方案有4种
第三方框架:JSONKit、SBJson、TouchJSON(性能从左到右,越差)
苹果原生(自带):NSJSONSerialization(性能最好)

NSJSONSerialization的常见方法

  • + (id)JSONObjectWithData:(NSData *)data options:(NSJSONReadingOptions)opt error:(NSError **)error;
    JSON数据 –> OC对象

  • + (NSData *)dataWithJSONObject:(id)obj options:(NSJSONWritingOptions)opt error:(NSError **)error;
    OC对象 –> JSON数据

44.2 XML的解析

XML的解析有2种
DOM:一次性将整个XML文档加载进内存,比较适合解析小文件
SAX:从根元素开始,按顺序一个元素一个元素往下解析,比较适合解析大文件

在iOS中,解析XML的手段有很多
苹果原生
NSXMLParser:SAX方式解析,使用简单

第三方框架
libxml2:纯C语言,默认包含在iOS SDK中,同时支持DOM和SAX方式解析
GDataXML:DOM方式解析,由Google开发,基于libxml2

XML解析方式的选择建议
大文件:NSXMLParser、libxml2
小文件:GDataXML

44.3 NSXMLParser

使用步骤

// 传入XML数据,创建解析器
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
// 设置代理,监听解析过程
parser.delegate = self;
// 开始解析
[parser parse];

NSXMLParser采取的是SAX方式解析,特点是事件驱动,下面情况都会通知代理
当扫描到文档(Document)的开始与结束
当扫描到元素(Element)的开始与结束

NSXMLParserDelegate

  • 当扫描到文档的开始时调用(开始解析)
    - (void)parserDidStartDocument:(NSXMLParser *)parser

  • 当扫描到文档的结束时调用(解析完毕)
    - (void)parserDidEndDocument:(NSXMLParser *)parser

  • 当扫描到元素的开始时调用(attributeDict存放着元素的属性)
    - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict

  • 当扫描到元素的结束时调用
    - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName

e.g.

#pragma mark - NSXMLParserDelegate
/** * 开始解析文档时调用 */
- (void)parserDidStartDocument:(NSXMLParser *)parser
{

}
/** * 结束解析文档时调用(解析完毕) */
- (void)parserDidEndDocument:(NSXMLParser *)parser
{

}

/** * 解析到一个元素的开头时调用 <videos> */
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
    if ([@"videos" isEqualToString:elementName]) { // 解析到一个videos标签
        self.videos = [NSMutableArray array];
    } else if ([@"video" isEqualToString:elementName]) { // 解析到一个video标签, 创建一个模型
        HMVideo *video = [HMVideo videoWithDict:attributeDict];
        [self.videos addObject:video];
    }
}

/** * 解析到一个元素的结尾时调用 </videos> */
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{

}

44.4 GDataXML

GDataXML配置以及使用

45 文件上传下载

45.1 文件下载

45.1.1 小文件下载

  • 直接用NSData的+ (id)dataWithContentsOfURL:(NSURL *)url;
  • 利用NSURLConnection发送一个HTTP请求去下载
  • 如果是下载图片,还可以利用SDWebImage框架

45.1.2 HTTP Range

通过设置请求头Range可以指定每次从网路下载数据包的大小

Range示例
bytes=0-499 从0到499的头500个字节
bytes=500-999 从500到999的第二个500字节
bytes=500- 从500字节以后的所有字节
bytes=-500 最后500个字节
bytes=500-599,800-899 同时指定几个范围

Range小结

-用于分隔
前面的数字表示起始字节数
后面的数组表示截止字节数,没有表示到末尾
,用于分组,可以一次指定多个Range,不过很少用

45.2 文件上传

也可以使用ASI、AFN达成目的,此处仅使用原生的达成目的。

文件上传一定要用POST方式。
首先要设置请求体,请求体里面有文件参数和普通参数(格外注意请求体的格式)
然后设置请求头,并声明这个请求时个文件上传
最后发送请求

e.g.

    // 文件上传
    NSURL *url = [NSURL URLWithString:@"http://localhost:8080/MJServer/upload"];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    request.HTTPMethod = @"POST";

    // 设置请求体
    NSMutableData *body = [NSMutableData data];

    /***************文件参数***************/
    // 参数开始的标志
    [body appendData:HMEncode(@"--heima\r\n")];      //HMEncode 为UTF8编码的宏
    // name : 指定参数名(必须跟服务器端保持一致)
    // filename : 文件名
    NSString *disposition = [NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", name, filename];
    [body appendData:HMEncode(disposition)];
    NSString *type = [NSString stringWithFormat:@"Content-Type: %@\r\n", mimeType];
    [body appendData:HMEncode(type)];

    [body appendData:HMEncode(@"\r\n")];
    [body appendData:data];
    [body appendData:HMEncode(@"\r\n")];

    /***************普通参数***************/
    [params enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
        // 参数开始的标志
        [body appendData:HMEncode(@"--heima\r\n")];
        NSString *disposition = [NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n", key];
        [body appendData:HMEncode(disposition)];

        [body appendData:HMEncode(@"\r\n")];
        [body appendData:HMEncode(obj)];
        [body appendData:HMEncode(@"\r\n")];
    }];

    /***************参数结束***************/
    // heima--\r\n
    [body appendData:HMEncode(@"--heima--\r\n")];
    request.HTTPBody = body;

    // 设置请求头
    // 请求体的长度
    [request setValue:[NSString stringWithFormat:@"%zd", body.length] forHTTPHeaderField:@"Content-Length"];
    // 声明这个POST请求是个文件上传
    [request setValue:@"multipart/form-data; boundary=heima" forHTTPHeaderField:@"Content-Type"];

    // 发送请求
    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
        if (data) {
            NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
            NSLog(@"%@", dict);
        } else {
            NSLog(@"上传失败");
        }
    }];

46. ASI

ASI配置

46.1 ASI缓存

46.1.1 ASI缓存单个请求

// 2.获得系统默认的缓存管理对象(决定着缓存存储路径)
ASIDownloadCache *cache = [ASIDownloadCache sharedCache];
// 默认的缓存加载策略 : 不读取缓存
cache.defaultCachePolicy = ASIDoNotReadFromCacheCachePolicy;

// 3.使用缓存
request.downloadCache = cache;
// 缓存加载策略
request.cachePolicy = ASIOnlyLoadIfNotCachedCachePolicy;
// 缓存存储策略(存储的时长)
request.cacheStoragePolicy = ASICachePermanentlyCacheStoragePolicy;


// 缓存加载的优先级: request.cachePolicy > cache.defaultCachePolicy

46.1.2 ASI缓存所有请求

// 2.获得系统默认的缓存管理对象(决定着缓存存储路径)
ASIDownloadCache *cache = [ASIDownloadCache sharedCache]; // 默认的缓存加载策略 : 不读取缓存 cache.defaultCachePolicy = ASIDoNotReadFromCacheCachePolicy; [ASIHTTPRequest setDefaultCache:cache];

46.2 发送请求

  • [request startAsynchronous]
    发送异步请求

  • [request startSynchronous]
    发送同步请求

46.3 GET\POST

  • GET请求
    ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL: url];

  • POST请求
    ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url1];
    // 添加普通参数(非文件参数,文件参数见下)
    [request setPostValue:@”dasda” forKey:@”username”];
    [request setPostValue:@”1515” forKey:@”pwd”];

46.4 文件下载

// 1.创建请求对象
NSURL *url = [NSURL URLWithString:@"http://192.168.1.200:8080/MJServer/resources/jre.zip"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];

// 2.设置所下载文件的存储路径
NSString *cache = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSString *filepath = [cache stringByAppendingPathComponent:@"jre.zip"];
request.downloadDestinationPath = filepath;

// 3.设置下载代理(可以直接设置一个有setProgress函数的对象,若没有,则需要实现setProgress方法)
request.downloadProgressDelegate = self.circleView;

// 4.支持断点下载
request.allowResumeForFileDownloads = YES;

// 5.发送请求
[request startAsynchronous];

46.5 文件上传

// 1.创建请求
NSURL *url = [NSURL URLWithString:@"http://192.168.1.200:8080/MJServer/upload"];
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];

// 2.设置(指定)所要上传文件的路径
NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSString *file = [caches stringByAppendingPathComponent:@"jre.zip"];
[request setFile:file forKey:@"file"];
// [request setFile:file withFileName:@"123.txt" andContentType:@"text/plain" forKey:@"file"];

// 3.设置其他请求参数
[request setPostValue:@"zhangsan" forKey:@"username"];

// 设置代理监听上传进度
request.uploadProgressDelegate = self.circleView;

// 4.发送请求
[request startAsynchronous];

// 程序进入后台, 继续发送请求
request.shouldContinueWhenAppEntersBackground = YES;

// 5.监听请求
[request setCompletionBlock:^{
    NSLog(@"上传完毕");
}];

46.6 监听请求过程

46.6.1 代理

  1. 设置代理 request.delegate = self;
  2. 遵循协议 ASIHTTPRequestDelegate
  3. 实现协议中的代理方法
    • - (void)requestStarted:(ASIHTTPRequest *)request
    • - (void)request:(ASIHTTPRequest *)request didReceiveData:(NSData *)data
    • - (void)requestFinished:(ASIHTTPRequest *)request
    • - (void)requestFailed:(ASIHTTPRequest *)request

46.6.2 SEL

  1. 设置代理
  2. 设置方法名 [request setDidFinishSelector:@selector(didFinishSelector)]

46.6.3 block

[request setStartedBlock:^{ }];

[request setDataReceivedBlock:^(NSData *data) { }];

[request setCompletionBlock:^{ }];

[request setFailedBlock:^{ }];

46.7 通过request获得服务器的响应

  • 获得响应头信息
    @property (atomic, retain) NSDictionary *responseHeaders;

  • 获得响应体(实体内容)
    - (NSData *)responseData
    - (NSString *)responseString

46.8 网络状态监测

ASI的网络监测使用的是Reachability

47 AFN

47.1 GET/POST请求

在AFN中,GET请求和POSY请求只是方法名不同而已(一为GET,另一为POST)

GET

// 1.获得请求管理者(管理请求, 帮助发请求)
AFHTTPRequestOperationManager *mgr = [AFHTTPRequestOperationManager manager];
// 声明服务器返回的数据是JSON
// mgr.responseSerializer = [AFJSONResponseSerializer serializer];

// 2.封装请求参数
NSMutableDictionary *params = [NSMutableDictionary dictionary];
params[@"username"] = @"123";
params[@"pwd"] = @"123";

// 3.发送GET请求
[mgr GET:@"http://192.168.1.200:8080/MJServer/login" parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) { 
// responseObject : 在这种情况下是字典
         NSLog(@"请求成功---%@", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
         NSLog(@"请求失败---%@", error);
}];

POST

// 1.获得请求管理者(管理请求, 帮助发请求)
AFHTTPRequestOperationManager *mgr = [AFHTTPRequestOperationManager manager];
// 声明服务器返回的数据是JSON
// mgr.responseSerializer = [AFJSONResponseSerializer serializer];

// 2.封装请求参数
NSMutableDictionary *params = [NSMutableDictionary dictionary];
params[@"username"] = @"123";
params[@"pwd"] = @"123";

// 3.发送GET请求
[mgr POST:@"http://192.168.1.200:8080/MJServer/login" parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) { 
// responseObject : 在这种情况下是字典
         NSLog(@"请求成功---%@", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
         NSLog(@"请求失败---%@", error);
}];

47.2 文件上传

// 1.获得请求管理者
AFHTTPRequestOperationManager *mgr = [AFHTTPRequestOperationManager manager];

// 2.发送请求(做文件上传)
// parameters : 只能放非文件参数
NSMutableDictionary *params = [NSMutableDictionary dictionary];
params[@"username"] = @"zhangsan";

[mgr POST:@"http://192.168.1.200:8080/MJServer/upload" parameters:params
    constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
     // 一定要在这个block中添加文件参数

     // 加载文件数据
     NSString *file = [[NSBundle mainBundle] pathForResource:@"test.txt" ofType:nil];
     NSData *data = [NSData dataWithContentsOfFile:file];

     // 拼接文件参数
     [formData appendPartWithFileData:data name:@"file" fileName:@"123.txt" mimeType:@"text/plain"];
} success:^(AFHTTPRequestOperation *operation, id responseObject) {
        NSLog(@"上传成功----%@", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        NSLog(@"上传失败----%@", error);
}];

47.3 网络状态监测

// 1.获得网络监控的管理者
AFNetworkReachabilityManager *mgr = [AFNetworkReachabilityManager sharedManager];

// 2.设置网络状态改变后的处理
[mgr setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
    // 当网络状态改变了, 就会调用这个block
    switch (status) {
        case AFNetworkReachabilityStatusUnknown: // 未知网络
            NSLog(@"未知网络");
            break;

        case AFNetworkReachabilityStatusNotReachable: // 没有网络(断网)
            NSLog(@"没有网络(断网)");
            break;

        case AFNetworkReachabilityStatusReachableViaWWAN: // 手机自带网络
            NSLog(@"手机自带网络");
            break;

        case AFNetworkReachabilityStatusReachableViaWiFi: // WIFI
            NSLog(@"WIFI");
            break;
    }
}];

// 3.开始监控
[mgr startMonitoring];

// mgr.isReachableViaWiFi
// mgr.isReachableViaWWAN

48 UIWebView

48.1 UIWebView的一些属性以及方法

  • webView.scalesPageToFit = YES; // 伸缩内容至适应屏幕尺寸
  • - (void)loadRequest:(NSURLRequest *)request;
  • - (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script; // 执行JS代码
  • @property (nonatomic) UIDataDetectorTypes dataDetectorTypes NS_AVAILABLE_IOS(3_0);
    要监测的数据类型(PhoneNumber、Link、Address、CalendarEvent、None、All)

48.2 UIWebViewDelegate

  • - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
  • - (void)webViewDidStartLoad:(UIWebView *)webView;
  • - (void)webViewDidFinishLoad:(UIWebView *)webView;
  • - (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error;

48.3 网页调用OC代码

UIWebView在跳转网页前都会调用代理的
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
函数。以此函数为桥梁可以实现网页调用OC代码

e.g.

/** 当webView发送一个请求之前都会调用这个方法, 返回YES, 可以加载这个请求, 返回NO, 代表禁止加载这个请求 */
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    NSString *url = request.URL.absoluteString;
    NSRange range = [url rangeOfString:@"ios://"];
    if (range.length != 0) {
        // 截取方法名
        NSString *method = [url substringFromIndex:range.location + range.length];
        // 将方法名转为SEL类型
        SEL selector = NSSelectorFromString(method);
        [self performSelector:selector withObject:nil];
    }
    return YES;
}

你可能感兴趣的:(ios)