工作生活

深入浅出iOS多线程(二)——pthraed和NSThread的

2019-07-02  本文已影响0人  struggle3g

深入浅出iOS多线程(一)——线程的概念
深入浅出iOS多线程(二)——pthraed和NSThread的使用
深入浅出iOS多线程(三)——GCD多线程
深入浅出iOS多线程(四)——NSOperation多线程
深入浅出iOS多线程(五)——多线程锁

pthread

pthread简介

pthread 是属于 POSIX 多线程开发框架,POSIX表示可移植操作系统接口(Portable Operating System Interface of UNIX,缩写为 POSIX ),如果想学习这套API,在网上是可以找到相关的资料等,由于在iOS中有NSThrad,如果不考虑移植性,那么在iOS开发中基本上不回去使用,所以只是了解,pthread是多线程的一种技术实现。

iOS中的pthread

在iOS中需要导入头文件pthread.h才能够使用pthrad的Api

#import <pthread.h>

pthraed的特点

pthread的简单使用

/**
 参数:
 1.指向线程标示的指针
 2.线程的属性
 3.指向函数的指针
 4.传递给该函数的参数
 
 返回值
 - 如果是0,标示正确
 - 如果非0,标示错误代码
 
 void *   (*)      (void *)
 返回值   (函数指针)  (参数)
 void *  和OC中的  id 是等价的!
 
 */
    
pthread_t pthreadId ;
    
NSString *str = @"敲代码";
    
int result = pthread_create(&pthreadId, 
                                  NULL, 
                                &doing, 
                 (__bridge void *)(str)
                           );
    
if(result == 0){
    NSLog(@"开启成功");
}else{
    NSLog(@"开启失败");
}


void * doing(void * param){
    
    NSLog(@"%@,%@",[NSThread currentThread],param);
    return NULL;
}

NSThread

iOS的多线程NSThread简介

NSThread是苹果官方提供面向对象操作线程的技术,简单方便,可以直接操作对象,需要手动控制线程的生命周期,平时iOS开发较少使用,使用最多的是获取当前线程

NSThread特点

NSThread的详细使用介绍

如何开启NSThread线程

NSThread初始化API

//初始化的API
- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument 
- (instancetype)initWithBlock:(void (^)(void))block 

//类对象方法
+ (void)detachNewThreadWithBlock:(void (^)(void))block 
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument;

上述实例方法以及类对象方法,一样都是创建一个新的线程,不一样的是,类对象方法不需要创建完成以后调用start方法,而alloc创建的线程需要手动start开启。

NSThread代码实现

//方法一:
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(demo1Doing:) object:@"hello"];
[thread start];
    
//方法二:
NSThread *thread1 = [[NSThread alloc]initWithBlock:^{
    NSLog(@"%s",__func__);
}];
[thread1 start];
    
//方法三:
[NSThread detachNewThreadSelector:@selector(demo1Doing:) toTarget:self withObject:@"hello"];
    
//方法四
[NSThread detachNewThreadWithBlock:^{
    NSLog(@"%s",__func__);
}];

NSThread主线程的API和获取主线程

其他创建线程的方式

总结

上述两种创建线程方式的优缺点:

控制线程状态

线程的优先级

多线程的安全隐患问题

安全隐患?

安全隐患问题分析

如何解决多线程的安全隐患(线程锁)

互斥锁

互斥锁需要注意的地方

如何解决多线程的安全隐患(原子与非原子对象)

nonatomic 非原子属性

atomic 原子属性

注意一个小细节

NSThread自定义

在NSThread的有init初始化方法:

//用alloc init 适用于自定义NSThread (子类)
NSThread * t = [[NSThread alloc]init];
需要创建一个新的子类继承NSThread方法,然后重写main方法

多线程下载网络图片

-(void)loadView{
}

如果重新了上述方法,SB和XIB都无效

代码如下:

#import "ViewController.h"

@interface ViewController ()<UIScrollViewDelegate>
@property(nonatomic,strong)UIScrollView * scrollView;
@property(nonatomic,weak) UIImageView * imageView;
@property(nonatomic,strong) UIImage * image;
@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.1;
    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];
    // Do any additional setup after loading the view.

    NSThread * t1 = [[NSThread alloc]initWithTarget:self selector:@selector(downloadImage) object:nil];
    [t1 start];
    

}

//MARK: - 下载图片
-(void)downloadImage{
    
    NSLog(@"%@",[NSThread currentThread]);
    
    //NSURL -> 统一资源定位符,每一个URL 对应一个网络资源!
    NSURL * url = [NSURL URLWithString:@"https://images.unsplash.com/photo-1496840220025-4cbde0b9df65?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2734&q=80"];
    
    //下载图片(在网络上传输的所有数据都是二进制!!)
    //为什么是二进制:因为物理层!!是网线!!网线里面是电流!!电流有高低电频!!高低电频表示二进制!!!
    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];
    
    
    
}


//这种写法 省略一个 _image ,主要原因是因为image 保存在了imageView里面了!
-(UIImage *)image{
    return self.imageView.image;
}


