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

多线程网络(二)

2015-11-12 15:00 603 查看
1.GCD常用的代码

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}

- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self gcdDemo4];
}

#pragma mark - 最常用的组合 - 线程间通讯
// MARK: 最常用的代码!
- (void)gcdDemo4 {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
// 执行耗时操作!
NSLog(@"耗时操作 %@", [NSThread currentThread]);

// 更新 UI dispatch_get_main_queue 主队列,专门用来调度任务在主线程上执行的
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"更新UI %@", [NSThread currentThread]);
});
});
}

#pragma mark - GCD演练
// MARK: 精简版
/**
与 NSThread 的对比

1. 所有的代码写在一起的,让代码更加简单,易于阅读和维护
NSThread 是通过 selector 来调用指定的方法
2. 使用 GCD 不需要管理线程的创建/销毁/复用的过程!
如果用 NSThread 显然不好搞!
*/
- (void)gcdDemo3 {
for (int i = 0; i < 10; ++i) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"%@", [NSThread currentThread]);
});
}
}

// MARK: 基本演练 - 同步执行任务不开线程
- (void)gcdDemo2 {
// 队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 任务
void (^task)() = ^ {
NSLog(@"%@", [NSThread currentThread]);
};
// 指定执行任务的函数
dispatch_sync(queue, task);
}

// MARK: 基本演练 - 异步执行任务,开线程
- (void)gcdDemo1 {
// 1. 队列-全局队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);

// 2. 任务-用 block 定义一个任务
void (^task)() = ^ {
NSLog(@"%@", [NSThread currentThread]);
};

// 3. 指定任务的"执行函数" -> 同步执行/异步执行
// 同步 - 这一条指令不执行完,"不会"执行下一条语句,顺序执行,不需要开启线程
// 异步 - 这一条指令不执行完,"就可以"执行下一条语句,会开启线程
//  "异步"是多线程的代名词
dispatch_async(queue, task);
}

@end


2.GCD加载网络图片

#import "ViewController.h"

@interface ViewController () <UIScrollViewDelegate>
@property (nonatomic, strong) UIScrollView *scrollView;
@property (nonatomic, weak) UIImageView *imageView;
@end

@implementation ViewController

#pragma mark - UIScrollViewDelegate
// "告诉" scrollView 缩放哪一个视图,就应该有"返回值"
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return self.imageView;
}

/**
加载视图,和 storyboard & xib 等价的,一旦写了这个方法 storyboard & xib 会实效

创建界面上所有子视图的层次结构!
*/
- (void)loadView {

_scrollView = [[UIScrollView alloc] initWithFrame:[UIScreen mainScreen].bounds];
// 设置缩放比例
_scrollView.minimumZoomScale = 0.5;
_scrollView.maximumZoomScale = 2;
// 设置代理
_scrollView.delegate = self;

self.view = _scrollView;

UIImageView *iv = [[UIImageView alloc] init];
_imageView = iv;
[self.view addSubview:iv];
}

/**
视图加载完毕后执行,加载数据
*/
- (void)viewDidLoad {
[super viewDidLoad];

// 在后台执行耗时的操作
dispatch_async(dispatch_get_global_queue(0, 0), ^{
// 确定网络上唯一的资源
NSURL *url = [NSURL URLWithString:@"http://d.hiphotos.baidu.com/image/pic/item/0df3d7ca7bcb0a46e18d93706863f6246a60afcf.jpg"];

// 加载图片数据->所有网络上传输的都是"二进制数据"
NSData *data = [NSData dataWithContentsOfURL:url];

// 将二进制数据转换成图像
UIImage *image = [UIImage imageWithData:data];

// 耗时操作执行完成后 - 更新UI - 主线程
dispatch_async(dispatch_get_main_queue(), ^{
// 设置图像视图
self.imageView.image = image;
// 调整图像视图大小和image一样
[self.imageView sizeToFit];

// 指定滚动视图的范围
self.scrollView.contentSize = image.size;
});
});
}

@end


