线程:
线程是操作系统中独有的
在早期的单片机等是没有的
NSThread NSOperation dispatch_queue_t
这三个 就是形式不一样的线程
官方推荐用GCD 所以 多用GCD
IOS中
主线程在main函数中创建
子线程需要手动创建
编码过程中的规则:
主线程不允许阻塞 主要负责UI交互
子线程主要负责数据处理 (数据库操作 图像处理 音视频压缩)
线程开销资源有系统自动回收
子线程并发 同时执行时无序的
******
@interface NSObject (NSThreadPerformAdditions)
- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg NS_AVAILABLE(10_5, 2_0);
创建线程的一种方法 第三个参数 是sel 的参数
自动执行
*****
@interface NSThread : NSObject
线程类
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument;
第二种创建线程的方法
自动执行
- (id)initWithTarget:(id)target selector:(SEL)selector object:(id)argument NS_AVAILABLE(10_5, 2_0);
第三种创建线程的方法
需要手动启动执行
- (void)start NS_AVAILABLE(10_5, 2_0);
开始线程
- (void)cancel NS_AVAILABLE(10_5, 2_0);
取消线程
- (NSString *)name NS_AVAILABLE(10_5, 2_0);
给线程打标签 取名字
这个线程类中有很多属性和方法 基本上望文生义
*******
线程带来了很多好处 也有一定的问题:
经典问题:——售票问题:
解决方法:
资源锁 枷锁后就会正常了 一下是通常写法
while (1) {
[_lock lock];
if (sum) {
[NSThread sleepForTimeInterval:1.0];
sum--;
count = 10-sum;
NSLog(@"总票数:%d, 售出:%d", sum, count);
} else {
break;
}
[_lock unlock];
}
@interface NSLock : NSObject <</SPAN>NSLocking>
锁 同时只能允许一个线程访问该资源
先到先锁
nonatomic 不加锁的 可以提高访问速度
atomic 加锁的 以牺牲系统性能为代价的获取资源安全
- (void)lock;
- (void)unlock;
成对出现
@interface NSCondition : NSObject <</SPAN>NSLocking>
条件锁
- (void)lock;
- (void)unlock;
- (void)signal;
- (void)wait;
A线程等B线程访问之后再访问
A 发信号 B等待 以下是通常写法
- (void)doSomethingA
{
[_con lock];
NSLog(@"start A");
[NSThread sleepForTimeInterval:2.0];
NSLog(@"end A");
[_con signal];
[_con unlock];
}
- (void)doSomethingB
{
[_con lock];
NSLog(@"start B");
NSLog(@"end B");
[_con wait];
[_con unlock];
}
******
@synchronized(xx)
加锁
****
RunLoop
@interface NSRunLoop : NSObject
每一个线程都会有RunLoop事件循环
RunLoop会套一个自动释放池
Runloop会让线程有活干就干活 没活就休息 让出CPU时间片
***
事件在IOS中分为两大类
事件源:用户输入+定时器
***
在三个地方会用到RunLoop(三个场景)
①
用户的异步滚动事件与定时器 一般写法是不能共存的 要想共存 需要用RunLoop
RunLoop有两种常用mode 默认模式是不支持共存的
CommomcModes是支持共存的
+ (NSRunLoop *)currentRunLoop;
- (BOOL)runMode:(NSString *)mode beforeDate:(NSDate *)limitDate;
- (void)addTimer:(NSTimer *)timer forMode:(NSString *)mode;
FOUNDATION_EXPORT NSString * const NSDefaultRunLoopMode;
FOUNDATION_EXPORT NSString * const NSRunLoopCommonModes NS_AVAILABLE(10_5, 2_0);
asi默认的是第二个mode
②
滚动事件与异步请求
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://dl_dir.qq.com/qqfile/qq/QQforMac/QQ_V2.4.1.dmg"]];
[NSURLConnection connectionWithRequest:request delegate:self];
当程序正常运行时 如果有异步请求 当发生滚动事件时- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
这个方法就会暂停(就是下载暂停) 等到滚动事件结束后就会继续这个方法
这样的话 用户体验是很不好的
若想共存写法:
NSURLConnection *con = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
[con scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
[con start];
分析:
- (id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate startImmediately:(BOOL)startImmediately NS_AVAILABLE(10_5, 2_0);
第三个参数一般要写NO
因为
YES if the connection should being loading data immediately, otherwise NO. If you pass NO, the connection is not scheduled with a run loop. You can then schedule the connection in the run loop and mode of your choice by calling scheduleInRunLoop:forMode:.
- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode NS_AVAILABLE(10_5, 2_0);
这个是NSURLConnection的方法 就是在RunLoop中与用户交互事件共存
③
在子线程中使用异步下载,那么子线程结束后会结束子线程中的所有异步下载。
解决办法
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
在子线程中跑一个while 在while中执行RunLoop 直到完成异步下载时结束while循环 即结束子线程
while需要退出的标记(bool值)作为退出条件
***********
使用线程 就可以使用同步下载方法 因为不会阻塞主线程
+ (id)dataWithContentsOfURL:(NSURL *)url;
同步下载方法
__unused NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://dl_dir.qq.com/qqfile/qq/QQforMac/QQ_V2.4.1.dmg"]];
同步下载
如果放到主线程中就会阻塞
放到子线程中就没关系
***
编码规范:
不要在子线程中给UI赋值
在子线程退出时 才会刷新UI
所以要在主线程中给UI赋值
***
__unused
用这个关键字 就可以让没用过的变量不被警告
***
***
线程之间通讯:
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
通知主线程执行SEL
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5, 2_0);
通知指定线程thr,执行SEL
如果传参比较多 可以直接传个字典过去。
****
********
线程池:
@interface NSOperationQueue : NSObject
线程池(队列)
@interface NSOperation : NSObject
一个线程
线程池用法:
①
创建一个类 MyThread: NSOperation
如果不继承 就无法添加到线程池中
②
在该类中重写 -(void)main()
每一个main就相当于一个独立的代码段 即一个线程
是线程的入口函数
③
将对象放到线程池中 就会自动启动线程(执行线程中 的main方法),线程池中的线程是并发执行的,顺序随机的
****
@interface NSOperationQueue : NSObject
- (void)addOperation:(NSOperation *)op;
- (void)addOperationWithBlock:(void (^)(void))block NS_AVAILABLE(10_6, 4_0);
添加线程
- (void)setSuspended:(BOOL)b;
- (BOOL)isSuspended;
挂起
@interface NSOperation : NSObject {
里面的 方法都是望文生义的
**************
GCD
多线程解决方案
纯C接口的封装
按种类来分分两种:
①串行队列
(在这个队列里一般做UI处理 队列中的任务依次单个执行)
②并行队列
(在这个队列中跟一般做数据处理 队列中的任务并发无序执行)
细分主要是3种队列:
①②是系统自带两个队列
①串行队列dispatch_get_main_queue();
可以用来回归主线程
(还有一个方法也能回归主线程:performSelectorOnMainThread)
(当触发几个事件的时候 也是可以触发主线程的:触摸 晃动 远程控制)
(在这个队列里一般做UI处理)
②并行队列dispatch_get_global_queue(优先级, 待扩展)
(在这个队列中跟一般做数据处理)
③自己创建的队列dispatch_queue_create(队列名字, 队列种类)
CocoaLigature1 例:
dispatch_queue_t _mainQueue;
dispatch_queue_t _globalQueue;
dispatch_queue_t _customQueue;
_mainQueue = dispatch_get_main_queue();
_globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
_customQueue = dispatch_queue_create("MyQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
相当于创建一个线程
例:
dispatch_async(_globalQueue, ^{
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://img.app.d1cm.com/news/img/201312021616153719.jpg"]];
dispatch_async(_mainQueue, ^{
iv.image = [UIImage imageWithData:data];
});
});
GCD可以无限制的嵌套
GCD可以分组
dispatch_group_t
dispatch_group_t
dispatch_group_create(void);
创建dispatch_group_t
void
dispatch_group_async(dispatch_group_t group,
dispatch_queue_t queue,
dispatch_block_t block);
同步执行
dispatch_group_notify(dispatch_group_t group,
dispatch_queue_t queue,
dispatch_block_t block);
group的其他queue都执行完了 才执行这个queue
这个方法 只能执行一次
void
dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
分割线队列 类似分割线的功能
分割线上边的队列并发 然后执行这个queue 然后执行分割线后边的队列
可以无限次画这个分割线队列
dispatch_once_t
单例
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{