一、引用计数

  1. 在OC中,对象什么时候会被释放?

    • 答案是当对象没有被任何变量引用(也可以说是没有指针指向该对象)的时候,就会被释放。
  2. 怎么知道对象已经没有被引用了呢?

    • OC采用引用计数(reference counting)的技术来进行管理:

      • 每个对象都有一个关联的整数,称为引用计数器

      • 当代码需要使用该对象时,则将对象的引用计数加1

      • 当代码结束使用该对象时,则将对象的引用计数减1

      • 当引用计数的值变为0时,表示对象没有被任何代码使用,此时对象将被释放。

    • 内存管理的思考方式:

      • 自己创建的对象自己管理。

      • 不是自己创建的对象,自己也能持有。

      • 不再需要自己持有的对象时,释放。

      • 不是自己持有的对象,不能释放。

      • 总结一句话就是:谁创建,谁释放,谁持有,谁管理。

    • 与之对应的消息发送方法如下:

      • 增加引用计数:alloc、new、copy、mutableCopy(此为生成),retain(此为持有对象)。

      • 减少引用计数:release(此为释放对象)。

      • 释放dealloc(此为废弃对象)

    • 下面通过一个简单的例子说明:

      • 新建Dog类,重写其创建和销毁的方法:

        @implementation Dog- (instancetype)init {if (self = [super init]) {NSLog(@"小狗被派出去啦!初始引用计数为 %ld",self.retainCount);}return self;}- (void)dealloc {NSLog(@"小狗回到宠物中心");[super dealloc];}
        @end

      • 在main方法中创建dog对象,給dog发送消息:

        //模拟:宠物中心派出小狗
        Dog * dog = [[Dog alloc]init];
        //模拟:xiaoming需要和小狗玩耍,需要将其引用计数加1
        [dog retain];
        NSLog(@"小狗的引用计数为 %ld",dog.retainCount);
        //模拟:xiaoming不和小狗玩耍了,需要将其引用计数减1
        [dog release];
        NSLog(@"小狗的引用计数为 %ld",dog.retainCount);
        //没人需要和小狗玩耍了,将其引用计数减1
        [dog release];
        //将指针置nil,否则变为野指针
        dog = nil;

      • 输出结果为:

        [34691:7638855] 初始引用计数为 1
        [34691:7638855] 小狗的引用计数为 2
        [34691:7638855] 小狗的引用计数为 1
        [34691:7638855] 销毁Dog

      • 可以看到,引用计数帮助宠物中心很好的标记了小狗的使用状态,在完成任务的时候及时收回到宠物中心。

    • 思考几个问题:

      • NSString引用计数问题:

        NSString * str = @"hello guys";
        NSLog(@"%ld", str.retainCount);
        // 会发现引用计数为-1,这可以理解为NSString实际上是一个字符串常量,是没有引用计数的(或者它的引用计数是一个很大的值(使用%lu可以打印查看),对它做引用计数操作没实质上的影响)。

      • 赋值不会拥有某个对象:

        NSString * name = dog.name;
        // 这里仅仅是指针赋值操作,并不会增加name的引用计数,需要持有对象必须要发送retain消息。

      • dealloc:

        • 由于释放对象是会调用dealloc方法,因此重写dealloc方法来查看对象释放的情况,如果没有调用则会造成内存泄露。在上面的例子中我们通过重写dealloc让小狗被释放的时候打印日志来告诉我们已经完成释放。

      • 在上面例子中,如果我们增加这样一个操作:

        //没人需要和小狗玩耍了,将其引用计数减1
        [dog release];
        NSLog(@"%ld",dog.retainCount);
        // 会发现获取到的引用计数为1,为什么不是0呢?
        // 这是因为对引用计数为1的对象release时,系统知道该对象将被回收,就不会再对该对象的引用计数进行减1操作,这样可以增加对象回收的效率。

