一、引用计数

  引用计数是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的引用计数的更多相关文章

  1. OC中对象元素的引用计数 自动释放池的相关概念

    OC中数组对象在是如何处理对象元素的引用计数问题的,同时介绍一下自动释放池的相关概念 一.数组对象是如何处理对象元素的引用计数问题[objc]  view plaincopy 1. //   2. / ...

  2. OC中的自动引用计数

    目录: 1,自动引用计数的定义 2,强引用和弱引用 3,类比手动引用 4,循环引用 5,CoreFoundation 内容: 自动引用计数的定义: (Automatic Reference Count ...

  3. c语言模拟实现oc引用计数

    #include<stdio.h> #include<stdlib.h> //在c中引入 引用计数机制 // 要解决的问题:  1,指向某块动态内存的指针有几个? //    ...

  4. OC基础15:内存管理和自动引用计数

    "OC基础"这个分类的文章是我在自学Stephen G.Kochan的<Objective-C程序设计第6版>过程中的笔记. 1.什么是ARC? (1).ARC全名为A ...

  5. OC - ARC(自动引用计数)

    1.什么是自动引用计数? 顾明思义,自动引用计数(ARC,Automatic Reference Counting)是指内存管理中对引用采取自动计数的技术. 在OC中采用ARC机制,让编译器来进行内存 ...

  6. oc引用计数原理-引用计数相关变化

    http://blog.csdn.net/null29/article/details/71191044 在 32 位环境下,对象的引用计数都保存在一个外部的表中,每一个对象的 Retain 操作,实 ...

  7. iOS开发--引用计数与ARC

    以下是关于内存管理的学习笔记:引用计数与ARC. iOS5以前自动引用计数(ARC)是在MacOS X 10.7与iOS 5中引入一项新技术,用于代替之前的手工引用计数MRC(Manual Refer ...

  8. Objective-C内存管理之-引用计数

    本文会继续深入学习OC内存管理,内容主要参考iOS高级编程,Objective-C基础教程,疯狂iOS讲义,是我学习内存管理的笔记 内存管理 1 内存管理的基本概念 1.1 Objective-C中的 ...

  9. ARC————自动引用计数

    一.内存管理/引用计数 1.引用计数式内存管理的方式(下面四种) 对象操作 OC方法 生成并持有对象 alloc/new/copy/mutableCopyd等方法 持有对象 retain方法 释放对象 ...

  10. Swift基础语法-内存管理, 自动引用计数

    1. 工作机制 Swift和OC一样,采用自动引用计数来管理内存 当有一个强引用指向某一个对象时,该对象的引用计数会自动+1 当该强引用消失时,引用计数会自动-1 当引用计数为0时,该对象会被销毁 2 ...

随机推荐

  1. CentOS 9 Basic Developing environment and IDEs installing guide

    I . Install Google Chrome browser Check the installed chrome and related packages with the command & ...

  2. 整理ionic 系列——页面生命周期

    前言 这是整理ionic的开篇,ionic 就不多介绍了,开发混合app的. 正文 Event Desc ionViewDidLoad 当页面加载的时候触发,仅在页面创建的时候触发一次,如果被缓存了, ...

  3. 推荐一个页面引导库 driver.js

    页面引导功能是 web 开发中常见的一个功能.通过页面引导功能,你可以让用户第一时间熟悉你的页面功能.今天给大家推荐一个页面引导库 driver.js. 简介 driver.js 是一款用原生 js ...

  4. 第 9章 数据分析案例:Python 岗位行情

    第 9章 数据分析案例:Python 岗位行情 9.1 数据爬取 (1)打开某招聘网站首页 https://www.lagou.com,选择"全国站",在搜索栏输入 Python, ...

  5. 阿里云全站加速DCDN重磅发布!打造新一代加速引擎

    简介: 新一代的加速引擎DCDN,安全.高效.可计算 在数字化转型变革逐步深入的当下,安全高效成为企业上云.全球化部署的关键需求. 随着应用场景复杂度不断提升.业务需求差异化发展,为了给企业提供更完善 ...

  6. 使用Databricks进行零售业需求预测的应用实践

    ​简介:本文从零售业需求预测痛点.商店商品模型预测的实践演示,介绍Databricks如何助力零售商进行需求.库存预测,实现成本把控和营收增长. 作者:李锦桂 阿里云开源大数据平台开发工程师 本文从零 ...

  7. 在 Dubbo3.0 上服务治理的实践

    ​简介: Dubbo 3.0 是在云原生背景下诞生的,使用 Dubbo 构建的微服务遵循云原生思想,能更好的复用底层云原生基础设施.贴合云原生微服务架构. Dubbo3.0 介绍 作者 | 十眠 自从 ...

  8. 数据库误操作后悔药来了:AnalyticDB PostgreSQL教你实现分布式一致性备份恢复

    ​简介: 本文将介绍AnalyticDB PostgreSQL版备份恢复的原理与使用方法. 一.背景 AnalyticDB PostgreSQL版(简称ADB PG)是阿里云数据库团队基于Postgr ...

  9. MAUI 自定义绘图入门

    在2022的5月份,某软正式发布了 MAUI 跨平台 UI 框架.我本来想着趁六一儿童节放假来写几篇关于 MAUI 入门的博客,可惜发现我不擅长写很入门的博客.再加上 MAUI 似乎是为了赶发布日期而 ...

  10. WPF 用到的触摸的 COM 接口

    本文记录 WPF 用到的触摸的 COM 接口 消息 用到了 WM_TABLET_ADDED 和 WM_TABLET_DELETED 消息 使用的代码是 src\Microsoft.DotNet.Wpf ...