iOS 多线程和内存管理

内存管理

  • iOS 管理对象 采用的是内存引用计数管理 当我们生产一个对象。

    • (生成对象) 我们就对这个对象引用计数加一 ,当我们在对这个对象 去做某些事(持有对象) 相当于我们持有这个对象 这事这个对象的引用计数又会 加一 。当我们做完某些事 不需要这个对象的时候 (释放对象)我们会对这个对象引用计数减一。当我们完全不使用的时候 我们应该对这个对象 进行销毁(废弃对象
    • 生成并持有对象 (alloc、new、copy、mutableCopy等方法
    • 持有对象 (retain 方法
    • 释放对象(release 方法
    • 废弃对象(dealloc方法
  • ARC 自动引用计数

    • 自动引用计数 是Xcode 4.2或以上版本 LLVM 编译器 3.0 或以上版本引入新技术。
    • 在编写代码时 无需再次键入 retain 或 release 代码 这在降低程序崩溃、内存泄露等风险的同时 很大程度上减少了开发程序的工作量。
  • GNU 和苹果源码 引用计数区别

    • GNU
      • alloc 类方法用 struct obj_layout中的retained 整数来保存引用计数,并写入对象内存头部(GNU 少量代码可以完成、能够统一管理引用计数用内存块与对象用内存块)
    • 苹果
    • 苹果实现大概使用了散列表来管理引用计数(对象内存块的分配无需考虑内存块头部、引用计数表各记录中存有内存块地址,可从各个记录追溯到各对象的内存块
  • autorelease

    • NSAutoreleasePool 对象的生存周期相当于C语言变量的作用域。对于所有autourelease 实例对象,在废弃NSAutorelease 对象时,都将调用release 方法。
    • NSRunLoop 每次循环过程中NSAutoreleasePool 对象被生成或废弃(NSAutoreleasePool 生命周期 在与NSRunLoop开发-结束)
    • GNU autorelease 源码实现
      • autorelease 实例方法本质调用 NSAutoreleasePool 对象的 addObject 方法
      • NSAutoreleasePool 内部保存对象池子是一个数组。 经常有人问 如果嵌套生成或持有 NSAutoreleasePool 对象 那个pool释放会有效果 连接列表 所有的对象池子都是一个,这个相同于往 MutableAry 对象追加数据一样
  • 修饰符

    • __strong==strong==copy 修饰符 ARC 下 通过 strong 修饰符 不必再次写入 retain 或 release 代码。通过 strong 修饰 自己生成的对象 自己持有 和 非自己生产的对象 自己也能持有。废弃带有strong 修饰的变量 或者对变量赋值 都可以做到不再需要自己持有的对象释放。
    • __weak==weak 修饰符 weak 修饰符可以避免循环引用 弱引用不能持有对象实例,在超出变量作用域时 对象就会释放 这样就可以解决循环引用问题。
      • 用weak 修饰对象 若该对象被废弃,则此弱引用将自动失效且处于nil 被赋值的状态
      • 在weak 修饰的变量 在访问引用对象 其实是访问注册到autoureleasePool的对象,这是因为在访问引用对象过程中,该对象有可能被废弃 把对象注册到autoureleasePool 里面 在autoureleasePool 结束之前都能保证该对象存在。
        • 如果大量使用附有weak修饰的变量 注册到autoureleasePool的对象也会大量的增加 因此在使用附有weak修饰的变量 最好暂时用strong 修饰符之后再使用 。用strong 修饰之后只会在autoureleasePool 注册一次 大大减少了系统工作量
      • 当用weak 修饰对象时 weak修饰的变量的地址注册到weak表里(weak表与引用计数表相同都是散列表) 当对象废弃时 则把变量的地址从weak表中删除。
        • 对象废弃步骤
        • 1 、从weak表中获取废弃对象的地址为键值的记录
        • 2、将包含在记录中所有附有weak 修饰变量的地址 赋值为nil
        • 3、从weak 表中删除该记录
        • 4、从引用计数表中删除废弃对象的地址为键值的记录
      • 如果大量的使用weak修饰符变量 则会消耗相应的CPU 资源。(只需要避免循环引用时使用)
  • __unsafe_unretained==assign 修饰符 作用类似weak 修饰符 但在使用 __unsafe_unretained 要确保被赋值的对象却是存在。 当对象废弃是 _unsafe_unretained修饰对象 不会置nil

Block

  • Block 简介
    • Block 是带有自动变量的匿名函数。 匿名函数就是不带名称函数
    • 表达式: ^ 返回值类型 参数列表 表达式
    • Block类型变量与C语言变量完全相同
      - 自动变量
      - 函数参数
      - 静态变量
      - 静态全局变量
      - 全部变量
    • Block 本质
      • OC 中由类生成对象 意味着结构体这样 生成由改类生成的对象的结构体实例。生成的各个对象 即由改类生成的对象各个结构体实例 通过成本变量isa 保存该类的结构体实例指针。通过clang 可以看到 Block 指针赋值给Block的结构体成员变量isa 所以Block也是oc对象。
    • Block 变量截取
      • 截获自动变量值 在执行Block 语法时 Block 语法表达式所使用的自动变量值被保存到Block的结构实例中(既Block 中),自动变量值被Block 截获 只能执行Block语法瞬间值。保存后就不能修改此值。
      • Block 中使用自动变量后 在Block 的结构体实例中重写改自动变量也不会改变原先截获的自动变量
      • 静态变量 、静态全局变量、全局变量 可以在Block 中修改
        -Block截获 静态变量时 静态变量是将指针 转递给 Block结构体的构造函数保存,这也是超出作用域使用变量的最简单的方法
    • __block 说明符
      • __block 是存储域说明符 用于指定将变量值设置到那个存储域中。
      • 当我们用 _block修饰变量 _block也同Block 一样变成__Block_byref_val_0 结构体类型的自动变量,即栈上生成的_Block_byref_val_0结构体实例。这意味着该结构体持有相当于原自动变量的成员变量。
      • __Block_byref_val_0 结构体实例的成员变量 _forwarding 持有指向该实例自身的指针。通过成员变量_forwarding访问成员变量val (val 就是截获的自动变量)
      • 为什么_forwarding会指向该实例自身的指针呢?
        • 配置全局Block 从变量作用域也可以通过指针安全地使用,但设置在栈上Block 如果所属的变量作用域结束 该block就被废弃。由于__block变量也配置在栈上,同样的 如果所属的变量作用域结束,则block变量也会被废弃。
        • Blocks 提供了将Block 和 __block 变量从栈上赋值到堆上方法。将栈上Block 赋值到堆上 即使变量作用域结束 堆上的block 还可以继续存在。(栈上_forwarding指向复制到堆上_block变量结构体)
        • __block 变量用结构体成员变量_forwarding可以实现无论_block 变量配置在栈上还是在堆上都能够正确的访问
    • Block存储类型
      • 全局Block 在使用全局变量的地方不能使用自动变量 所以不存在对自动变量的截获
      • 栈Block
      • 堆Block
    • 截获对象
      • 当Block 截获oc 对象 会在_mian_blokc_desc_0 结构体中增加的成员变量copy和dispose函数 ,作为指针赋值给成员变量_main_block_copy_0函数 和_mian_blokc_dispose_0函数
        • _main_block_copy_0 里面调用 block_object_assgin 相当于 retain 实例方法函数,将对象赋值在对象类型的结构成员变量中(调用时机 栈上block 复制到堆上)
        • _mian_blokc_dispose_0 里面调用 block_object_dispose 函数 相当于release实例函数方法 释放赋值在对象类型的结构体成员变量中的对象(调用时机 堆上block 被废弃时)
      • 什么时候栈上block 会被复制到堆上?
        • 调用 block 的copy 实例方法
        • block 作为函数的返回值返回时
        • 将block 赋值给附有 strong 修饰符 id类型或block 成员变量时
  • Block 循环引用
    • 可以通过weak 和 unsafe_unretained 来修饰 但要注意unsafe_unretained 修饰变量销毁值 不置nil 情况
    • 也可以使用__block 来修饰 配合。 但要注意block 必须执行block 要不然就会出现循环引用问题

GCD

  • Dispatch Queue
    • 执行处理的等待队列 。通过dispatch_async 函数等API,在block 语法中编写想要执行的处理并将其追加到 Dispatch Queue 中。 Dispatch Queue 按照追加的顺序FIFO 执行处理。
    • Serial Dispatch Queue (串行) 等待现在执行中任务处理结束
    • Concurrent Dispatch Queue(并行)不等待现在执行中任务处理结束
  • GCD API
    • dispatch_after

      • dispatch_after 函数并不是在指定时间后执行处理,而是指定时间追加处理到Dispatch Queue。
      • 因为Mian Dispatch Queue 在主线程的RunLoop 中执行,所以在比如每隔1/60 秒执行RunLoop 中 Block 最快在3秒后执行,最慢在3秒+1/60 秒执行,并且 在Mian Dispatch Queue 有大量任务追加或主线程的处理本身有延迟的。
    • Dispatch Group

      • 当我们处理多个并行队列时想在多个任务处理结束时 执行其他操作 。我们这是应该就需要用 Dispatch Group,使用Dispatch Group 可以监视任务结束状态,一旦监视到任务结束,就可将结束的处理追加到Dispatch Queue 中。
      • dispatch_group_notify 函数会将执行Block 追加到Dispatch Queue 中 ,第一个参数指定要监视的Dispatch Group 第三个参数Block 追加到第二参数Dispatch Queue中。
    • dispatch_barrier_async

      • dispatch_barrier_async 函数会等待追加到并行队列并行任务处理全部结束之后 在将指定处理追加到该并行队列中。 然后在由dispatch_barrier_async 函数追加处理执行完毕后 并行队列在恢复之前操作 执行追加到该队列的任务。
    • dispatch_sync&&dispatch_async

      • dispatch_async 函数 async 意味 非同步 就是将指定block 非同步地追加到指定 Dispatch Queue 中,dispatc_async 函数不做任何等待。
      • dispatch_sync 函数 sync 意味 同步 就是将指定block 同步地追加到指定 Dispatch Queue 中,在追加block结束之前 dispatch_sync 函数会一直等待
    • dispatch_apply

      • dispatch_apply 函数是dispatch_sync 函数 和Dispatch Group 的管理API。该函数按指定的次数将指定block 追加到指定Dispatch Queue 中 并等待全部出了执行结束。
    • Dispatch Semaphore

      • Dispatch Semaphore 是持有计数的信号,该计数底是多线程编程中的计数类型信号,计数为0时等待 计数为1或大于1时,减去1而不等待
      • dispatch_semaphore_create(X) X为计数初始值 保存可访问对象线程个数
      • dispatch_semaphore_wait 是将 Dispatch Semaphore 的计数 值减一 (由于 Dispatch Semaphore 的计数值大于等于1 所以 Dispatch Semaphore 的计数 值减一)
      • dispatch_semaphore_signal 是将 Dispatch Semaphore 的计数 值加一 (排他控制处理结束 将 Dispatch Semaphore 的计数 值加一 恢复之前状态)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349