iOS开发-------多线程编程

来源:互联网 发布:seo专业优化公司 编辑:程序博客网 时间:2024/05/03 14:15

       在大学里接触了一部分编程语言,如C++,Java,C#,虽然学的不是很好,但是每每提到多线程,总会感觉很高大上,毕竟学语言的一开始都不会接触到这一部分。

       需要明白的是,每一个程序不是一个线程,而是一个进程,CPU将时间片分配给进程,然后是进程在将时间片分给自己的线程,也就是说一个系统有多个进程,一个进程又有多个线程。

       学过的操作系统说过,所谓的多线程,实际就是CPU在处理各种事务的时候分成了时间片,时间片的间隔时机极短,就是说人根本感觉不出来,所以会认为是多线程,也就是并发操作。实际还是按照系统内部的非分配方法来操作的。

       为什么要使用多线程呢,比如当某个任务需要长时间运行,但是如果只是单线程,那么后面的程序就要等到它执行完毕之后才可以运行,这样就浪费了很多的事件,也会造成用户体验的不爽。

  Objective-C中的多线程,一般讲的时候都是分3类,楼主学的时候也是,但是学完之后会觉得还是GCD比较好用,现在看来比较高大上,通过一个简单的小例子来看,会简单去多,那么依旧采用之前的风格,通过实例来说明东西。


      例子就是说,通过点击一个按钮,将进度条动态的前进。为了方便演示(demonstrate),楼主用stroyboard来布局。



NSThread(线程类)

通过名字来看,直接了当,就是一个线程的类,那么首先来了解一下NSThread的基本用法
    /**     *  创建一个线程,是一个目标动作回调的方法     *     *  @param object   表示需要传入的参数是什么,一般指线程的名字     */    NSThread * thread = [[NSThread alloc]initWithTarget:self selector:@selector(test1) object:nil];        //越高表示cpu的时间优先分配,0 ~ 1,并不是说越高就立马执行    thread.threadPriority = 1;        //线程休眠时间为1秒,表示让出主线程队列的位置,休眠结束后,继续在主线程的队列排队    [NSThread sleepForTimeInterval:1];        //进入就绪队列,但不代表能够立即执行,需要在线程队列中排队    [thread start];

如果突然看到一个[NSThread sleepForTimeInterval:1]的方法,是不是貌似有一个实现的灵感呢,会觉得没有什么难度,用这个方法是没错的,是不是如下呢

首先在button的点击方法中创建一个线程
//start按钮被点击时- (IBAction)startButtonClick:(id)sender{    //创建一个线程    NSThread * thread1 = [[NSThread alloc]initWithTarget:self selector:@selector(threadFun) object:nil];        //添加到就绪队列    [thread1 start];}

接下来完成线程的回调方法
//子线程执行的方法-(void)threadFun{    for (NSInteger i = 0; i <= 100; i++)    {        //休眠0.01秒        [NSThread sleepForTimeInterval:0.02];        [self updateProgress];    }}

更新进度条的方法如下
//progress更新的办法-(void)updateProgress{    self.progressView.progress += 0.01;}

那么看一下结果吧,是不是想象的那么样呢



结果恐怕不是之前想的那样,这样子是在2秒后突然进度条就满了,原因呢:

1、progressView是在主线程中创建的,所以子线程没有权利修改,即使有的时候可以改,但是一般是错误的。
2、在子线程中执行的方法,那么这个方法就是子线程的。
3、最本质的原因,就是在runloop中,当按钮点击了之后,他的首响应是button,无暇顾及progress进度,只有当button的回调方法结束后,才可以管progressView,但是那个时候progress早已经运行完了,所以2秒后突然进度条满了。

怎么修改呢,只需要子线程执行的方法中改一句话即可
//子线程执行的方法-(void)threadFun{    for (NSInteger i = 0; i <= 100; i++)    {        //休眠0.01秒        [NSThread sleepForTimeInterval:0.02];                //在主线程执行该方法         [self performSelectorOnMainThread:@selector(updateProgress) withObject:nil waitUntilDone:NO];//waitUntilDone 表示下面如果还有语句的话,为YES表示必须执行完这个方法之后才能运行下面的语句    }}

这样用NSThread就完成了。


NSOperation(操作类)

      NSOperation(操作类)楼主感觉比较麻烦,但是又不能不知道,所以也按照自己的理解来描述一下,如果不对,也请及时告诉我,不要继续误导其他人
      
      首先用到这个类,要写一个类继承于NSOoeration,并且要重写它的 main( ) 方法,这个方法不同于C语言的main方法,它仅仅是一个方法的名字,只不过当operation执行的时候会自动的调用这个main方法。
  
      然后就是有的特点和NSThread有点相似,都是将进程或者操作放入系统的队列中进行排队,比如下面是button点击时候的回调方法
//start按钮点击的回调- (IBAction)buttonPressed:(id)sender{    //判断是否已经存在    if (!self.operation)    {        //创建一个operation操作类        self.operation = [[MyOperation alloc]init];    }        //创建一个操作队列    NSOperationQueue * queue = [[NSOperationQueue alloc]init];        //如果没有完成这个操作,也就是说一旦开始,就不能将进程杀死,就要等他执行完毕才可    if ([self.operation isFinished] == NO)     {        //将操作放入操作队列        [queue addOperation:self.operation];//运行的时候自动调用MyOperation的main方法,必须是main方法    }  }

       因为之前也提到过,子线程中是不能够改变主线程创建的组件的属性的,那么如何在子线程中告诉主线程呢,首先就想到了回调,但是楼主技术不达标,所以最熟悉的Block回调不能解决相关问题,所以选择了委托回调的方式。

