多线程

这篇博客详细介绍了iOS中多线程的实现,包括NSThread的三种创建方式、线程间通信、GCD的同步异步任务、主队列与串行队列的区别,以及NSOperation的高级用法。还探讨了多线程的目的、可能出现的问题和解决策略,如互斥锁与自旋锁。同时,提到了sdwebimage库的位移枚举和NSCache的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

网络多线程学习笔记

一 .NSThread

1.NSThread的三种创建方式

1>直接创建!

 NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(demo:) object:@"Thread"];
 [thread start];

2>detachNewThreadSelector

*这是在后台执行的方法
[NSThread detachNewThreadSelector:@selector(demo:) toTarget:self withObject:@"Detach"];

3>performSelectorInBackground:

-NSObject 的一个分类方法,意味着所有的 NSObject 都可以使用此方法,在其他线程执行方法!
- 特点:没有thread字眼,一旦制定,就会立即在后台线程执行  selector 方法
- performSelectorInBackground 隐式的多线程方法,这种方法,在使用的时候更加灵活!
[对象  performSelectorInBackground:@selector(loadData) withObject:nil];
2.线程的状态

线程状态

  • 注意:在终止线程之前,应该注意释放之前分配的对象!

  • 如果是 ARC 开发,需要注意,清理 C 语言框架创建的对象!否则会出现内存泄漏!

3.NSThread属性
* t1.name = @"Thread A";
* 用途:在大的商业项目中,通常希望程序崩溃的时候,能够获取到程序准确执行所在的线程!
* t1.threadPriority = 0.1;
* 优先级,是一个浮点数,从0~1.0,1.0表示优先级最高,默认优先级是0.5!优先级只是保证 CPU 调度的可能性会高!
* ![NSThread isMainThread
* 判断是否是主线程
4.多线程的目的
  • 多线程的目的:将耗时的操作放在后台,不阻塞主线程和用户的交互!
  • 个人建议,在开发的时候,不要修改优先级!优先级翻转!
  • 多线程开发,有一个原则:尽量简单!
5.多线程会出现的问题
  • 资源抢夺

这里写图片描述

  • 互斥锁 - 保证锁内的代码,同一时间,只有一条线程能够执行!
  • 多线程的目的:耗时的操作放在后台
  • 多线程的优点:通过并发提高程序的执行效率!
  • 互斥锁的锁定范围,应该尽量小,锁定范围越大,效率越差!
  • 实际上,原子属性内部也有一把锁,自旋锁

    “`

    • 自旋锁 & 互斥锁
      - 共同点
      都能够保证同一时间,只有一条线程执行锁定范围的代码
      - 不同点
      互斥锁:如果发现有其他线程正在执行锁定的代码,线程会进入休眠状态,等待其他线程执行完毕,打开锁之后,线程会被唤醒
      自旋锁:如果发现有其他线程正在执行锁定的代码,线程会用死循环的方式,一直等待锁定代码执行完成!
      自旋锁更适合执行非常短的代码!
  • 无论什么锁,都是要付出代价的!

    “`

6.线程间通信
performSelectorOnMainThread "线程间通讯"
[self performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:NO];
1. 在主线程执行的方法
2. 传递给方法的参数
3. 是否等待被调用方法执行完成,有可能也会等待调用方法的执行完成!几率极少!
7.一些问题

  • 问题1:为什么scrollView是strong,imageView是weak?
    在 OC 中如果一个对象没有其他对象做强引用,会被立即释放!


  • 问题2: UIImage & UIImageView有什么区别

    1. UIImage 是图像 - 照片
    2. UIImageView 是视图 - 相框

8.定时器
 以下两句两种添加时钟的方法是等价的
 1.   NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(updateTimer) userInfo:nil repeats:YES];

  // 加入运行循环
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

  2.   [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateTimer) userInfo:nil repeats:YES];


  *  NSDefaultRunLoopMode - 时钟,网络事件;
  *  NSRunLoopCommonModes - 用户交互;