3.GCD队列

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController
/**
GCD 核心概念

- 队列-负责"调度"任务
* "串行"队列 - 一个接一个 - 一个任务不调度完,"不会"调度下一个
* "并发"队列 - 可以同时的 - 一个任务不调度完,"可以"调度下一个
* 主队列 - 负责在主线程上调度任务,实现线程间通讯
注意:主队列不是主线程
* 全局队列 - 为了方便程序员使用,提供的一个全局队列,可以直接 get
全局队列就是并发队列,执行效果和自己创建的并发队列完全一样!

* 问题:全局队列&并发队列
- 全局队列没有名字
无论 MRC & ARC 都不需要考虑释放
- 并发队列有名字,和 NSThread 的 name 属性很像
如果在 MRC 开发时,需要使用 dispatch_release(q); 释放相应的对象

- 如果我们前期日常开发中,建议使用"全局队列"
- 并发队列,是在专业的应用程序或者第三方框架中使用的

- 任务(名词) - 是用 block 封装的一个代码块
block 是一组预先转被好的代码,在需要的时候执行!

- 指定任务执行的函数(动词)
dispatch_async  - 异步执行
* 这一句话不执行完,就可以执行下一句
* 不是阻塞式的
* 异步是多线程的代名词

dispatch_sync   - 同步执行
* 这一句不执行完,不能执行下一句
* 是阻塞式的
* 不会开启线程

- 小结
- 开不开线程由执行任务的函数决定
* 异步开,多线程的代名词
* 同步不开
- 异步执行的函数,开几条线程由队列决定
* 串行队列 开一条线程
* 并发队列 开多条线程

- 队列的选择
* 多线程的目的:将耗时的操作放在后台执行!

* 串行队列(斯坦福大学的视频),只开一条线程,所有任务顺序执行
* 如果任务有先后执行顺序的要求
* 效率低 -> 执行慢,"省电"
* 有的时候,用户其实不希望太快!使用 3G 流量,"省钱"

* 并发队列,会开启多条线程,所有任务不按照顺序执行
* 如果任务没有先后执行顺序的要求
* 效率高 -> 执行快,"费电"
* WIFI,包月

- 在实际开发中,线程数量如何决定?
* WIFI 线程数 6 条
* 3G / 4G 移动开发的时候,2~3条,再多会费电费钱!

- 同步任务的作用 - "依赖"唯一
在多线程开发的时候,有的时候,需要使用同步任务,挡住后面的异步任务,
让所有后续的任务,都等待同步任务执行完成之后,才能继续

* 所有后续的任务,都"依赖"最前面的同步任务

"异步是多线程的代名词!"
*/
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}

- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self gcdDemo3];
}

