ARC是iOS 5推出的新功能,全称叫 ARC(Automatic Reference Counting)。简单地说,就是代码中自动加入了retain/release,原先需要手动添加的用来处理内存管理的引用计数的代码可以自动地由编译器完成了。
该机能在 iOS 5/ Mac OS X 10.7 开始导入,利用 Xcode4.2 可以使用该机能。简单地理解ARC,就是通过指定的语法,让编译器(LLVM 3.0)在编译代码时,自动生成实例的引用计数管理部分代码。有一点,ARC并不是GC,它只是一种代码静态分析(Static Analyzer)工具。
使用时, 对iOS5及以上版本, 有关键字__strong, __weak, __autoreleasing 三个,
而如果需要在ios4上使用这个ARC, 则只能使用__strong, __unsafe_unretained, autoreleasing(这个没试过), __unsafe_unretained其实就和非ARC的assign是一样的效果, 指向的对象释放后,它不会自动赋为nil, 而assign也是这样子的。 __weak指向的对象释放后,会自动赋为nil. 所以还是使用ARC安全。据http://amattn.com/2011/12/07/arc_best_practices.html说并不建议在ios4上使用ARC。
定义变量时:默认为自动加上__strong。表示它为该对象的持有者。
__strong NSString *firstString = [[NSStringalloc]initWithFormat:@"China"];
// 下面的__weak表示该对象为弱引用,不会是该对象的持有者,
__weak NSString *secondString = [[NSStringalloc]initWithFormat:@"USA"];
// 对上面两句,再加下面一句运行会发现:
NSLog(@"firstString=%@, secondString=%@", firstString, secondString);
// firstString仍然指向 “China”, 而secondString指向nil。 刚赋值后,立马被释放。然后我们看一下__autoreleasing关键字的使用;(下面是两种最常见的使用情况,一个是修饰传入的参数,另一个是修饰返回的变量)
- (NSString *)whatYourName:(__autoreleasingNSString **)theName {
*theName = [[NSStringalloc]initWithFormat:@"Hello, jacky"];
__autoreleasing NSString *stoback = [[NSStringalloc]initWithFormat:@"china is the best."];
return stoback;
}
调用如下:
NSString *a =nil;
NSString *newStrig = [selfwhatYourName:&a];// [self initTheName:&a];
NSLog(@"a=%@, newStrig=%@", a, newStrig);
a=Hello, jacky, newStrig=china is the best.
// 说明一下关于__autoreleasing. &a是为了在 whatYourName方法中进行初始化,而返回的stoback是为了使返回的值不被立马释放。
总结一下ARC使用:
1代码中不能使用retain, release, retain, autorelease
2不重载dealloc(如果是释放对象内存以外的处理,是可以重载该函数的,但是不能调用[super dealloc])
3不能使用NSAllocateObject, NSDeallocateObject
4不能在C结构体中使用对象指针
5id与void *间的如果cast时需要用特定的方法(__bridge关键字)
6不能使用NSAutoReleasePool、而需要@autoreleasepool块
7不能使用“new”开始的属性名称 (如果使用会有下面的编译错误”Property’s synthesized getter follows Cocoa naming convention for returning ‘owned’ objects”)
__weak NSString *firstString = [[NSStringalloc]initWithFormat:@"China"];
__weak NSString *secondString = @"China";
NSLog(@"firstString=%@, secondString=%@", firstString, secondString);
运行时,会发现firstString=nil.而secondString=@"China"。 具体原因我认为应该是由于@"China"这样生成的串,有一个隐式的强指针指向它吧,而且这个使用域貌似还挺大的, 不光在当前方法里可以使用,在同一文件的其它方法中也可以使用。有兴趣可以再研究看看是什么原因。或许我觉得跟C#里面的字符串处理方式类似,对同样内容的固定串,内存中只会存储一个对象,从而加快处理,属特殊内存处理。如有高人路过,可顺便指导一下,以解我惑,感激。。。。
ARC的国外文章总结
因为ios7的发布,使得ARC的使用已经迫在眉捷了,这里大概根据国外网站的解释,做了一个简单的使用总结,国外原网页:http://amattn.com/2011/12/07/arc_best_practices.html
1, 普通的标量还是使用
@poperty (nonatomic, assign) int scaliest;
2, 对象类型的属性,在对象的结构上(object hierarchy)属于自已所有的,使用
@property (nonatomic, strong) id childObject;
3, 在对象的结构上(object hierarchy)上不属于自己的,通常是用来指向自己属于它的,这种使用weak,
@property (nonatomic, weak) id parentObject;
@property (nonatomic, weak) NSObject <Somedelegate> *delegate;
4, 对于block属性,使用copy.
@property (nonatomic, copy) SomeBlockType someblock;
5, IBOutlets应该使用weak, 而对于top-level的IBOutlets,则应该使用strong.
由于使用了ARC, 所以不能在dealloc中写[super dealloc], 而在dealloc方法中可以做
1, remove observers, 监视器
2, unregister for notifications, 移除通知
3, set any non-weak delegates to nil, 设置所有的非weak的代理为nil, 如果本身这个代理申请为weak,则不需要在这里去处理
4, invalidate any timers, 停止并使用timer无效
对于桥接对象,则使用桥接对象所提供的CFRelease, __brideg_retained 方法等去释放或者retain
在ARC中,输出的参数默认使用__autoreleasing, (虽然是默认,当然最好还是加上__autoreleasing)
- (BOOL)performWithError:(__autoreleasing NSError **)error
{
// ... some error occurs ...
if (error)
{
// write to the out-parameter, ARC will autorelease it
*error = [[NSError alloc] initWithDomain:@""
code:-1
userInfo:nil];
return NO;
}
else
{
return YES;
}
}
如果在外部还想使用这个NSError对象,如下即可
NSError __autoreleasing *error = error;
BOOL OK = [myObject performOperationWithError:&error];
if (!OK)
{
// handle the error.
}
接下来是@autoreleasepool的使用, 可被嵌套循环
// If someArray is huge
for (id obj in someArray)
{
@autoreleasepool
{
// or you are creating lots
// of temporary objects here...
}
}
最后是Block的使用
当需要把block加入到一个容器中时,需要先把它们进行copy
someBlockType someBlock = ^{NSLog(@"hi");};
[someArray addObject:[someBlock copy]];
使用Block的时候,最容易引发retain cycle的问题,应该如下使用
如果是当前类对象的block, 应该如下使用
__weak SomeObjectClass *weakSelf = self;
SomeBlockType someBlock = ^{
SomeObjectClass *strongSelf = weakSelf;
if (strongSelf == nil)
{
// The original self doesn't exist anymore.
// Ignore, notify or otherwise handle this case.
}
else
{
[strongSelf someMethod];
}
};
如果是当前对象里面的某个变量的block, 则应该如下使用:
SomeObjectClass *someObject = ... __weak SomeObjectClass *weakSomeObject = someObject; someObject.completionHandler = ^{ SomeObjectClass *strongSomeObject = weakSomeObject; if (strongSomeObject == nil) { // The original someObject doesn't exist anymore. // Ignore, notify or otherwise handle this case. } else { // okay, NOW we can do something with someObject [strongSomeObject someMethod]; } };
UIColor *redColor = [UIColor redColor];
CGColorRef redRef = redColor.CGColor;
// do some stuff with redRef.
如果这样使用的话, 因为redColor接下来再也没有被使用到过, 所以它会在执行完UIColor *redColor = [UIColor redColor];后就会被释放,从而会导致接下来使用这个redColor中的CGColor变量失效,这个问题通常不会在模拟器上被发现,但常常会出现在真机中。
有两种解决办法,
1,
UIColor * __autoreleasing redColor = [UIColor redColor];
CGColorRef redRef = redColor.CGColor;
2,
UIColor *redColor = [UIColor redColor];
CGColorRef redRef = CFRetain(redColor.CGColor);
// use redRef and when done release it:
CFRelease(redRef)
注意2里面的这种写法,不能写成下面的情况:否则会崩溃
UIColor *redColor = [UIColor redColor];
CGColorRef redRef = redColor.CGColor; // redColor is released right after this...
CFRetain(redRef); // This may crash...
...
最后的最后,对单例的处理,通常来说,单例类是不需要执行释放的,但如果确实想进行释放,请采用下面的方法, 这种情况通常只出现在单元测试中UnitTest.
- (void)destroyAndRecreateSingleton
{
__sharedMyClass = [[self alloc] init];
}