多线程


多线程的实现四种基本实现方案
多线程_第1张图片
多线程的实现方案.png

pthread

import //导入头文件

  • (void)viewDidLoad {
    [super viewDidLoad];
    pthread_t thread;
    //第一个参数:线程地址
    //第二个参数:线程的属性
    //第三个参数 void ()(void *) 指向函数的指针,需要一个函数
    //【注意:void *(^)(void *)是一个block】
    //第四个参数函数的一些限制
    pthread_create(&thread, NULL, run, NULL);
    pthread_t thread1;
    pthread_create(&thread1, NULL, run, NULL);
    }
    void *run(void *param)
    {
    //耗时操作放到这个函数中.......
    NSLog(@"%@",[NSThread currentThread]);
    return NULL;
    }

---
#####Thread
>```
方式一、
 //这里的线程不必要担心是局部变量。内部会保证这个线程不会销毁。
     NSThread *thread =   [[NSThread alloc]initWithTarget:self selector:@selector(run) object:nil];
    thread.name = @"xdyb";//设置线程名字
    [thread start];//手动开启
方式二、
//分离一个线程,没有返回值,不能设置线程名字。不需要手动开启start。
  [NSThread detachNewThreadSelector:@selector(run) toTarget:self
                           withObject:@"传参数"];
方式三、
//创建一个后台线程。
    [self performSelectorInBackground:@selector(run) withObject:@"传参数"];

线程的状态:

新建状态,就绪状态,运行状态,阻塞状态,死亡状态

强制退出线程

+ (void) exit;//类方法

线程间通信

    //回到主线程调用self的show方法,并且将image传递到showImage方法中(showImage:方法需要手动实现)
    [self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:YES];
    //回到主线程,调用imageView的setImage方法,设置image(setImage:这个方法是imageView系统的)
    [self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:YES];
//这两个方法中的YES表示愿意等待performSelectorOnMainThread这个方法执行完毕,再执行下面的代码。NO表示不愿意等待。
    [self performSelector:@selector(setImage:) onThread:[NSThread mainThread] withObject:nil waitUntilDone:NO];

线程之间通信的原理:

//子线程要想传递一些数据个主线程,主线程需要返回一个和主线程相关联的port对象,让子线程拥有这个port。子线程操作这个port对象,主线程就知道子线程要做什么事情。
//主线程想要告诉子线程一些事情,那么子线程需要返回一个和子线程相关联的port对象,让主线程去拥有,主线程拥有这个port对象,就能去操作这个子线程。

![线程之间通信的原理.png](http://upload-images.jianshu.io/upload_images/1170347-860714eda0685ae9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

---
#####GCD
>```
核心:队列和任务。
将任务放入队列中,并使用同步或者异步函数。
GCD会自动将队列中的任务取出来,放到对应的线程中执行。
任务的取出遵循FIFO原则(先进先出,后进后出)
GCD注意:使用sync函数往当前串行队列中添加任务,会卡住当前的串行队列。
GCD回到主线程
dispatch_async(dispatch_get_main_queue(), ^{
});
dispatch_sync(dispatch_get_main_queue(), ^{
});
两种方式的选择:
async是异步,不需要等待队列中的任务执行完毕,就可以执行后面的代码。(有可能先执行任务,有可能先执行后面的代码。)
sync是同步,需要等待队列中的任务执行完毕,才能执行后面的代码。
尽量使用

栅栏函数、延迟、一次性代码、快速迭代、队列组

1、dispatch_barrier_async栅栏函数,在这个函数之后的任务会等在这个函数之前的先执行完毕才执行后面的任务。

2、延迟操作:
NSObject的方法:

  • (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay;
    GCD的方法:
    dispatch_after(dispatch_time_t when,
    dispatch_queue_t queue,
    dispatch_block_t block);
    NSTimer的方法:
    //最后一个参数表示是否重复
  • (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;
    3、一次性代码:整个程序运行过程中只用一次,不适合放到懒加载中。
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{});
    4、快速迭代:需要放到并发队列中:比如文件剪切。
    dispatch_apply(size_t iterations, dispatch_queue_t queue,
    void (^block)(size_t));
    5、队列组:先执行组里面的任务,最后执行notify中的任务
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_group_t group;
    dispatch_group_async(group, queue, ^{
    //任务
    });
    dispatch_group_async(group, queue, ^{
    //任务
    });
    dispatch_group_notify(group, queue, ^{
    //当group中的任务都执行完毕才会执行这个任务。
    });

---
#####GCD以函数的方式来存放任务。
>```
//第一个参数是队列
//第二个参数是函数的参数
//第三个参数是函数【typedef void (*dispatch_function_t)(void *)并且这个函数是带参数没有返回值的】
void
dispatch_async_f(dispatch_queue_t queue,
    void *context,
    dispatch_function_t work);
void
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
这两个函数的区别在于,dispatch_async_f是以函数的方式来存放任务的代码,而dispatch_async是以block的方式来存放代码。
返回以_f结尾的,基本上都是与函数有关。

NSOperation

NSOperation底层是GCD。
步骤:将任务封装到NSOperation中,然后将NSOperation添加到队列里面。
NSOperation是个抽象类,需要用他的两个子类NSBlockOperation、NSInvocationOperation或者继承自NSOperation然后实现这个NSOperation的main方法。
获取NSOperationQueue方式:
[NSOperationQueue mainQueue];//凡是添加到主队列中的任务,都会在主线程中执行。
[[NSOperationQueue alloc]init];//添加到这种队列中的任务会自动在子线程中执行。
//将任务添加到队列中会自动开启start。
NSOperationQueue的方法:

  • (void)addOperation:(NSOperation *)op;//添加操作,不需要手动start
  • (void)addOperationWithBlock:(void (^)(void))block//不需要手动创建操作。
    queue.maxConcurrentOperationCount = 10;//默认是-1,自动创建线程。设置最大并发数。当变成1时就是串行执行,当是0时不执行。
    NSBlockOperation的对象方法addExecutionBlock,直接添加操作,不需要手动开启。
    queue.suspended = YES;暂停队列。当用户拖拽时,让队列暂停,松手时让恢复queue.suspended = NO;
    [queue cancelAllOperations];//取消任务。
    注意:一个任务开启就无法取消或暂停,必须等到这个任务执行完毕,才能取消或暂停,后面的任务才不会执行。如果想要在任务里面取消或暂停,必须在任务里面判断外面是否有取消或暂停操作。有就取消或暂停。
    凡是做了一个耗时操作就判断下是否取消了。提高用户体验。
    NSOperation能设置依赖哪个操作
  • (void)addDependency:(NSOperation *)op;//添加依赖的操作。等到依赖的操作执行完再执行。
    NSBlockOperation:通过Block封装任务代码(任务添加到操作中)。
    NSInvocationOperation:通过方法封装任务代码(任务添加到操作中)。











---
#####获取某个地址下的所有文件夹及子文件夹下的文件路径
>```
NSFileManager *mgr = [NSFileManager defaultManager];
NSArray *subpaths =  [mgr subpathsAtPath:@"某个地址"];
[mgr moveItemAtPath:@"将这个文件路径移动" toPath:@"到这个文件路径下" error:nil];
//拼接路径,并且自动添加斜杠'\'。
- (NSString *)stringByAppendingPathComponent:(NSString *)str;

绘图

//开启一个新的图形上下文
UIGraphicsBeginImageContext(CGSizeMake(100, 100));
//绘制图片
[self.imageView.image drawInRect:CGRectMake(0, 0, 100, 100)];
//获取当前上下文的图片
UIImage *image = [UIGraphicsGetImageFromCurrentImageContext() ];
//结束图形上下文
UIGraphicsEndImageContext();


#####NSInvocation
>```
'IOS中有一个类型是SEL,它的作用很相似与函数指针,通过performSelector:withObject:函数可以直接调用这个消息。
但是perform相关的这些函数,有一个局限性,其参数数量不能超过2个,否则要做很麻烦的处理,
与之相对,NSInvocation也是一种消息调用的方法,并且它的参数没有限制。'
在官方文档中有明确说明,NSInvocation对象只能使用其类方法来初始化,不可使用alloc/init方法。
它执行调用之前,需要设置两个方法:setSelector: 和setArgument:atIndex:
- (void)viewDidLoad {
    [super viewDidLoad];
    SEL myMethod = @selector(myLog);
    //创建一个函数签名,这个签名可以是任意的,但需要注意,签名函数的参数数量要和调用的一致。
    NSMethodSignature * sig  = [NSNumber instanceMethodSignatureForSelector:@selector(init)];
    //通过签名初始化
    NSInvocation * invocatin = [NSInvocation invocationWithMethodSignature:sig];
    //设置target
    [invocatin setTarget:self];
    //设置selecteor
    [invocatin setSelector:myMethod];
    //消息调用
    [invocatin invoke];  
}
-(void)myLog{
    NSLog(@"MyLog");
}
注意:签名函数的参数数量要和调用函数的一致。测试后发现,当签名函数参数数量大于被调函数时,也是没有问题的。

你可能感兴趣的:(多线程)