#pragma mark - 全局队列
/**
执行效果:开多个线程,并发执行
队列的效果:全局队列就是并发队列,执行效果和自己创建的并发队列完全一样!
*/
- (void)gcdDemo8 {
/**
参数

1. 服务质量(队列对任务调度的优先级)/iOS 7.0 之前,就是优先级
- iOS 8.0
QOS_CLASS_USER_INTERACTIVE 0x21,                用户交互(希望最快完成-不能用太耗时的操作)
QOS_CLASS_USER_INITIATED 0x19,                  用户期望(希望快,也不能太耗时)
QOS_CLASS_DEFAULT 0x15,                         默认(用来底层重置队列使用的,不是给程序员用的)
*** QOS_CLASS_UTILITY 0x11,                     实用工具(专门用来处理耗时操作!)
QOS_CLASS_BACKGROUND 0x09,                      后台
QOS_CLASS_UNSPECIFIED 0x00,                     未指定,可以和iOS 7.0 适配
- iOS 7.0
DISPATCH_QUEUE_PRIORITY_HIGH 2                  高优先级
DISPATCH_QUEUE_PRIORITY_DEFAULT 0               默认优先级
DISPATCH_QUEUE_PRIORITY_LOW (-2)                低优先级
DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN    后台优先级

结论:如果要适配 iOS 7.0 & 8.0,使用以下代码:
dispatch_get_global_queue(0, 0);

提示:不要选择"后台"的优先级,苹果认为,后台优先级的任务,用户不需要关心什么时候执行完!
** 任务执行会慢得令人发指!不利于调试!

2. 为未来保留使用的,应该永远传入0
*/
dispatch_queue_t q = dispatch_get_global_queue(0, 0);

for (int i = 0; i < 10; ++i) {
dispatch_async(q, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}
NSLog(@"come here");
}

#pragma mark - 同步任务作用!
- (void)gcdDemo7 {
/**
在有的时候,是会希望任务执行过程中有一定的先后顺序 -> 依赖关系

举个例子:登录,下载小说 A,下载小说 B...
实际目标:下载过程是并发的,但是登录一定要在最前面!
*/
dispatch_queue_t q = dispatch_queue_create("ios", DISPATCH_QUEUE_CONCURRENT);

// 登录必须第一个执行完,才能执行后序的任务
dispatch_sync(q, ^{
NSLog(@"登录 %@", [NSThread currentThread]);
});
dispatch_async(q, ^{
NSLog(@"download A %@", [NSThread currentThread]);
});
dispatch_async(q, ^{
NSLog(@"download B %@", [NSThread currentThread]);
});
}

#pragma mark - 主队列
// MARK: 主队列,同步执行
/**
提问:come here?
猜测:最后/没有!
答案:死锁!
*/
- (void)gcdDemo6 {
dispatch_queue_t q = dispatch_get_main_queue();
NSLog(@"!!!");

dispatch_sync(q, ^{
NSLog(@"%@", [NSThread currentThread]);
});

NSLog(@"come here");
}

// MARK: 主队列,异步执行
/**
提问:执行顺序,会开启线程吗?come here?
猜测:顺序,不会,最后!
答案:顺序,不会,最前
*/
- (void)gcdDemo5 {
// 1. 队列,主队列->主队列是随着程序启动就存在的->主队列不需要创建,可以直接获取
dispatch_queue_t q = dispatch_get_main_queue();

// 2. "异步"执行
for (int i = 0; i < 10; ++i) {
dispatch_async(q, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}
NSLog(@"睡会");
[NSThread sleepForTimeInterval:2.0];
NSLog(@"come here");
}

#pragma mark - GCD 演练
// MARK: 并发队列,"同步"任务
/**
提问:执行顺序,会开启线程吗?come here?
猜测:顺序,开"一条",不是最后?
结果:顺序,不开线程,最后

并发队列:可以同时调度多个任务
同步执行:不会开启线程
*/
- (void)gcdDemo4 {
// 1. 队列
dispatch_queue_t q = dispatch_queue_create("ios", DISPATCH_QUEUE_CONCURRENT);

// 2. 任务
for (int i = 0; i < 10; ++i) {
dispatch_sync(q, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}

NSLog(@"come here");
}

// MARK: 并发队列,"异步"任务
/**
提问:执行顺序,会开启线程吗?come here?
答案:不是顺序,开"多条",不是最后

并发队列:同时调度多个任务
异步执行:会开启线程
*/
- (void)gcdDemo3 {
// 1. 队列
dispatch_queue_t q = dispatch_queue_create("ios", DISPATCH_QUEUE_CONCURRENT);

// 2. 执行任务
for (int i = 0; i < 10; ++i) {
dispatch_async(q, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}

NSLog(@"come here");
}

// MARK: 串行队列,异步任务
/**
提问:执行顺序,会开启线程吗?come here?
答案:顺序,开"一条",不是最后

原因:
队列:一个接一个,最多只需要开一个就可以
任务:异步,会开启线程
*/
- (void)gcdDemo2 {
// 1. 队列
dispatch_queue_t q = dispatch_queue_create("ios", NULL);

// 2. 执行任务的函数
for (int i = 0; i < 10; ++i) {
NSLog(@"%d------", i);

dispatch_async(q, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}
NSLog(@"come here");
}

// MARK: 串行队列,同步任务(不用!)
/**
提问:执行顺序,会开启线程吗?come here?
答案:顺序,不开,最后!

原因:串行队列一个接一个的调度任务  -> 顺序的
同步执行,本身不需要开线程
*/
- (void)gcdDemo1 {
// 1. 队列
/**
参数:
1. 队列的名称
2. 队列的属性 - 指定是什么类型的队列
DISPATCH_QUEUE_SERIAL           串行
- NULL
DISPATCH_QUEUE_CONCURRENT       并发
- 太长,使用 \ 拼接
*/
// 以下两句代码是等价的!
//    dispatch_queue_t q = dispatch_queue_create("ios", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t q = dispatch_queue_create("ios", NULL);

// 2. 指定任务执行的函数
for (int i = 0; i < 10; ++i) {
NSLog(@"%d------", i);

dispatch_sync(q, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}
NSLog(@"come here");
}

@end


4.GCD增强

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}

- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self gcdDemo4];
}

/**
通过 block 的嵌套,可以组合出非常复杂的任务关系!

很难用一些表格或者规则表述出所有的情况!
要理解队列和任务之间的关系,一定要理清"队列"的特点&"执行任务"函数的特点!

提示:多线程开发要"尽量简单"!不要给自己"埋的太深"!
*/
// MARK: 同步任务增强 - 依赖关系
- (void)gcdDemo4 {
dispatch_queue_t q = dispatch_get_global_queue(0, 0);

void (^task)() = ^ {
// 1. 异步执行,在其他线程执行
NSLog(@"===> %@", [NSThread currentThread]);

// 2. 同步任务不开启线程,就在当前线程立即执行
// 同步任务不执行完,就不能执行下一条指令
// 同步任务执行完毕,线程就空闲出来,后面的并发完全可以利用!
dispatch_sync(q, ^{
NSLog(@"login %@", [NSThread currentThread]);
});

// 3. 同步执行完毕后,并发执行后续两个任务
dispatch_async(q, ^{
NSLog(@"download A %@", [NSThread currentThread]);
});
dispatch_async(q, ^{
NSLog(@"download B %@", [NSThread currentThread]);
});
};

dispatch_async(q, task);
}

- (void)gcdDemo3 {

dispatch_queue_t q = dispatch_get_global_queue(0, 0);

// 问题: login 在主线程,用户登录也是耗时操作!
// 因为同步任务不会开启线程 => 同步任务在那一个线程都不会开启线程
dispatch_sync(q, ^{
NSLog(@"login %@", [NSThread currentThread]);
});
dispatch_async(q, ^{
NSLog(@"download A %@", [NSThread currentThread]);
});
dispatch_async(q, ^{
NSLog(@"download B %@", [NSThread currentThread]);
});
}

// MARK:同步任务不死锁
/**
主队列在调度任务的时候,如果主线程上有任务,就暂时不调度

造成主线程和主队列相互等待,造成死锁
*/
- (void)gcdDemo2 {
dispatch_queue_t q = dispatch_get_global_queue(0, 0);

void (^task)() = ^ {
NSLog(@"hehe");

dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"%@", [NSThread currentThread]);
});
};

dispatch_async(q, task);

NSLog(@"come here");
}