9. 运行循环
  • 保证程序不退出!
  • 负责监听事件,监听 iOS 中所有的事件:触摸,时钟,网络事件
  • 如果没有事件发生,会让程序进入休眠状态
10.开发多线程程序的步骤
  1. 保证单个线程执行正常!
  2. 每一个售票逻辑(窗口)应该把所有的票卖完
  3. 添加线程

二 .02 -GCD

  1. GCD 核心概念:将任务添加到队列,指定任务执行的方法
 - 任务
    -使用 block 封装
    -block就是一个提前准备好的代码块,在需要的时候执行
 - 队列(负责调度任务)

    - 串行队列:一个接一个的调度任务
    - 并发队列:可以同时调度多个任务
    - 主队列:  专门用来在主线程上调度任务的"队列"
        主队列不能在其他线程中调度任务!
        如果主线程上当前正在有执行的任务,主队列暂时不会调度任务的执行!
        注意:主队列不是主线程
        - 异步任务,会在主线程的方法执行完成后,被调度
        - 同步任务,会造成死锁
    - 全局队列,系统提供给程序员,方便程序员使用的全局队列
        有关服务质量问题,使用以下代码能够做到 iOS7 & iOS8 的适配
        dispatch_get_global_queue(0, 0); 
 - 执行任务的函数
    - 同步执行:当前指令不完成,就不会执行下一条指令
    - 异步执行:当前指令不完成,同样可以执行下一条指令
        异步是多线程的代名词

 小结:
    - 开不开线程,取决于执行任务的函数,同步不开,异步开
    - 开几条线程,取决于队列,串行开一条,并发开多条(异步)                 
 同步任务在多线程中的用处:
 - 可以队列调度多个异步任务前,指定一个同步任务,让所有的异步任务,等待同步任务执行完成,这就是所谓的依赖关系!

 全局队列 & 并发队列
    1> 名称,并发队列有名称,适合于商业级软件跟踪错误报告!
    2> release, 在 MRC 开发时,并发队列需要使用 
        dispatch_release(q);
    结论:目前绝大多数的软件都会使用全局队列。比较优秀的第三方框架会使用自定义的并发队列!
    日常使用:用全局!

 全局队列 & 串行队列选择
    全局队列:并发,能够调度多个线程,执行效率高
        -费电
    串行队列:一个接一个,只能够开启一条线程,执行效率低(斯坦福大学)
        -如果任务之间需要依赖,使用串行队列
        -省电,省钱,省流量

    判断依据:用户的上网方式
        -WIFI,可以多开线程,6条
        -3G/4G,尽量少开线程,2~3条    
  1. 全局对列的参数
    • dispatch_queue_t q = dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0);
     ***<参数>
  1. 涉及到系统适配
       iOS 8       服务质量(让线程响应的更快还是更慢)
        - QOS_CLASS_USER_INTERACTIVE            用户交互(用户迫切希望线程快点被执行,不要用耗时的操作)
         - QOS_CLASS_USER_INITIATED             用户需要的(同样不要使用耗时操作)
         - QOS_CLASS_DEFAULT                    默认的(给系统用来重置队列的)
         ** QOS_CLASS_UTILITY                    实用工具(用来做耗时操作)
         - QOS_CLASS_BACKGROUND                 后台
         - QOS_CLASS_UNSPECIFIED                没有指定优先级

   iOS 7       调度的优先级
         - DISPATCH_QUEUE_PRIORITY_HIGH 2       高优先级
         - DISPATCH_QUEUE_PRIORITY_DEFAULT 0    默认优先级
         - DISPATCH_QUEUE_PRIORITY_LOW (-2)     低优先级
         - DISPATCH_QUEUE_PRIORITY_BACKGROUND   后台优先级    
     提示:尤其不要选择 BACKGROUND 优先级和服务质量,用户不需要知道线程什么时候执行完成!线程的执行会慢的令人发指!  
     有关服务质量的介绍,用在与 XPC 框架结合使用的,XPC 用在 MAC 平台上做进程间通讯的框架!  
     因为大家工作后,暂时会考虑 iOS7 & iOS8 的适配,无法使用服务质量,直接指定 0,能够做到 iOS7 & 8 的适配
     dispatch_get_global_queue(0, 0);    
     2. 为未来使用保留的,应该始终传入0
  1. 同步任务的作用

    利用同步任务,能够做到,任务依赖关系,前一个同步任务不执行完,队列就不会调度后面的任务!


