多线程的优缺点:
优点
1. 能适当提高程序的执行效率
2. 能适当提高资源利用率(CPU,内存利用率)
缺点
1. 创建线程是有开销的,iOS下主要成本包括:内核数据结构(大约1KB),栈空间(子线程512KB,主线程1MB,也可以使用-setStackSize;设置),但必须是4K的倍数,而且最小是16K,创建线程大约需要90毫秒的创建时间。
2. 如果开启大量的线程,会降低程序的性能
3. 线程越多,CPU在调度线程上的开销越大(手机发烫,耗电增多)
多线程在iOS开发中的应用
什么是主线程?
- 一个iOS程序运行后,默认会开启1条线程,成为“主线程”或“UI线程”
主线程的主要作用
- 显示|刷新UI界面
- 处理UI事件(比如点击事件,滚动事件,拖拽事件等)
主线程的使用注意
- 别将比较耗时的操作放在主线程中
- 耗时操作会卡住主线程,严重影响UI的流程度,给用户一种”卡”的坏体验
iOS中多线程的实现方案
pthread的简单使用
#import "ViewController.h"
#import <pthread.h>
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[self pthreadDemo];
}
-(void)pthreadDemo{
//注意:多线程开发中,千万不要相信一次的执行结果!!!
/*
pthread 是属于 POSIX 多线程开发框架
参数:
1.指向线程代号的指针
2.线程的属性
3.指向函数的指针
4.传递给该函数的参数
返回值
-如果是0,表示正确
-如果非0,表示错误代码
void* (*)(void *)
返回值 (函数指针) (参数)
void* 和OC中的id是等价的!
*/
NSString * str = @"hello wt";
pthread_t threadId;
/*
-在 ARC 开发中,如果涉及到和C语言中的相同的数据类型进行转换,需要使用__bridge "桥接"
-在 MRC 不需要
*/
int result = pthread_create(&threadId, NULL,&demo, (__bridge void *)(str));
if(result == 0){
NSLog(@"OK");
}else{
NSLog(@"error %d",result);
}
}
//定义一个函数
void * demo(void * param){
NSLog(@"%@ %@",[NSThread currentThread],param);
return NULL;
}
@end
NSThread
#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)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[self threadDemo3];
}
-(void)threadDemo3{
//number=1
NSLog(@"A---%@",[NSThread currentThread]);
//InBackground 就是在后台(子线程)运行!!
//是NSObject的分类,意味着所有的继承NSObject的都可以使用这个方法
//非常方便,不用NSThread对象
[self performSelectorInBackground:@selector(demo:) withObject:@"background"];
//number=1
NSLog(@"B---%@",[NSThread currentThread]);
}
-(void)threadDemo2{
//number=1
NSLog(@"A---%@",[NSThread currentThread]);
//detach => 分离(立刻启动子线程)
[NSThread detachNewThreadSelector:@selector(demo:) toTarget:self withObject:@"Detach"];
//number=1
NSLog(@"B---%@",[NSThread currentThread]);
}
-(void)threadDemo1{
NSLog(@"A--------------");
//创建一个NSThread
NSThread * thread =[[NSThread alloc]initWithTarget:self selector:@selector(demo:) object:@"Thread"];
//启动线程
[thread start];
for (int i=0; i<10; i++) {
NSLog(@"%d",i);
}
NSLog(@"B--------------");
}
-(void)demo:(id)obj{
for (int i=0; i<2; i++) {
//number !=1
NSLog(@"C--------------%@",[NSThread currentThread]);
}
}
@end
多线程加载网络图片
#import "ViewController.h"
@interface ViewController ()<UIScrollViewDelegate>
@property(nonatomic,strong)UIScrollView * scrollView;
@property(nonatomic,weak)UIImageView *imageView;
@property(nonatomic,strong)UIImage * image;
/**
问题一.为什么scrollView用strong修饰?imageView用week?
*/
@end
@implementation ViewController
/**
加载视图结构,纯代码开发
功能 SB&XIB 是一样的
如果重写了这个方法,SB和XIB都无效
*/
-(void)loadView{
//搭建界面
self.scrollView =[[UIScrollView alloc]initWithFrame:[UIScreen mainScreen].bounds];
self.view =self.scrollView;
//MARK:-设置缩放属性
self.scrollView.delegate = self;
self.scrollView.minimumZoomScale = 0.5;
self.scrollView.maximumZoomScale = 2.0;
//imageView
UIImageView * iv =[[UIImageView alloc]init];
//会调用view的getter方法。loadView方法在执行的过程中,如果self.view == nil,会自动调用loadView加载
[self.view addSubview:iv];
self.imageView = iv;
}
/**
视图加载完毕
一般做初始化工作
*/
- (void)viewDidLoad {
[super viewDidLoad];
// [self downloadImage];
NSThread * t1 =[[NSThread alloc]initWithTarget:self selector:@selector(downloadImage) object:nil];
[t1 start];
}
//MARK: - 下载图片
-(void)downloadImage{
NSLog(@"%@",[NSThread currentThread]);
//NSUTL -> 统一资源定位符,每一个URL,对应着一个网络资源
NSURL * url =[NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1525326853527&di=56bda83da629c944572273eb3793ede9&imgtype=0&src=http%3A%2F%2Fscimg.jb51.net%2Fallimg%2F170209%2F106-1F20916102V08.jpg"];
//下载图片(在网络上传输的所有数据都是二进制)
//为什么是二进制:因为物理层是网线,网线里面是电流,电流有高低电频,高低电频表示二进制.数字信号转模拟信号转电信号
NSData * data =[NSData dataWithContentsOfURL:url];
//将二进制数据转成图片并且设置图片
//提示:不是所有的更新UI在后台线程支持都会有问题
//重点提示:不要去尝试在后台线程更新UI,出了问题是非常诡异的
// self.image =[UIImage imageWithData:data];
//在UI线程去更新UI
/**
*1.SEL:在主线程执行的方法
*2.传递给方法的参数
*3.是否让当前线程等待(注意:如果当前线程是主线程,YES没有用)
*/
//线程间通信
[self performSelectorOnMainThread:@selector(setImage:) withObject:[UIImage imageWithData:data] waitUntilDone:NO];
NSLog(@"come here");
}
-(void)setImage:(UIImage *)image{
_image =image;
NSLog(@"更新UI在%@",[NSThread currentThread]);
//直接将图片设置到控件上
self.imageView.image = image;
//让imageView和image一样大
[self.imageView sizeToFit];
//指定scrollView的contentSize
self.scrollView.contentSize=image.size;
}
#pragma mark - <UIScrolView代理>
//告诉ScrollView 缩放哪个View
-(UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView{
return self.imageView;
}
/** 二维空间中, 平面中,表示一个集合图形 x y width height
*transform 矩阵
*struct CGAffineTransform {
CGFloat a(缩放比例), b, c, d(缩放比例);
CGFloat tx(位移), ty(位移);
};
*/
-(void)scrollViewDidZoom:(UIScrollView *)scrollView{
NSLog(@"%@",NSStringFromCGAffineTransform(self.imageView.transform));
}
@end