- (void)gcdDemo1 {
dispatch_queue_t q = dispatch_get_main_queue();

NSLog(@"hehe");

dispatch_sync(q, ^{
NSLog(@"%@", [NSThread currentThread]);
});
}

@end


5.GCD延时

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}

- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self delay];
}

// MARK: 延时功能
- (void)delay {

/**
从现在开始,经过多少纳秒,由"队列"调度异步执行 block 中的代码

参数
1. when    从现在开始,经过多少纳秒
2. queue   队列
3. block   异步执行的任务
*/
dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC));

NSLog(@"haha");
//    dispatch_after(when, dispatch_get_main_queue(), ^{
//        NSLog(@"%@", [NSThread currentThread]);
//    });

// 全局队列 延时多长时间后,异步执行某些方法!
//    dispatch_after(when, dispatch_get_global_queue(0, 0), ^{
//        NSLog(@"%@", [NSThread currentThread]);
//    });
// 串行队列 - 也会开线程!
dispatch_after(when, dispatch_queue_create("ios", NULL), ^{
NSLog(@"%@", [NSThread currentThread]);
});

NSLog(@"come here");
}

@end


6.只执行一次的代码

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}

- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self demoOnce];
}

// MARK: 多线程测试 dispatch_once
- (void)demoOnce {

for (int i = 0; i < 10; ++i) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[self once];
});
}
}