二、自动释放池

  1. 思考

    • 现在已经明确了,当不再使用一个对象时应该将其释放,但是在某些情况下,我们很难理清一个对象什么时候不再使用(比如xiaoming和小狗玩耍结束的时间不确定),这可怎么办?

    • ObjC提供autorelease方法来解决这个问题,当給一个对象发送autorelease消息时,方法会在未来某个时间給这个对象发送release消息将其释放,在这个时间段内,对象还是可以使用的。

  2. 那autorelease的原理是什么呢?

    • 原理就是对象接收到autorelease消息时,它会被添加到了当前的自动释放池中,当自动释放池被销毁时,会給池里所有的对象发送release消息。

  3. 创建

    • 方法一:使用NSAutoreleasePool来创建

      NSAutoreleasePool * pool = [[NSAutoreleasePool alloc]init];
      //这里写代码
      [pool release];

    • 方法二:使用@autoreleasepool创建

      @autoreleasepool {//这里写代码
      }

    • 自动释放池创建后,就会成为活动的池子,释放池子后,池子将释放其所包含的所有对象。

    • 以上两种方法推荐第一种,因为将内存交给ObjC管理更高效。

    • 自动释放池什么时候创建?

      • app使用过程中,会定期自动生成和销毁自动释放池,一般是在程序事件处理之前创建,当然我们也可以自行创建自动释放池,来达到我们一些特定的目的。

    • 自动释放池什么时候销毁?

      • 自动释放池的销毁时间是确定的,一般是在程序事件处理之后释放,或者由我们自己手动释放。

  4. 下面举例说明自动释放池的工作流程:

    • 代码

      //创建一个自动释放池
      NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
      //模拟:宠物中心派出小狗
      Dog * dog = [[Dog alloc]init];
      //模拟:xiaoming需要和小狗玩耍,需要将其引用计数加1
      [dog retain];
      NSLog(@"小狗的引用计数为 %ld",dog.retainCount);
      //模拟:xiaohong需要和小狗玩耍,需要将其引用计数加1
      [dog retain];
      NSLog(@"小狗的引用计数为 %ld",dog.retainCount);
      //模拟:xiaoming确定不想和小狗玩耍了,需要将其引用计数减1
      [dog release];
      NSLog(@"小狗的引用计数为 %ld",dog.retainCount);
      //模拟:xiaohong不确定何时不想和小狗玩耍了,将其设置为自动释放
      [dog autorelease];
      NSLog(@"小狗的引用计数为 %ld",dog.retainCount);
      //没人需要和小狗玩耍了,将其引用计数减1
      [dog release];
      NSLog(@"释放池子");
      [pool release];
      //创建一个自动释放池
      @autoreleasepool {//模拟:宠物中心派出小狗Dog * dog = [[Dog alloc]init];//模拟:xiaoming需要和小狗玩耍,需要将其引用计数加1
          [dog retain];NSLog(@"小狗的引用计数为 %ld",dog.retainCount);//模拟:xiaohong需要和小狗玩耍,需要将其引用计数加1
          [dog retain];NSLog(@"小狗的引用计数为 %ld",dog.retainCount);//模拟:xiaoming确定不想和小狗玩耍了,需要将其引用计数减1
          [dog release];NSLog(@"小狗的引用计数为 %ld",dog.retainCount);//模拟:xiaohong不确定何时不想和小狗玩耍了,将其设置为自动释放
          [dog autorelease];NSLog(@"小狗的引用计数为 %ld",dog.retainCount);//没人需要和小狗玩耍了,将其引用计数减1
          [dog release];NSLog(@"释放池子");
      }

    • 结果

      [34819:7801589] 初始引用计数为 1
      [34819:7801589] 小狗的引用计数为 2
      [34819:7801589] 小狗的引用计数为 3
      [34819:7801589] 小狗的引用计数为 2
      [34819:7801589] 小狗的引用计数为 2
      [34819:7801589] 释放池子
      [34819:7801589] 销毁Dog
      // 可以看到,当池子释放后,dog对象才被释放,因此在池子释放之前,xiaohong都可以尽情地和小狗玩耍。

  5. 使用自动释放池需要注意:

    • 自动释放池实质上只是在释放的时候給池中所有对象对象发送release消息,不保证对象一定会销毁,如果自动释放池向对象发送release消息后对象的引用计数仍大于1,对象就无法销毁。

    • 自动释放池中的对象会集中同一时间释放,如果操作需要生成的对象较多占用内存空间大,可以使用多个释放池来进行优化。比如在一个循环中需要创建大量的临时变量,可以创建内部的池子来降低内存占用峰值。

    • autorelease不会改变对象的引用计数。

  6. 自动释放池的常见问题

    • 在管理对象释放的问题上,自动帮助我们释放池节省了大量的时间,但是有时候它却未必会达到我们期望的效果,比如在一个循环事件中,如果循环次数较大或者事件处理占用内存较大,就会导致内存占用不断增长,可能会导致不希望看到的后果。

    • 示例代码:

      for (int i = 0; i < 100000; i ++) {NSString * log  = [NSString stringWithFormat:@"%d", i];NSLog(@"%@", log);
      }

    • 前面讲过,自动释放池的释放时间是确定的,这个例子中自动释放池会在循环事件结束时释放,那问题来了:在这个十万次的循环中,每次都会生成一个字符串并打印,这些字符串对象都放在池子中并直到循环结束才会释放,因此在循环期间内存不增长。

    • 这类问题的解决方案是在循环中创建新的自动释放池,多少个循环释放一次由我们自行决定。

      for (int i = 0; i < 100000; i ++) {@autoreleasepool {NSString * log  = [NSString stringWithFormat:@"%d", i];NSLog(@"%@", log);}
      }

  7. 被autorelease处理过的对象的释放时机

    • autorelease并不是根据作用域来决定释放时机的,而是Runloop。当一个runloop结束时系统才会一次性清理掉被autorelease处理过的对象,其实本质上说是在本次runloop迭代结束时清理掉被本次迭代期间被放到autorelease pool中的对象的。至于何时runloop结束并没有固定的duration!

