您的位置:首页 > 理论基础 > 计算机网络

网络多线程3

2016-12-13 00:00 344 查看
NMTDAY03
1.GCD的高级功能
1.1GCD阻塞(Barrier)
1.2GCD延迟操作
1.3GCD一次性执行once
1.4GCD调度组group
1.5GCD单例设计模式Singleton
1.5.1两种方式创建单例
1.5.1懒汉式单例(lazyinitialization)
1.5.2饿汉式单例(eagerinitialization)
2.NSOperation
2.1NSInvocationOpearion
2.2NSBlockOperation
2.3NSOperation和GCD的对比
3.NSOperation的高级功能
3.1队列的最大并发数
3.2队列的暂停、继续、取消
3.2.1队列暂停
3.2.2队列继续
3.2.3队列取消
3.3操作间的依赖关系
3.4.操作的优先级和监听操作执行完成的回调
4.列表异步加载网络图片
4.1SDWebImage加载网络图片
4.2NSOperation加载网络图片
5.GitHub初体验
6.AFN请求json数据
7.SDWebImage

1.GCD的高级功能

阻塞Barrier

一次性执行Once

延迟操作After

调度组Group

1.1GCD阻塞(Barrier)

注意:

barrier使用的时候规定要使用自定义的并行队列

大部分情况下都是正确的,但是不能绝对信任。

1.2GCD延迟操作

延迟操作:‘dispatch_after’这个函数默认是异步执行的

使用代码块



dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(1*NSEC_PER_SEC)),dispatch_get_main_queue(),^{

NSLog(@"iamstarting");

});

自己手写



//dispatch_time_twhen:延迟多久开始执行,可以精确到纳秒

//dispatch_queue_t:在那个队列执行任务

//参数三:执行的任务代码块

dispatch_after(dispatch_time_twhen,dispatch_queue_t_Nonnullqueue,^{

code

})



//dispatch_time_twhen:延迟多久开始执行,可以精确到纳秒

//dispatch_queue_t:在那个队列执行任务

//参数三:执行的任务代码块

dispatch_time_twhen=dispatch_time(DISPATCH_TIME_NOW,(int64_t)(2.0*NSEC_PER_SEC));

dispatch_queue_tqueue=dispatch_get_global_queue(0,0);

dispatch_block_tblock=^{

NSLog(@"I'mlate:%@",[NSThreadcurrentThread]);

};

dispatch_after(when,queue,block);

NSLog(@"~~~~end~~~~~");

1.3GCD一次性执行once

dispatch_once_t内部有一把锁,是能够保证线程安全.而且是苹果公司推荐使用的.

名词解释:线程安全
线程安全:这个任务在同一时
7ff0
刻有且只能被一个线程执行。
线程不安全:是说这个任务在同一时刻可以被多个线程执行。

在开发中,有些代码只想就只执行一次.典型的应用场景就是设计单例模式.

dispatch_once_t能够确保只执行一次的原理:

onceToken有个初始值,当第一次执行时,判断是否是初始值,如果是初始值就执行函数内部的代码。

执行结束之前,修改onceToken初始值。

第二次执行的时候,因为onceToken已经不再是初始值,所以就不会再次执行了。



staticdispatch_once_tonceToken;

NSLog(@"%ld",onceToken);

dispatch_once(&onceToken,^{

NSLog(@"hello");

});

1.4GCD调度组group

调度组中的所有异步任务执行结束之后,会得到统一的通知.

使用场景:监听一组异步任务是否执行结束,如果执行结束就能够得到统一的通知.



//dispatch_group_t:调度组

//dispatch_queue_t:队列

//block:任务

