分享

iOS ARC内存管理总结

 最初九月雪 2015-02-03

特酷吧[]采用"署名-非商业用途-保持一致"的创作共用协议,使用本文内容请遵循该协议。
博主在现在的公司已经两年了,现在正考虑着换工作。趁着这个机会,准备把一些基础的iOS知识系统的整理下,这一篇就来说说iOS内存管理方面的,主要是ARC环境。后面可以看到,虽然ARC在本质上也是采用了引用计数的概念,但作为开发者来讲,ARC环境基本上很少考虑非ARC环境中的复杂的引用计数关系。在ARC中,只需要明确强引用和弱引用的概念,其他的问题编译器自动帮助我们完成了。
一,ARC中的变量所有权修饰符
变量修饰符,主要用来标示对象的生命周期。在手动内存管理方式中没有这些概念。
ARC环境下NSObject/id变量所有权修饰符主要有以下几个:
__strong 
__weak
__unsafe_unretained
__autoreleasing
详细说明:
(1)变量默认都是__strong修饰
只要强引用存在,对象就不会释放。当超过了对象的作用域以及没有强引用时,对象会自动销毁。__strong属性基本上能适应ARC环境下的所有情况。是我们最常用的,如果不写,默认是__strong属性
(2)__weak不持有对象,只是简单引用。
__weak不会阻止对象被销销毁,也就是__weak修饰的对象在其它地方没有强引用时,会自动销毁,这时__weak变量会自动设置为nil。主要是为了防止循环引用而引入的。看这个例子:

折叠C/C++ Code复制内容到剪贴板
  1. NSString * __weak string = [[NSString alloc] initWithFormat:@“hello”];  
  2. NSLog(@"string: %@", string);  


没有强引用的变量是会被立即释放的,所以打印的string会是nil。
(3)__unsafe_unretained,类似__weak,只是不会在没有引用的时候自动设置为nil。iOS5.0以下没有引入__weak之前使用,现在基本可以不要使用了。
(4) __autoreleasing
用于标识id*的引用参数(也就是对象的指针的指针),或者需要自动释放的返回的对象(返回值默认都会加上__autoreleasing属性,可以不用写),用来指示通过引用传递的参数。主要作用是延迟对象释放,使方法内部生成的对象可以在外部访问。等同于ARC无效时调用对象的autorelease方法。典型的就是NSError。关于__autoreleasing,我们一般很少显式的使用,但是在一些情况下默认是添加了__autoreleasing修饰的:
1,作为返回值的对象默认加上了__autoreleasing修饰
参考后面的ARC的方法命名规则,在ARC时,编译器会检查方法是否以alloc/new/copy/mutableCopy/init等开头,如果不是且有返回值的话会自己将返回值加入__autoreleasing修饰。
2,访问__weak修饰的变量时,实际上是访问到了注册到__autoreleasing的对象
例如:
id __weak obj1 = obj0;
NSLog(@"%@",[obj1 class]);
实际上是执行了:
id __weak obj1 = obj0;
id __autoreleasing obj2 = obj1;
NSLog(@"%@",[obj2 class]);
3,id的指针或对象的指针在没有显式的指定时会自动加上__autoreleasing修饰。
赋值给对象指针时,所有权修饰符必须一致

(5)__strong、__weak和__autoreleasing修饰的栈变量默认都被初始化为nil
(6)无修饰符的NSObject对象指针默认是__strong,而无修饰符的id指针(也就是对象的指针的指针,id本身就是指针,好比void *)默认的修饰符为__autoreleasing

表达方法:
类名*  修饰符  变量名
或者
 修饰符 类名*  变量名
示例:

折叠C/C++ Code复制内容到剪贴板
  1. __strong NSString *string1 = @"tekuba";  
  2. NSString * __strong string11 = @"tekuba";  
  3. __weak NSString *string2 = @"tekuba";  
  4. NSString * __weak string22 = @"tekuba";  
  5. __autoreleasing NSString *string3 = @"tekuba";  
  6. NSString * __autoreleasing string33 = @"tekuba”;  


二,ARC新增的属性访问器修饰符Property Attributes
就是@property修饰的属性。
ARC之前的有retain,assign
retain:相当于arc的strong。
assign:相当于arc的weak,主要还是用在一些元数据类型int、BOOL之类的。
copy:作用是一样的
和手动管理内存方式相比,ARC增加了strong,和 weak。相当于是给各属性的类成员变量增加对应的所有权修饰符。如strong对应的是__strong。
另外需要注意:在声明类成员变量时,如果同属性声明中的所有权修饰符不一致则会引起编译错误。如:
id obj;
@property (nonatomic , weak) id obj;
obj成员变量默认是__strong,而属性修饰却用了weak,这里改成strong即可。