三、ARC

  1. 简介

    • ARC,自动引用计数,是指iOS的内存管理使用引用计数的技术。

    • 在OC中采用Automatic Reference Counting的机制,让编译器进行内存管理。在新一代的Apple LLVM编译器中设置ARC为有效状态,就不用再次键入retain、release代码,这在降低程序崩溃、内存泄漏等风险的同时,很大程度上减少了开发程序的工作量。编译器完全清楚目标对象,并能立刻释放那些不再被使用的对象(有待斟酌)。如此一来,应用程序将具有可预测性,且运行流畅,速度也将大幅提升。(摘自苹果官方文档说明)

  2. ARC修饰符

    • __strong:强引用,持有所指向对象的所有权,无修饰符情况下的默认值。如需强制释放,可置nil。

      • 比如我们常用的定时器:NSTimer * timer = [NSTimer timerWith...];

      • 相当于NSTimer * __strong timer = [NSTimer timerWith...];

      • 当不需要使用时,强制销毁定时器:[timer invalidate];timer = nil;

    • __weak:弱引用,不持有所指向对象的所有权,引用指向的对象内存被回收之后,引用本身会置nil,避免野指针。

      • __weak __typeof(self) weakSelf = self;

    • __autoreleasing:自动释放对象的引用,一般用于传递参数

    • __unsafe_unretained:为兼容iOS5以下版本的产物,可以理解成MRC下的weak,现在基本用不到,这里不作描述

    • 使用__autoreleasing可能会遇到哪些问题?

    • 使用修饰符的正确姿势

      NSString * __weak str = @"hehe"; // 正确!
      
      __weak NSString *str = @"hehe";  // 错误!// 我相信很多人都和我一样,从开始用ARC就一直用上面那种错误的写法。
      // 那这里就有疑问了,既然文档说是错误的,为啥编译器不报错呢?好吧,是苹果考虑到很多人会用错,所以在编译器这边贴心地帮我们忽略并处理掉了这个错误:)
      // 虽然不报错,但是我们还是应该按照正确的方式去使用这些修饰符
      // 如果你以前也常常用错误的写法,那看到这里记得以后不要这么写了,哪天编译器怒了,再不支持错误的写法,就要郁闷了。
      // (参见苹果官方文档)

  3. sasf

转载于:https://www.cnblogs.com/zhuyiios/p/6710190.html