3. demo
// MARK: 串行队列,同步执行
/**
 不会开线程,会顺序执行
 */
- (void)gcdDemo1 {
    // 1. 队列
    /**
     参数
     1. 队列的名称
     2. 队列的属性 
        DISPATCH_QUEUE_SERIAL(NULL) 表示串行
     */
//    dispatch_queue_t q = dispatch_queue_create("itcast", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t q = dispatch_queue_create("itcast", NULL);
    // 2. 执行任务
    for (int i = 0; i<10; i++) {
        dispatch_sync(q, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
}

 MARK: 串行队列,异步执行
/**
 串行队列,一个一个调度任务
 执行的方法:异步,会开线程

 问题:会开几条线程?顺序执行吗?come here?
 猜测:不确定,会/不会顺序执行,一上来就执行
 答案:只会开一条线程:是因为队列是按照先进先出的顺序调度任务,因此有一个线程就足够了
      顺序执行,FIFO
 */
- (void)gcdDemo2 {
    dispatch_queue_t q = dispatch_queue_create("itcast", NULL);

    for (int i = 0; i<10; i++) {
//        NSLog(@"%d------", i);

        dispatch_async(q, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
    NSLog(@"come here");
}

// MARK: 并发队列,异步执行
/**
 队列:并发队列,可以调度多个任务
 异步:可以开启线程

 问题:会开几条线程?顺序执行吗?come here?
 猜测:会,不会,不是最后
 */
- (void)gcdDemo3 {
    // 1. 队列
    dispatch_queue_t q = dispatch_queue_create("itcast", DISPATCH_QUEUE_CONCURRENT);

    // 2. 异步执行
    for (int i = 0; i<10; i++) {
        dispatch_async(q, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
    NSLog(@"come here");
}

// MARK: 并发队列,同步执行
/**
 队列:并发队列,可以调度多个任务
 同步:不能开线程

 问题:会开几条线程?顺序执行吗?come here?
 猜测:不会,会,最后
 效果:和串行队列,同步执行效果一样!
 */
- (void)gcdDemo4 {
    // 1. 队列
    dispatch_queue_t q = dispatch_queue_create("itcast", DISPATCH_QUEUE_CONCURRENT);

    // 2. 异步执行
    for (int i = 0; i<10; i++) {
        dispatch_sync(q, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
    NSLog(@"come here");
}

// MARK: 主队列,异步执行
/**
 队列:主队列,不可能在其他线程执行
 任务:异步,开线程,不会阻塞线程执行,不用等待着一条语句执行完,就能执行下一句

 猜测:不开线程,顺序执行,come here 出现在最后!!!!!
 */
- (void)gcdDemo5 {
    // 1. 主队列 - 程序启动之后已经存在主线程,主队列同样存在
    dispatch_queue_t q = dispatch_get_main_queue();

    // 2. 安排一个任务
    for (int i = 0; i<10; i++) {
        dispatch_async(q, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }

    NSLog(@"睡会");
    [NSThread sleepForTimeInterval:2.0];
    NSLog(@"come here");
}

// MARK: 主队列,同步执行
/**
 主队列:不开线程
 同步任务:不开线程

 猜测:come here??? | 卡死
 */
- (void)gcdDemo6 {
    dispatch_queue_t q = dispatch_get_main_queue();

    NSLog(@"卡死了吗?");

    dispatch_sync(q, ^{
        NSLog(@"我来了");
    });

    NSLog(@"come here");
}

// MARK: 同步任务的作用!
/**
 在网络开发中,通常会把很多任务放在后台异步执行,有的时候,有些任务会彼此有"依赖"关系!

 例子:小说的网站,用户登录,扣费,下载小说 A,扣费,下载小说 B...

 利用同步任务,能够做到,任务依赖关系,前一个同步任务不执行完,队列就不会调度后面的任务!

 come here 在用户登录后面!
 */
- (void)gcdDemo7 {

    dispatch_queue_t q = dispatch_queue_create("itcast", DISPATCH_QUEUE_CONCURRENT);

    // 1. 用户登录,必须要第一个执行
    dispatch_sync(q, ^{
        NSLog(@"用户登录 %@", [NSThread currentThread]);
    });
    // 2. 扣费
    dispatch_async(q, ^{
        NSLog(@"扣费 %@", [NSThread currentThread]);
    });
    // 3. 下载
    dispatch_async(q, ^{
        NSLog(@"下载 %@", [NSThread currentThread]);
    });

    NSLog(@"come here");
}
#3.其他用法
1. 延时执行
// 从现在开始,经过多少纳秒之后
    dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC));
2. 一次性执行
- (void)once {
    NSLog(@"来了");
    // 苹果提供了一个一次行执行的机制,不仅能够保证只被执行一次,而且是"线程安全"的
    // 苹果推荐使用 dispatch_once_t 来做一次性执行!因为效率高!
    // 不要使用互斥锁,互斥锁效率低!
    static dispatch_once_t onceToken;
    NSLog(@"%ld", onceToken);
    dispatch_once(&onceToken, ^{
        // 只会执行一次的代码
        NSLog(@"执行了!%@", [NSThread currentThread]);
    });
}
3. 调度组
// MARK: 调度组1
- (void)group1 {
    // 1. 队列
    dispatch_queue_t q = dispatch_get_global_queue(0, 0);

    // 2. 调度组
    dispatch_group_t g = dispatch_group_create();

    // 3. 添加任务,让队列调度,指定任务执行函数,最终通知群组
    dispatch_group_async(g, q, ^{
        NSLog(@"download A %@", [NSThread currentThread]);
    });
    dispatch_group_async(g, q, ^{
        [NSThread sleepForTimeInterval:1.0];
        NSLog(@"download B %@", [NSThread currentThread]);
    });
    dispatch_group_async(g, q, ^{
        [NSThread sleepForTimeInterval:0.8];
        NSLog(@"download C %@", [NSThread currentThread]);
    });

    // 4. 所有任务执行完毕后,获得通知
    // 用一个调度组,可以监听全局队列调度的任务,执行完毕后,在主队列执行最终处理!
    // dispatch_group_notify 本身是异步的
    dispatch_group_notify(g, dispatch_get_main_queue(), ^{
        // 更新UI,通知用户!
        NSLog(@"OK %@", [NSThread currentThread]);
    });

    NSLog(@"come here");
}
4.注意点
  • -子线程运行循环默认不启动
    image

  • 子线程运行循环

  • image

  • 10-串行队列异步执行示意图
    image

5. 主队列 & 串行队列的区别
 主队列 & 串行队列的区别

 容易产生误解的地方:主队列也是一个一个安排任务
 队列的特点:以先进先出的方式调度任务!就是一个接一个调度任务!

 - 并发队列,可以调度很多任务
 - 串行队列,必须等待一个任务执行完成,再调度另外一个任务
    - 最多只能开启一条线程
 - 主队列,以先进先出调度任务,如果主线程上有任务在执行,主队列不会调度任务
    - 主要是负责在主线程上调度任务,达到线程间通讯的目的!

 关于 GCD Block 嵌套的建议:要尽量减少层次!最多不要超过 3 层!
 层次越多越复杂!

 多线程开发的原则:简单!

 // MARK: 主队列同步任务(增强版-不死锁-代码精简)
- (void)gcdDemo4 {
    dispatch_async(dispatch_get_global_queue(0, 0), ^ {
        NSLog(@"这里!!! %@", [NSThread currentThread]);

        // 2. 同步执行任务 -》 死锁
        dispatch_sync(dispatch_get_main_queue(), ^{
            NSLog(@"能来吗? %@", [NSThread currentThread]);
        });

        NSLog(@"睡会");
        [NSThread sleepForTimeInterval:1.0];

        NSLog(@"come here");
    });
}

这里写图片描述

三 NSOperation

1.简介
 NSOperation 是苹果公司大力推荐的"并发"技术!程序员已经不在需要关心线程以及线程的生命周期!

 NSOperation 的核心概念:将"操作"添加到"队列"
 GCD 的核心概念:将"任务"添加到队列,指定任务的执行函数(方法)

 ---
 GCD & NSOperation 的对比

 GCD 在 iOS 4.0 推出的,主要针对多核处理器做了优化的并发技术,是 C 语言的
    -将"任务[block]"添加到"队列[串行/并发/主队列/全局队列]",并且指定执行任务的函数[同步/异步]
    -线程间通讯 dispatch_get_main_queue()
    -提供了一些 NSOperation 不具备的功能
        - 一次性执行
        - 延迟执行
        - 调度组(在op中也可以做到,就是有点麻烦)

 NSOperation 在 iOS 2.0 推出的,苹果推出 GCD 之后,对 NSOperation 底层重写了一遍
    -将操作[异步执行的任务]添加到队列[并发队列],就会立即异步执行
    -mainQueue
    -提供了一些 GCD 实现起来比较困难的功能!
        - 最大并发操作数
        - 队列的暂停/继续
        - 取消所有操作
        - 指定操作之间的依赖关系 (GCD 用同步实现)

 ---
 NSOperation 类是一个抽象类
 特点:
    - 不能直接使用!
 目的:
    - 定义子类共有的属性和方法

 子类
    - NSInvocationOperation
    - NSBlockOperation

 - UIGestureRecognizer
 - CAAnimation
    - CAPropertyAnimation
        - BASIC
        - Keyframe
2.demo
// MARK: 线程间通讯
- (void)demo6 {
    [self.opQueue addOperationWithBlock:^{
        NSLog(@"耗时的操作 %@", [NSThread currentThread]);
        // 主线程更新UI,主队列

        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            NSLog(@"UIUIUI --- %@", [NSThread currentThread]);
        }];
    }];
}

// MARK: 全局队列 - 只要是 NSOpeation 的子类,都可以添加到队列!
- (void)demo5 {
    // 直接添加任务
    for (int i = 0; i < 10; i++) {
        [self.opQueue addOperationWithBlock:^{
            NSLog(@"%@ --- %d", [NSThread currentThread], i);
        }];
    }

    // block operation 同样可以添加到队列
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"BLOCK ---- %@ ---- %d ", [NSThread currentThread], 100);
    }];
    [self.opQueue addOperation:op1];

    // invocation operation
    NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downloadImage:) object:@"invocation"];
    [self.opQueue addOperation:op2];
}

