zoukankan      html  css  js  c++  java
  • iOS开发-多线程知识

    多线程方案


    创建线程一般不应超过5条(包括本来存在的主线程)即非主线程不应超过4条

    NSThread

    1、创建线程

    // 方式1,创建线程,设置,启动线程,如果调用的方法需要参数,可以用object这个参数传参
        NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(download) object:nil];
        thread.name = @"下载东西";
        [thread start];
        // 方式2,创建线程后自启动线程
        [NSThread detachNewThreadSelector:@selector(download) toTarget:self withObject:nil];
        
        // 方式3,隐式创建并启动线程
        [self performSelectorInBackground:@selector(download) withObject:nil];
        // 后面两种方式优点在于简单快捷,缺点就是没法做详细设置,如设置name

    2、获得当前线程

    - (void)download
    {
        // 打印当前所在的线程,会显示name和线程标号,1为主线程,其他为子线程
        NSLog(@"---%@---",[NSThread currentThread]);
    }

    3、判断线程是否主线程

    + (NSThread *)mainThread; // 获得主线程
    - (BOOL)isMainThread; // 是否为主线程
    + (BOOL)isMainThread; // 是否为主线程

    控制线程的方法

    // 进入就绪状态 -> 运行状态,当线程任务执行完毕,自动进入死亡状态
    - (void)start;
    
    // 进入阻塞状态
    1.直到某刻
    + (void)sleepUntilDate:(NSDate *)date;
    2.几秒之后
    + (void)sleepForTimeInterval:(NSTimeInterval)ti;
    
    // 进入死亡状态
    + (void)exit;

    多线程的安全隐患

    当多个线程访问同一块资源的时候会出现数据错乱的问题,所以使用多线程会存在安全隐患。
    解决方法:使用互斥锁
    互斥锁格式:(※锁对象可以使任何对象,但是不要频繁创建,否则浪费资源;※一份代码对应一把锁)

    @synchronized(锁对象) { // 需要锁定的代码  }

    其实互斥锁的本质就是让线程同步,使线程按顺序执行。

    优点:能有效防止因多线程抢夺资源造成的数据安全问题

    缺点:需要消耗大量的CPU资源

    原子和非原子属性

    原子属性:atomic,为setter方法加锁(默认就是atomic

    非原子属性:nonatomic,不会为setter方法加锁

    对比:
    atomic:线程安全,需要消耗大量的资源
    nonatomic:非线程安全,适合内存小的移动设备
    建议定义属性都用nonatomic属性,在需要加锁的地方才用互斥锁,这样能节省资源,更加适合移动设备上的开发,并且需要注意的是,加锁和资源抢夺等业务逻辑都交给服务器端去处理。

    线程间的通信

    1.主线程:UI线程,用于显示、刷新UI界面,处理UI控件的事件
    2.子线程:后台线程,异步线程,用于耗时操作
    如何通信?
    1.A线程传递数据给B线程
    2.一个线程执行完特定任务后,转到另一个线程执行,如下载网络图片,一般由子线程从网络上下载图片,然后让主线程刷新UIImageView上的图片。

    NSOperation
    配合使用NSOperation和NSOperationQueue也能实现多线程
    实现步骤:
    1.先将需要执行的操作封装到一个NSOperation对象中
    2.然后将NSOperation对象添加到NSOperationQueue中
    3.系统会自动将NSOperationQueue中的NSOperation取出来
    4.将取出的NSOperation封装的操作放到一条新线程中执行

    但是,NSOperation是一个抽象类,并不具备封装操作的能力,所以我们必须使用它的子类

     使用NSOperation子类的方式有下面3种:
     1.NSInvocationOperation(不常用)
    2.NSBlockOperation
    3.自定义子类继承NSOperation,实现内部相应的方法

    NSInvocationOperation的基本实现

        NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download) object:nil];
         // operation直接调用start,是同步执行(在当前线程执行,不会另开线程
    //    [operation start];)
        // 要创建一个operation队列然后add进去才能实现多线程
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        [queue addOperation:operation];
    
    - (void)download
    {
        NSLog(@"---%@---",[NSThread currentThread]);
    }


    NSBlockOperation的基本实现
    没使用队列

    NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"---下载1---%@",[NSThread currentThread]);
        }];
        
        //addExecutionBlock方法可以往operation中添加任务
        [operation1 addExecutionBlock:^{
            NSLog(@"--下载11---%@",[NSThread currentThread]);
        }];
        
        [operation1 addExecutionBlock:^{
            NSLog(@"--下载12---%@",[NSThread currentThread]);
        }];
        
        // 如果调用start,operation的任务数大于1的话,第1个任务在主线程执行,其他的异步(另开线程)串行执行(按顺序执行)
        [operation1 start];

    添加到队列

    NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"---下载1---%@",[NSThread currentThread]);
        }];
        
        //addExecutionBlock方法可以往operation中添加任务
        [operation1 addExecutionBlock:^{
            NSLog(@"--下载11---%@",[NSThread currentThread]);
        }];
        
        
        NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"---下载2---%@",[NSThread currentThread]);
        }];
        
        // 放进下面队列的任务都是异步(不同线程)并发执行(同时执行)
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        [queue addOperation:operation1];
        [queue addOperation:operation2];

    直接使用队列,不用新建NSBlockOperation对象

    // 1.创建队列(非主队列)
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    // 2.调用addOperationWithBlock方法,直接往队列里面添加任务
        [queue addOperationWithBlock:^{
            NSLog(@"---下载1---%@",[NSThread currentThread]);
        }];
        [queue addOperationWithBlock:^{
            NSLog(@"---下载2---%@",[NSThread currentThread]);
        }];
        [queue addOperationWithBlock:^{
            NSLog(@"---下载3---%@",[NSThread currentThread]);
        }];
    // 以上都是异步并发执行的

    自定义NSOperation

    重写- (void)main方法,在里面实现想执行的任务
    
    重写- (void)main方法的注意点
    1.自己创建自动释放池(因为如果是异步操作,无法访问主线程的自动释放池)
    2.经常通过- (BOOL)isCancelled方法检测操作是否被取消,对取消做出响应

    NSOperationQueue的使用
    类型:
    1.主队列[NSOperationQueue mainQueue];
    2.非主队列[[NSOperationQueue alloc] init];
    可以设置线程的最大并发数,设置之后可以优化性能,因为它会重复利用用过的线程

    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    queue.maxConcurrentOperationCount = 2;
    //或者
    [queue setMaxConcurrentOperationCount:2];

    NSOperation的其他使用
    >可以通过设置依赖来保证执行顺序,注意两个operation不能互相依赖
    >两个不同队列中的operation之间也能设置依赖

    /**
         假设有A、B、C三个操作,要求:
         1. 3个操作都异步执行
         2. 执行完A再执行B再执行C
         */
        
        // 1.创建一个队列(非主队列)
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        
        // 2.创建3个操作
        NSBlockOperation *operationA = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"A1---%@", [NSThread currentThread]);
        }];
        
        NSBlockOperation *operationB = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"B---%@", [NSThread currentThread]);
        }];
        NSBlockOperation *operationC = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"C---%@", [NSThread currentThread]);
        }];
        
        // 设置依赖
        [operationB addDependency:operationA];
        [operationC addDependency:operationB];
        
        // 3.添加操作到队列中(自动异步执行任务)
        [queue addOperation:operationC];
        [queue addOperation:operationA];
        [queue addOperation:operationB];

    >可以为某个operation设置监听,等它执行完后执行想要的代码

    NSBlockOperation *operationA = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"A1---%@", [NSThread currentThread]);
        }];
    [operationA setCompletionBlock:^{
         NSLog(@"AAAAA---%@", [NSThread currentThread]);
     }];

    >线程间的通信,NSThread,GCD,NSOperation之间可以混合使用

    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        [queue addOperationWithBlock:^{
            // 1.异步下载图片
            NSURL *url = [NSURL URLWithString:@"http://d.hiphotos.baidu.com/image/pic/item/37d3d539b6003af3290eaf5d362ac65c1038b652.jpg"];
            NSData *data = [NSData dataWithContentsOfURL:url];
            UIImage *image = [UIImage imageWithData:data];
            
            // 2.回到主线程,显示图片
    // 2.1 NSThread
    //        [self performSelectorOnMainThread:<#(SEL)#> withObject:<#(id)#> waitUntilDone:<#(BOOL)#>];
    // 2.2 GCD
    //        dispatch_async(dispatch_get_main_queue(), ^{
    //            
    //        });
    // 2.3
            [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                self.imageView.image = image;
            }];
        }];

    >队里的取消,暂停,恢复及一般应用的地方

    * 取消所有的操作
    - (void)cancelAllOperations;

    * 暂停所有的操作
    [queue setSuspended:YES];

    * 恢复所有的操作
    [queue setSuspended:NO];

    - (void)didReceiveMemoryWarning
    {
        [super didReceiveMemoryWarning];
        
    //    [queue cancelAllOperations]; // 取消队列中的所有任务(不可恢复)
    }
    
    - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
    {
    //    [queue setSuspended:YES]; // 暂停队列中的所有任务
    }
    
    - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
    {
    //    [queue setSuspended:NO]; // 恢复队列中的所有任务
    }

    如何避免cell中的图片重复下载(思路)
    需要:创建字典images(根据URL(key)存储图片(value))、字典operations(根据URL存储操作)

    用第三方框架:SDWebImage即可实现

     

    把图片写入沙盒

        // UIImage --> NSData --> File
        NSData *data = UIImagePNGRepresentation(image);
        
        // 一般存在沙盒中Library的caches文件夹里面,Document和Library中的preferences会影响iTunes的软件同步,而tmp文件夹不安全,里面的数据随时会被系统删除
        // 获得caches的文件路径
        NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, NO)];
        // 获得网络图片的URL
        NSString *filename = [imageURL lastpathComponent];
        // 拼接文件路径
        NSString *file = [caches stringByAppendingPathComponent:filename];
        [data writeToFile:file atomically:YES];

    从沙盒中读取图片

        // 获得caches的文件路径
        NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, NO)];
        // 获得网络图片的URL
        NSString *filename = [imageURL lastpathComponent];
        // 拼接文件路径
        NSString *file = [caches stringByAppendingPathComponent:filename];
        
        NSData *data = [NSData dataWithContentsOfFile:file];
        UIImage *image = [UIImage imageWithData:data];

     

  • 相关阅读:
    今天才知道还有这个地址 MS 的
    提供一个在线翻译,多语言互译的好网址.
    程序员有多少读过《人性的弱点》?项目经理呢?
    [下载]ASP.NET开发技巧集锦
    真不好意思,关于小猪妹(妖精)的
    poj 1330 LCA问题 (LCA问题转化为RMQ || tarjan算法)
    poj 2688 (bfs+dfs)
    Codeforces Round #143 (Div. 2) B
    poj 1160 dp
    poj 1032(整数拆分)
  • 原文地址:https://www.cnblogs.com/jierism/p/6106702.html
Copyright © 2011-2022 走看看