长路漫漫,唯剑作伴--Automatic Reference Counting相关推荐

  1. 同学们,长路漫漫伴你闯

    终于 最后一批学生也要启程了 不愿说离别伤感话 自古英雄出少年 相处多日 临别之时 无以相送 唯老歌一曲伴你走天涯 (挥手--) <长路漫漫任我闯>林子祥 长路漫漫任我闯 带一身胆色和热肠 ...

  2. 当我尝试写一个自动写小说的AI,长路漫漫的踩坑之路 ToT

    起因 事情是这样的,前几天我在刷B站的时候看到一个大佬用训练了一个自动写高考作文的AI 链接: https://www.bilibili.com/video/BV1pr4y1w7uM 那我就想既然别人 ...

  3. 长路漫漫,java为伴之java学习路线篇

    学习规划篇 作为一名java程序员,你需要了解你学习路线,下面就是规划了!!! 接下来对在实际开发中从上而下用到的各个部分说起: 1.前端 2.网关层 3.服务层 4.数据层 5.大数据 6.搜索引擎 ...

  4. 亚马逊CEO:Kindle电子阅读器彩色化仍长路漫漫

    C114讯 北京时间5月27日午间消息(蒋均牧)亚马逊(Amazon)Kindle电子阅读器的彩色版终会到来,但不会很快. 周二在西雅图举行的年度在线零售商股东会上,亚马逊创始人兼首席执行官杰夫·贝索 ...

  5. error: 'release' is unavailable: not available in automatic reference counting,该怎么解决

    编译出现错误: 'release' is unavailable: not available in automatic reference counting mode.. 解决办法: You nee ...

  6. Automatic Reference Counting

    Automatic Reference Counting http://clang.llvm.org/docs/AutomaticReferenceCounting.html 转载于:https:// ...

  7. Xcode 4.2 中的Automatic Reference Counting (ARC) (转)

    Automatic Reference Counting (ARC), 自动引用计数,是开发Cocoa程序时的一个编译级别的特性,用于自动内存管理. 在XCode 4.2中,使用模板新建一个工程,该工 ...

  8. Xcode 4.2 中的Automatic Reference Counting (ARC)

    Automatic Reference Counting (ARC), 自动引用计数,是开发Cocoa程序时的一个编译级别的特性,用于自动内存管理. 在XCode 4.2中,使用模板新建一个工程,该工 ...

  9. 错误解决:release' is unavailable: not available in automatic reference counting mode

    解决办法: You need to turn off Automatic Reference Counting. You do this by clicking on your project in ...

最新文章

  1. python 权限控制 linux_16linux的acl的控制权限的介绍
  2. 《看聊天记录都学不会C语言?太菜了吧》(14)这么神奇?我写了20行代码竟然一行就可以搞定?
  3. java linkedlist源码分析_LinkedList源码分析(基于Java8)
  4. 深入学习Spring框架(二)- 注解配置
  5. 数据访问优化性能(转载)
  6. 网络安全辅助工具:免费MD5解密网站
  7. android恢复短信中心号码,短信中心号码怎么查 安卓手机查看短信中心号码方法...
  8. 宁王比神联手,率锂电池和材料厂,决战锂矿
  9. 机器学习Class 6:分类及描述
  10. 遗传算法实现寻找函数最值
  11. 如何在头条做好影视解说自媒体?
  12. 【SSH项目实战】国税协同平台-1.项目介绍
  13. 21天python百度网盘_《21天学通Python》PDF 高清版百度网盘下载
  14. MySQL约束条件和多表查询方式详解
  15. 你可曾知道,Java为什么需要虚拟机?
  16. 浙江仙居“土味”民俗迎中国年:留住乡愁记忆
  17. 【华为OD机试真题2023 JAVA】网上商城优惠活动(一)
  18. Excel 去重内部原理知识点详解
  19. 免密登录远程服务器(适用于命令窗口和VScode)
  20. python网络爬虫--浏览器伪装

热门文章

  1. 一个用JavaScript生成思维导图(mindmap)的github repo
  2. 一位全加器的结构描述vhdl_小学数学结构化学习的评价实践探索
  3. c语言wb和wb 区别,C语言文件 w+与wb+区别
  4. wps 模拟分析 规划求解_【网友来稿】利用Excel求解线性规划问题
  5. yolov3训练自己的数据_YOLOv4 训练自己的数据集
  6. python pip在哪里_Python 2.7.9(Linux)中的pip在哪里
  7. python目标检测答案_你好,这里有一份2019年目标检测指南
  8. php本地测试还是线上,PHP本地与远程测试(一)
  9. echarts 堆叠柱状图3d效果_【python可视化】:pyecharts:柱形图、堆叠条形图、极坐标堆叠柱形图、极坐标堆叠分类条形图...
  10. java 三维数组长方体求体积_已知任意一个四面体的六条棱长,如何求出其体积?...