iOS 多线程

iOS多线程开发

一、pthread创建与使用

Pthread是纯C语言实现的,属于跨平台,使用前需要先导入头文件 #import pthread.h

1. 定义一个函数
void *run(void *param)
{
    NSString *str = (__bridge NSString *)(param);
    // 耗时操作放在这里执行
    for (int i = 0; i < 20000; i++) {
        NSLog(@"%@----%@", [NSThread currentThread], str);
    }
    return NULL;
}
2. 使用pthread创建线程
- (void)test
{
    // 声明一个线程变量
    pthread_t threadId;
    /*
     参数:
     1. 要开的线程的变量
     2. 线程的属性
     3. 要在这个子线程执行的函数(任务)
     4. 这个函数(任务)需要传递的参数
     */
    
    id str = @"hello";

    pthread_create(&threadId, NULL, run, (__bridge void *)(str));
}

二、NSThread线程

1. NSThread的三种创建方式

1.隐式创建的方法

[self performSelectorInBackground:@selector(run:) withObject:@"cy"];

2.静态创建的方法

[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"hello"];

3.实例化创建的方法

// 实例化一个线程对像
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
// 让线程开始工作,启动线程, 在新开的线程执行run方法
[thread start];
//耗时操作
- (void)run:(NSString *)str
{
    for (int i = 0; i < 10; i++) {
        NSLog(@"%@--%d", [NSThread currentThread], i);
    }
}
2. 线程的属性设置
NSThread *threadA = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"hello"];
threadA.name = @"thraed A";   // 为线程设置名称
threadA.threadPriority = 0.1; // 设置优先级
[threadA start];

NSThread *threadB = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"hello"];
threadB.name = @"thraed B";   // 为线程设置名称
threadB.threadPriority = 1.0; // 设置优先级
[threadB start];

**注意:线程优先级,必须调用很多次的时候,才能体现出来。取值范围:(0.0~1.0),默认值:0.5
优先级的值越大,就越优先被调用
3. 线程的状态

新建(线程被创建)-->就绪(线程调用start方法)-->运行(任务的执行)-->阻塞(等待、或者睡眠,会返回运行状态从新执行)-->消亡(线程耗尽,或者被强制退出调用exit方法)

// 创建一个线程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];

// 放到可调度线程池,等待被调度。 这时候是就绪状态
[thread start];

- (void)run
{
    NSLog(@"%s", __func__);
   // 刚进来就睡会, 睡2秒
   // [NSThread sleepForTimeInterval:2.0];
    
   // 睡到指定的时间点
   // [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:5.0]];
    
    for (int i = 0; i < 20; i++) {
        // 满足某一个条件以后,阻塞线程的执行。 也就是让线程休息一会
        if (i == 10) {
            [NSThread sleepForTimeInterval:3.0];
        }
        
        // 一旦达到某一个条件,就强制终止线程的执行
        if (i == 15) {
            // 一旦强制终止,就在不能重新启动
            // 一旦强制终止,后面的代码都不会执行
            [NSThread exit];
        }
        
        NSLog(@"%@--- %d", [NSThread currentThread], i);
    }
    
    NSLog(@"线程结束");
}
4. 线程安全隐患
  1. 加锁,互斥锁
  2. 加锁,锁定的代码尽量少。
  3. 加锁范围内的代码, 同一时间只允许一个线程执行
  4. 互斥锁的参数:任何继承 NSObject *对象都可以。
  5. 要保证这个锁,所有的线程都能访问到, 而且是所有线程访问的是同一个锁对象
5. 演示代码

@property(nonatomic, assign) int tickets;

// 共享的资源
self.tickets = 20;

// 线程A
NSThread *threadA = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicktes) object:nil];
threadA.name = @"售票员A";
[threadA start];
// 线程B 
NSThread *threadB = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicktes) object:nil];
threadB.name = @"售票员B";
[threadB start];
- (void)saleTicktes
{
    while (YES) {
    
   // 1.模拟一下延时,卖一张休息1秒
   [NSThread sleepForTimeInterval:1.0];
   // 加锁的位置可以传任意对象
   NSObject *obj = [[NSObject alloc] init];
   // 2.加锁,性能差不推荐,self指的是当前类本身
   `@synchronized(self)`{ //可任意对象
   
    //3. 判断是否还有票,
    if (self.tickets > 0) {
    
     // 4. 如果有票,就卖一张
     self.tickets--;
     NSLog(@"剩余的票数--%d--%@", self.tickets,[NSThread currentThread]);

      }else {
        //5.如果没有就返回
        NSLog(@"没票了");
        break;
      }
    }
  }
}