// MARK: 更简单的
- (void)demo4 {
    // 1. 队列 -> 队列如果每次分配会比较浪费!
    // 在实际开发中,会使用全局的队列,统一调度所有异步执行的操作
    NSOperationQueue *q = [[NSOperationQueue alloc] init];

    // 2. 添加操作
    for (int i = 0; i<10; i++) {
        [q addOperationWithBlock:^{
            NSLog(@"%@ --- %d", [NSThread currentThread], i);
        }];
    }
}

// MARK: NSBlockOperation 演练
// NSBlockOperation 所有的代码都写在一起,更加好维护
- (void)demo3 {
    // 1. 队列
    NSOperationQueue *q = [[NSOperationQueue alloc] init];

    // 2. 操作
    for (int i = 0; i<10; i++) {
        NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"%@ ---- %d ", [NSThread currentThread], i);
        }];

        [q addOperation:op];
    }
}

// MARK: NSInvocationOperation演练
/**
 结果:开启多个线程,不会顺序执行 -> GCD 并发队列,异步执行任务

 NSOperation 本质上是对 GCD 的面相对象的封装

 - 队列:本质上就是 GCD 的并发队列
 - 操作:异步执行的任务
 */
- (void)demo2 {
    // 1. 队列
    NSOperationQueue *q = [[NSOperationQueue alloc] init];

    for (int i = 0; i < 10; i++) {
        NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downloadImage:) object:@(i)];

        // 添加到队列
        [q addOperation:op];
    }
}

