苹果iOS系统源码思考:对象的引用计数存储在哪里?--从runtime源码得到的启示
栏目: Objective-C · 发布时间: 7年前
内容简介:引言:这篇文章旨在从runtime源码中分析出iOS开发者都知道OC里面的内存管理是通过对象的引用计数来管理的,或手动MRC,或自动ARC,有些操作可以让引用计数加1,有些可以减1,一旦一个对象的引用计数为0,就回收内存了。可是,你仅仅知道这里就行了吗?指望你能造火箭造飞机的面试官可不这么想了,比如问你一句,一个对象的
引言:这篇文章旨在从runtime源码中分析出 引用计数 值本身的保存位置,适合对底层原理有兴趣的朋友,或者面试造火箭的同学(比如百度的面试官非常喜欢问底层原理:好,我知道你说了深浅复制的区别一大堆,如果我让你自己实现一个copy,你能实现吗?如果我让你实现引用计数的功能,你有思路吗?)。因而本文并 不适用于 专注业务层快速开发的同学,因为这里将贴有大量的源码。没有耐心的同学可以先收藏暂时回避一下,日后造火箭造飞机的时候再来。
核心问题
iOS开发者都知道OC里面的内存管理是通过对象的引用计数来管理的,或手动MRC,或自动ARC,有些操作可以让引用计数加1,有些可以减1,一旦一个对象的引用计数为0,就回收内存了。
可是,你仅仅知道这里就行了吗?指望你能造火箭造飞机的面试官可不这么想了,比如问你一句,一个对象的 引用计数本身 保存在哪里??不关注底层的面试者,这时候可能会懵逼。
研究方式
这篇文章不同于其它文章通过 clang编译 一个类文件以查看它的实现原理(笔者曾用clang编译分析Block的原理,传送门),而是直接通过下载runtime的源码来查看分析。
依据版本
苹果开源了runtime的代码,查看的方式既可以通过在线网页版 预览,也可以下载归档文件 到本地查看。本篇文件讨论的版本是objc4-723。
目录
-
- 类与对象
- 1.1 对象 -- Object
- 1.2 对象 -- NSObject
- 1.3 对象 -- objc_object
- 1.4 类 -- objc_class
- 1.5 NSObject,objc_object,objc_class 三者的关系
-
- 手动引用对引用计数的影响 -- retain操作
- 2.1 两种对象:NSObject与Object的引用增加
- 2.2 归根结底 -- NSObject对象的rootRetain()
-
- isa与Tagged Pointer
- 3.1 NSObject的唯一成员变量 -- isa
- 3.2 isa_t联合体里面的数据含义
- 3.3 isa_t联合体里面的宏
- 3.4 是否Tagged Pointer的判断
- 3.5 与isa类型有关的宏
- 3.6 怎么判断是否支持优化的isa指针?-- 看设备、自己设置。
- 3.7 怎么判断是否Tagged Pointer的对象?-- 看对象、自己设置
- 3.8 引用计数的存储形式 -- 散列表
-
- 散列表
- 4.1 增加引用计数 -- sidetable_retain()
- 4.2 增加引用计数 -- sidetable_tryRetain()
- 4.3 获取散列表 -- SideTable()
-
- 设置变量导致的引用计数变化 -- objc_retain操作
- 5.1 情况1
- 5.2 情况2
- 5.3 objc_storeStrong导致的retain
-
- 新建对象(分配内存与初始化)导致的引用计数变化 -- alloc 和 init 操作
- 6.1 分配内存 -- alloc
- 6.2 初始化 -- init
-
- 获取引用计数
-
- 结论
-
- 拓展阅读
1. 类与对象
下载完工程,打开查看
module.modulemap 头文件描述文件
module ObjectiveC [system] [extern_c] {
umbrella "."
export *
module * {
export *
}
module NSObject {
requires objc
header "NSObject.h"
export *
}
#if defined(BUILD_FOR_OSX)
module List {
// Uses @defs, which does not work in ObjC++ or non-ARC.
requires objc, !objc_arc, !cplusplus
header "List.h"
export *
}
module Object {
requires objc
header "Object.h"
export *
}
module Protocol {
requires objc
header "Protocol.h"
export *
}
#endif
#if !defined(BUILD_FOR_OSX)
// These file are not available outside macOS.
exclude header "hashtable.h"
exclude header "hashtable2.h"
#endif
}
复制代码
这里的Module本质上是一个描述文件,用来描述Module中包涵的内容,每个Module中必须包涵一个umbrella头文件,这个文件用来#import所有这个Module下的文件,比如 #import <UIKit/UIKit.h> 这个UIKit.h就是一个umbrella文件。关于Module更多参考这篇文章。
从 #if defined(BUILD_FOR_OSX) 这句逻辑判断可知, Object是针对macOS的,iOS开发暂时只关心NSObject即可。
1.1 对象 -- Object
Object.mm Object
#include "objc-private.h"
#undef id
#undef Class
typedef struct objc_class *Class;
typedef struct objc_object *id;
#if __OBJC2__
__OSX_AVAILABLE(10.0)
__IOS_UNAVAILABLE __TVOS_UNAVAILABLE
__WATCHOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE
OBJC_ROOT_CLASS
@interface Object {
Class isa;
}
@end
@implementation Object
+ (id)initialize
{
return self;
}
+ (id)class
{
return self;
}
-(id) retain
{
return _objc_rootRetain(self);
}
-(void) release
{
_objc_rootRelease(self);
}
-(id) autorelease
{
return _objc_rootAutorelease(self);
}
+(id) retain
{
return self;
}
+(void) release
{
}
+(id) autorelease
{
return self;
}
@end
复制代码
1.2 对象 -- NSObject
NSObject.h NSObject
#ifndef _OBJC_NSOBJECT_H_
#define _OBJC_NSOBJECT_H_
#if __OBJC__
#include <objc/objc.h>
#include <objc/NSObjCRuntime.h>
@class NSString, NSMethodSignature, NSInvocation;
@protocol NSObject
- (BOOL)isEqual:(id)object;
@property (readonly) NSUInteger hash;
@property (readonly) Class superclass;
- (Class)class OBJC_SWIFT_UNAVAILABLE("use 'type(of: anObject)' instead");
- (instancetype)self;
- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
- (BOOL)isProxy;
- (BOOL)isKindOfClass:(Class)aClass;
- (BOOL)isMemberOfClass:(Class)aClass;
- (BOOL)conformsToProtocol:(Protocol *)aProtocol;
- (BOOL)respondsToSelector:(SEL)aSelector;
- (instancetype)retain OBJC_ARC_UNAVAILABLE;
- (oneway void)release OBJC_ARC_UNAVAILABLE;
- (instancetype)autorelease OBJC_ARC_UNAVAILABLE;
- (NSUInteger)retainCount OBJC_ARC_UNAVAILABLE;
- (struct _NSZone *)zone OBJC_ARC_UNAVAILABLE;
@property (readonly, copy) NSString *description;
@optional
@property (readonly, copy) NSString *debugDescription;
@end
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0)
OBJC_ROOT_CLASS
OBJC_EXPORT
@interface NSObject <NSObject> {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
Class isa OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
}
+ (void)load;
+ (void)initialize;
- (instancetype)init
#if NS_ENFORCE_NSOBJECT_DESIGNATED_INITIALIZER
NS_DESIGNATED_INITIALIZER
#endif
;
+ (instancetype)new OBJC_SWIFT_UNAVAILABLE("use object initializers instead");
+ (instancetype)allocWithZone:(struct _NSZone *)zone OBJC_SWIFT_UNAVAILABLE("use object initializers instead");
+ (instancetype)alloc OBJC_SWIFT_UNAVAILABLE("use object initializers instead");
- (void)dealloc OBJC_SWIFT_UNAVAILABLE("use 'deinit' to define a de-initializer");
- (void)finalize OBJC_DEPRECATED("Objective-C garbage collection is no longer supported");
- (id)copy;
- (id)mutableCopy;
+ (id)copyWithZone:(struct _NSZone *)zone OBJC_ARC_UNAVAILABLE;
+ (id)mutableCopyWithZone:(struct _NSZone *)zone OBJC_ARC_UNAVAILABLE;
+ (BOOL)instancesRespondToSelector:(SEL)aSelector;
+ (BOOL)conformsToProtocol:(Protocol *)protocol;
- (IMP)methodForSelector:(SEL)aSelector;
+ (IMP)instanceMethodForSelector:(SEL)aSelector;
- (void)doesNotRecognizeSelector:(SEL)aSelector;
- (id)forwardingTargetForSelector:(SEL)aSelector OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
- (void)forwardInvocation:(NSInvocation *)anInvocation OBJC_SWIFT_UNAVAILABLE("");
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector OBJC_SWIFT_UNAVAILABLE("");
+ (NSMethodSignature *)instanceMethodSignatureForSelector:(SEL)aSelector OBJC_SWIFT_UNAVAILABLE("");
- (BOOL)allowsWeakReference UNAVAILABLE_ATTRIBUTE;
- (BOOL)retainWeakReference UNAVAILABLE_ATTRIBUTE;
+ (BOOL)isSubclassOfClass:(Class)aClass;
+ (BOOL)resolveClassMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
+ (BOOL)resolveInstanceMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
+ (NSUInteger)hash;
+ (Class)superclass;
+ (Class)class OBJC_SWIFT_UNAVAILABLE("use 'aClass.self' instead");
+ (NSString *)description;
+ (NSString *)debugDescription;
@end
#endif
#endif
复制代码
1.3 对象 -- objc_object
关键信息:
-
isa:isa_t类型的指针,详情可见下面3.2节。简单的说,它是这样的一个联合体,包含了bits(是一个uintptr_t类型的值,作为isa初始化列表中必初始化的值,可以用来获取isa结构体)和cls(该变量会指向对象所属的类的结构,在 64 位设备上会占用 8byte)。
objc-private.h objc_object
struct objc_object {
private:
isa_t isa;
public:
// ISA() assumes this is NOT a tagged pointer object
Class ISA();
// getIsa() allows this to be a tagged pointer object
Class getIsa();
// initIsa() should be used to init the isa of new objects only.
// If this object already has an isa, use changeIsa() for correctness.
// initInstanceIsa(): objects with no custom RR/AWZ
// initClassIsa(): class objects
// initProtocolIsa(): protocol objects
// initIsa(): other objects
void initIsa(Class cls /*nonpointer=false*/);
void initClassIsa(Class cls /*nonpointer=maybe*/);
void initProtocolIsa(Class cls /*nonpointer=maybe*/);
void initInstanceIsa(Class cls, bool hasCxxDtor);
// changeIsa() should be used to change the isa of existing objects.
// If this is a new object, use initIsa() for performance.
Class changeIsa(Class newCls);
bool hasNonpointerIsa();
bool isTaggedPointer();
bool isBasicTaggedPointer();
bool isExtTaggedPointer();
bool isClass();
// object may have associated objects?
bool hasAssociatedObjects();
void setHasAssociatedObjects();
// object may be weakly referenced?
bool isWeaklyReferenced();
void setWeaklyReferenced_nolock();
// object may have -.cxx_destruct implementation?
bool hasCxxDtor();
// Optimized calls to retain/release methods
id retain();
void release();
id autorelease();
// Implementations of retain/release methods
id rootRetain();
bool rootRelease();
id rootAutorelease();
bool rootTryRetain();
bool rootReleaseShouldDealloc();
uintptr_t rootRetainCount();
// Implementation of dealloc methods
bool rootIsDeallocating();
void clearDeallocating();
void rootDealloc();
private:
void initIsa(Class newCls, bool nonpointer, bool hasCxxDtor);
// Slow paths for inline control
id rootAutorelease2();
bool overrelease_error();
#if SUPPORT_NONPOINTER_ISA
// Unified retain count manipulation for nonpointer isa
id rootRetain(bool tryRetain, bool handleOverflow);
bool rootRelease(bool performDealloc, bool handleUnderflow);
id rootRetain_overflow(bool tryRetain);
bool rootRelease_underflow(bool performDealloc);
void clearDeallocating_slow();
// Side table retain count overflow for nonpointer isa
void sidetable_lock();
void sidetable_unlock();
void sidetable_moveExtraRC_nolock(size_t extra_rc, bool isDeallocating, bool weaklyReferenced);
bool sidetable_addExtraRC_nolock(size_t delta_rc);
size_t sidetable_subExtraRC_nolock(size_t delta_rc);
size_t sidetable_getExtraRC_nolock();
#endif
// Side-table-only retain count
bool sidetable_isDeallocating();
void sidetable_clearDeallocating();
bool sidetable_isWeaklyReferenced();
void sidetable_setWeaklyReferenced_nolock();
id sidetable_retain();
id sidetable_retain_slow(SideTable& table);
uintptr_t sidetable_release(bool performDealloc = true);
uintptr_t sidetable_release_slow(SideTable& table, bool performDealloc = true);
bool sidetable_tryRetain();
uintptr_t sidetable_retainCount();
#if DEBUG
bool sidetable_present();
#endif
};
复制代码
1.4 类 -- objc_class
关键信息:
-
isa: 继承于objc_object -
superclass: 指向自己父类的指针 -
cache: 方法缓存 -
bits: 它是一个class_data_bits_t类型的指针。作为本类的实例方法链表。
注意区别:
这里的 bits 是 class_data_bits_t 类型的,上一节objc_object的 isa_t 类型数据中也有一个 uintptr_t 类型的 bits ,但是这是两种结构。
由此可见, objc_class 继承于 objc_object , 所以也是包含一个isa的类。在OC里,不只是对象的实例包含一个isa,这个对象的类本身也有这么一个isa,类本身也是一个对象。
objc-runtime-new.h objc_class
struct objc_class : objc_object {
// Class ISA;
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
class_rw_t *data() {
return bits.data();
}
void setData(class_rw_t *newData) {
bits.setData(newData);
}
void setInfo(uint32_t set) {
assert(isFuture() || isRealized());
data()->setFlags(set);
}
void clearInfo(uint32_t clear) {
assert(isFuture() || isRealized());
data()->clearFlags(clear);
}
// set and clear must not overlap
void changeInfo(uint32_t set, uint32_t clear) {
assert(isFuture() || isRealized());
assert((set & clear) == 0);
data()->changeFlags(set, clear);
}
bool hasCustomRR() {
return ! bits.hasDefaultRR();
}
void setHasDefaultRR() {
assert(isInitializing());
bits.setHasDefaultRR();
}
void setHasCustomRR(bool inherited = false);
void printCustomRR(bool inherited);
bool hasCustomAWZ() {
return ! bits.hasDefaultAWZ();
}
void setHasDefaultAWZ() {
assert(isInitializing());
bits.setHasDefaultAWZ();
}
void setHasCustomAWZ(bool inherited = false);
void printCustomAWZ(bool inherited);
bool instancesRequireRawIsa() {
return bits.instancesRequireRawIsa();
}
void setInstancesRequireRawIsa(bool inherited = false);
void printInstancesRequireRawIsa(bool inherited);
bool canAllocNonpointer() {
assert(!isFuture());
return !instancesRequireRawIsa();
}
bool canAllocFast() {
assert(!isFuture());
return bits.canAllocFast();
}
bool hasCxxCtor() {
// addSubclass() propagates this flag from the superclass.
assert(isRealized());
return bits.hasCxxCtor();
}
void setHasCxxCtor() {
bits.setHasCxxCtor();
}
bool hasCxxDtor() {
// addSubclass() propagates this flag from the superclass.
assert(isRealized());
return bits.hasCxxDtor();
}
void setHasCxxDtor() {
bits.setHasCxxDtor();
}
bool isSwift() {
return bits.isSwift();
}
// Return YES if the class's ivars are managed by ARC,
// or the class is MRC but has ARC-style weak ivars.
bool hasAutomaticIvars() {
return data()->ro->flags & (RO_IS_ARC | RO_HAS_WEAK_WITHOUT_ARC);
}
// Return YES if the class's ivars are managed by ARC.
bool isARC() {
return data()->ro->flags & RO_IS_ARC;
}
#if SUPPORT_NONPOINTER_ISA
// Tracked in non-pointer isas; not tracked otherwise
#else
bool instancesHaveAssociatedObjects() {
// this may be an unrealized future class in the CF-bridged case
assert(isFuture() || isRealized());
return data()->flags & RW_INSTANCES_HAVE_ASSOCIATED_OBJECTS;
}
void setInstancesHaveAssociatedObjects() {
// this may be an unrealized future class in the CF-bridged case
assert(isFuture() || isRealized());
setInfo(RW_INSTANCES_HAVE_ASSOCIATED_OBJECTS);
}
#endif
bool shouldGrowCache() {
return true;
}
void setShouldGrowCache(bool) {
// fixme good or bad for memory use?
}
bool isInitializing() {
return getMeta()->data()->flags & RW_INITIALIZING;
}
void setInitializing() {
assert(!isMetaClass());
ISA()->setInfo(RW_INITIALIZING);
}
bool isInitialized() {
return getMeta()->data()->flags & RW_INITIALIZED;
}
void setInitialized();
bool isLoadable() {
assert(isRealized());
return true; // any class registered for +load is definitely loadable
}
IMP getLoadMethod();
// Locking: To prevent concurrent realization, hold runtimeLock.
bool isRealized() {
return data()->flags & RW_REALIZED;
}
// Returns true if this is an unrealized future class.
// Locking: To prevent concurrent realization, hold runtimeLock.
bool isFuture() {
return data()->flags & RW_FUTURE;
}
bool isMetaClass() {
assert(this);
assert(isRealized());
return data()->ro->flags & RO_META;
}
// NOT identical to this->ISA when this is a metaclass
Class getMeta() {
if (isMetaClass()) return (Class)this;
else return this->ISA();
}
bool isRootClass() {
return superclass == nil;
}
bool isRootMetaclass() {
return ISA() == (Class)this;
}
const char *mangledName() {
// fixme can't assert locks here
assert(this);
if (isRealized() || isFuture()) {
return data()->ro->name;
} else {
return ((const class_ro_t *)data())->name;
}
}
const char *demangledName(bool realize = false);
const char *nameForLogging();
// May be unaligned depending on class's ivars.
uint32_t unalignedInstanceStart() {
assert(isRealized());
return data()->ro->instanceStart;
}
// Class's instance start rounded up to a pointer-size boundary.
// This is used for ARC layout bitmaps.
uint32_t alignedInstanceStart() {
return word_align(unalignedInstanceStart());
}
// May be unaligned depending on class's ivars.
uint32_t unalignedInstanceSize() {
assert(isRealized());
return data()->ro->instanceSize;
}
// Class's ivar size rounded up to a pointer-size boundary.
uint32_t alignedInstanceSize() {
return word_align(unalignedInstanceSize());
}
size_t instanceSize(size_t extraBytes) {
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;
return size;
}
void setInstanceSize(uint32_t newSize) {
assert(isRealized());
if (newSize != data()->ro->instanceSize) {
assert(data()->flags & RW_COPIED_RO);
*const_cast<uint32_t *>(&data()->ro->instanceSize) = newSize;
}
bits.setFastInstanceSize(newSize);
}
void chooseClassArrayIndex();
void setClassArrayIndex(unsigned Idx) {
bits.setClassArrayIndex(Idx);
}
unsigned classArrayIndex() {
return bits.classArrayIndex();
}
};
复制代码
1.5 NSObject,objc_object,objc_class 三者的关系
1)NSObject与objc_class
NSObject有一个Class类型,名为isa成员变量
继续查看Class的本质,可以发现Class 其实就是 C 语言定义的结构体类型(struct objc_class)的指针,这个声明说明 Objective-C 的 类 实际上就是 struct objc_class 。
另外,第二个定义是经常遇到的 id 类型,这里可以看出 id 类型是 C 语言定义的结构体类型(struct objc_object)的指针,我们知道我们可以用 id 来声明一个对象,所以这也说明了 Objective-C 的 对象 实际上就是 struct objc_object 。
2)objc_object与objc_class
继续查看objc_class的本质,可以发现objc_class是一个 继承 自objc_object的结构体。所以 Objective-C 中的 类 自身也是一个 对象 ,只是除了 objc_object 中定义的成员变量外,还有另外三个成员变量:superclass、cache 和 bits。
注意,这里面的 “结构体” 并非 C语言 里面的结构体,而是 C++语言 里面的结构体,而且这个概念仅限字面意思的结构体。严格来讲,其实struct关键字定义的是 类 ,跟class关键字定义的类除了默认访问权限的区别,没有区别。这一点,国内人写的C++书籍却很少有注意到。下面是比较权威的《C++ Primer》(第546页)一书关于这点的说明。
3)知识补课
C++中的struct对C中的struct进行了扩充,它已经不再只是一个包含不同数据类型的数据结构了,它已经获取了太多的功能。下面简单列一下C++的struct跟C中的struct不一样的地方:
- struct能包含成员函数
- struct能继承
- struct能实现多态
2. 手动引用对引用计数的影响 -- retain操作
2.1 两种对象:NSObject与Object的引用增加
① NSObject的retain
NSObject.mm retain
+ (id)retain {
return (id)self;
}
// Replaced by ObjectAlloc
- (id)retain {
return ((id)self)->rootRetain();
}
复制代码
② Object的retain
Object.mm retain
+(id) retain
{
return self;
}
-(id) retain
{
return _objc_rootRetain(self);
}
复制代码
NSObject.mm _objc_rootRetain(id obj)
id
_objc_rootRetain(id obj)
{
assert(obj);
return obj->rootRetain();
}
复制代码
可见,无论是NSObject还是Object的 retain ,归根结底,调用的都是 objc_object 的 rootRetain() 。
2.2 归根结底 -- NSObject对象的rootRetain()
objc4/objc4-723/runtime/objc-object.h objc_object::rootRetain()
ALWAYS_INLINE id
objc_object::rootRetain()
{
return rootRetain(false, false);
}
复制代码
objc4/objc4-723/runtime/objc-object.h objc_object::rootRetain(bool tryRetain, bool handleOverflow)
ALWAYS_INLINE 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();
}
// don't check newisa.fast_rr; we already called any RR overrides
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); // extra_rc++
if (slowpath(carry)) {
// newisa.extra_rc++ overflowed
if (!handleOverflow) {
ClearExclusive(&isa.bits);
return rootRetain_overflow(tryRetain);
}
// Leave half of the retain counts inline and
// prepare to copy the other half to the side table.
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)) {
// Copy the other half of the retain counts to the side table.
sidetable_addExtraRC_nolock(RC_HALF);
}
if (slowpath(!tryRetain && sideTableLocked)) sidetable_unlock();
return (id)this;
}
复制代码
其中,手动retain对引用计数的影响关键在这么一句话:
newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry); // extra_rc++ 复制代码
对isa的 extra_rc 变量进行+1,前面说到isa会存很多东西。
3. isa与Tagged Pointer
3.1 NSObject的唯一成员变量 -- isa
NSObject.h NSObject的isa
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0)
OBJC_ROOT_CLASS
OBJC_EXPORT
@interface NSObject <NSObject> {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
Class isa OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
}
复制代码
其中, Class isa 继续查看Class的定义:
objc-private.h Class
typedef struct objc_class *Class; typedef struct objc_object *id; 复制代码
其中,objc_object类内部结构:
其中,私有的成员数据isa为isa_t类型的联合体:
objc-private.h isa_t
union isa_t
{
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
#if SUPPORT_PACKED_ISA
// extra_rc must be the MSB-most field (so it matches carry/overflow flags)
// nonpointer must be the LSB (fixme or get rid of it)
// shiftcls must occupy the same bits that a real class pointer would
// bits + RC_ONE is equivalent to extra_rc + 1
// RC_HALF is the high bit of extra_rc (i.e. half of its range)
// future expansion:
// uintptr_t fast_rr : 1; // no r/r overrides
// uintptr_t lock : 2; // lock for atomic property, @synch
// uintptr_t extraBytes : 1; // allocated with extra bytes
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
struct {
uintptr_t nonpointer : 1;
uintptr_t has_assoc : 1;
uintptr_t has_cxx_dtor : 1;
uintptr_t shiftcls : 33; // MACH_VM_MAX_ADDRESS 0x1000000000
uintptr_t magic : 6;
uintptr_t weakly_referenced : 1;
uintptr_t deallocating : 1;
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 19;
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
};
# elif __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; // MACH_VM_MAX_ADDRESS 0x7fffffe00000
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)
};
# else
# error unknown architecture for packed isa
# endif
// SUPPORT_PACKED_ISA
#endif
#if SUPPORT_INDEXED_ISA
# if __ARM_ARCH_7K__ >= 2
# define ISA_INDEX_IS_NPI 1
# define ISA_INDEX_MASK 0x0001FFFC
# define ISA_INDEX_SHIFT 2
# define ISA_INDEX_BITS 15
# define ISA_INDEX_COUNT (1 << ISA_INDEX_BITS)
# define ISA_INDEX_MAGIC_MASK 0x001E0001
# define ISA_INDEX_MAGIC_VALUE 0x001C0001
struct {
uintptr_t nonpointer : 1;
uintptr_t has_assoc : 1;
uintptr_t indexcls : 15;
uintptr_t magic : 4;
uintptr_t has_cxx_dtor : 1;
uintptr_t weakly_referenced : 1;
uintptr_t deallocating : 1;
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 7;
# define RC_ONE (1ULL<<25)
# define RC_HALF (1ULL<<6)
};
# else
# error unknown architecture for indexed isa
# endif
// SUPPORT_INDEXED_ISA
#endif
};
复制代码
其中, cls 变量会指向对象所属的类的结构,在 64 位设备上会占用 8byte。
另外, bits 变量保存着isa的唯一标志(可以根据bits获取isa),是一个类型为 uintptr_t 的数据, uintptr_t 的定义:
typedef unsigned long uintptr_t; 复制代码
知识回顾
不熟悉C++的朋友可能很难看出来 bits 会是如何初始化的,其实,这是一种与构造函数并列的初始化办法 --初始化列表。关于初始化列表的定义,截取百度百科的一段话:
所以,再回过来看 bits , bits 以 isa_t(uintptr_t value) 中的value为初始化的值:
例如isa初始化的API objc_object::initIsa(Class cls)`中,有这样一句:
isa_t newisa(0); newisa.bits = ISA_INDEX_MAGIC_VALUE; //... 复制代码
而这个 bits 值可以用来获取isa(注意区分左右两边的 bits 分别是两个东西):
isa_t bits = LoadExclusive(&isa.bits); 复制代码
其中,LoadExclusive根据平台的不同,实现体并不一样,这是 __arm64__ 平台的实现体:
#if __arm64__
static ALWAYS_INLINE
uintptr_t
LoadExclusive(uintptr_t *src)
{
uintptr_t result;
asm("ldxr %x0, [%x1]"
: "=r" (result)
: "r" (src), "m" (*src));
return result;
}
复制代码
对这个isa (这里是左边的 bits ,它是个 isa ,而非右边的 uintptr_t ) 的调用,比如获取引用计数的源代码中就有几处:
inline uintptr_t
objc_object::rootRetainCount()
{
if (isTaggedPointer()) return (uintptr_t)this;
sidetable_lock();
isa_t bits = LoadExclusive(&isa.bits);
ClearExclusive(&isa.bits);
if (bits.nonpointer) {
uintptr_t rc = 1 + bits.extra_rc;
if (bits.has_sidetable_rc) {
rc += sidetable_getExtraRC_nolock();
}
sidetable_unlock();
return rc;
}
sidetable_unlock();
return sidetable_retainCount();
}
复制代码
调用的有: bits.extra_rc bits.nonpointer bits.has_sidetable_rc
3.2 isa_t联合体里面struct的数据含义
nonpointer
该变量占用 1bit 内存空间,可以有两个值:0 和 1,分别代表不同的 isa_t 的类型:
-
0 表示 isa_t 没有开启指针优化,不使用 isa_t 中定义的结构体。访问 objc_object 的 isa 会直接返回 isa_t 结构中的 cls 变量,cls 变量会指向对象所属的类的结构,在 64 位设备上会占用 8byte。
-
1 表示 isa_t 开启了指针优化,不能直接访问 objc_object 的 isa 成员变量 (因为 isa 已经不是一个合法的内存指针了,而是一个 Tagged Pointer ),从其名字 nonpointer 也可获知这个 isa 已经不是一个指针了。但是 isa 中包含了类信息、对象的引用计数等信息,在 64 位设备上充分利用了内存空间。
shiftcls
存储类指针的值。开启指针优化的情况下,在 arm64 架构中有 33 位用来存储类指针。
has_assoc
该变量与对象的关联引用有关,当对象有关联引用时,释放对象时需要做额外的逻辑。关联引用就是我们通常用 objc_setAssociatedObject 方法设置给对象的,这里对于关联引用不做过多分析,如果后续有时间写关联引用实现时再深入分析关联引用有关的代码。
has_cxx_dtor
表示该对象是否有 C++ 或者 Objc 的析构器,如果有析构函数,则需要做析构逻辑,如果没有,则可以更快的释放对象。
magic
用于判断对象是否已经完成了初始化,在 arm64 中 0x16 是调试器判断当前对象是真的对象还是没有初始化的空间(在 x86_64 中该值为 0x3b)。
weakly_referenced
标志对象是否被指向或者曾经指向一个 ARC 的弱变量,没有弱引用的对象可以更快释放。
deallocating
标志对象是否正在释放内存。
extra_rc
表示该对象的引用计数值,实际上是引用计数值减 1,例如,如果对象的引用计数为 10,那么 extra_rc 为 9。如果引用计数大于 10,则需要使用到下面的 has_sidetable_rc。
has_sidetable_rc
当对象引用计数大于 10 时,则has_sidetable_rc 的值为 1,那么引用计数会存储在一个叫 SideTable 的类的属性中,这是一个散列表。
ISA_MAGIC_MASK
通过掩码方式获取 magic 值。
ISA_MASK
通过掩码方式获取 isa 的类指针值。
RC_ONE 和 RC_HALF
用于引用计数的相关计算。
3.3 isa_t联合体里面的宏
SUPPORT_PACKED_ISA
表示平台是否支持在 isa 指针中插入除 Class 之外的信息。
nonpointer
小结: 在 iOS 以及 MacOSX 设备上, SUPPORT_PACKED_ISA 定义为 1 。
__arm64__ 、 __x86_64__
表示 CPU 架构,例如电脑一般是 __x86_64__ 架构,手机一般是 arm 结构,这里 64 代表是 64 位 CPU。上面只列出了 __arm64__ 架构的定义。
小结: iOS 设备上 __arm64__ 是 1 。
SUPPORT_INDEXED_ISA
SUPPORT_INDEXED_ISA 表示 isa_t 中存放的 Class 信息是 Class 的地址,还是一个索引(根据该索引可在类信息表中查找该类结构地址)。可以看出,多了一个 uintptr_t indexcls : 15; 。
小结: iOS 设备上 SUPPORT_INDEXED_ISA 是 0 。
3.4 是否Tagged Pointer的判断
objc-object.h objc_object::isTaggedPointer()
inline bool
objc_object::isTaggedPointer()
{
return _objc_isTaggedPointer(this);
}
复制代码
objc-internal.h _objc_isTaggedPointer(const void * _Nullable ptr)
static inline bool
_objc_isTaggedPointer(const void * _Nullable ptr)
{
return ((uintptr_t)ptr & _OBJC_TAG_MASK) == _OBJC_TAG_MASK;
}
复制代码
3.5 与isa类型有关的宏
SUPPORT_NONPOINTER_ISA
用于标记是否支持优化的 isa 指针,其字面含义意思是 isa 的内容不再是类的指针了,而是包含了更多信息,比如引用计数,析构状态,被其他 weak 变量引用情况。下面看看 SUPPORT_NONPOINTER_ISA 及其相关宏的定义:
objc-config.h SUPPORT_TAGGED_POINTERS
// Define SUPPORT_TAGGED_POINTERS=1 to enable tagged pointer objects // Be sure to edit tagged pointer SPI in objc-internal.h as well. #if !(__OBJC2__ && __LP64__) # define SUPPORT_TAGGED_POINTERS 0 #else # define SUPPORT_TAGGED_POINTERS 1 #endif // Define SUPPORT_MSB_TAGGED_POINTERS to use the MSB // as the tagged pointer marker instead of the LSB. // Be sure to edit tagged pointer SPI in objc-internal.h as well. #if !SUPPORT_TAGGED_POINTERS || !TARGET_OS_IPHONE # define SUPPORT_MSB_TAGGED_POINTERS 0 #else # define SUPPORT_MSB_TAGGED_POINTERS 1 #endif // Define SUPPORT_INDEXED_ISA=1 on platforms that store the class in the isa // field as an index into a class table. // Note, keep this in sync with any .s files which also define it. // Be sure to edit objc-abi.h as well. #if __ARM_ARCH_7K__ >= 2 # define SUPPORT_INDEXED_ISA 1 #else # define SUPPORT_INDEXED_ISA 0 #endif // Define SUPPORT_PACKED_ISA=1 on platforms that store the class in the isa // field as a maskable pointer with other data around it. #if (!__LP64__ || TARGET_OS_WIN32 || TARGET_OS_SIMULATOR) # define SUPPORT_PACKED_ISA 0 #else # define SUPPORT_PACKED_ISA 1 #endif // Define SUPPORT_NONPOINTER_ISA=1 on any platform that may store something // in the isa field that is not a raw pointer. #if !SUPPORT_INDEXED_ISA && !SUPPORT_PACKED_ISA # define SUPPORT_NONPOINTER_ISA 0 #else # define SUPPORT_NONPOINTER_ISA 1 #endif 复制代码
3.6 怎么判断是否支持优化的isa指针?-- 看设备、自己设置。
-
已知iOS系统的
SUPPORT_PACKED_ISA为1,SUPPORT_INDEXED_ISA为0,根据4.5节中源代码的定义可知,iOS系统的SUPPORT_NONPOINTER_ISA为1。 -
在环境变量中设置
OBJC_DISABLE_NONPOINTER_ISA。
即, iOS系统 支持 优化的isa指针 。
在 64 位环境下,优化的 isa 指针并不是就一定会存储引用计数,毕竟用 19bit (iOS 系统)保存引用计数不一定够。需要注意的是这 19 位保存的是引用计数的值减一。
3.7 怎么判断是否Tagged Pointer的对象?-- 看对象、自己设置
-
可以启用Tagged Pointer的类对象有:NSDate、NSNumber、NSString。Tagged Pointer专门用来存储小的对象。
-
在环境变量中设置OBJC_DISABLE_TAGGED_POINTERS=YES强制不启用Tagged Pointer。
3.8 引用计数的存储形式 -- 散列表
下面对 sidetable_retain 进行分析。
4. 散列表
4.1 增加引用计数 -- sidetable_retain()
第2节的增加引用假设,以及后面第8节的获取引用计数会用到下面的API:
NSObject.mm objc_object::sidetable_retain()
id
objc_object::sidetable_retain()
{
#if SUPPORT_NONPOINTER_ISA
assert(!isa.nonpointer);
#endif
SideTable& table = SideTables()[this];
table.lock();
size_t& refcntStorage = table.refcnts[this];
if (! (refcntStorage & SIDE_TABLE_RC_PINNED)) {
refcntStorage += SIDE_TABLE_RC_ONE;
}
table.unlock();
return (id)this;
}
复制代码
4.2 增加引用计数 -- sidetable_tryRetain()
NSObject.mm objc_object::sidetable_tryRetain()
bool
objc_object::sidetable_tryRetain()
{
#if SUPPORT_NONPOINTER_ISA
assert(!isa.nonpointer);
#endif
SideTable& table = SideTables()[this];
// NO SPINLOCK HERE
// _objc_rootTryRetain() is called exclusively by _objc_loadWeak(),
// which already acquired the lock on our behalf.
// fixme can't do this efficiently with os_lock_handoff_s
// if (table.slock == 0) {
// _objc_fatal("Do not call -_tryRetain.");
// }
bool result = true;
RefcountMap::iterator it = table.refcnts.find(this);
if (it == table.refcnts.end()) {
table.refcnts[this] = SIDE_TABLE_RC_ONE;
} else if (it->second & SIDE_TABLE_DEALLOCATING) {
result = false;
} else if (! (it->second & SIDE_TABLE_RC_PINNED)) {
it->second += SIDE_TABLE_RC_ONE;
}
return result;
}
复制代码
4.3 获取散列表 -- SideTable()
NSObject.mm SideTable
struct SideTable {
spinlock_t slock;
RefcountMap refcnts;
weak_table_t weak_table;
SideTable() {
memset(&weak_table, 0, sizeof(weak_table));
}
~SideTable() {
_objc_fatal("Do not delete SideTable.");
}
void lock() { slock.lock(); }
void unlock() { slock.unlock(); }
void forceReset() { slock.forceReset(); }
// Address-ordered lock discipline for a pair of side tables.
template<HaveOld, HaveNew>
static void lockTwo(SideTable *lock1, SideTable *lock2);
template<HaveOld, HaveNew>
static void unlockTwo(SideTable *lock1, SideTable *lock2);
};
复制代码
其中, RefcountMap 以及 HaveOld , HaveNew 的定义为:
// RefcountMap disguises its pointers because we
// don't want the table to act as a root for `leaks`.
typedef objc::DenseMap<DisguisedPtr<objc_object>,size_t,true> RefcountMap;
// Template parameters.
enum HaveOld { DontHaveOld = false, DoHaveOld = true };
enum HaveNew { DontHaveNew = false, DoHaveNew = true };
复制代码
llvm-DenseMap.h DenseMap/DenseMapBase
DenseMapBase
DenseMap
5. 设置变量导致的引用计数变化 -- objc_retain操作
5.1 情况1
runtime.h 设置strong变量
/**
* Sets the value of an instance variable in an object.
*
* @param obj The object containing the instance variable whose value you want to set.
* @param ivar The Ivar describing the instance variable whose value you want to set.
* @param value The new value for the instance variable.
*
* @note Instance variables with known memory management (such as ARC strong and weak)
* use that memory management. Instance variables with unknown memory management
* are assigned as if they were strong.
* @note \c object_setIvar is faster than \c object_setInstanceVariable if the Ivar
* for the instance variable is already known.
*/
OBJC_EXPORT void
object_setIvarWithStrongDefault(id _Nullable obj, Ivar _Nonnull ivar,
id _Nullable value)
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0, 2.0);
复制代码
objc-class.mm object_setIvarWithStrongDefault
void object_setIvarWithStrongDefault(id obj, Ivar ivar, id value)
{
return _object_setIvar(obj, ivar, value, true /*strong default*/);
}
复制代码
objc-class.mm _object_setIvar
static ALWAYS_INLINE
void _object_setIvar(id obj, Ivar ivar, id value, bool assumeStrong)
{
if (!obj || !ivar || obj->isTaggedPointer()) return;
ptrdiff_t offset;
objc_ivar_memory_management_t memoryManagement;
_class_lookUpIvar(obj->ISA(), ivar, offset, memoryManagement);
if (memoryManagement == objc_ivar_memoryUnknown) {
if (assumeStrong) memoryManagement = objc_ivar_memoryStrong;
else memoryManagement = objc_ivar_memoryUnretained;
}
id *location = (id *)((char *)obj + offset);
switch (memoryManagement) {
case objc_ivar_memoryWeak: objc_storeWeak(location, value); break;
case objc_ivar_memoryStrong: objc_storeStrong(location, value); break;
case objc_ivar_memoryUnretained: *location = value; break;
case objc_ivar_memoryUnknown: _objc_fatal("impossible");
}
}
复制代码
5.2 情况2
runtime.h 设置unsafe_unretained变量
/**
* Sets the value of an instance variable in an object.
*
* @param obj The object containing the instance variable whose value you want to set.
* @param ivar The Ivar describing the instance variable whose value you want to set.
* @param value The new value for the instance variable.
*
* @note Instance variables with known memory management (such as ARC strong and weak)
* use that memory management. Instance variables with unknown memory management
* are assigned as if they were unsafe_unretained.
* @note \c object_setIvar is faster than \c object_setInstanceVariable if the Ivar
* for the instance variable is already known.
*/
OBJC_EXPORT void
object_setIvar(id _Nullable obj, Ivar _Nonnull ivar, id _Nullable value)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
复制代码
objc-class.mm
void object_setIvar(id obj, Ivar ivar, id value)
{
return _object_setIvar(obj, ivar, value, false /*not strong default*/);
}
void object_setIvarWithStrongDefault(id obj, Ivar ivar, id value)
{
return _object_setIvar(obj, ivar, value, true /*strong default*/);
}
复制代码
- 可见,这里同样调用了
_object_setIvar,代码情况1,是同一个API。其中,有一个objc_storeStrong,下面分析一下。
5.3 objc_storeStrong导致的retain
NSObject.mm objc_storeStrong(id *location, id obj)
void
objc_storeStrong(id *location, id obj)
{
id prev = *location;
if (obj == prev) {
return;
}
objc_retain(obj);
*location = obj;
objc_release(prev);
}
复制代码
NSObject.mm objc_retain(id obj)
/***********************************************************************
* Optimized retain/release/autorelease entrypoints
**********************************************************************/
#if __OBJC2__
__attribute__((aligned(16)))
id
objc_retain(id obj)
{
if (!obj) return obj;
if (obj->isTaggedPointer()) return obj;
return obj->retain();
}
__attribute__((aligned(16)))
void
objc_release(id obj)
{
if (!obj) return;
if (obj->isTaggedPointer()) return;
return obj->release();
}
__attribute__((aligned(16)))
id
objc_autorelease(id obj)
{
if (!obj) return obj;
if (obj->isTaggedPointer()) return obj;
return obj->autorelease();
}
// OBJC2
#else
// not OBJC2
id objc_retain(id obj) { return [obj retain]; }
void objc_release(id obj) { [obj release]; }
id objc_autorelease(id obj) { return [obj autorelease]; }
#endif
复制代码
可知: 1)如果TaggedPointer,则返回本身。 2)如果非TaggedPointer,则由对象的retain()返回。
objc-object.h objc_object::retain()
// Equivalent to calling [this retain], with shortcuts if there is no override
inline id
objc_object::retain()
{
assert(!isTaggedPointer());
if (fastpath(!ISA()->hasCustomRR())) {
return rootRetain();
}
return ((id(*)(objc_object *, SEL))objc_msgSend)(this, SEL_retain);
}
复制代码
objc-object.h objc_object::rootRetain()
// Base retain implementation, ignoring overrides.
// This does not check isa.fast_rr; if there is an RR override then
// it was already called and it chose to call [super retain].
//
// tryRetain=true is the -_tryRetain path.
// handleOverflow=false is the frameless fast path.
// handleOverflow=true is the framed slow path including overflow to side table
// The code is structured this way to prevent duplication.
ALWAYS_INLINE id
objc_object::rootRetain()
{
return rootRetain(false, false);
}
复制代码
这里的 rootRetain(false, false); 正是上文第2.2节中介绍的,不再赘述。
6. 新建对象(分配内存与初始化)导致的引用计数变化 -- alloc 和 init 操作
首先,新建一个对象的典型写法:
NSObject *obj = [NSObject alloc] init]; 复制代码
6.1 分配内存 -- alloc
+ (id)alloc {
return _objc_rootAlloc(self);
}
复制代码
// Base class implementation of +alloc. cls is not nil.
// Calls [cls allocWithZone:nil].
id
_objc_rootAlloc(Class cls)
{
return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
复制代码
// Call [cls alloc] or [cls allocWithZone:nil], with appropriate
// shortcutting optimizations.
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
if (slowpath(checkNil && !cls)) return nil;
#if __OBJC2__
if (fastpath(!cls->ISA()->hasCustomAWZ())) {
// No alloc/allocWithZone implementation. Go straight to the allocator.
// fixme store hasCustomAWZ in the non-meta class and
// add it to canAllocFast's summary
if (fastpath(cls->canAllocFast())) {
// No ctors, raw isa, etc. Go straight to the metal.
bool dtor = cls->hasCxxDtor();
id obj = (id)calloc(1, cls->bits.fastInstanceSize());
if (slowpath(!obj)) return callBadAllocHandler(cls);
obj->initInstanceIsa(cls, dtor);
return obj;
}
else {
// Has ctor or raw isa or something. Use the slower path.
id obj = class_createInstance(cls, 0);
if (slowpath(!obj)) return callBadAllocHandler(cls);
return obj;
}
}
#endif
// No shortcuts available.
if (allocWithZone) return [cls allocWithZone:nil];
return [cls alloc];
}
复制代码
分支1 -- obj->initInstanceIsa(cls, dtor);
inline void
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
assert(!cls->instancesRequireRawIsa());
assert(hasCxxDtor == cls->hasCxxDtor());
initIsa(cls, true, hasCxxDtor);
}
复制代码
inline void
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor)
{
assert(!isTaggedPointer());
if (!nonpointer) {
isa.cls = cls;
} else {
assert(!DisableNonpointerIsa);
assert(!cls->instancesRequireRawIsa());
isa_t newisa(0);
#if SUPPORT_INDEXED_ISA
assert(cls->classArrayIndex() > 0);
newisa.bits = ISA_INDEX_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE
newisa.has_cxx_dtor = hasCxxDtor;
newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else
newisa.bits = ISA_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE
newisa.has_cxx_dtor = hasCxxDtor;
newisa.shiftcls = (uintptr_t)cls >> 3;
#endif
// This write must be performed in a single store in some cases
// (for example when realizing a class because other threads
// may simultaneously try to use the class).
// fixme use atomics here to guarantee single-store and to
// guarantee memory order w.r.t. the class index table
// ...but not too atomic because we don't want to hurt instantiation
isa = newisa;
}
}
复制代码
上述代码中, newisa.bits = ISA_MAGIC_VALUE; 是为了对 isa 结构赋值一个初始值,通过对 isa_t 的结构分析,我们可以知道此次赋值只是对 nonpointer 和 magic 部分进行了赋值。
newisa.shiftcls = (uintptr_t)cls >> 3; 是将类的地址存储在对象的 isa 结构中。这里右移三位的主要原因是用于将 Class 指针中无用的后三位清除减小内存的消耗,因为类的指针要按照字节(8 bits)对齐内存,其指针后三位都是没有意义的 0。关于类指针对齐的详细解析可参考: 从 NSObject 的初始化了解 isa 。
分支2 -- id obj = class_createInstance(cls, 0);
id
class_createInstance(Class cls, size_t extraBytes)
{
return _class_createInstanceFromZone(cls, extraBytes, nil);
}
复制代码
/***********************************************************************
* class_createInstance
* fixme
* Locking: none
**********************************************************************/
static __attribute__((always_inline))
id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
bool cxxConstruct = true,
size_t *outAllocatedSize = nil)
{
if (!cls) return nil;
assert(cls->isRealized());
// Read class's info bits all at once for performance
bool hasCxxCtor = cls->hasCxxCtor();
bool hasCxxDtor = cls->hasCxxDtor();
bool fast = cls->canAllocNonpointer();
size_t size = cls->instanceSize(extraBytes);
if (outAllocatedSize) *outAllocatedSize = size;
id obj;
if (!zone && fast) {
obj = (id)calloc(1, size);
if (!obj) return nil;
obj->initInstanceIsa(cls, hasCxxDtor);
}
else {
if (zone) {
obj = (id)malloc_zone_calloc ((malloc_zone_t *)zone, 1, size);
} else {
obj = (id)calloc(1, size);
}
if (!obj) return nil;
// Use raw pointer isa on the assumption that they might be
// doing something weird with the zone or RR.
obj->initIsa(cls);
}
if (cxxConstruct && hasCxxCtor) {
obj = _objc_constructOrFree(obj, cls);
}
return obj;
}
复制代码
其中,有个 obj->initIsa(cls); ,初始化isa的操作:
inline void
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor)
{
assert(!isTaggedPointer());
if (!nonpointer) {
isa.cls = cls;
} else {
assert(!DisableNonpointerIsa);
assert(!cls->instancesRequireRawIsa());
isa_t newisa(0);
#if SUPPORT_INDEXED_ISA
assert(cls->classArrayIndex() > 0);
newisa.bits = ISA_INDEX_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE
newisa.has_cxx_dtor = hasCxxDtor;
newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else
newisa.bits = ISA_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE
newisa.has_cxx_dtor = hasCxxDtor;
newisa.shiftcls = (uintptr_t)cls >> 3;
#endif
// This write must be performed in a single store in some cases
// (for example when realizing a class because other threads
// may simultaneously try to use the class).
// fixme use atomics here to guarantee single-store and to
// guarantee memory order w.r.t. the class index table
// ...but not too atomic because we don't want to hurt instantiation
isa = newisa;
}
}
复制代码
可见,alloc的时候会初始化isa,并通过 newisa(0) 的初始化列表办法生成一个isa,并根据是否支持indexed isa分别设置 .bits 的值。
6.2 初始化 -- init
- (id)init {
return _objc_rootInit(self);
}
复制代码
id
_objc_rootInit(id obj)
{
// In practice, it will be hard to rely on this function.
// Many classes do not properly chain -init calls.
return obj;
}
复制代码
7. 获取引用计数
NSObject.mm retainCount
- (NSUInteger)retainCount {
return ((id)self)->rootRetainCount();
}
复制代码
objc-object.h objc_object::rootRetainCount()
inline uintptr_t
objc_object::rootRetainCount()
{
if (isTaggedPointer()) return (uintptr_t)this;
sidetable_lock();
isa_t bits = LoadExclusive(&isa.bits);
ClearExclusive(&isa.bits);
if (bits.nonpointer) {
uintptr_t rc = 1 + bits.extra_rc;
if (bits.has_sidetable_rc) {
rc += sidetable_getExtraRC_nolock();
}
sidetable_unlock();
return rc;
}
sidetable_unlock();
return sidetable_retainCount();
}
复制代码
可见,获取引用计数的关键在这么一句话:
uintptr_t rc = 1 + bits.extra_rc; 复制代码
bits.extra_rc 表示引用计数减1。当然,这只针对情况1,即 bits.nonpointer 为1(开启了指针优化),且 bits.has_sidetable_rc 为0(表示不存储在散列表Side Table中,而存储在 extra_rc 中)。
- 情况0 -- TaggedPointer
直接返回isa值本身。
- 情况1 -- 非TaggedPointer,且开启了指针优化,且存储在
extra_rc中
objc-os.h LoadExclusive(uintptr_t *src)
static ALWAYS_INLINE
uintptr_t
LoadExclusive(uintptr_t *src)
{
return *src;
}
复制代码
- 情况2 -- 非TaggedPointer,且开启了指针优化,且存储在散列表中
NSObject.mm objc_object::sidetable_getExtraRC_nolock()
size_t
objc_object::sidetable_getExtraRC_nolock()
{
assert(isa.nonpointer);
SideTable& table = SideTables()[this];
RefcountMap::iterator it = table.refcnts.find(this);
if (it == table.refcnts.end()) return 0;
else return it->second >> SIDE_TABLE_RC_SHIFT;
}
复制代码
可见,其逻辑就是先从 SideTable 的静态方法获取当前实例对应的 SideTable 对象,其 refcnts 属性就是之前说的存储引用计数的散列表,这里将其类型简写为 RefcountMap:
typedef objc::DenseMap RefcountMap; 复制代码
然后在引用计数表中用迭代器查找当前实例对应的键值对,获取引用计数值,并在此基础上 +1 并将结果返回。这也就是为什么之前说引用计数表存储的值为实际引用计数减一。
需要注意的是为什么这里把键值对的值做了向右移位操作( it->second >> SIDE_TABLE_RC_SHIFT ):
// The order of these bits is important. #define SIDE_TABLE_WEAKLY_REFERENCED (1UL<<0) #define SIDE_TABLE_DEALLOCATING (1UL<<1) // MSB-ward of weak bit #define SIDE_TABLE_RC_ONE (1UL<<2) // MSB-ward of deallocating bit #define SIDE_TABLE_RC_PINNED (1UL<<(WORD_BITS-1)) #define SIDE_TABLE_RC_SHIFT 2 #define SIDE_TABLE_FLAG_MASK (SIDE_TABLE_RC_ONE-1) 复制代码
可以看出值的第一个 bit 表示该对象是否有过 weak 对象,如果没有,在析构释放内存时可以更快;第二个 bit 表示该对象是否正在析构。从第三个 bit 开始才是存储引用计数数值的地方。所以这里要做向右移两位的操作,而对引用计数的 +1 和 -1 可以使用 SIDE_TABLE_RC_ONE ,还可以用 SIDE_TABLE_RC_PINNED 来判断是否引用计数值有可能溢出。
- 情况3 -- 默认值
NSObject.mm objc_object::sidetable_retainCount()
uintptr_t
objc_object::sidetable_retainCount()
{
SideTable& table = SideTables()[this];
size_t refcnt_result = 1;
table.lock();
RefcountMap::iterator it = table.refcnts.find(this);
if (it != table.refcnts.end()) {
// this is valid for SIDE_TABLE_RC_PINNED too
refcnt_result += it->second >> SIDE_TABLE_RC_SHIFT;
}
table.unlock();
return refcnt_result;
}
复制代码
以上所述就是小编给大家介绍的《苹果iOS系统源码思考:对象的引用计数存储在哪里?--从runtime源码得到的启示》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 深入讲解Flutter应用模板源码:计数器示例
- 基于ServerLess的极简网页计数器:源码分析与最佳实践
- 神经网络目标计数概述:通过Faster R-CNN实现当前最佳的目标计数
- C#——性能计数器
- C#——性能计数器
- 尺度不变提升人群计数性能
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Learn Python 3 the Hard Way
Zed A. Shaw / Addison / 2017-7-7 / USD 30.74
You Will Learn Python 3! Zed Shaw has perfected the world’s best system for learning Python 3. Follow it and you will succeed—just like the millions of beginners Zed has taught to date! You bring t......一起来看看 《Learn Python 3 the Hard Way》 这本书的介绍吧!
HTML 压缩/解压工具
在线压缩/解压 HTML 代码
图片转BASE64编码
在线图片转Base64编码工具