dispatch_group_async(dispatch_group_t_Nonnullgroup,dispatch_queue_t_Nonnullqueue,<#^(void)block#>)

enter和leave都是成对儿出现的。

enter如果比leave多,那就永远不会被通知,监测实效。

enter如果比leave,程序直接crash。



//下载三首歌曲ABC,歌曲的下载顺序不做要求,但是要知道什么时候下载完成

-(void)downloadSongs{

//dispatch_group_t:调度组

//dispatch_queue_t:队列

//block:任务

dispatch_group_tgroup=dispatch_group_create();

dispatch_queue_tqueue=dispatch_get_global_queue(0,0);

dispatch_group_enter(group);

dispatch_group_async(group,queue,^{

NSLog(@"A歌曲下载完毕%@",[NSThreadcurrentThread]);

dispatch_group_leave(group);

});

dispatch_group_enter(group);

dispatch_group_async(group,queue,^{

NSLog(@"B歌曲下载完毕%@",[NSThreadcurrentThread]);

dispatch_group_leave(group);

});

dispatch_group_enter(group);

dispatch_group_async(group,queue,^{

NSLog(@"C歌曲下载完毕%@",[NSThreadcurrentThread]);

dispatch_group_leave(group);

});

//所有歌曲下载完之后,通知用户ABC都下载完毕了。

dispatch_notify(group,dispatch_get_main_queue(),^{

NSLog(@"WellDone.%@",[NSThreadcurrentThread]);

});

}

1.5GCD单例设计模式Singleton

单例设计模式的特点:

有一个全局访问点(供全局实例化单例的类方法)

单例保存在静态存储区

在内存有且只有一份

生命周期跟APP一样长

场景:

例如QQ音乐播放器,只有一个播放器,播放所有的音频。

数据库大量的读写操作

工程里面常用的工具类:获取沙盒路径的类、处理网络数据的类、用户登录

系统提供的单例:

[NSUserDefaultsstandardUserDefaults];perfermance设置

[UIApplicationsharedApplication];Application

[NSNotificationCenterdefaultCenter];通知中心

[NSFileManagerdefaultManager];文件管理器

还有日历,等等

注意:单例不要滥用,因为单例的生命周期跟APP一样,也会占用内存。

潜规则系统一般定义,单例都是以default,shared开通。

命名规则:shared+类名称
+(instancetype)sharedNetworkTool

现在我们的目的是类无论创建多少次,都只分配一次存储空间,而分配存储空间的操作是在alloc:中执行,所以我们要重写类中的alloc:类方法

在重写alloc:过程中,我们发现有个allocWithZone:方法。alloc:方法内部最终会去调用allocWithZone:方法来分配存储空间,所以为了能更深层控制,我们直接重写allocWithZone:方法。



+(instancetype)alloc

+(instancetype)allocWithZone:(struct_NSZone*)zone

为了让单例严谨一点,我们还要考虑copy这种情况,所以我们还要重写copy和mutableCopy。要重写copy和mutableCopy方法,必须先遵守协议。



@interfaceNetworkTool()<NSCopying,NSMutableCopying>

-(id)copyWithZone:(NSZone*)zone

{

//直接返回变量即可,因为只有创建了对象,才能使用copy方法

return_instance;

}

-(id)mutableCopyWithZone:(NSZone*)zone

{

return_instance;

}

在学习多线程之前,写单例的方法:BUT,在多线程下是错误的,因为线程不安全。



+(instancetype)allocWithZone:(struct_NSZone*)zone{

staticid_instance;

if(!_instance){

_instance=[superallocWithZone:zone];

}

return_instance;

}

1.5.1两种方式创建单例

多线程之后,有两种方法创建单例。
方式一:加线程锁



+(instancetype)allocWithZone:(struct_NSZone*)zone{

staticid_instance;

@synchronized(self){

if(!_instance){

_instance=[superallocWithZone:zone];

}

}

return_instance;

}

方式二:不需要做判断了,因为dispatch_once只能执行一次,并且线程安全。推荐使用。这个就是专门为单例设计的,高效+安全。



+(instancetype)allocWithZone:(struct_NSZone*)zone{

staticid_instance;

staticdispatch_once_tonceToken;

dispatch_once(&onceToken,^{

_instance=[superallocWithZone:zone];

});

return_instance;

}

1.5.1懒汉式单例(lazyinitialization)

在使用时才会创建。

1.5.2饿汉式单例(eagerinitialization)

尽早的创建单例,可以利用initialize方法创建单例。

initialize会在类第一次被使用时调用

initialize调用是线程安全的。



+(void)initialize

{

_instance=[[selfalloc]init];

}

//饿汉式单例

+(instancetype)sharedNetworkManager

{

return_instance;

}

拓展:
饿汉模式、懒汉模式的优缺点

饿汉式在类创建的同时,已经创建好对象,以后不再改变,所以天生就是线程安全的

懒汉模式,如果需要线程安全还需要借助线程锁或者dispatch_once。无论是哪个方法,对性能都有降低,而实际99%的情况下都不需要同步。

2.NSOperation

是OC语言中基于GCD的面向对象的封装.

使用起来比GCD更加简单(面向对象)

提供了一些用GCD不好实现的功能.

苹果推荐使用,使用NSOperation不用关心线程以及线程的生命周期.

NSOperation是个抽象类,无法直接使用.因为方法只有声明没有实现。

作为父类使用的.约束子类共有的属性和方法.

子类:
2.1.NSInvocationOperation//系统提供的
2.2.NSBlockOperation//系统提供的
2.3.自定义NSOperation

操作默认是异步的.

队列:NSOperationQueue

队列默认是并发的.

核心:
4.1.GCD的核心:将任务添加到队列中
4.2.OP的核心:将operation添加到队列中

使用步骤

先将需要执行的operation封装到一个NSOperation对象中.创建NSOperation对象.(其实是封装到NSOperation的子类中去。)

将NSOperation对象添加到NSOperationQueue中.

NSOperationQueue会自动将NSOperation取出来.

将取出的NSOperation封装的操作自动放到一条对应的新线程中执行.

2.1NSInvocationOpearion

核心:将操作添加到队列

使用start的方法,会在当前线程执行@selector方法。不会开启新线程。

正确开启新线程的姿势



-(void)invocationOperation{

//将op封装成NSInvocationOperation

NSInvocationOperation*invocationOperation=[[NSInvocationOperationalloc]initWithTarget:selfselector:@selector(invocationMethod)object:nil];

//start也会执行,但是不会开启新线程

//[invocationOperationstart];

//创建NSOperationQueue

NSOperationQueue*queue=[[NSOperationQueuealloc]init];

//将op添加入队列

[queueaddOperation:invocationOperation];

}

2.2NSBlockOperation

NSOperationQueue只有一种类型.就是并发队列.

在实际开发时,如果要使用到NSOperationQueue,可以直接定义成全局的队列

使用start的方法,会在当前线程执行@selector方法。不会开启新线程。

2.3NSOperation和GCD的对比

名称GCDNSOperation
核心概念将任务添加到队列中把操作添加到队列中
语言类型COC
推出版本iOS4.0iOS2.0,在推出GCD之后又对底层进行了重写
封装地点任务封装在block中任务凤凰在Operation对象中
是否支持停止可以,但是很麻烦可以取消任务,但是正在执行的任务除外
优先级只能设置队列的优先级可以设置队列中每一个操作的优先级
依赖关系建立任务间依赖关系比较复杂可以跨腿咧设置操作的依赖关系
高级功能barrier\once\after\group最大操作并发数、继续/暂停/取消任务、跨队列设置操作的依赖关系

3.NSOperation的高级功能

最大操作并发数

继续/暂停/取消全部

操作的优先级和监听操作执行完成的回调

操作间依赖关系

3.1队列的最大并发数

是队列的一个属性.



@propertyNSIntegermaxConcurrentOperationCount;

限制同时执行的任务数.

比如,最大并发数设置成3,队列就会保证只同时执行3个任务.从而间接的控制了线程的数量.

线程可以复用.而且在线程回收的间隙可以及时的准备线程保证并发性.

注意:队列最大并发数不是线程数!!!

准备队列:也可以采用成员变量的方式定义

3.2队列的暂停、继续、取消

-(BOOL)isSuspended;暂停和继续队列的属性.

YES代表暂停队列,NO代表恢复队列.

cancelAllOperations:取消队列中的全部操作.

cancel:取消队列中的单个操作.

队列处于暂停状态时,是没有办法修改operationCount数值。

3.2.1队列暂停

将队列挂起之后,队列中的操作就不会被调度,但是正在执行的操作不受影响

operationCount:操作计数,没有执行和没有执行完的操作,都会计算在操作计数之内



[self.queuesetSuspend:YES];

正在执行的操作,没有办法暂停。

3.2.2队列继续



[self.queuesetSuspend:NO];

3.2.3队列取消

一旦调用cancelAllOperations方法,队列中的操作,都会被移除,正在执行的操作除外.

正在执行的操作取消不了,如果要取消,需要自定义NSOperation.

队列取消全部操作时,会有一定的时间延迟

3.3操作间的依赖关系

不能循环建立操作间依赖关系.否则,队列不调度操作执行

操作间可以跨队列建立依赖关系

要将操作间的依赖建立好了之后,再添加到队列中

先建立操作依赖关系

再把操作添加到队列



//登录-》付费-》下载

-(void)downloadSongs{

//创建OP

NSBlockOperation*login=[NSBlockOperationblockOperationWithBlock:^{

NSLog(@"login");

}];

NSBlockOperation*pay=[NSBlockOperationblockOperationWithBlock:^{

NSLog(@"payment");

}];

NSBlockOperation*download=[NSBlockOperationblockOperationWithBlock:^{

NSLog(@"download");

}];

//添加依赖关系

[downloadaddDependency:pay];

[payaddDependency:login];

//即便op在不同的队列里面,依赖关系依然存在

NSOperationQueue*queue=[[NSOperationQueuealloc]init];

[queueaddOperations:@[login,download]waitUntilFinished:NO];

[[NSOperationQueuemainQueue]addOperation:pay];

NSLog(@"WellDone");

}

3.4.操作的优先级和监听操作执行完成的回调



NSBlockOperation*op=[NSBlockOperationblockOperationWithBlock:^{

for(inti=0;i<10;i++){

NSLog(@"op%d",i);

}

}];

//监听操作执行完成

[opsetCompletionBlock:^{

NSLog(@"------CompletionBlock-------");

}];

4.列表异步加载网络图片

4.1SDWebImage加载网络图片

4.2NSOperation加载网络图片

注意:异步下载完图片之后,刷新UI一定要回到主线程

5.GitHub初体验

6.AFN请求json数据

导入AFN框架和头文件#import“AFNetworking.h

定义获取json文件的主方法

参数一:URL地址
参数二:不需要
参数三:下载进度
参数四:成功的block
参数五:失败的block

使用block字典转模型

数据加载之后,需要刷新页面

7.SDWebImage

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  网络多线程