注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

网易杭州 QA Team

务实 专注 分享 做有态度的QA

 
 
 
 
 

日志

 
 

iOS内存管理  

来自姜cong   2016-12-15 14:21:15|  分类: iOS开发 |举报 |字号 订阅

  下载LOFTER 我的照片书  |
作为一个测试知道内存管理的重要性,但不知道如何判断内存是否存在泄漏,经过一年的iOS开发自学,整理了下iOS开发过程中的引用计数的相关知识点,有利于测试review开发代码,排查隐藏的内存泄漏。
在iOS开发过程中,内存管理遵循以下原则:

1.自己生成的对象,自己持有(alloc/new/copy/mutableCopy)

2.非自己生成的对象,自己也能持有(retain)

3.释放不再需要自己持有对象(release)

4.非自己持有的对象无法释放(dealloc)

其中这些操作都是针对对象的引用计数。

引用计数

ARC下系统维护一张引用计数表,管理内存中的对象

  • 散列的引用计数表
  • 表中记录=引用计数+内存地址(追溯到对象)
  • 引用计数对栈无效,如int, bool数据类型
    NSString *str = [NSString stringWithFormat:@"1"]
    //retain=-1  常量,由系统管理内存,存放在栈中
    NSString *str = [NSString stringWithFormat:@"测试测试测试测试测试测试"]
    //retain=1
    //长度大于12个字符,系统自动将变量存放在堆
  • 引用计数对堆无效,如NSObject
    NSMutableString *str = [NSMutableString stringWithFormat:@"1"]
    //retain=1
    //可变变量,存放在堆中

所有权修饰符

__strong

强引用

  • id类型或对象默认的的修饰符(arc)
    ARC使用
    {
      id obj = [[NSObject alloc] init];
    }
    编译器处理后代码
    {
      id __strong obj = [[NSObject alloc] init];  //编译器会自动添加修饰符表述对象
      //NSObject对象的引用计数+1
      [obj release]; //超出作用域会编译器会自动添加release释放对象
    }
    //NSObject对象的引用计数-1为0,释放
  • 循环引用(伪代码)
    相互强引用,出了作用域系统释放对象,发现项目被循环引用而无法释放成功
    {
      id obj1 = [[MyObject alloc]init]; 
      id obj2 = [[MyObject alloc]init]; 
      [obj1 setChild obj2]; //设置对象的属性,且属性为strong
      [obj2 setChild obj1];
    }

__weak

弱引用:

  • 对引用计数无效
    {
      id __weak obj = [[NSObject alloc] init]; //编译器会警告
      NSLog(@"%@", obj);    //输出nil
    }
  • autoReleasePool持有__weak对象(只针对除alloc/new/copy/mutableCopy返回对象的其他方法,比如[NSMutableArray array],及a符合命名规范的如llocObject)
    {
      id __weak obj1 = [NSMutableArray array];  
      //非alloc/new/copy/mytableCopye方法获得的返回值
      //autoreleasePool持有对象
      NSLog(@"obj:%@", obj1);   //输出为非nil
    }
    //超出作用域后,对象随着autoreleasePool释放而被释放
  • 若对象被释放,弱引用将自动失效切处于nil被赋值的状态
    id __weak obj;
    {
      id __strong temp = [[NSObject alloc] init]; 
      obj = temp;
      NSLog(@"%@", obj);    //输出非nil
    }
    NSLog(@"%@", obj);   //输出为nil

__unsafe_unretained

不安全的所有权修饰符
与__weak一致,唯一差别,对象被释放,__unsafe_unretained将自动失效但不处于nil的被赋值状态

  • 野指针
    id __unsafe_unretained obj;
    {
      id __strong temp = [[NSObject alloc] init]; 
      obj = temp;
      NSLog(@"%@", obj);    //输出非nil
    }
    NSLog(@"%@", obj);   //输出非nil

_autoreleasing

延迟释放对象

  • 显示

    @autoreleasePool{
       //输出引用计数为1:
      //autoreleasePool持有对象,obj1 = [[NSObject alloc]init];
      id __autoreleasing obj1 = [[NSObject alloc]init];
      printf("retain count = %ld\n",CFGetRetainCount((__bridge CFTypeRef)(obj1))); 
    
      //输出引用计数为2
      //autoreleasePool持有对象,
      //[NSMutableArray array] return一次持有;
      //obj2 = [NSMutableArray array]持有一次
      id __autoreleasing obj2 = [NSMutableArray array];
      printf("retain count = %ld\n",CFGetRetainCount((__bridge CFTypeRef)(obj2)));  
    }
  • 隐式(一)
    作为函数返回值,编译器会将对象注册到autoreleasePool中
    +(id)array{
       id obj = [[NSMutableArray alloc] init];  //持有对象
       return obj;
    }
    //return使得对象超出作用域,该强引用对应的自己持有的对象被释放;
    //但该对象作为函数的返回值,编译器自动将其注册到autoreleasePool
  • 隐式(二)
    使用附有__weak修饰符的变量,必定要使用注册到autoreleasePool中的对象
    //arc使用
    id __weak obj1 = obj0;
    NSLog(@"class=%@", [obj1 class]);
    //编译处理为
    id __weak obj1 = obj0;
    id __autoreleasing temp = obj1;
    NSLog(@"class=%@", [temp class]);
    //原因是弱引用,对象很可能会被释放,因此会讲弱引用所对应的对象注册到atuoreleasePool;
  • 隐式(三)
    id的指针或者对象的指针在没有显示的指定时会被附加上__autoreleasing修饰符
    -(BOOL) performOperationWithError:(NSError **)error;
    编译器处理:
    -(BOOL) performOperationWithError:(NSError * __autoreleasing*)error{
      *error = [[NSError alloc] initWithDomain:MyAppDomain code:errorcode userInfo:nil];
      return No;
    }
    //因为error是作为参数传入,因此当函数超出作用域时生成的对象会被释放,因此编译器自动将对象注册到autoreleasePool中,保证能够暂时持有对象

属性

属性修饰符
strong__strong
copy__strong
retain__strong
weak__weak
assign__unsafe_unretained
unsafe_unretain__unsafe_unretained

ARC下查看引用计数/autoreleaePool命令

查看引用计数

id obj = [[NSObject alloc]init];printf("retain count = %ld\n",CFGetRetainCount((__bridge CFTypeRef)(obj)));

查看自动释放池

extern void _objc_autoreleasePoolPrint();
_objc_autoreleasePoolPrint();

  评论这张
 
阅读(389)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017