委托模式必须要有协议,那么协议如下
@protocol MyOperationDelegate <NSObject>@optional/** *  回调返回的进度增量 * *  @param myOperation 当前的操作类对象 *  @param i           进度条的增量 */-(void)myOperation:(MyOperation *)myOperation updateProess:(CGFloat)i;@end

接着就开始继承于NSOperation(操作类)的自定义类MyOperation
////  MyOperation.h//  多线程 NSOperation////  Created by YueWen on 15/10/1.//  Copyright (c) 2015年 YueWen. All rights reserved.//#import <Foundation/Foundation.h>#import <UIKit/UIKit.h>@protocol MyOperationDelegate;@interface MyOperation : NSOperation//履行协议的归零弱引用的代理对象@property(nonatomic,weak)id<MyOperationDelegate> delegate;@end

因为说过用NSOperation,要重写main()方法,重写如下
-(void)main{    for (NSInteger i = 0; i <= 100; i++)    {        //休眠0.02秒        [NSThread sleepForTimeInterval:0.02];                //判断代理是否有这个方法        if ([self.delegate respondsToSelector:@selector(myOperation:updateProess:)])        {            //用主线程执行代理的委托方法            [self performSelectorOnMainThread:@selector(updateProessd) withObject:nil waitUntilDone:NO];        }    }}

调用代理的方法
-(void)updateProessd{    [self.delegate myOperation:self updateProess:0.01];}

最后是viewController了,不要忘记设置代理啊,楼主表示经常忘,导致没有相关的效果
//设置代理self.operation.delegate = self;

履行的协议方法
#pragma mark - MyOperation Delegate//履行协议方法-(void)myOperation:(MyOperation *)myOperation updateProess:(CGFloat)i{    self.progressView.progress += i;}

来看看效果图吧



GCD    (Grand Gentral Dispatch   中央处理)

       上面的两种方法,第一种NSThread类,好理解,但是感觉不是高大上,第二种NSOperation类,有感觉繁琐,因为用到了回调,那有没有一种代码简介又显得高大上的方法呢,自然是有的,就是这里说的GCD(又中央来对线程进行派发处理)

第一看会觉得很麻烦,因为出现了许多的‘_’,所以会有这种感觉,首先分开了解一下

//首先获得一个派发队列,queue即为获得的派发队列,是个单例,后面的参数...(楼主表示不懂,但是都是这么写的0.0),是一个C语言类型,不带*dispatch_queue_attr_t queue = dispatch_get_global_queue(0, 0);

接着就是在队列中添加异步操作了,即子线程的任务
//在派发队列上放置一个子线程的异步操作dispatch_async(myQueue, ^{         NSLog(@"我是子线程的异步操作");        });

如何获得主线程,来进行操作呢,毕竟progressView的修改是在主线程中进行修改的
//获得主线程dispatch_async(dispatch_get_main_queue(), ^{                //主线程的操作     NSLog(@"我是主线程的操作");            });

大体用到的语法就是如此,但是还需要认识一个小小的区别
    dispatch_async(dispatch_queue_t queue, ^(void)block);//表示不需要等待上面的代码执行完毕    dispatch_sync(dispatch_queue_t queue, ^(void)block);//表示必须等待上面的代码执行完毕才可执行,有点同步的意思,区别就和方法中的waitUntilDo相似


接下来就是完成上述功能了,其他的操作无异,依旧用storyboard的拖入控件实现
//按钮点击时的回调方法- (IBAction)startButtonClick:(id)sender{    [self updateProgress];}

接着是按钮点击后执行的方法
//更新进程-(void)updateProgress{    //首先获得一个派发队列,queue即为获得的派发队列,是个单例,后面的参数...(楼主表示不懂,但是都是这么写的0.0)    dispatch_queue_attr_t myQueue = dispatch_get_global_queue(0, 0);        //在派发队列上放置一个子线程的异步操作    dispatch_async(myQueue, ^{                for (int i = 0; i <= 100; i++)        {            //休眠0.02秒            [NSThread sleepForTimeInterval:0.02];                        //获得主线程            dispatch_async(dispatch_get_main_queue(), ^{                //主线程的操作                self.progressView.progress += 0.01;            });        }            });}

这样子看起来是不是怪怪的,那么组合起来用会显得更加高大上,如下
//更新进程-(void)updateProgress{    //在派发队列中添加子线程异步操作    dispatch_async(dispatch_get_global_queue(0, 0), ^{                for (int i = 0; i <= 100; i++)        {            //进程休眠0.02秒            [NSThread sleepForTimeInterval:0.02];                        //主线程修改进度条            dispatch_async(dispatch_get_main_queue(), ^{                               //修改进度条                self.progressView.progress += 0.01;                            });        }            });    }

看一下效果吧

以上看法以及用法属于个人理解,如果有错误,请指正,避免误导其他人,3Q




0 0