6. 在主线程更新UI有什么好处?

a.只在主线程更新UI,就不会出现多个线程同时改变 同一个UI控件
b.主线程的优先级最高,也就意味UI的更新优先级高,会让用户感觉很流畅

7. 线程安全的概念

就是在多个线程同时执行的时候,能够保证资源信息的准确性.

8. atomic原子属性--默认属性
  1. 原子属性就是针对多线程设计的。 原子属性实现 单(线程)写 多(线程)读
  2. 因为写的安全级别要求更高。 读的要求低一些,可以多读几次来保证数据的正确性
  3. 如果同时重写了setter和getter方法,"_成员变量" 就不会提供
  4. 可以使用 @synthesize 合成指令,告诉编译器属性的成员变量的名称
9. 自旋锁和互斥锁
  1. 共同点: 都可以锁定一段代码。 同一时间, 只有线程能够执行这段锁定的代码
  2. 区别:互斥锁,在锁定的时候,其他线程会睡眠,等待条件满足,再唤醒自旋锁,在锁定的时候, 其他的线程会做死循环,一直等待这条件满足,满足后立马去执行,少了一个唤醒过程

三、GCD开发

1. 主线程间的通信

开启子线程调用下载图片方法的三种方式:

[self performSelectorInBackground:@selector(downloadImage) withObject:nil];

1.方式一

[self performSelectorOnMainThread:@selector(downloadFinish:) withObject:image waitUntilDone:NO];

2.方式二

[self performSelector:@selector(downloadFinish:) onThread:[NSThread mainThread] withObject:image waitUntilDone:NO];

3.方式三

[self.iconView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:YES];
2. 开启下载任务
//1.url, 确定一个网络上的资源路径
NSURL *url = [NSURL URLWithString:@"http://h.hiphotos.baidu.com/image/pic/item/5366d0160924ab1828b7c95336fae6cd7b890b34.jpg"];

//2.通过url可以下载对应的网络资源, 网络资源传输的都是二进制 
NSData *data = [NSData dataWithContentsOfURL:url];

//3.二进制转成图片
UIImage *image = [UIImage imageWithData:data];

//4.下载完成回到主线程中更新图片
[self performSelectorOnMainThread:@selector(downloadFinish:) withObject:image waitUntilDone:NO];
3. 下载完成回到主线程刷新UI
- (void)downloadFinish:(UIImage *)image
{
    NSLog(@"%s---%@", __func__, [NSThread currentThread]);
    self.iconView.image = image;
}
总结:子线程里面的runloop默认不开启, 也就意味不会自动创建自动释放池, 子线程里面autorelease的对象 就会没有池子可放。 也就意味后面没办法进行释放。 造成内存泄露

四、GCD的使用

1. 核心概念:
  1. 任务:block
  2. 队列:把任务放到队列里面,队列先进先出的原则
  3. 串行队列:顺序,一个一个执行(必须一个任务执行完了,才能从队列里面取出下一个任务)
  4. 并发队列:同时,同时执行很多个任务(可以同时取出很多个任务,只要有线程去执行)
  5. 同步sync:不会开新线程
  6. 异步async:会开新线程,多线程的代名词
  7. 串行队列同步执行:不开线程,在原来线程里面一个一个顺序执行
  8. 串行队列异步执行:开一条线程,在这个新线程里面一个一个顺序执
  9. 并发队列异步执行:开多个线程,并发执行(不一定是一个一个)执
  10. 并发队列同步执行:不开线程,在原来线程里面一个一个顺序执行
2. 串行队列-同步执行
  1. 串行队列:顺序,一个一个执行
  2. 同步任务:不会开辟新线程,是在当前线程执行
  3. 结果:不开新线程,在当前线程顺序执行
  4. dispatch : 调度,GCD里面函数,都是以dispatch开头的
1. 创建一个串行队列 
// 参数一:队列标签 参数二:队列的属性
dispatch_queue_t queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_SERIAL);