/**
有的时候,在程序开发中,有些代码只想从程序启动就只执行一次
典型的应用场景就是“单例”

有的人会以为单例内部使用的是"互斥锁"!
*/
- (void)once {
// token 令牌,只要 token == 0,才会执行 block 中的代码
// dispatch 内部也有一把锁,是能够保证"线程安全"的!而且是苹果公司推荐使用的
// 应该能够做到,多线程执行的时候,block 同样只会执行一次!
static dispatch_once_t onceToken;

NSLog(@"come here %ld", onceToken);

// 同步执行!可以保证后续的代码能够使用到一次性执行后的结果!
dispatch_once(&onceToken, ^{
// 只需要执行一次的代码
[NSThread sleepForTimeInterval:1.0];
NSLog(@"%@", [NSThread currentThread]);
});

NSLog(@"come here");
}

@end


7.单例

#import <Foundation/Foundation.h>
/**
"单"例的特点

1. 在内存中只有一个实例
2. 提供一个全局的访问点-类方法统一方法

目前使用过的单例:
- UIApplication
- NSUserDefaults
- NSFileManager
- NSNotificationCenter
...

目前在 iOS 开发中,单例已经到达被滥用的程度!很多公司的面试题中都要求手写单例!
*/
@interface Singleton : NSObject

/**
*  用 dispatch_once 建立的对象
*/
+ (instancetype)sharedSingleton;

/**
*  用互斥锁建立的对象
*/
+ (instancetype)syncSingleton;

@end


#import "Singleton.h"

@implementation Singleton

+ (instancetype)sharedSingleton {
// 永远只有一个实例,因此需要保存在静态区,随着应用程序启动就分配空间,随着应用程序一起被销毁
static id instance;

// 可以保证对象只被实例化一次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});

return instance;
}

+ (instancetype)syncSingleton {
static id syncInstance;

@synchronized(self) {
// 懒加载的写法,不能保证线程安全
if (syncInstance == nil) {
syncInstance = [[self alloc] init];
}
}

return syncInstance;
}

@end


#import "ViewController.h"
#import "Singleton.h"
@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];
}

- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

/**
互斥锁真的没有dispatch_once性能好!

dispatch_once是苹果推荐的一次性执行的解决方案!
*/
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
long largeNumber = 1000 * 10000;

// dispatch_once
// 开始时间
CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();

for (int i = 0; i < largeNumber; ++i) {
[Singleton sharedSingleton];
}

// 结束时间
CFAbsoluteTime end = CFAbsoluteTimeGetCurrent();
NSLog(@"dispatch once %f", end - start);

// 互斥锁
start = CFAbsoluteTimeGetCurrent();
for (int i = 0; i < largeNumber; ++i) {
[Singleton syncSingleton];
}
end = CFAbsoluteTimeGetCurrent();
NSLog(@"互斥锁 %f", end - start);
}

@end


8.调度组

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController
/**
调度组

有的时候,在执行异步任务的时候,会希望所有的相关任务执行完毕后,统一得到通知!

举个例子:下载小说:红楼梦,西游记,其他小说...
每次提示也是耗电的!
*/
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}

- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self group2];
}

// MARK: 调度组
/**
AFN,内部使用到了 enter & leave 代码!

终端输入
$ man dispatch_group_enter

void dispatch_group_async(dispatch_group_t group, dispatch_queue_t queue, dispatch_block_t block)
{
dispatch_retain(group);
dispatch_group_enter(group);
dispatch_async(queue, ^{
block();
dispatch_group_leave(group);
dispatch_release(group);
});
}
*/
- (void)group2 {
// 实例化调度组
dispatch_group_t group = dispatch_group_create();

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

// 进入组(增加一个标记-后面再定义的 block,就会被添加到组中)
// *** dispatch_group_enter & dispatch_group_leave 必须成对出现
// *** dispatch_group_leave 必须是最后一句
dispatch_group_enter(group);
// 定义任务
void (^task1)() = ^ {
NSLog(@"download A %@", [NSThread currentThread]);

// "当所有任务完成之后",离开组->group 就能够知道任务完成了!
dispatch_group_leave(group);
};

dispatch_group_enter(group);
// 定义任务
void (^task2)() = ^ {
NSLog(@"download B %@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:2.0];

// "当所有任务完成之后",离开组->group 就能够知道任务完成了!
dispatch_group_leave(group);
};

// 将任务添加到队列
dispatch_async(queue, task1);
dispatch_async(queue, task2);

// 监听群组任务的结束
//    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
//        NSLog(@"OVER");
//    });
// 等待调度组中所有任务执行完毕,时间是永远->死等,一直要等到所有任务执行完成
// dispatch_group_wait 等待方式是同步的(知道就行!)
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

NSLog(@"come here");
}

// MARK: 调度组
- (void)group1 {
// 实例化调度组
dispatch_group_t group = dispatch_group_create();

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

// 添加任务
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:1.0];
NSLog(@"download A, %@", [NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:0.8];
NSLog(@"download B, %@", [NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:0.9];
NSLog(@"download C, %@", [NSThread currentThread]);
});

// dispatch_group_notify 可以监听调度组中所有的异步任务,在执行完毕后,获得通知!
// 监听工作是异步的!
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 监听到所有任务执行完成后,在主线程执行 block
// 以下载小说为例:可以异步下载小说,下载完毕后,在主线程更新UI
NSLog(@"OVER %@", [NSThread currentThread]);
});
NSLog(@"come here");
}

@end


9.时钟

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController
/**
运行循环

作用:
1. 保证程序不会退出
2. 监听所有的事件:触摸、时钟、网络事件
3. 如果没有任何事件发生,会进入休眠状态,省电
4. 一旦有事件发生,运行循环会再次启动,并且处理事件

在"今天"的实际开发中,运行循环几乎用不倒,概念非常重要!

runloop是在iOS开发的原始时代,需要大量使用的!
*/
- (void)viewDidLoad {
[super viewDidLoad];

// 就是用 default 模式,将时钟添加到运行循环!
//    [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateTimer) userInfo:nil repeats:YES];
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(updateTimer) userInfo:nil repeats:YES];
// 将时钟添加到运行循环
/**
NSDefaultRunLoopMode - 默认的运行循环模式:监听时钟,网络等跟UI无关的事件
NSRunLoopCommonModes - 通用的运行循环模式:监听用户交互事件,优先级最高,保证用户一旦交互,立即做出响应!
*/
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}

/**
时钟的触发方法是同步的还是异步的 => 并没有开启线程,是同步的!

一定需要注意的,时钟触发方法中,不能有太耗时的操作!否则会造成主线程的卡顿!
*/
- (void)updateTimer {

static int num = 0;
NSLog(@"睡");
[NSThread sleepForTimeInterval:1.0];

NSLog(@"%d %@", num++, [NSThread currentThread]);
}

- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

@end