- (void)demo1 {
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downloadImage:) object:@"invocation"];

    // start 方法,会在当前线程执行调度方法
//    [op start];
    // 队列
    NSOperationQueue *q = [[NSOperationQueue alloc] init];
    // 将操作添加到队列 - 会自动异步执行调度方法
    [q addOperation:op];
}

- (void)downloadImage:(id)obj {
    NSLog(@"%@ %@", [NSThread currentThread], obj);
}
3.高级演示
  • 最大并发数
   设置同时最大并发操作数量
   WIFI: 56
   2G/3G/4G: 23
   self.opQueue.maxConcurrentOperationCount = 2;
    // 向队列添加任务
    // 从 iOS 8.0 开始,无论使用 GCD 还是 NSOperation,都会开启很多线程
    // 在 iOS 7.0 以前,GCD 通常只会开启 5~6 条线程!
    // 目前的前程数多说明:
    // 1. 底层的线程池更大了,能够拿到的线程资源更多了!
    // 2. 对控制同时并发的线程数,要求就更高!
  • 暂停与继续
- (IBAction)pause {
    // 在设置队列的挂起属性时,并不会判断队列中是否有操作!
    // 如果不希望用户产生困惑,可以提前做判断
    // 判断队列中当前是否有操作
    if (self.opQueue.operationCount == 0) {
        NSLog(@"没有操作!");
        return;
    }

    // 判断队列是否挂起
    if (self.opQueue.isSuspended) {
        // 在暂停的时候,队列中的操作数,是包含正在执行的操作的
        NSLog(@"继续 %tu", self.opQueue.operationCount);
        self.opQueue.suspended = NO;
    } else {
        // 再次继续的时候,如果之前执行的操作已经完成,队列中的操作数就只有没有调度的操作
        NSLog(@"暂停 %tu", self.opQueue.operationCount);
        self.opQueue.suspended = YES;
    }
}
  • 依赖关系
