OC的引用计数
一、引用计数
引用计数是Objetive-C语言的内存管理机制,用于管理OC对象(通常指包含isa指针的结构体)的内存。
一个对象的引用计数为大于0的计数,表示这个对象被持有,不能被释放,当引用计数为0时表示这个对象需要被释放掉。
改变引用计数的方法有,retain、release、alloc、autorelease、reatinautorelease、copy、multicopy方法。其中后面的两种方法内部也是调用前面alloc的方法改变union isa_t
union isa_t
{
isa_t() { }
isa_t(uintptr_t value) : bits(value) { } Class cls;
uintptr_t bits; # __x86_64__
# define ISA_MASK 0x00007ffffffffff8ULL
# define ISA_MAGIC_MASK 0x001f800000000001ULL
# define ISA_MAGIC_VALUE 0x001d800000000001ULL
struct {
uintptr_t nonpointer : 1;
uintptr_t has_assoc : 1;
uintptr_t has_cxx_dtor : 1;
uintptr_t shiftcls : 44;
uintptr_t magic : 6;
uintptr_t weakly_referenced : 1;
uintptr_t deallocating : 1;
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 8;
# define RC_ONE (1ULL<<56)
# define RC_HALF (1ULL<<7)
};
}
跟引用计数相关的为两个变量,一个是extra_rc 一个是has_sidetable_rc
第二个字段跟sidetable相关
struct SideTable {
spinlock_t slock;
RefcountMap refcnts;
weak_table_t weak_table;
void lock() { slock.lock(); }
void unlock() { slock.unlock(); }
void forceReset() { slock.forceReset(); }
// Address-ordered lock discipline for a pair of side tables.
static void lockTwo(SideTable *lock1, SideTable *lock2);
static void unlockTwo(SideTable *lock1, SideTable *lock2);
};
通过sidetable中的RefCountMap,应用计数hash表来查找某个对象的引用计数,对引用计数进行操作;具体的hash方法是
#if __LP64__
static inline uint32_t ptr_hash(uint64_t key)
{
key ^= key >> 4;
key *= 0x8a970be7488fda55;
key ^= __builtin_bswap64(key);
return (uint32_t)key;
}
#else
static inline uint32_t ptr_hash(uint32_t key)
{
key ^= key >> 4;
key *= 0x5052acdb;
key ^= __builtin_bswap32(key);
return key;
}
#endif
综上,引用计数存储在两个地方,优先存储到extra_rc中,存不下的时候放到sidetable中
retain的过程如下:
[NSObject retain];
- (id)retain {
return ((id)self)->rootRetain();
}
id objc_object::rootRetain()
{
return rootRetain(false, false);
}
id objc_object::rootRetain(bool tryRetain, bool handleOverflow)
{
if (isTaggedPointer()) return (id)this;
bool sideTableLocked = false;
bool transcribeToSideTable = false;
isa_t oldisa;
isa_t newisa;
do {
transcribeToSideTable = false;
oldisa = LoadExclusive(&isa.bits);
newisa = oldisa;
if (slowpath(!newisa.nonpointer)) {
ClearExclusive(&isa.bits);
if (!tryRetain && sideTableLocked) sidetable_unlock();
if (tryRetain) return sidetable_tryRetain() ? (id)this : nil;
else return sidetable_retain();
}
if (slowpath(tryRetain && newisa.deallocating)) {
ClearExclusive(&isa.bits);
if (!tryRetain && sideTableLocked) sidetable_unlock();
return nil;
}
uintptr_t carry;
newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry);
if (slowpath(carry)) {
if (!handleOverflow) {
ClearExclusive(&isa.bits);
return rootRetain_overflow(tryRetain);
}
if (!tryRetain && !sideTableLocked) sidetable_lock();
sideTableLocked = true;
transcribeToSideTable = true;
newisa.extra_rc = RC_HALF;
newisa.has_sidetable_rc = true;
}
} while (slowpath(!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)));
if (slowpath(transcribeToSideTable)) {
sidetable_addExtraRC_nolock(RC_HALF);
}
if (slowpath(!tryRetain && sideTableLocked)) sidetable_unlock();
return (id)this;
}
那extra_rc中能存下多少呢,一个字节最大255,当大于255会发生溢出,然后extra_rc减半,一半存储到sidetable中
二、Tagged Pointer
苹果为了优化部分小对象的存储效率,没有针对这一类小对象使用引用计数的方式,比如小NSString、NSNumber
具体使用Tagged Pointer的类型有:
OBJC_TAG_NSAtom = 0,
OBJC_TAG_1 = 1,
OBJC_TAG_NSString = 2,
OBJC_TAG_NSNumber = 3,
OBJC_TAG_NSIndexPath = 4,
OBJC_TAG_NSManagedObjectID = 5,
OBJC_TAG_NSDate = 6,
OBJC_TAG_7 = 7
比如代码:
NSString *str1 = [[NSString alloc] initWithCString:"1"
encoding:NSUTF8StringEncoding];
NSString *str2 = [[NSString alloc] initWithCString:"20000xdsfdsadwd"
encoding:NSUTF8StringEncoding]; NSLog(@"%@", [str1 valueForKey:@"retainCount"]);
NSLog(@"%@", [str2 valueForKey:@"retainCount"]);
输出:
2018-11-09 10:37:04.591196+0800 ARCTest2[506:158351] 18446744073709551615
2018-11-09 10:37:04.591240+0800 ARCTest2[506:158351] 1
具体Tagged Pointer的内存布局