10.时钟(多线程)

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController
/**
运行循环

作用:
1. 保证程序不会退出
2. 监听所有的事件:触摸、时钟、网络事件
3. 如果没有任何事件发生,会进入休眠状态,省电
4. 一旦有事件发生,运行循环会再次启动,并且处理事件

在"今天"的实际开发中,运行循环几乎用不倒,概念非常重要!

runloop是在iOS开发的原始时代,需要大量使用的!

NSDefaultRunLoopMode - 默认的运行循环模式:监听时钟,网络等跟UI无关的事件
NSRunLoopCommonModes - 通用的运行循环模式:监听用户交互事件,优先级最高,保证用户一旦交互,立即做出响应!

*/
- (void)viewDidLoad {
[super viewDidLoad];

// 将时钟放在异步触发
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"开启时钟 %@", [NSThread currentThread]);

NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(updateTimer) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];

// 如果要监听事件,需要启动子线程的运行循环!仅供了解
// 这句话是真正的死循环!启动运行循环,不再执行后续的代码!
// 如果用 run 直接启动运行循环,很难停掉循环!线程永远不会被终止
[[NSRunLoop currentRunLoop] run];

NSLog(@"come here");
});
}

- (void)updateTimer {

static int num = 0;
NSLog(@"睡");
[NSThread sleepForTimeInterval:1.0];

NSLog(@"%d %@", num++, [NSThread currentThread]);
}

// 停止时钟
- (IBAction)stopTimer:(id)sender {

}

- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

@end


11.停止运行循环

#import "ViewController.h"

@interface ViewController ()
// 标记是否停止
@property (nonatomic, assign, getter=isFinished) BOOL finished;
@end

@implementation ViewController
/**
运行循环

作用:
1. 保证程序不会退出
2. 监听所有的事件:触摸、时钟、网络事件
3. 如果没有任何事件发生,会进入休眠状态,省电
4. 一旦有事件发生,运行循环会再次启动,并且处理事件

在"今天"的实际开发中,运行循环几乎用不倒,概念非常重要!

runloop是在iOS开发的原始时代,需要大量使用的!

NSDefaultRunLoopMode - 默认的运行循环模式:监听时钟,网络等跟UI无关的事件
NSRunLoopCommonModes - 通用的运行循环模式:监听用户交互事件,优先级最高,保证用户一旦交互,立即做出响应!

*/
- (void)viewDidLoad {
[super viewDidLoad];

// 将时钟放在异步触发
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"开启时钟 %@", [NSThread currentThread]);

NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(updateTimer) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];

// 如果要监听事件,需要启动子线程的运行循环!仅供了解
self.finished = NO;
// 手写了一个死循环
// 问题:性能不好!这种方法是通过 OC 停止运行循环使用最广泛的方法。
while (!self.isFinished) {
// 让运行循环监听 0.1 秒的事件
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
}

// 一旦执行到这句话,就说明线程要死亡了!
NSLog(@"come here");
});
}

- (void)updateTimer {

static int num = 0;
NSLog(@"睡");
[NSThread sleepForTimeInterval:1.0];

NSLog(@"%d %@", num++, [NSThread currentThread]);
}

// 停止时钟
- (IBAction)stopTimer:(id)sender {
self.finished = YES;
}

- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

@end


12.CF停止运行循环

#import "ViewController.h"

@interface ViewController ()
/** 时钟线程所在的运行循环 */
@property (nonatomic, assign) CFRunLoopRef timerRunloop;
@end

@implementation ViewController
/**
运行循环

作用:
1. 保证程序不会退出
2. 监听所有的事件:触摸、时钟、网络事件
3. 如果没有任何事件发生,会进入休眠状态,省电
4. 一旦有事件发生,运行循环会再次启动,并且处理事件

在"今天"的实际开发中,运行循环几乎用不倒,概念非常重要!

runloop是在iOS开发的原始时代,需要大量使用的!

NSDefaultRunLoopMode - 默认的运行循环模式:监听时钟,网络等跟UI无关的事件
NSRunLoopCommonModes - 通用的运行循环模式:监听用户交互事件,优先级最高,保证用户一旦交互,立即做出响应!

*/
- (void)viewDidLoad {
[super viewDidLoad];

// 将时钟放在异步触发
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"开启时钟 %@", [NSThread currentThread]);

NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(updateTimer) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];

