ARC机制

2024-05-29 04:08
文章标签 机制 arc

本文主要是介绍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;
}


这篇关于ARC机制的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1012605

相关文章

Spring排序机制之接口与注解的使用方法

《Spring排序机制之接口与注解的使用方法》本文介绍了Spring中多种排序机制,包括Ordered接口、PriorityOrdered接口、@Order注解和@Priority注解,提供了详细示例... 目录一、Spring 排序的需求场景二、Spring 中的排序机制1、Ordered 接口2、Pri

MySQL 缓存机制与架构解析(最新推荐)

《MySQL缓存机制与架构解析(最新推荐)》本文详细介绍了MySQL的缓存机制和整体架构,包括一级缓存(InnoDBBufferPool)和二级缓存(QueryCache),文章还探讨了SQL... 目录一、mysql缓存机制概述二、MySQL整体架构三、SQL查询执行全流程四、MySQL 8.0为何移除查

一文详解Java Condition的await和signal等待通知机制

《一文详解JavaCondition的await和signal等待通知机制》这篇文章主要为大家详细介绍了JavaCondition的await和signal等待通知机制的相关知识,文中的示例代码讲... 目录1. Condition的核心方法2. 使用场景与优势3. 使用流程与规范基本模板生产者-消费者示例

一文带你理解Python中import机制与importlib的妙用

《一文带你理解Python中import机制与importlib的妙用》在Python编程的世界里,import语句是开发者最常用的工具之一,它就像一把钥匙,打开了通往各种功能和库的大门,下面就跟随小... 目录一、python import机制概述1.1 import语句的基本用法1.2 模块缓存机制1.

Redis主从/哨兵机制原理分析

《Redis主从/哨兵机制原理分析》本文介绍了Redis的主从复制和哨兵机制,主从复制实现了数据的热备份和负载均衡,而哨兵机制可以监控Redis集群,实现自动故障转移,哨兵机制通过监控、下线、选举和故... 目录一、主从复制1.1 什么是主从复制1.2 主从复制的作用1.3 主从复制原理1.3.1 全量复制

Redis缓存问题与缓存更新机制详解

《Redis缓存问题与缓存更新机制详解》本文主要介绍了缓存问题及其解决方案,包括缓存穿透、缓存击穿、缓存雪崩等问题的成因以及相应的预防和解决方法,同时,还详细探讨了缓存更新机制,包括不同情况下的缓存更... 目录一、缓存问题1.1 缓存穿透1.1.1 问题来源1.1.2 解决方案1.2 缓存击穿1.2.1

Java如何通过反射机制获取数据类对象的属性及方法

《Java如何通过反射机制获取数据类对象的属性及方法》文章介绍了如何使用Java反射机制获取类对象的所有属性及其对应的get、set方法,以及如何通过反射机制实现类对象的实例化,感兴趣的朋友跟随小编一... 目录一、通过反射机制获取类对象的所有属性以及相应的get、set方法1.遍历类对象的所有属性2.获取

MySQL中的锁和MVCC机制解读

《MySQL中的锁和MVCC机制解读》MySQL事务、锁和MVCC机制是确保数据库操作原子性、一致性和隔离性的关键,事务必须遵循ACID原则,锁的类型包括表级锁、行级锁和意向锁,MVCC通过非锁定读和... 目录mysql的锁和MVCC机制事务的概念与ACID特性锁的类型及其工作机制锁的粒度与性能影响多版本

Spring使用@Retryable实现自动重试机制

《Spring使用@Retryable实现自动重试机制》在微服务架构中,服务之间的调用可能会因为一些暂时性的错误而失败,例如网络波动、数据库连接超时或第三方服务不可用等,在本文中,我们将介绍如何在Sp... 目录引言1. 什么是 @Retryable?2. 如何在 Spring 中使用 @Retryable

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization