iOS开发之多线程技术(NSThread、OperationQueue、GCD)

简介:

 在前面的博客中如果用到了异步请求的话,也是用到的第三方的东西,没有正儿八经的用过iOS中多线程的东西。其实多线程的东西还是蛮重要的,如果对于之前学过操作系统的小伙伴来说,理解多线程的东西还是比较容易的,今天就做一个小的demo来详细的了解一下iOS中的多线程的东西。可能下面的东西会比较枯燥,但还是比较实用的。

  多线程用的还是比较多的,废话少说了,下面的两张截图是今天我们实验的最终结果,应该是比较全的,小伙伴们由图来分析具体的功能吧:

  功能说明:

    1、点击同步请求图片,观察整个UI界面的变化,并点击测试按钮,红色是否会变成绿色。  

    2、NSThread按钮,是由NSThread方式创建线程并执行相应的操作。

    3、Block操作按钮是用Block创建操作,并在操作队列中执行,下面的是Invocation操作

    4、serial是GCD中的串行队列,concurrent是GCD中的并行队列

  好啦,上面的咸蛋先到这儿,代码该走起啦。

  一、准备阶段

     1.不管使用代码写,还是storyboard或者xib等,先把上面所需的控件初始化好以便使用

     2.点击测试UI按钮,改变下边label的颜色的代码如下:

//改变lable的颜色,在红绿颜色之间进行交换
- (IBAction)tapTestButton:(id)sender {
    static int i = 1;
    if (i == 1) {
        _testLabel.backgroundColor = [UIColor redColor];
        i = 0;
    }
    else
    {
        _testLabel.backgroundColor = [UIColor greenColor];
        i = 1;
    }
    
}

 

    3.从网络上获取图片,并使用主线程显示进程调用情况

//从wang'lu获取图片数据
-(NSData *) getImageData
{
    
    _count ++;
    int count = _count;
    //线程开始启动
  NSString *str = [NSString stringWithFormat:@"%d.线程%@",count,[NSThread currentThread]];
    NSLog(@"%@",str);
    NSData *data;
    [NSThread sleepForTimeInterval:0.5];
    data = [NSData dataWithContentsOfURL:[NSURL URLWithString:IMAGEURL]];

    NSString *str = [NSString stringWithFormat:@"%d.线程%@完毕",count,[NSThread currentThread]];
    //请求数据的任务由其他线程解决,所以LogTextView的内容由主线程更新,也只有主线程才能更新UI
    [self performSelectorOnMainThread:@selector(updateTextViewWithString:) withObject:str waitUntilDone:YES];
    return data;
}

 

    4.上面的用到了主线程来调用updateTextViewWithString方法,因为只有主线程才能更新UI,updateTextViewWithString:这个方法负责把线程的执行信息显示在View上,代码如下:

//在ViewController上显示图片请求情况
-(void)updateTextViewWithString:(NSString *)str
{
    NSString *old_str = [NSString stringWithFormat:@"%@\n%@",_logTextView.text, str];
    
    _logTextView.text = old_str;
    //改变Label的颜色,便于观察
    [self tapTestButton:nil];
}

 

    5.把请求完的图片加载到ImageView上

//更新图片
-(void) updateImageWithData:(NSData *)data
{
    UIImage *image = [UIImage imageWithData:data];
    [_testImage setImage:image];
}

 

    6.加载图片的,也就是请求数据后在ImageView上显示

//由其他线程请求数据,由主线程来更新UI
-(void)loadImageWithThreadName:(NSString *)threadName
{
    [[NSThread currentThread] setName:threadName];
    
    NSData *data = [self getImageData];
    [self performSelectorOnMainThread:@selector(updateImageWithData:) withObject:data waitUntilDone:YES];
}

 

  二、通过各种方式来

    1.同步请求图片测试,请求数据和更新UI都放在主线程中顺序执行,这样在请求数据的时候UI会卡死,代码如下;

1 //同步请求图片,视图阻塞的,因为主线程被占用,无法进行视图的更新
2 - (IBAction)tapButton:(id)sender {
3     NSData *data = [self getImageData];
4     [self updateImageWithData:data];
5 }

 

    2.NSThread创建线程测试,用detachNewThreadSelector方法来创建新的线程会自动启动并执行,而不用调用start方法。代码如下:

//NSThread
- (IBAction)tapButton2:(id)sender {
    //点击一次button就创建一个新的线程来请求图片数据
    for (int i = 0;i < 10; i ++) {
        [NSThread detachNewThreadSelector:@selector(loadImageWithThreadName:) toTarget:self withObject:@"NSThread"];
    }
 }

 

   

    3.NSInvocationOperation的使用,新建一个调用操作,然后添加到队列中执行,代码如下:

//NSInvocationOperation
- (IBAction)tapInvocationOperation:(id)sender {
 
    
    //上面的调用操作需要放到调用队列里才执行的
    //创建操作队列
    NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];

    for (int i = 0;i < 10; i ++) {
        //实例化一个调用操作,来执行数据请求
        NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(loadImageWithThreadName:) object:@"Invocation"];
        //把上面的调用操作放到操作队列里,队列会自动开启一个线程调用我们指定的方法
        [operationQueue addOperation:invocationOperation];
    }
}

 

 

    4.block的操作,新建一个block操作,并添加到队列中执行,代码如下:

//BlockOperation
- (IBAction)tapBlockOperation:(id)sender {
    __weak __block ViewController *copy_self = self;
    
    //创建BlockOperation
    NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
        [copy_self loadImageWithThreadName:@"Block"];
    }];
    
    //添加到操作队列
    NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
    [operationQueue addOperation:blockOperation];
    
    for (int i = 0;i < 10; i ++) {
    
        //另一种方式
        [operationQueue addOperationWithBlock:^{
            [copy_self loadImageWithThreadName:@"Block"];
        }];
    }
}

 

 

    5.GCD中的串行队列:

//串行队列
- (IBAction)tapGCDserialQueue:(id)sender {
    //创建串行队列
    dispatch_queue_t serialQueue = dispatch_queue_create("mySerialQueue", DISPATCH_QUEUE_SERIAL);
    
    
    __weak __block ViewController *copy_self = self;
    
    
    for (int i = 0;i < 10; i ++) {
        //异步执行队列
        dispatch_async(serialQueue, ^{
            [copy_self loadImageWithThreadName:@"Serial"];
        });
    }
    
    
}

 

 

    6.GCD中的并行队列:

//并行队列
- (IBAction)tapGCDConcurrentQueue:(id)sender {
    //创建并行队列
    dispatch_queue_t concurrentQueue = dispatch_queue_create("myConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
    __weak __block ViewController *copy_self = self;
    
    for (int i = 0;i < 10; i ++) {
        //异步执行队列
        dispatch_async(concurrentQueue, ^{
            [copy_self loadImageWithThreadName:@"Concurrent"];
        });

    }
    
}

 

    以上是各个按钮对应的方法,下面的截图是执行结果:

 

  三、线程间的同步问题(为我们的线程添加上同步锁)

    在操作系统中讲多线程时有一个名词叫脏数据,就是多个线程操作同一块资源造成的,下面就修改一下代码,让数据出现问题,然后用同步锁来解决这个问题

    1.在getImageData方法(标题一中的第3个方法)中有两条语句。这个用来显示线程的标号。上面的标号是没有重复的。

1     _count ++;
2     int count = _count;

    在两条语句中间加一个延迟,如下:
    _count ++;
    [NSThread sleepForTimeInterval:1];
    int count = _count;

    如果运行的话,会有好多标号是重复的,如图一,__count是成员变量,多个线程对此他进行操作,所以会出现标号不一致的情况,下面我们加上同步锁

     (1)用NSLock加同步锁,代码如下:

//通过NSLock加锁
    [_lock lock];
    _count ++;
    [NSThread sleepForTimeInterval:1];
    int count = _count;
    [_lock unlock];

    (2)通过@synchronized加同步锁,代码如下:
//通过synchronized加锁
    int count;
    @synchronized(self){
        _count ++;
        [NSThread sleepForTimeInterval:1];
         count = _count;
    }

      加锁前后的运行效果如下:

  GCD的串行队列开始执行的顺序如下,下面是是在一个线程中按FIFO的顺序执行的:

  GCD中的并行队列,是在不同的线程中同时执行的:

相关文章
|
13天前
|
iOS开发 开发者
uniapp开发ios打包Error code = -5000 Error message: Error: certificate file(p12) import failed!报错问题如何解决
uniapp开发ios打包Error code = -5000 Error message: Error: certificate file(p12) import failed!报错问题如何解决
109 67
uniapp开发ios打包Error code = -5000 Error message: Error: certificate file(p12) import failed!报错问题如何解决
|
2月前
|
开发框架 前端开发 Android开发
安卓与iOS开发中的跨平台策略
在移动应用开发的战场上,安卓和iOS两大阵营各据一方。随着技术的演进,跨平台开发框架成为开发者的新宠,旨在实现一次编码、多平台部署的梦想。本文将探讨跨平台开发的优势与挑战,并分享实用的开发技巧,帮助开发者在安卓和iOS的世界中游刃有余。
|
1月前
|
iOS开发 开发者 MacOS
深入探索iOS开发中的SwiftUI框架
【10月更文挑战第21天】 本文将带领读者深入了解Apple最新推出的SwiftUI框架,这一革命性的用户界面构建工具为iOS开发者提供了一种声明式、高效且直观的方式来创建复杂的用户界面。通过分析SwiftUI的核心概念、主要特性以及在实际项目中的应用示例,我们将展示如何利用SwiftUI简化UI代码,提高开发效率,并保持应用程序的高性能和响应性。无论你是iOS开发的新手还是有经验的开发者,本文都将为你提供宝贵的见解和实用的指导。
127 66
|
24天前
|
存储 监控 API
app开发之安卓Android+苹果ios打包所有权限对应解释列表【长期更新】-以及默认打包自动添加权限列表和简化后的基本打包权限列表以uniapp为例-优雅草央千澈
app开发之安卓Android+苹果ios打包所有权限对应解释列表【长期更新】-以及默认打包自动添加权限列表和简化后的基本打包权限列表以uniapp为例-优雅草央千澈
|
1月前
|
开发框架 Android开发 iOS开发
安卓与iOS开发中的跨平台策略:一次编码,多平台部署
在移动应用开发的广阔天地中,安卓和iOS两大阵营各占一方。随着技术的发展,跨平台开发框架应运而生,它们承诺着“一次编码,到处运行”的便捷。本文将深入探讨跨平台开发的现状、挑战以及未来趋势,同时通过代码示例揭示跨平台工具的实际运用。
152 3
|
1月前
|
Java 调度 Android开发
安卓与iOS开发中的线程管理差异解析
在移动应用开发的广阔天地中,安卓和iOS两大平台各自拥有独特的魅力。如同东西方文化的差异,它们在处理多线程任务时也展现出不同的哲学。本文将带你穿梭于这两个平台之间,比较它们在线程管理上的核心理念、实现方式及性能考量,助你成为跨平台的编程高手。
|
2月前
|
存储 前端开发 Swift
探索iOS开发:从新手到专家的旅程
本文将带您领略iOS开发的奇妙之旅,从基础概念的理解到高级技巧的掌握,逐步深入iOS的世界。文章不仅分享技术知识,还鼓励读者在编程之路上保持好奇心和创新精神,实现个人成长与技术突破。
|
2月前
|
API Android开发 iOS开发
深入探索Android与iOS的多线程编程差异
在移动应用开发领域,多线程编程是提高应用性能和响应性的关键。本文将对比分析Android和iOS两大平台在多线程处理上的不同实现机制,探讨它们各自的优势与局限性,并通过实例展示如何在这两个平台上进行有效的多线程编程。通过深入了解这些差异,开发者可以更好地选择适合自己项目需求的技术和策略,从而优化应用的性能和用户体验。
|
2月前
|
安全 IDE Swift
探索iOS开发之旅:从初学者到专家
在这篇文章中,我们将一起踏上iOS开发的旅程,从基础概念的理解到深入掌握核心技术。无论你是编程新手还是希望提升技能的开发者,这里都有你需要的指南和启示。我们将通过实际案例和代码示例,展示如何构建一个功能齐全的iOS应用。准备好了吗?让我们一起开始吧!
|
安全 iOS开发
ios中的线程安全单例实现
我们这里重点讨论的是线程安全,而不是单例的严格实现(若需要严格实现还需要复写一些方法,更改一些实现代码): gcd实现:   static AccountManager *sharedAccountManagerInstance = nil;  + (A...
1018 0

热门文章

最新文章