二,和手动内存管理的区别
(1)ARC不再使用NSZone的相关内容;
(2)自定义的dealloc方法,不需要调用[super dealloc];
(3)不能实现和调用retain、release、retainCount和autorelease等相关方法;
(4)id和void*没有自动转换,需要使用__bridge。
主要用在CF框架和NS框架数据转换的时候,典型的如下用法:
(__bridge void *)(self)
(__bridge MyNSObject *)inClientData
档案编译器不会自动管理Core foundation对象的生命周期。开发者必须根据CoreFoundation的内存管理规则,使用CFRetain和CFRelease。
关于__bridge也有几个所有权标示符:
__bridge
不改变所有权的情况下,将OC和Core foundaton对象之间转换。
__bridge_retained/CFBridgingRetain
将OC和Corefoundaton对象之间进行所有权转换。
__bridge_transfer/CFBridgingRelease
将一个非OC指针,转化为OC指针,ARC负责释放对象。
(5)开发者不能使用NSAutoreleasePool对象。ARC下使用@autoreleasepool
(6)ARC的方法命名规则
对象生成和持有的方法必须是alloc,new,copy,mutableCopy等开头的方法(ARC和非ARC都是这样),特别的init开头的方法必须是实例方法,且必须要返回对象。
基于这个规则,访问器方法不能已new,init(注意initiaize这样的不属于这个规则之内的,initMyObject这样的才算),copy等开头。比如你不能声明一个已new开头的属性,除非你给你指定一个getter。内部参数没有这个限制 。
如果你这么做了,在编译的时候会提示:
property's synthesized getter follows Cocoa naming convention for returning 'owned’ objects
原因是:You take ownership of an object if you create it using a method whose name begins with “alloc”, “new”, “copy”, or “mutableCopy”.


三,ARC模式下的单例
在之前MRC模式下通常这样写:

折叠C/C++ Code复制内容到剪贴板
  1. #import <Foundation/Foundation.h>  
  2.    
  3.  @interface Singleton : NSObject  
  4.  +(Singleton *) shareInstance;  
  5. @end  
  6.    
  7.  @implementation Singleton  
  8. +(Singleton *) shareInstance  
  9.  {  
  10.    static Singleton *sharedSingleton = nil;  
  11.    @synchronized(self){   
  12.        if(sharedSingleton == nil){  
  13.           sharedSingleton =[[super allocWithZone:NULL] init];  
  14.        }  
  15.    }  
  16.    return sharedSingleton;  
  17.  }  
  18.    
  19. - (id)init  
  20. {  
  21.     self = [super init];  
  22.     if (self) {  
  23.         // 通常在这里做一些相关的初始化任务  
  24.     }  
  25.     return self;  
  26. }  
  27.   
  28.   
  29.  + (id) allocWithZone:(NSZone *)zone  
  30.  {  
  31.    return [[self shareInstance] retain];  
  32.  }  
  33.    
  34.  - (id) copyWithZone:(NSZone*)zone  
  35.  {  
  36.    return self;  
  37.  }  
  38.    
  39.  - (id) retain  
  40.  {  
  41.    return self;  
  42.  }  
  43.    
  44.  - (NSUInteger) retainCount  
  45.  {  
  46.    return NSUIntegerMax;  
  47.  }  
  48.    
  49.   
  50.  -(oneway void)release  
  51.  {  
  52.  }  
  53.    
  54.  - (id) autorelease  
  55.  {  
  56.    return self;  
  57.  }  
  58.    
  59. -(void)dealloc  
  60. {  
  61. }  
  62.   
  63.  @end  


在ARC中由于废弃了autorelease,release等方法,再结合GCD的相关知识,单例的写法可以变得很简单,
苹果的官方例子如下:

折叠C/C++ Code复制内容到剪贴板
  1. + (NetworkManager *)sharedInstance  
  2. {  
  3.     static dispatch_once_t  onceToken;  
  4.     static NetworkManager * sSharedInstance;  
  5.    
  6.     dispatch_once(&onceToken;, ^{  
  7.         sSharedInstance = [[NetworkManager alloc] init];  
  8.     });  
  9.     return sSharedInstance;  
  10. }  


甚至可以写成宏定义的形式传入一个类名即可。

四,回过来看看手动内存管理
手动内存管理的规则是
(1)使用alloc,new,copy或者mutalbeCopy等方法时,以及使用addObject等时引用计数+1,release时引用计数减去1,当引用计数为0时释放内存。
(2)Property Attributes包括retain,assign
retain:相当于arc的strong
assign:相当于arc的weak
也基本上是这些内容。手动内存管理没有arc中的__strong等这样的对象变量修饰符。举个例子,下面的代码:
-(void)test{
    NSString *string = [[NSString alloc] init];
}
在手动内存管理中则会造成内存泄露,需要执行[string release]。在arc中完全没有问题,因为string默认是__strong,在超过作用域时自动释放。
认识所限,文章难免有错误的地方,欢迎批评指正。
转载请注明来自特酷吧,本文地址:http://www./program/346/
推荐阅读:
IOS ARC机制
Objective-C Runtime分析(二)-Class,Method,SEL,IMP
iOS App转让流程须知

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多