1.耗时操作
MARk - 耗时操作
[NSTread currenThread]:当前线程
通常用来开发调试过程中 可以用来判断是否是主线程
number = 1 就是主线程
number != 1 就是子线程 不要纠结数字,只要不是1 就是子线程 具体数组,程序员不能决定
代码:
耗时的操作:
- (void)longOperatiion {
for (int i = 0; i < 10; ++i) {
// 提示:NSLog的性能非常差!在发布应用程序的时候,要把NSLog过滤掉
NSLog(@"%@ %@", @(i), [NSThread currentThread]);
}
}
for (int i = 0; i < 10; ++i) {
// 提示:NSLog的性能非常差!在发布应用程序的时候,要把NSLog过滤掉
NSLog(@"%@ %@", @(i), [NSThread currentThread]);
}
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
// [self longOperatiion];
// 在后台执行耗时操作
[self performSelectorInBackground:@selector(longOperatiion) withObject:nil];
// [self longOperatiion];
// 在后台执行耗时操作
[self performSelectorInBackground:@selector(longOperatiion) withObject:nil];
}
2.pthred 演练
pthread_t threadId = NULL;
// 准备参数
id str = @"hello thread";
int result = pthread_create(&threadId, NULL, demo, (__bridge void *)(str));
if (result) {
NSLog(@"线程创建失败 %d", result);
} else {
NSLog(@"OK");
}
}
// 线程要调用执行的函数
// [NSThread currentThread] 可以在所有的多线程技术中使用!
// 查看当前代码执行所在的线程
void * demo (void * param) {
NSString *str = (__bridge NSString *)(param);
NSLog(@"%@ --- %@", [NSThread currentThread], str);
return NULL;
// 准备参数
id str = @"hello thread";
int result = pthread_create(&threadId, NULL, demo, (__bridge void *)(str));
if (result) {
NSLog(@"线程创建失败 %d", result);
} else {
NSLog(@"OK");
}
}
// 线程要调用执行的函数
// [NSThread currentThread] 可以在所有的多线程技术中使用!
// 查看当前代码执行所在的线程
void * demo (void * param) {
NSString *str = (__bridge NSString *)(param);
NSLog(@"%@ --- %@", [NSThread currentThread], str);
return NULL;
}
返回值:
- 线程创建成功 则返回 0
- 线程创建失败 则返回出错误编号
* 成功的原因只要一个,失败的原因可以有很多 在很多C语言中都会使用这个套路!
参数:
1> 第一个参数为执行线程标示符的指针
1)在C语言框架中,没有对象的概念,对象是通过结构体来实现的
2)通常”对象”的类型 会以_t/Ref 结尾
3)定义的时候 不需要使用*
2> 第二个参数用来设置线程的属性
3> 第三个参数是线程运行”函数”的”起始地址"
vid*(*)(void *)
* block 匿名的函数指针
* 定义返回值(^)(参数)
在C 语言 的void* 和oc 的id 是等于的
id (函数名激素hi指向函数起始位置的地址 的别名)(id)
*数组名: 指向数组第一个元素的地址
4> 最后一个参数是运行的参数
关于 __bridge — 桥接
内存管理概念:
特点:
1> 编译器在编译代码的时候,会根据 “oc”代码的结构,自动添加retain/relesse/autorelses
2>ARC只负责 OC部分的代码 不负责C/C++ 语言部分代码
3>如果开发的时候,涉及到混合语言开发,,如果使用C语言函数,出现create/copy/tetain/new 等子项 大多数需要程序员手动relese 否则出现内存泄露问题
4>在混合开发的时候 如果涉及到c框架和OC框架之间传递参数,需要使用”桥接”告诉编译器如如何管理内存
__bridge 就是保存原有的管理方法
5>提问MRC 中需要桥接嘛??/不需要,MRC中所有的内存都市程序员负责的
6>提问:管理内存,管理的是哪一区的内存,堆区 特点 :alloc/copy/ration 等等的函数都市和堆区有关的
三:创建线程(NSThread)的三种办法
第一种
// 实例化一个 NSThread 对象
NSThread *t1 = [[NSThread alloc] initWithTarget:selfselector:@selector(longOperation:) object:@"NSThread"];
// 启动线程
NSThread *t1 = [[NSThread alloc] initWithTarget:selfselector:@selector(longOperation:) object:@"NSThread"];
// 启动线程
[t1 start];
第二种 类方法,分离detach
// detach -> "分离"
// 直接新建线程,并且执行 @selector 方法
// 直接新建线程,并且执行 @selector 方法
[NSThread detachNewThreadSelector:@selector(longOperation:) toTarget:selfwithObject:dic];
第三种方法 per
是NSObject 的一个分类方法
是 thread 的字眼 却是可以开启后台线程 执行selector方法
因数的多线程方法 类方法类似 可以直接开启线程执行方法
[self performSelectorInBackground:@selector(longOperation:) withObject:@"perform"];
四.线程的状态
// 1. 实例化一个线程对象(新建)
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run)object:nil];
// 2. 启动线程(就绪,线程对象会被添加到可调度线程池,等待 CPU 调度)
[thread start];
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run)object:nil];
// 2. 启动线程(就绪,线程对象会被添加到可调度线程池,等待 CPU 调度)
[thread start];
}
* 在后台执行的方法
*/
- (void)run {
// 如果满足某一个条件,可以让线程进入"阻塞"状态
// 休眠 - 让当前线程休眠
NSLog(@"睡会");
// 从现在开始睡几秒
[NSThread sleepForTimeInterval:1.0];
for (int i = 0; i < 20; ++i) {
// 有可能满足另外一个条件后,再次进入休眠状态
if (i == 8) {
NSLog(@"再睡会");
// 睡到指定的日期 - 从现在开始间隔 2 秒
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]];
}
NSLog(@"%@ %d", [NSThread currentThread], i);
CGMutablePathRef path = CGPathCreateMutable();
// 死亡 - 满足某一个条件后,退出当前所在线程
// 一旦退出了当前线程,后续的所有代码都不会执行!
if (i == 12) {
// Invoking this method should be avoided as it does not give your thread a chance to clean up any resources it allocated during its execution.
// 调用此方法,需要注意,此方法不会给任何机会去清理线程执行过程中分配的资源
// 注意:如果使用了 C 语言框架,尤其做了分配对象的工作,需要注意在何时的位置增加 release
CGPathRelease(path);
[NSThread exit];
}
CGPathRelease(path);
}
NSLog(@"结束了!");
*/
- (void)run {
// 如果满足某一个条件,可以让线程进入"阻塞"状态
// 休眠 - 让当前线程休眠
NSLog(@"睡会");
// 从现在开始睡几秒
[NSThread sleepForTimeInterval:1.0];
for (int i = 0; i < 20; ++i) {
// 有可能满足另外一个条件后,再次进入休眠状态
if (i == 8) {
NSLog(@"再睡会");
// 睡到指定的日期 - 从现在开始间隔 2 秒
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]];
}
NSLog(@"%@ %d", [NSThread currentThread], i);
CGMutablePathRef path = CGPathCreateMutable();
// 死亡 - 满足某一个条件后,退出当前所在线程
// 一旦退出了当前线程,后续的所有代码都不会执行!
if (i == 12) {
// Invoking this method should be avoided as it does not give your thread a chance to clean up any resources it allocated during its execution.
// 调用此方法,需要注意,此方法不会给任何机会去清理线程执行过程中分配的资源
// 注意:如果使用了 C 语言框架,尤其做了分配对象的工作,需要注意在何时的位置增加 release
CGPathRelease(path);
[NSThread exit];
}
CGPathRelease(path);
}
NSLog(@"结束了!");
}
六.原子属性
nonatomic 飞原子属性
atomic 原子属性 是默认属性
* 是在多线程开发是,抱着多个线程的写入的时候,能够保证只有一条线程执行写入操作
* 是一个单(线程)写多(线程)读的多线程技术
* 原子属性 解决不了卖票问题,因为卖票的读写都需要锁定
* 有可能会出现”脏数据"重新读书一下就可以了
* 原子属性内部也有一把锁
* 原子属性的锁的性能比互斥锁高
自旋锁 & 互斥锁
共同点:
都市保证同一时间,只有一条线程能够执行锁定范围的代码
区别:
互斥锁:如果发现代码已经被(其他线程)锁定,当前线程会进入休眠状态,等锁解除之后,重新被唤醒
想当于出了可调度缓冲池
自旋锁:如果发现代码已经被(其他线程)锁定 当前线程会进入死循环的方法,一直判断是否解除,一旦解除立即执行, 适合锁定非常短的代码,能够保持更高的执行性能,不会离开可调度缓冲池
提示:互斥锁在开发中,要尽量少用,性能很差
结论:只要用到锁,性能都高不了
*线程安全
如果一个属性,在多个线程执行的情况下,仍然能够保证得到正确的结果,就叫做线程安全
要实现线程安全,就必须要使用”锁”->性能不好!
UI 线程 -> 约定: 主线程,所有UI更新,都需要在主线程上执行
原因:UIKit不是线程安全,就是为了更高的性能
取舍!!!!!
-----------------------------------------------------------------------------------------------------------------------------------------------------
模拟原子属性的实现 - 如果重写了原子属性的setter方法,想当于覆盖了系统提供的setter方法
此时系统要求,必须重写getter方法
定义属性是,系统默认提供getter&setter方法 并且生成一个 _成员变量
但是如果自己重写了getter & setter 方法 _成员变量不会自己生成
@synthesize 合成指令 可以指令保存属性数值的成员变量
现在这个方法太古老了,遇到的代码不要看了
@synthesize
obj2 = _obj2;
- (void)setObj2:(NSObject *)obj2 {
// 使用互斥锁
@synchronized(self) {
_obj2 = obj2;
}
}
- (NSObject *)obj2 {
return _obj2;
}
- (void)viewDidLoad {
[super viewDidLoad];
NSData *data = nil;
// 写入文件的原子性!先写入到一个临时文件,所有写入完成后,再一次写入目标文件!
// 能够对文件的写入进行保护
// [data writeToFile:@"path" atomically:YES];
}
// 测试自旋锁&互斥锁的效率!
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
long largeNumber = 1000 * 1000;
// 自旋锁
// 2001-01-01 00:00:00 到现在的秒数
CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
for (long i = 0; i < largeNumber; ++i) {
self.obj3 = [[NSObject alloc] init];
}
CFAbsoluteTime end = CFAbsoluteTimeGetCurrent();
NSLog(@"自旋锁 %f", end - start);
// 互斥锁
// 2001-01-01 00:00:00 到现在的秒数
start = CFAbsoluteTimeGetCurrent();
for (long i = 0; i < largeNumber; ++i) {
self.obj2 = [[NSObject alloc] init];
}
end = CFAbsoluteTimeGetCurrent();
NSLog(@"互斥锁 %f", end - start);
}
- (void)setObj2:(NSObject *)obj2 {
// 使用互斥锁
@synchronized(self) {
_obj2 = obj2;
}
}
- (NSObject *)obj2 {
return _obj2;
}
- (void)viewDidLoad {
[super viewDidLoad];
NSData *data = nil;
// 写入文件的原子性!先写入到一个临时文件,所有写入完成后,再一次写入目标文件!
// 能够对文件的写入进行保护
// [data writeToFile:@"path" atomically:YES];
}
// 测试自旋锁&互斥锁的效率!
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
long largeNumber = 1000 * 1000;
// 自旋锁
// 2001-01-01 00:00:00 到现在的秒数
CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
for (long i = 0; i < largeNumber; ++i) {
self.obj3 = [[NSObject alloc] init];
}
CFAbsoluteTime end = CFAbsoluteTimeGetCurrent();
NSLog(@"自旋锁 %f", end - start);
// 互斥锁
// 2001-01-01 00:00:00 到现在的秒数
start = CFAbsoluteTimeGetCurrent();
for (long i = 0; i < largeNumber; ++i) {
self.obj2 = [[NSObject alloc] init];
}
end = CFAbsoluteTimeGetCurrent();
NSLog(@"互斥锁 %f", end - start);
}
七:线程的属性
提示:线程栈区大小都市512k 但是可以修改!!
在以前的ios办法,主线程栈区1m 子线程是512k 而且不能修改
NSThread *t1 = [[NSThread alloc] initWithTarget:self selector:@selector(demo)object:nil];
// 线程的名称,通常在"大的商业应用"中,希望应用程序上架后,捕获到用户使用崩溃的一些场景
// 如果知道程序是在哪一个线程中崩溃,能够辅助排查问题!
// 如果线程非常多,而且在调试的时候,起到一定的辅助作用!
// 线程的名称,通常在"大的商业应用"中,希望应用程序上架后,捕获到用户使用崩溃的一些场景
// 如果知道程序是在哪一个线程中崩溃,能够辅助排查问题!
// 如果线程非常多,而且在调试的时候,起到一定的辅助作用!
t1.name = @"Thread AAA";
- (void)demo {
for (int i = 0; i < 10; ++i) {
// %tu 是可以针对不同的 CPU 架构,自动调整 无符号整数的长度 NSUInteger
// %zd 是可以针对不同的 CPU 架构,自动调整 有符号整数的长度 NSInteger
NSLog(@"%@ %d %tuK", [NSThread currentThread], i, [NSThreadcurrentThread].stackSize / 1024);
for (int i = 0; i < 10; ++i) {
// %tu 是可以针对不同的 CPU 架构,自动调整 无符号整数的长度 NSUInteger
// %zd 是可以针对不同的 CPU 架构,自动调整 有符号整数的长度 NSInteger
NSLog(@"%@ %d %tuK", [NSThread currentThread], i, [NSThreadcurrentThread].stackSize / 1024);
}
The thread’s priority, which is specified by a floating point number from 0.0 to 1.0, where 1.0 is highest priority.
线程的优先级,取值范围
0~1.0
,
1.0
的优先级最高
“typical” thread priority might be 0.5
默认的线程优先级是
0.5
t1.threadPriority = 0;
优先级搞只能说明cpu在调度的时候,会优先去调度,并不意味着,优先极地的就不被调度
在多线程开发的时候,有几点提示:
1.在ios开发中,多线程的最主要的母的:就是把耗时的操作放在后台运行
//从现在开始的描述
CFAbsoluteTime
start =
CFAbsoluteTimeGetCurrent
();