2.同步执行任务
// 一般只要使用”同步“执行,串行队列对添加的同步任务,会立马执行
dispatch_sync(queue, ^{
    NSLog(@"%@", [NSThread currentThread]);
});

3. 串行队列-异步执行
  1. 串行队列:一个一个执行
  2. 异步执行:肯定会开新线程,在新线程执行
  3. 结果:只会开一个线程,而且所有任务都在这个新的线程里面执行
1. 串行队列
// 下面两种写法是一样的
//dispatch_queue_t queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_SERIAL);
 dispatch_queue_t queue = dispatch_queue_create("itcast", NULL);
 
// 2. 异步执行
for (int i = 0; i < 10; i++) {
    dispatch_async(queue, ^{
        NSLog(@"%@ %d", [NSThread currentThread], i);
    });
}
4. 并发队列-同步执行
  1. 并发队列:可以同时执行多个任务
  2. 同步任务:不会开辟新线程,是在当前线程执行
  3. 结果:不开新线程,顺序一个一个执行。
//1. 并行队列
dispatch_queue_t queue = dispatch_queue_create("cz", DISPATCH_QUEUE_CONCURRENT);
    
// 2. 同步执行任务
for (int i = 0; i < 10; i++) {
    dispatch_sync(queue, ^{
        NSLog(@"%@ %d", [NSThread currentThread], i);
    });
}
5. 并发队列-异步执行
  1. 并发队列:可以同时执行多个任务
  2. 异步执行:会开新线程,在新线程执行
  3. 结果:会开很多个线程,同时执行
//1. 并行队列
dispatch_queue_t queue = dispatch_queue_create("cz", DISPATCH_QUEUE_CONCURRENT);
    
// 2. 异步执行任务
for (int i = 0; i < 10; i++) {
    dispatch_async(queue, ^{
        NSLog(@"%@ %d", [NSThread currentThread], i);
    });
}
6. 主队列-异步执行
  1. 主队列:专门负责在主线程上调度任务,不会在子线程调度任务,在主队列不允许开新线程.
  2. 异步执行: 会开新线程,在新线程执行
  3. 结果: 不开线程, 只能在主线程上面,顺序执行!
// 1. 获得主队列-> 程序启动,--> 至少有一个主线程-> 一开始就会创建主队列
dispatch_queue_t queue = dispatch_get_main_queue();
    
NSLog(@"1----");
    
// 2. 异步执行任务
for (int i = 0; i < 10; i++) {
    NSLog(@"调度前---");
    // 异步:把任务放到主队列里,但是不需要马上执行
    dispatch_async(queue, ^{
        NSLog(@"%@ %d", [NSThread currentThread], i);
    });
    NSLog(@"睡会");
    [NSThread sleepForTimeInterval:2.0];
}
NSLog(@"完成----");
7. 主队列-同步执行
  1. 主队列:专门负责在主线程上调度任务,不会在子线程调度任务,在主队列不允许开新线程.
  2. 同步执行:要马上执行
  3. 结果:死锁
1. 获得主队列-> 程序启动,--> 至少有一个主线程-> 一开始就会创建主队列
dispatch_queue_t queue = dispatch_get_main_queue();

// 2. 同步执行任务
for (int i = 0; i < 10; i++) {
    NSLog(@"调度前---");
    // 同步:把任务放到主队列里,但需是马上执行
    dispatch_sync(queue, ^{
        NSLog(@"%@ %d", [NSThread currentThread], i);
    });
    NSLog(@"睡会");
    [NSThread sleepForTimeInterval:2.0];
}

8. 全局队列-同步执行

主队列:如果主队列正在执行代码,就不调度任务

同步执行:如果第一个任务没有执行,就继续等待第一个任务执行完毕,再执行下一个任务此时相互等待,程序无法往下执行(死锁)代码从上往下执行,相互等待产生死锁

for (int i = 0; i < 10; i++) {
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"hello :%@ %d",[NSThread currentThread],i);
    });
}

主队列同步执行(解决死锁的问题)

dispatch_async(dispatch_get_global_queue(0, 0), ^{
    for (int i = 0; i < 10; i++) {
        dispatch_sync(dispatch_get_main_queue(), ^{
            NSLog(@"hello :%@ %d",[NSThread currentThread],i);
        });
     }
  })
9. 全局队列-异步执行

第一个参数: 一般写0
第二个参数: 保留参数 0

 iOS7
 DISPATCH_QUEUE_PRIORITY_HIGH 2     高优先级
 DISPATCH_QUEUE_PRIORITY_DEFAULT 0  默认优先级
 DISPATCH_QUEUE_PRIORITY_LOW (-2)   低优先级
 DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN 后台优先级
  
(可以适配iOS7 &iOS8)iOS8的质量与iOS7的对应的
 QOS_CLASS_DEFAULT  0
// 获得全局队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);

// 添加异步任务
for (int i = 0; i < 10; i++) {
    dispatch_async(queue, ^{
    NSLog(@"%@ %d", [NSThread currentThread], i);
    });
}
10. 全局队列跟并发队列的区别
  1. 全局队列没有名称 并发队列有名称
  2. 全局队列,是供所有的应用程序共享。
  3. 在MRC开发,并发队列,创建完了,需要释放。 全局队列不需要我们管理
11. 同步任务的作用一

有一个小说网站->必须登录->才能下载小说

// 1.并发队列
dispatch_queue_t  queue = dispatch_queue_create("cy", DISPATCH_QUEUE_CONCURRENT);

// 2.同步任务,需要马上执行。 不开新线程
dispatch_sync(queue, ^{
    NSLog(@"用户登录 %@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
    NSLog(@"下载小说A %@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
    NSLog(@"下载小说B %@", [NSThread currentThread]);
});

控制台打印结果

2018-03-09 00:23:52.137020+0800 05-GCD的使用[25734:836418] 用户登录 <NSThread: 0x600000075d80>{number = 1, name = main}
2018-03-09 00:23:52.137341+0800 05-GCD的使用[25734:836691] 下载小说B <NSThread: 0x60000046b9c0>{number = 6, name = (null)}
2018-03-09 00:23:52.137375+0800 05-GCD的使用[25734:836692] 下载小说A <NSThread: 0x60400046bf40>{number = 5, name = (null)}
12. 同步任务的作用二

做三件耗时的操作:appStore->输入密码->扣费->下载应用

dispatch_async(dispatch_get_global_queue(0, 0), ^{
    // 1.全局队列-同步下载
    dispatch_sync(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"输入密码: %@",[NSThread currentThread]);
    });
    // 2.全局队列-同步下载
    dispatch_sync(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"扣费: %@",[NSThread currentThread]);
    });
    // 3.全局队列-异步下载
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"下载应用: %@",[NSThread currentThread]);
    });
});

控制台打印结果

2018-03-09 00:21:09.547556+0800 05-GCD的使用[25639:833211] 输入密码: <NSThread: 0x60000027d300>{number = 3, name = (null)}
2018-03-09 00:21:09.547794+0800 05-GCD的使用[25639:833211] 扣费: <NSThread: 0x60000027d300>{number = 3, name = (null)}
2018-03-09 00:21:09.548115+0800 05-GCD的使用[25639:833211] 下载应用: <NSThread: 0x60000027d300>{number = 3, name = (null)}
13. 阶段性总结:
  1. 开不开线程,由执行任务方法决定,同步不开线程,异步肯定开线程</br>
  2. 开多少线程,由队列决定,串行 最多 开一个线程, 并发可以开多个线程。 具体开多少个,有GCD底层决定,程序猿不能控制
14. GCD其他用法

一次性执行

// GCD创建的一次执行
static dispatch_once_t onceToken;
 NSLog(@"%ld", onceToken);
 dispatch_once(&onceToken, ^{
    NSLog(@"%----ld", onceToken);
 });
NSLog(@"完成");

GCD延时操作一

 // 参数: when->表示从现在开始,经过多少纳秒以后
dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC));

dispatch_after(when, dispatch_get_global_queue(0, 0), ^{
    NSLog(@"点我了-- %@", [NSThread currentThread]);
});

GCD延时操作二

// GCD创建方式(延时2秒)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        
});
// 隐式创建线程(延时2秒)
[self performSelector:@selector(run) withObject:nil afterDelay:2.0];
// 定时器方式(延时2秒)
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:NO];