- (void)dependecy {

    /**
     例子:例如从网络上下载小说的压缩包、完成之后解压缩、通知用户
     */
    // 1. 下载
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"下载 - %@", [NSThread currentThread]);
    }];
    // 2. 解压缩
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        [NSThread sleepForTimeInterval:2.0];
        NSLog(@"解压缩 - %@", [NSThread currentThread]);
    }];
    // 3. 通知用户
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"通知用户 - %@", [NSThread currentThread]);
    }];

    // NSOperation 提供了依赖关系
    // NSOperation的所有操作都是异步执行的,但是为了建立任务之间的依赖,提供了dependency的功能
    // GCD 中,可以直接通过同步任务来实现,也可以通过串行队列
    [op2 addDependency:op1];
    [op3 addDependency:op2];

    // !!!: 注意,不要指定循环依赖,一旦指定了循环依赖,队列就不工作!
    // 不会造成死锁!以前版本的 Xcode 会死锁!
//    [op1 addDependency:op3];

    // waitUntilFinished,是否等待所有操作完成
    // 等待是阻塞式的,类似于 dispatch_group_wait(g, DISPATCH_TIME_FOREVER);
    [self.opQueue addOperations:@[op1, op2] waitUntilFinished:NO];

    // 主线程通知用户
    [[NSOperationQueue mainQueue] addOperation:op3];
    NSLog(@"come here");
}

四 sdwebimage

