多线程NSThread
/*------------------------------ GCD使用 1.队列和任务------------------------------------------*/
重点:1. “串行队列”? “并发队列”? 2.block?
1.GCD(Crand Central Dispatch) — “牛逼的中枢调度器”!
//C语言框架/ 自动管理线程的生命周期(创建/释放)
推出GCD 的目的:取代NSThread!
为”多核”的”并发” 运算提出的解决方案!
优点:
<1> GCD 能够自动利用更多的CPU的核数(双核/四核)!
<2> GCD 会自动管理线程的生命周期.
程序员只需要告诉GCD 想要执行的任务(代码)!
2.GCD中的两个核心概念:
“任务”:
想要做的事情/执行什么操作
GCD 中的任务定义在block中.
void(^myblock)( ) = ^{
// 想要做的事情/任务
}
“队列”:
用来”存放”任务!
队列 != 线程! 队列不是线程
队列中存放的任务最后都要由线程来执行!
队列的原则: 先进先出,后进后出(FIFO/ first In First Out)
队列的类型:
queue 队列
Serial 串
Dispatch
<1> 串行 队列:(Serial Dispatch Queue)
// 创建一个串行队列
dispatch_queue_t serialQueue = dispatch_queue_create(“serial”,DISPATCH_QUEUE_SERIAL);
<2> 并发队列 :(Concurrent Dispatch Queue)
concurrent 同时
//创建一个并发队列
dispatch_queue_t concurrentQueue = dispatch_queue_create(“concurrent”,DISPATCH_QUEUE_CONCURRENT);
注意两个非常常用的特殊队列;
<1> 主队类: // UI操作放在主队列中执行!
跟主线程相关联的队列!
主队列是 GCD 自带的一种特殊的串行队列!
主队列中的任务都会在主线程执行!
//获取主队类
dispatch_queue_t mainQueue = dispatch_get_main_queue();
<2> 全局并发队列 :// 一般情况下,并发任务都可以放在全局并发队列中!
//获取主队列
dispatch_queue_t globalQueue = dispatch_get_global_queue(0,0);
/*----------------------------------- GCD使用 2.执行任务 -------------------------------------*/
重点:1 .同步函数 异步函数 2.容易混淆的四个概念: 串行 并发,同步,异步之间的区别?
问题:串行队列中的任务必定按照顺序吗?并发队列中的任务必定同时执行吗?
GCD中有两个用来执行任务的函数;
同步 执行任务:
dispatch_sync(^(void)block dispatch_dispatch_queue_t queue,)
异步 执行任务;
dispatch_async(dispatch_queue_t queue,^(void)block)
//dispatch_queue_t queue 队列
//^(void)block: 任务
同步和异步的区别:
同步:只能在当前线程任务中执行任务,不具备开启新线程的能力
异步: 可以在新的线程中执行任务,具备开启新线程的能力
GCD 使用有两个步骤;
<1> 将任务添加到队列中;
<2> 选择同步还是异步的方式执行任务.
注意:四个容易混淆的术语
串行 并发 ( 同步 异步 函数)
/*------------------------------- GCD使用 3.各种队列的执行效果 ---------------------------------*/
重点: 1.掌握连个常用的组合!
常见的组合(掌握)
async 异步执行函数
1>dispatch_async + 全局并发队列 (可以开启多条线程)
2>dispatch_async + 自己创建的串行队列 (开启一条线程)
只有异步执行并发队列,才可以开启多条线程
注意:
在主线程中同步执行主队列任务,会造成 主线程和主队列 相互等待,卡住线程!
/*------------------------------- GCD使用 4.线程间通信 ---------------------------------------*/
重点:1.从子线程回到主线程(经典用法) ! 2.两个注意点.
1.经典用法(子线程下载(耗时操作),主线程刷新UI):
dispatch_async(dispatch_get_global_queue)(0,0),^{
//执行 耗时的异步操作
dispatch_async(dispatch_get_main_queue(),^{
//回到主线程,执行UI刷新操作
});
});
2.注意: custom 习惯
<1> 需要设置按钮的image,建议先把按钮类型改为custom,才能保证设置成功
<2> 属性名不能以new开头
/*------------------------------- GCD使用 5.延时执行 -----------------------------------------*/
重点: 1. iOS常见的两种延时执行方式
iOS中的延时执行方式;
//定制好延时任务后,不会阻塞当前线程.
<1> 调用NSObject 方法:
[self performSelector:@selector(run) withObject:nil afterDelay:2.0];
//2秒后再调用self的run方法
<2> GCD 函数实现延时执行:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)
(2.0 * NSEC_PER_SEC)),dispatch_get_main_queue(),^{
//2秒后执行这里的代码…在哪个线程执行,跟队列类型有关
});
注意:
不要使用sleep,会阻塞当前线程,
/*------------------------------- GCD使用 6.队列组 ------------------------------------------*/
重点:1.了解队列组的使用方法
项目需求:
首先:分别异步执行两个耗时操作;
其次:等两次耗时操纵都执行完毕后,在回到主线程执行操作.
使用队列组(dispatch_group_t)快速,高效的实现上述需求.
group 群组
dispatch_group_t group = dispatch_group_create();//队列组
dispatch_group_t group = dispatch_get_global_queue(0,0); //全局并发队列
dispatch_group_async(group,queue,^{
//longTime2
}); //异步执行操作
dispatch_group_notify(group,dispatch_get_main_queue(),
^{
//在主线程刷新新数据
reload 刷新
//reload Data
});
/*------------------------------- GCD使用 7.一次性代码 ---------------------------------------*/
重点:1.掌握一次性代码的实现
一次性代码;
static 静止的不变的
Token 标志
static dispatch_once_t onceToken;
dispatch_once(&onceToken),^{
//只执行一次的代码(这里面默认是线程安全的)
});
/*-------------------------------------- 补充: 单例设计模式 -----------------------------------*/
重点:1.掌握单例!
1.单例简介:
作用;
保证程序在运行过程中,一个类只有一个实例对象,这个实例对象容易被外界访问!
控制器实例对象个数(只有一个) ,节约系统资源.
使用场合;
在整个应用程序中,共享一份资源(这份资源只需要创建初始化一次).
举例;
打印机/视图窗口/一些网络工具类等等
//懒汉式:用到的时候再加载
//饿汉式:只要程序运行就加载: //不需要掌握.也不要这么写!
//掌握懒汉式
2.单例实现:(两种方式:互斥锁(@synchronized)(self))和一次性代码(dispatch_once));
2.1 互斥锁 @synchronized(self);
<1> 在.m文件中保留一个全局的 static 的实例
static id _instance;
zone 分区 with 和...一起 alloc 分配存储空间
<2> 重写若干方法(allocWithZone:和 copyWithZone:) 并提供一个类方法让外界访问唯一的实例.
//(1) allocWithZoneL:方法,在这里创建唯一的实例(注意线程安全). //alloc 内部都会调用这个方法
+(instancetype) allocWIthZone:(struct _NSZone *)zone{
if(_instance == nil){ //防止频繁加锁
@synchronized (self){
if(_instance == nil) //防止创建多次
{
_instance = [super allocWithZone:zone];
}
}
}
return _instance;
}
//(2) 重写copyWithZone:方法
+(id)copyWithZone:(struct _NSZone *)zone
{
return _instance;
}
//(3) 提供一个类方法让外界访问唯一的实例
+(instancetype) sharSingleton
{
if(!_instance) //防止频繁加锁
@synchronized(self){
if(!_instance) { //防止创建多次
_instance = [[self alloc] init];
}
}
return _instance;
}
2.1 一次性代码(dispatch_once);
<1> 在.m文件中保留一个全局的static的实例
static id _instance;
<2> 重写如若干方法(allocWithZone:和 copyWithZone:) 并提供一个类方法让外界访问唯一的实例
//(1)重写allocWithZone:方法,在这里创建唯一的实例(注意线程安全).
+(id)allocWithZone:(struct _NSZone *)zone
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken,^{
_instance = [super allocWithZone:zone];
})
return _instance;
}
//重写copyWithZone:方法
+(id)copyWithZone:(struct _NSZone)zone
{
return _instance;
}
//(3) 提供一个类方法让外界访问唯一的实例
+(instancetype) shareSingleton
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken,^{
_instance = [[self alloc] init];
})
return _instace;
}
注意:在ARC 和MRC中单例的实现方式略有不同,MRC 下单例的实现比ARC 多了几个内存管理的方法;
MRC 中增加如下方法的实现
-(instancetype) retain {return self;}
-(NSUInteger) retainCount {return self;}
-(oneway void)release {}
-(instancetype)autorelease{return self;}
3.判断当前环境(ARC/MRC)
#if__has_feature(objc_arc)
//ARC
#else
//MRC
#endif
4.注意两个方法
//面试问题:两个方法的区别?
<1> +(void)load;
//当类加载到OC运行环境(内存)中的时候,就会调用一次(一个类只会加载一次).
//程序一启动就会调用
//程序运行过程中,只会调用一次
<2>+(void)initialize;
//当第一次使用这个类的时候,(比如调用了类的某个方法)才会调用.
//并发程序一启动就会调用