也就是,对于Tagged Pointer指向的对象,值就存在于指针的内存区域中
它的特征:
Tagged Pointer 专门用来存储小的对象,例如 NSNumber 和 NSDate(后来可以存储小字符串)
Tagged Pointer 指针的值不再是地址了,而是真正的值。所以,实际上它不再是一个对象了,它只是一个披着对象皮的普通变量而已。
它的内存并不存储在堆中,也不需要 malloc 和 free,所以拥有极快的读取和创建速度。
三、附录一道相关面试题
@property (nonatomic, strong) NSString *target;
//.... dispatch_queue_t queue = dispatch_queue_create("parallel", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 1000000 ; i++) {
dispatch_async(queue, ^{
self.target = [NSString stringWithFormat:@"ksddkjalkjd%d",i];
});
}
上面的变量target在多线程访问之下,其指向的对象存在多线程中被释放的问题。但是如果将后面的string 改为小数字就不会,因为小对象的内存不需要free。
OC的引用计数的更多相关文章
- OC中对象元素的引用计数 自动释放池的相关概念
OC中数组对象在是如何处理对象元素的引用计数问题的,同时介绍一下自动释放池的相关概念 一.数组对象是如何处理对象元素的引用计数问题[objc] view plaincopy 1. // 2. / ...
- OC中的自动引用计数
目录: 1,自动引用计数的定义 2,强引用和弱引用 3,类比手动引用 4,循环引用 5,CoreFoundation 内容: 自动引用计数的定义: (Automatic Reference Count ...
- c语言模拟实现oc引用计数
#include<stdio.h> #include<stdlib.h> //在c中引入 引用计数机制 // 要解决的问题: 1,指向某块动态内存的指针有几个? // ...
- OC基础15:内存管理和自动引用计数
"OC基础"这个分类的文章是我在自学Stephen G.Kochan的<Objective-C程序设计第6版>过程中的笔记. 1.什么是ARC? (1).ARC全名为A ...
- OC - ARC(自动引用计数)
1.什么是自动引用计数? 顾明思义,自动引用计数(ARC,Automatic Reference Counting)是指内存管理中对引用采取自动计数的技术. 在OC中采用ARC机制,让编译器来进行内存 ...
- oc引用计数原理-引用计数相关变化
http://blog.csdn.net/null29/article/details/71191044 在 32 位环境下,对象的引用计数都保存在一个外部的表中,每一个对象的 Retain 操作,实 ...
- iOS开发--引用计数与ARC
以下是关于内存管理的学习笔记:引用计数与ARC. iOS5以前自动引用计数(ARC)是在MacOS X 10.7与iOS 5中引入一项新技术,用于代替之前的手工引用计数MRC(Manual Refer ...
- Objective-C内存管理之-引用计数
本文会继续深入学习OC内存管理,内容主要参考iOS高级编程,Objective-C基础教程,疯狂iOS讲义,是我学习内存管理的笔记 内存管理 1 内存管理的基本概念 1.1 Objective-C中的 ...
- ARC————自动引用计数
一.内存管理/引用计数 1.引用计数式内存管理的方式(下面四种) 对象操作 OC方法 生成并持有对象 alloc/new/copy/mutableCopyd等方法 持有对象 retain方法 释放对象 ...
- Swift基础语法-内存管理, 自动引用计数
1. 工作机制 Swift和OC一样,采用自动引用计数来管理内存 当有一个强引用指向某一个对象时,该对象的引用计数会自动+1 当该强引用消失时,引用计数会自动-1 当引用计数为0时,该对象会被销毁 2 ...
随机推荐
- modbus通信案例简单介绍
介绍: 1.仪表等其他智能设备的modbus通信协议,确定其内部功能码地址.以型号U-MIK-P350-SCN2的杭州美控公司的压力变送器为例.查看对应手册20页.2.PLC端的编程配置.以西门子s7 ...
- ABP -Vnext框架一步一步入门落地教程——使用ABP -Vnext创建一个WEBAPI接口(二)
开发主题:何谓开发应用服务端 在官方开发教程这一段的内容叫做开发应用服务端,作为现在前后端分离的开发模式来说,一个应用就分为前端页面框架和后端API,页面框架调用WEBAPI实现业务就完事了.所以咱们 ...
- 《c#高级编程》第2章C#2.0中的更改(一)——泛型
一.实例 当我们需要编写一些通用的代码,但是不确定它们将处理的数据类型时,泛型就非常有用了.下面是一个简单的 C# 泛型示例: using System; public class Example { ...
- 力扣645(java)-错误的集合(简单)
题目: 集合 s 包含从 1 到 n 的整数.不幸的是,因为数据错误,导致集合里面某一个数字复制了成了集合里面的另外一个数字的值,导致集合 丢失了一个数字 并且 有一个数字重复 . 给定一个数组 nu ...
- 力扣665(java)-非递减数列(中等)
题目: 给你一个长度为 n 的整数数组 nums ,请你判断在 最多 改变 1 个元素的情况下,该数组能否变成一个非递减数列. 我们是这样定义一个非递减数列的: 对于数组中任意的 i (0 <= ...
- 力扣498(java)-对角线遍历(中等)
题目: 给你一个大小为 m x n 的矩阵 mat ,请以对角线遍历的顺序,用一个数组返回这个矩阵中的所有元素. 输入:mat = [[1,2,3],[4,5,6],[7,8,9]]输出:[1,2,4 ...
- 力扣670(java)-最大交换(中等)
题目: 给定一个非负整数,你至多可以交换一次数字中的任意两位.返回你能得到的最大值. 示例 1 : 输入: 2736输出: 7236解释: 交换数字2和数字7.示例 2 : 输入: 9973输出: 9 ...
- HarmonyOS NEXT应用开发—自定义视图实现Tab效果
介绍 本示例介绍使用Text.List等组件,添加点击事件onclick,动画,animationTo实现自定义Tab效果. 效果预览图 使用说明 点击页签进行切换,选中态页签字体放大加粗,颜色由灰变 ...
- 阿里云安全运营中心:DDoS攻击趁虚而入,通过代理攻击已成常态
应用层DDoS攻击与传统的DDoS攻击有着很大不同.传统的DDoS攻击通过向攻击目标发起大流量并发式访问造成服务不可用,系统瘫痪,这种方式比较容易被识破,且市场上已经有成熟的应对方案.而近年来兴起的应 ...
- 阿里云日志服务SLS,打造云原生时代智能运维
2021年10月21日,阿里云针对企业运维难题,在云栖大会为大家带来了一场<智能运维论坛>的主题演讲.在会上,阿里云资深技术专家.日志服务技术负责人简志提出"云原生时代,企业业 ...