GCD-调度组

// 开发的时候,有的时候出现多个网络请求都完成以后(每一个网络请求的事件长短不一定),再统一通知用户 
// 比如:下载小说:三国演义->红楼梦->西游记->水浒传

// 实例化一个调度组
dispatch_group_t group = dispatch_group_create();

// 队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);

// 任务添加到队列queue
dispatch_group_async(group, queue, ^{
    NSLog(@"下载小说A---%@", [NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
    NSLog(@"下载小说B---%@", [NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
    NSLog(@"下载小说X---%@", [NSThread currentThread]);
});

// 在调度组完成通知里,可以跨队列通信
dispatch_group_notify(group, queue, ^{
    NSLog(@"下载完成,请观看%@", [NSThread currentThread]); // 异步的
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
   
    });
});

// 获得所有调度组里面的异步任务完成的通知
/**
dispatch_group_notify(group, queue, ^{
    NSLog(@"下载完成,请观看%@", [NSThread currentThread]); // 异步的
});
*/

GCD-Barrier阻塞用法

dispatch_queue_t queue = dispatch_queue_create("12312312", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        NSLog(@"----1-----%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"----2-----%@", [NSThread currentThread]);
    });
        
    dispatch_barrier_async(queue, ^{
        NSLog(@"----barrier-----%@", [NSThread currentThread]);
    });
        
    dispatch_async(queue, ^{
        NSLog(@"----3-----%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"----4-----%@", [NSThread currentThread]);
    });
    
解释:在执行到dispatch_barrier_async以前,异步线程是无序执行的,之后是有序执行的

GCD-dispatch_apply

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_apply(subpaths.count, queue, ^(size_t index) {

});

非GCD单例

static id _instance;

+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
    @synchronized(self) {
        if (_instance == nil) {
            _instance = [super allocWithZone:zone];
        }
    }
    return _instance;
}

+ (instancetype)sharedInstance
{
    @synchronized(self) {
        if (_instance == nil) {
            _instance = [[self alloc] init];
        }
    }
    return _instance;
}

- (id)copyWithZone:(NSZone *)zone
{
    return _instance;
}

GCD-宏定义单例

使用时导入其头文件和实现文件即可,名称修改成自定义
// .h文件
#define CYSingletonH(name) + (instancetype)shared##name;

// .m文件
#define CYSingletonM(name) \
static id _instance; \
 \
+ (instancetype)allocWithZone:(struct _NSZone *)zone \
{ \
    static dispatch_once_t onceToken; \
    dispatch_once(&onceToken, ^{ \
        _instance = [super allocWithZone:zone]; \
    }); \
    return _instance; \
} \
 \
+ (instancetype)shared##name \
{ \
    static dispatch_once_t onceToken; \
    dispatch_once(&onceToken, ^{ \
        _instance = [[self alloc] init]; \
    }); \
    return _instance; \
} \
 \
- (id)copyWithZone:(NSZone *)zone \
{ \
    return _instance; \
}

五、NSOperation开发

1. NSOperation的作用

配合使用NSOperation和NSOperationQueue也能实现多线程编程
NSOperation和NSOperationQueue实现多线程的具体步骤

  1. 先将需要执行的操作封装到一个NSOperation对象中
  2. 然后将NSOperation对象添加到NSOperationQueue中
  3. 系统会自动将NSOperationQueue中的NSOperation取出来
  4. 将取出的NSOperation封装的操作放到一条新线程中执行
  5. NSOperation是个抽象类,并不具备封装操作的能力,必须使用它的子类
2. 使用NSOperation有三种方式
  1. NSInvocationOperation
  2. NSBlockOperation
  3. 自定义子类继承NSOperation,实现内部相应的方法
3. 使用block创建Operation
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
    // 在主线程
    NSLog(@"下载1------%@", [NSThread currentThread]);
 }];
    
// 添加额外的任务(在子线程执行)
[op addExecutionBlock:^{
    NSLog(@"下载2------%@", [NSThread currentThread]);
 }];

[op addExecutionBlock:^{
    NSLog(@"下载3------%@", [NSThread currentThread]);
 }];
[op addExecutionBlock:^{
    NSLog(@"下载4------%@", [NSThread currentThread]);
 }];
    
[op start];
4. 使用invocation创建Operation
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];

