ARC机制


一、ARC简介

ARC是自iOS 5之后增加的新特性,完全消除了手动管理内存的烦琐,编译器会自动在适当的地方插入适当的

retain、release、autorelease语句。开发者不再需要担心内存管理,因为编译器为你处理了一切

ARC 是编译器特性,而不是 iOS 运行时特性,它也不是类似于其它语言中的垃圾收集器。因此 ARC 和手动内

存管理性能是一样的,有时还能更加快速,因为编译器还可以执行某些优化

二、ARC原理

1、编译器如何自动管理内存

Clang编译器项目自带一个“静态分析器”,用于志明程序里应用计数出问题的地方,比如有Person类中有一个类方法

if(self == [Person Class])
{
  Person *p = [[self alloc] init];
  NSLog(@"%@",p);
}
else
{
  NSLog(@"%@是Person的子类",self);
}

由于if语句之外无法引用p,如果没有使用ARC机制,这段代码就会有内存泄漏,此时p对象内存中的计数器值为

1,因为没有相关语句来释放,所以这个对象会一直占据着它所在的内存空间。判定内存是否泄漏所用的规则很简

明:调用Person的alloc方法所返回的p对象的保留计数比期望值要多1,之后却没有与之对应的释放操作来抵消,计算

机可以将这些规则套用在程序上,从而分析出有内存泄漏问题的对象,“静态分析器”还可以根据需要,预先加入适当

的retain或release操作以避免内存泄漏的问题,自动引用计数正是源自于此。于是在前面那段代码的if语句结束之前,

可以于对象p上自动执行release操作,也就是把代码自动改写成以下形式:

if(self == [Person Class])
{
  Person *p = [[self alloc] init];
  NSLog(@"%@",p);
  [p release];
}
else
{
  NSLog(@"%@是Person的子类",self);
}

2、ARC对代码的优化

在编译期,ARC会把能够互相抵消的retain、release和autorelease操作约简,如果发现在同一个对象上执行了多

次保留和释放操作,那么ARC有时可以成对地移除这两个操作。比如有些方法在返回对象前执行了autorelease操作,

而调用方法地代码可能需要将返回地对象保留,例如在非ARC环境下有个初始化Person的类方法

+ (id)personWithAge:(int)age
{
    Person *p = [[[self alloc] init] autorelease];
    p.age = age;
    return p;
}>

假如当某个类里有个Person类的强引用成员变量_myPerson,当初始化该类时就可以有

<span style="font-size:18px;">_myPerson = [Person PersonWithAge:10];</span>

调用personWithAge方法会返回新的Person对象,而此方法在返回对象之前为其调用了autorelease方法,由于实例变

量_myPerson是个强引用,所以编译器在设置其值的时候还需要执行一次保留操作,因此,上面那句代码等价于下面

这段手工管理引用计数代码

Person *p = [Person personWithAge:10];
_myPerson = [p retain];


或者可以设想以下,当_myPerson = [p retain]该语句不执行retain操作,也就是直接_myPerson = p,那么当自动释放

池回收对象时,p对象的引用计数值刚好执行完release之后降为0,那么该内存对象就会被回收,而_myPerson就会指

向一个僵尸对象。

此时应该能看出personWithAge方法里的autorelease和上段代码中的retain操作时多余的,为提升性能,可以将

二者删去。

当ARC在运行期检测到这一对多余的操作时,也就是autorelease其后紧跟着retain,为了优化代码,在方法中返

回自动释放的对象时会执行一个特殊的函数,此时不直接调用对象的autorelease方法,而改为调用

objc_autoreleaseReturnValue。此函数会检视当前方法返回之后即将要执行的那段代码,若发现那段代码要在返回的

对象上执行retain操作,则设置全局数据结构中的一个标志位,而不执行autorelease操作。与之相似,如果方法返回

了一个自动释放的对象,而调用方法的代码要保留此对象,那么此时不执行retain,而是执行

objc_retainAutoreleaseReturnValue函数,此函数要检测刚才提到的那个标志位,若已置位,则不执行retain操作,设

置并检测标志位要比autorelease和retain更快。下面这段代码演示类ARC是如何优化程序的

在ARC编译环境下,对于Person类中的初始化的类方法

+ (id)personWithAge:(int)age
{
    Person *p = [[[self alloc] init] autorelease];
    p.age = age;
    objc_autoreleaseReturnValue(p);
}
而使用该方法时

<span style="font-size:18px;">Person *p = [Person personWithAge:10];
_myPerson = objc_retainAutoreleaseReturnValue(p);</span>

为了得到最佳效率,这些特殊函数得实现代码都会因处理器而异,下面得伪代码演示了这一步骤

id objc_autoreleaseReturnValue(id object)
{
	if(/*后面的代码有一次retain操作*/)
	{
		set_flag(object);
		return object;		// 不会再进行autorelease操作
	}
	else
	{
		return [object autorelease];

	}
}

id objc_retainAutoreleaseReturnValue(id object)
{
	if(get_flag(object))		// 即该retain操作前有autorelease
	{
		clear_flag(object);
		return object;		// 不再进行retain操作
	}
	else
	{
		return [object retain];
	}
}


objc_autoreleaseReturnValue函数是如何检测方法调用者是否会立刻retain对象呢,这要根据处理器来决定,也就是

只有编译器的开发者能实现此函数了。

三、ARC的使用

1、规则

ARC的规则非常简单,只要还有一个强指针指向对象,对象就会保持再内存中。

2、强指针和弱指针

默认所有实例变量和局部变量都是强指针;

弱指针指向的对象被回收后,弱指针会自动变成nil指针,不会引发野指针错误

3、使用注意

(1)不能调用release、retain、autorelease、retainCount、copy
(2)可以重写dealloc,但是不能调用[super dealloc]
(3)@property : 想长期拥有某个对象,应该用strong,其他对象用weak
(4)其他基本数据类型依然用assign
(5)两端互相引用时,一端用strong、一端用weak


代码示例:

	
#import <Foundation/Foundation.h>
@class Person;

@interface Dog : NSObject
//谨记:@property后面跟着分号;
@property (nonatomic,strong) Person *person;
@end

#import "Dog.h"

@implementation Dog
- (void)dealloc
{
    NSLog(@"Dog is dealloc");
    // [super dealloc]; 不需要再调用了
    
}
@end


/*
 strong:表示该变量引用的是强指针(默认)
 weak:表示该变量引用的是弱指针(多用于对象循环引用)
 
 */

#import "Person.h"
//谨记:@class 类名;,后面跟分号;
@class Dog;

@interface Person : NSObject

@property (nonatomic,assign) int age;
//强指针类型
//@property (nonatomic,strong) Dog *dog;
//使用弱指针,当main函数执行完会先回收局部变量,因为指向够的指针是弱指针,所以狗对象会被释放,狗对象被释放之后里面的成员变量person也不存在了所以就不会指向person,所以person也会被释放
@property (nonatomic,weak) Dog *dog;

@end



#import "Person.h"

@implementation Person
- (void) dealloc
{
    NSLog(@"age = %d Person---dealloc",_age);
}
@end


/*
 指针:
 强指针:默认情况下,所有指针都是强指针 __strong
 弱指针:__weak,弱指针指向的对象会被回收
 */
#import <Foundation/Foundation.h>
#import "Person.h"
#import "Dog.h"
int main()
{
    
    Person *p = [[Person alloc] init];
    Dog *d = [[Dog alloc] init];
    
    p.age = 10;
    /*
    当两个成员变量都是强指针且循环引用,则两个强指针一直互相指向则arc机制不会释放对象。
    p.dog = d;
    d.person = p;
     */
    
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值