1.位移枚举
  • 利用位移枚举,可以通过按位或的方式,使用一个参数传递多个类型的组合!
  • 在所有 C 语言中,如果看到位移枚举做参数,第一个选项不是0
    暗示:传入 0 做参数,表示什么附加操作都不做!执行效率最高!
2.NSCache
 NSCache 是苹果提供的一套缓存机制,使用起来和 NSMutabDictionary 非常像

 特点
 - 是线程安全的
 - 当内存不足的时候,会自动释放
 - 指定缓存的限额
    - 缓存数量
    - 缓存成本

 ---
 属性

 totalCostLimit;    总成本限额,默认为0
 countLimit;        总数量限额,默认为0

 什么是成本?成本我们可以自己指定

 举个栗子!

 图像:宽 * 高 = 总像素点,越大,像素越高,越清晰,内存占用越大
 SDWebImage中,缓存的成本就是按照图像 宽 * 高 来指定的!

 ---
 指定限额的好处,一旦超过限额,缓存就自动清理!
 // *** 这个代理方法,仅供测试了解 NSCache 的工作机制的,平时不要实现,否则会严重影响缓存的执行性能!
- (void)cache:(NSCache *)cache willEvictObject:(id)obj {
    [NSThread sleepForTimeInterval:0.3];
    NSLog(@"---删除 %@", obj);
}

五 网络基础

1.通知中心
 观察者模式-发生事件后,以广播的形式,通知所有的监听者

 通知中心的本质到底是什么?就是监听一个字符串!
 - defaultCenter 返回进程的默认通知中心!开发的时候,不能创建通知中心!

 - 通知中心一旦获取到"字符串",就以广播的形式广播字符串,可以有多个观察者!

 面试题:通知中心是同步的,还是异步的? - 同步的!

 - 通知里面到底有什么?

    - name          通知的名称字符串,通知中心本质上就是监听这个字符串
    - object        发布通知的时候,希望传递给监听者的对象
    - userInfo      发布通知的时候,希望传递给监听者的附加信息字典

 - 通知和代理有什么区别?
    - 代理是一对一的,设置 delegate 属性,在需要的时候,通过 delegate 调用 selector 方法!
    - 通知是一对多的,只要发生事件,会以广播的形式,通知所有的监听者

    - 通知中心/发布通知的对象不需要知道谁是监听者,发布对象和监听者之间的耦合度很低
    - 监听者需要知道通知的名称字符串,如果发布者还传递 userInfo 字典,监听者同时需要知道字典的键名
    - 技巧:在 OC 的框架中,如果定义通知,通常通知的名字包含"Notification"字样,同时 key 会和 通知定义在一起!

    - 通知监听方法不能有返回值!
    - 代理可以有返回值!
 对于一些对性能消耗比较大的功能
 例如:地图导航(GPS),利用加速计/陀螺仪,通知中心……

 可以在界面出现的时候,开启功能,界面消失的时候,关闭功能,能够达到省电的目的!

 如果是通知中心,利用这一技巧,可以避免和其他的界面中监听相同事件发生冲突!
2.通知队列
 通知队列是一个缓冲,可以判断什么时候来发布通知!
     利用发送通知的 Style,同样可以实现简单的异步!
     提示:通知中心本身的工作机制没有发生任何变化,变化的是队列的缓冲效果!

     NSPostWhenIdle = 1,    Idle 发呆,空闲时发布
     NSPostASAP = 2,        ASAP as soon as possible尽快
     NSPostNow = 3          立即发布

     coalesce : 合并/聚合
     NSNotificationNoCoalescing = 0,        不合并
     NSNotificationCoalescingOnName = 1,    按照名称合并
     NSNotificationCoalescingOnSender = 2   按照发布者合并

     合并的用处:有的时候,可能会接收到多个通知,但是程序只想做出一次响应!
     发布者可以利用通知队列,指定监听者工作几次!
     */
    // 最后一个参数,指定发布通知的运行循环的模式数组,使用 nil,就是 默认的运行循环模式
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值