[op start];

<注意>

  1. 默认情况下,调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作
  2. 只有将NSOperation放到一个NSOperationQueue中,才会异步执行操作
5. NSOperationQueue作用
  1. NSOperation可以调用start方法来执行任务,但默认是同步执行的
  2. 如果将NSOperation添加到NSOperationQueue(操作队列)中,系统会自动异步执行NSOperation中的操作
6. 添加操作到NSOperationQueue中
- (void)addOperation:(NSOperation *)op;
- (void)addOperationWithBlock:(void (^)(void))block;
7. 最大并发数

概念:同时执行的任务数,比如同时开3个线程执行3个任务,并发数就是3

8. 最大并发数的相关方法
- (NSInteger)maxConcurrentOperationCount;
- (void)setMaxConcurrentOperationCount:(NSInteger)cnt;
9. 队列的取消、暂停、恢复

取消队列的所有操作
提示:也可以调用NSOperation的- (void)cancel方法取消单个操作

- (void)cancel;
- (void)cancelAllOperations;

10. 暂停和恢复队列
- (void)setSuspended:(BOOL)b; // YES代表暂停队列,NO代表恢复队列
- (BOOL)isSuspended;
11. 操作的监听

可以监听一个操作的执行完毕

- (void (^)(void))completionBlock;
- (void)setCompletionBlock:(void (^)(void))block;
12. 自定义NSOperation

自定义NSOperation的步骤很简单

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

13. 重写- (void)main方法的注意点
  1. 自己创建自动释放池(因为如果是异步操作,无法访问主线程的自动释放池)
  2. 经常通过- (BOOL)isCancelled方法检测操作是否被取消,对取消做出响应
14. 自定义NSOperation
#import "CYOperation.h"

@implementation CYOperation

/**
 * 需要执行的任务
 */
- (void)main
{
    for (NSInteger i = 0; i<1000; i++) {
        NSLog(@"download1 -%zd-- %@", i, [NSThread currentThread]);
    }
    if (self.isCancelled) return;
    
    for (NSInteger i = 0; i<1000; i++) {
        NSLog(@"download2 -%zd-- %@", i, [NSThread currentThread]);
    }
    if (self.isCancelled) return;
    
    for (NSInteger i = 0; i<1000; i++) {
        NSLog(@"download3 -%zd-- %@", i, [NSThread currentThread]);
    }
    if (self.isCancelled) return;
}

- (void)operationQueue1
{
    // 创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    // 创建操作(任务)
    // 创建NSInvocationOperation
    NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download1) object:nil];
    NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download2) object:nil];
    
    // 创建NSBlockOperation
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"download3 --- %@", [NSThread currentThread]);
    }];
    
    [op3 addExecutionBlock:^{
        NSLog(@"download4 --- %@", [NSThread currentThread]);
    }];
    [op3 addExecutionBlock:^{
        NSLog(@"download5 --- %@", [NSThread currentThread]);
    }];
    
    
    NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"download6 --- %@", [NSThread currentThread]);
    }];
    
    // 创建CYOperation
    CYOperation *op5 = [[CYOperation alloc] init];
    
    // 添加任务到队列中
    [queue addOperation:op1]; // [op1 start]
    [queue addOperation:op2]; // [op2 start]
    [queue addOperation:op3]; // [op3 start]
    [queue addOperation:op4]; // [op4 start]
    [queue addOperation:op5]; // [op5 start]
}

- (void)download1
{
    NSLog(@"download1 --- %@", [NSThread currentThread]);
}