// 如果要监听事件,需要启动子线程的运行循环!仅供了解
// 使用 CoreFoundation 是 C 语言的框架
// 以下代码,是绝大多数第三方框架中使用的方法!
self.timerRunloop = CFRunLoopGetCurrent();
// 启动运行循环
// 利用底层的方式启动运行循环,不需要程序员再写 while 死循环,就能够利用 runloop 本身的特性
// 没有事件自动休眠
CFRunLoopRun();

// 一旦执行到这句话,就说明线程要死亡了!
NSLog(@"come here");
});
}

- (void)updateTimer {

static int num = 0;
NSLog(@"睡");
[NSThread sleepForTimeInterval:1.0];

NSLog(@"%d %@", num++, [NSThread currentThread]);
}

// 停止时钟
- (IBAction)stopTimer:(id)sender {
// 让时钟所在线程的运行循环停止
CFRunLoopStop(self.timerRunloop);
}

- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

@end


13.自动释放池

#import <Foundation/Foundation.h>

@interface Person : NSObject

@property (nonatomic, copy) NSString *name;

+ (instancetype)personWithName:(NSString *)name;

@end


#import "Person.h"

@implementation Person
/**
有alloc/init就需要有release
*/
+ (instancetype)personWithName:(NSString *)name {
// autorelease 的目的->延迟释放
// autorelase的对象出了作用域才会被添加到自动释放池中->达到延迟释放
//    Person *p = [[[self alloc] init] autorelease];
Person *p = [[self alloc] init];

p.name = name;

//    [p release];
return p;
//    [p release];
}

@end


#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}

- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

// 一个大数
long largeNumber = 1000;

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/**
问题:以下代码有什么问题?
如果有?如何解决?
有几种解决方式?请简述各种方式的优缺点及效率!

在 Foundation 框架中,如果是返回对象的类方法,对象都是自动释放的 - autorelease 的对象

答案:
1. 每一个str 都是自动释放的,如果循环次数很大,会占用过多的自动释放池空间!甚至让自动释放池耗尽!

2. 解决方法:网络上有两种解决方法!
1> 在for循环外面使用自动释放池,一个运行循环不仅仅只有一个for,前后都会有代码
结果:在for循环结束后,倾倒自动释放池!

2> 在for循环内使用自动释放池
结果:内存非常的平缓!

3. 哪一个执行效率高!
网上答案统一是外面快!

在实际工作中,应该毫不犹豫的使用自动释放池!
*/
CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
[self question1];
CFAbsoluteTime end = CFAbsoluteTimeGetCurrent();
NSLog(@"%f", end - start);

start = CFAbsoluteTimeGetCurrent();
[self question2];
end = CFAbsoluteTimeGetCurrent();
NSLog(@"%f", end - start);
}

- (void)question2 {
NSLog(@"开始");
for (int i = 0; i < largeNumber; i++) {
@autoreleasepool {
NSString *str = [NSString stringWithFormat:@"Hello - %04d", i];
str = [str stringByAppendingString:@" - World"];
str = [str uppercaseString];
}
}
NSLog(@"内 --- 结束");
}

- (void)question1 {
NSLog(@"开始");
@autoreleasepool {
for (int i = 0; i < largeNumber; i++) {
NSString *str = [NSString stringWithFormat:@"Hello - %04d", i];
str = [str stringByAppendingString:@" - World"];
str = [str uppercaseString];
}
}
NSLog(@"外 --- 结束");
}

- (void)question {
// 问题:以下代码有什么问题?如果有?如何解决?有几种解决方式?请简述各种方式的优缺点及效率!
for (int i = 0; i < largeNumber; i++) {
NSLog(@"----- %d", i);

NSString *str = [NSString stringWithFormat:@"Hello - %04d", i];
NSLog(@"%p", str);
str = [str stringByAppendingString:@" - World"];
NSLog(@"%p", str);
str = [str uppercaseString];
NSLog(@"%p", str);

NSLog(@"%@", str);
}
}

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