-(void)setImage:(UIImage *)image{
    NSLog(@"更新 UI 在====%@",[NSThread currentThread]);
    //直接将图片设置到控件上
    self.imageView.image = image;
    //让imageView和image一样大
    [self.imageView sizeToFit];
    //指定ScrollView 的contentSize
    self.scrollView.contentSize = image.size;
    
    NSLog(@"\n\n\n\n\n\n\n\n\n\n\n%@",self.image);
}

#pragma mark - <scrollView代理>
//告诉 ScrollView 缩放哪个View
-(UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView{
    return self.imageView;
}

/**
 * transform 矩阵
 *  CGFloat a(缩放比例), b, c, d(缩放比例);  共同决定角度!
 *  CGFloat tx(x方向位移), ty(y方向的位移);
 
 *
 */
-(void)scrollViewDidZoom:(UIScrollView *)scrollView
{
    NSLog(@"%@",NSStringFromCGAffineTransform(self.imageView.transform));
}

@end

线程间的通信

@interface NSObject (NSThreadPerformAdditions)
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg;
@end

NSPort实现线程通信

代码如下:

@interface ViewController () <NSPortDelegate>
@property (nonatomic, strong) NSPort* subThreadPort;
@property (nonatomic, strong) NSPort* mainThreadPort;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.mainThreadPort = [NSPort port];
    self.mainThreadPort.delegate = self;
    [[NSRunLoop currentRunLoop] addPort:self.mainThreadPort forMode:NSDefaultRunLoopMode];
    [self task];
}

- (void) task {
    NSThread* thread = [[NSThread alloc] initWithBlock:^{
        self.subThreadPort = [NSPort port];
        self.subThreadPort.delegate = self;
        
        [[NSRunLoop currentRunLoop] addPort:self.subThreadPort forMode:NSDefaultRunLoopMode];
        [[NSRunLoop currentRunLoop] run];
    }];
    [thread setName:@"子线程"];
    [thread start];
}

- (void)handlePortMessage:(id)message {
    NSLog(@"%@", [NSThread currentThread]);
    
    if (![[NSThread currentThread] isMainThread]) {
        NSMutableArray* sendComponents = [NSMutableArray array];
        NSData* data = [@"world" dataUsingEncoding:NSUTF8StringEncoding];
        [sendComponents addObject:data];
        [self.mainThreadPort sendBeforeDate:[NSDate date] components:sendComponents from:self.subThreadPort reserved:0];
        return;
    }
    sleep(2);
    NSMutableArray* components = [message valueForKey:@"components"];
    
    if ([components count] > 0) {
        NSData* data = [components objectAtIndex:0];
        NSString* str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        NSLog(@"%@", str);
    }

}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
    NSMutableArray* components = [NSMutableArray array];
    NSData* data = [@"hello" dataUsingEncoding:NSUTF8StringEncoding];
    [components addObject:data];
    
    [self.subThreadPort sendBeforeDate:[NSDate date] components:components from:self.mainThreadPort reserved:0];
}

@end

NSThread需要注意的地方

子线程执行太快,还会调用线程通信的代码吗?

不会
有时候会出现这个问题,代码如下:

    NSThread * t1 = [[NSThread alloc]initWithTarget:self selector:@selector(demo) object:nil];
    [t1 start];
    //不执行地方原因,是因为 demo 方法执行的快!""
    [self performSelector:@selector(otherMethod) onThread:t1 withObject:nil waitUntilDone:NO];
    
-(void)demo{
    NSLog(@"%@",[NSThread currentThread]);
}
-(void)otherMethod{

    self.finished = YES;
}

如何解决上述问题

在子线程开启RunLoop,

    NSThread * t1 = [[NSThread alloc]initWithTarget:self selector:@selector(demo) object:nil];
    [t1 start];
    //不执行地方原因,是因为 demo 方法执行的快!""
    [self performSelector:@selector(otherMethod) onThread:t1 withObject:nil waitUntilDone:NO];
    
-(void)demo{
    NSLog(@"%@",[NSThread currentThread]);
   [[NSRunLoop currentRunLoop] run];
}
-(void)otherMethod{
    NSLog(@"%s %@",__FUNCTION__,[NSThread currentThread]);
}
@interface ViewController ()
/** 循环条件 */
@property(assign,nonatomic,getter=isFinished)BOOL finished;
@end

@implementation ViewController


    NSThread * t1 = [[NSThread alloc]initWithTarget:self selector:@selector(demo) object:nil];
    [t1 start];
    
    self.finished = NO;
    
    //不执行地方原因,是因为 demo 方法执行的快!""
    [self performSelector:@selector(otherMethod) onThread:t1 withObject:nil waitUntilDone:NO];

-(void)demo{
    NSLog(@"%@",[NSThread currentThread]);
    //启动当前RunLoop  哥么就是一个死循环!!
    //使用这种方式,可以自己创建一个线程池!
    //    [[NSRunLoop currentRunLoop] run];
    
    //在OC中使用比较多的,退出循环的方式!
    while (!self.isFinished) {
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
    }
}
-(void)otherMethod{
    for (int i = 0; i<10; i++) {
        
        NSLog(@"%s %@",__FUNCTION__,[NSThread currentThread]);
    }
    self.finished = YES;
}

上一篇下一篇

猜你喜欢

热点阅读