- (void)download2
{
    NSLog(@"download2 --- %@", [NSThread currentThread]);
}
- (void)operationQueue2
{
    // 创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    // 创建操作
    // NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
    // NSLog(@"download1 --- %@", [NSThread currentThread]);
    // }];
    
    // 添加操作到队列中
    // [queue addOperation:op1];
    
    
    [queue addOperationWithBlock:^{
        NSLog(@"download1 --- %@", [NSThread currentThread]);
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"download2 --- %@", [NSThread currentThread]);
    }];
}
- (void)opetationQueue3
{
    // 创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    // 设置最大并发操作数
    //    queue.maxConcurrentOperationCount = 2;
    queue.maxConcurrentOperationCount = 1; // 就变成了串行队列
    
    // 添加操作
    [queue addOperationWithBlock:^{
        NSLog(@"download1 --- %@", [NSThread currentThread]);
        [NSThread sleepForTimeInterval:0.01];
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"download2 --- %@", [NSThread currentThread]);
        [NSThread sleepForTimeInterval:0.01];
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"download3 --- %@", [NSThread currentThread]);
        [NSThread sleepForTimeInterval:0.01];
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"download4 --- %@", [NSThread currentThread]);
        [NSThread sleepForTimeInterval:0.01];
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"download5 --- %@", [NSThread currentThread]);
        [NSThread sleepForTimeInterval:0.01];
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"download6 --- %@", [NSThread currentThread]);
        [NSThread sleepForTimeInterval:0.01];
    }];
}
15. NSOperation操作依赖

@property (weak, nonatomic) IBOutlet UIImageView *imageView;

  NSOperationQueue *queue = [[NSOperationQueue alloc] init];
  
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"download1----%@", [NSThread  currentThread]);
    }];
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"download2----%@", [NSThread  currentThread]);
    }];
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"download3----%@", [NSThread  currentThread]);
    }];
    NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
        for (NSInteger i = 0; i<10; i++) {
            NSLog(@"download4----%@", [NSThread  currentThread]);
        }
    }];
    NSBlockOperation *op5 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"download5----%@", [NSThread  currentThread]);
    }];
    op5.completionBlock = ^{
        NSLog(@"op5执行完毕---%@", [NSThread currentThread]);
    };
    
    // 设置依赖
    [op3 addDependency:op1];
    [op3 addDependency:op2];
    [op3 addDependency:op4];
    
    [queue addOperation:op1];
    [queue addOperation:op2];
    [queue addOperation:op3];
    [queue addOperation:op4];
    [queue addOperation:op5];
16. NSOperation-线程间的通信
 NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    __block UIImage *image1 = nil;
    // 下载图片1
    NSBlockOperation *download1 = [NSBlockOperation blockOperationWithBlock:^{
        
        // 图片的网络路径
        NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
        
        // 加载图片
        NSData *data = [NSData dataWithContentsOfURL:url];
        
        // 生成图片
        image1 = [UIImage imageWithData:data];
    }];
    
    __block UIImage *image2 = nil;
    // 下载图片2
    NSBlockOperation *download2 = [NSBlockOperation blockOperationWithBlock:^{
        
        // 图片的网络路径
        NSURL *url = [NSURL URLWithString:@"http://pic38.nipic.com/20140228/5571398_215900721128_2.jpg"];

        
        // 加载图片
        NSData *data = [NSData dataWithContentsOfURL:url];
        
        // 生成图片
        image2 = [UIImage imageWithData:data];
    }];
    
    // 合成图片
    NSBlockOperation *combine = [NSBlockOperation blockOperationWithBlock:^{
        // 开启新的图形上下文
        UIGraphicsBeginImageContext(CGSizeMake(100, 100));
        
        // 绘制图片
        [image1 drawInRect:CGRectMake(0, 0, 50, 100)];
        image1 = nil;
        
        [image2 drawInRect:CGRectMake(50, 0, 50, 100)];
        image2 = nil;
        
        // 取得上下文中的图片
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
        
        // 结束上下文
        UIGraphicsEndImageContext();
        
        // 回到主线程
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            self.imageView.image = image;
        }];
    }];
    [combine addDependency:download1];
    [combine addDependency:download2];
    
    [queue addOperation:download1];
    [queue addOperation:download2];
    [queue addOperation:combine];
}

/**
 * 线程之间的通信
 */
- (void)test
{
    [[[NSOperationQueue alloc] init] addOperationWithBlock:^{
        // 图片的网络路径
       NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
        
        // 加载图片
        NSData *data = [NSData dataWithContentsOfURL:url];
        
        // 生成图片
        UIImage *image = [UIImage imageWithData:data];
        
        // 回到主线程
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            self.imageView.image = image;
        }];
    }];
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,864评论 6 494
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,175评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,401评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,170评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,276评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,364评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,401评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,179评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,604评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,902评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,070评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,751评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,380评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,077评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,312评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,924评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,957评论 2 351

推荐阅读更多精彩内容