一、关于引用计数:
读了《Objective-C高级编程(iOS与OS X多线程和内存管理)》,发现吊炸天的感觉,所以记录下。嗯 还是自己太菜了,好像是7年前的书了。 自己尽然没有去认真的去研读,惭愧,惭愧!
- ARC之前引用计数是得程序员自己管理的,ARC之后是编译器帮我们管理的。但是 是不是就不用了解呢?如果你还在用OC的话绝对用处很大,农村人,不讲套路。
- 引用计数,引用书中的例子:
最早进入办公室的人开灯 ->最后离开办公室的人关灯
中间进入就是++,离开--
- 管理方式:
1.自己生成的对象,自己持有
2.非自己生成的对象,自己也能持有
3.不需要自己持有持有的对象时释放
4.非自己持有的对象无法释放
以上就是对引用计数的概括,有点枯燥,但是把内存管理读完再来看,就非常美妙。
二、alloc/retain/release/dealloc实现
-
必须先搞定这几个鬼东西,这几个可是OC内存管理的基石,按照惯例,思维导图 怼一发:
- 密密麻麻,看着是不是很反感。跟着走进去你会发现花姑娘
因为NSObject不开源,所以这个GNUstep现在是2.7.0版本。这个鬼基本由于NSObject内部实现方法一样,查不了多少,还有一个runtime的源代码也准备好 开搞
1.alloc方法的实现
-在base/Source/Foundation/NSObject.m的+ (id) alloc方法里,最后调用
NSAllocateObject(Class aClass, NSUInteger extraBytes, NSZone *zone)
// 简化后主要代码
// size:计算容纳对象所需内存的大小
//obj_layout:保存着引用计数retained
// 苹果用哈希表实现 GNUstep用占用头部内存实现
int size = class_getInstanceSize(aClass) + extraBytes + sizeof(struct obj_layout);
if (zone == 0)
{
// NSZone是为防止内存碎片化引入的结构,对内存分配区域进行多重化管理
// 根据对象的目的、大小、分配
zone = NSDefaultMallocZone();
}
// 给对象分配内存空间
new = NSZoneMalloc(zone, size);
if (new != nil)
{
// 将内存空间置为0
memset (new, 0, size);
new = (id)&((obj)new)[1];
object_setClass(new, aClass);
AADD(aClass, new);
}
// 返回对象指针
return new;
2.retain方法的实现
- 在base/Source/Foundation/NSObject.m的- (id) retain方法里,最后调用
static id retain_fast(id anObject)
1.weak修饰的时候返回本身不做处理
2.static id objc_retain_fast_np_internal(id anObject)主要的就是 ((obj)anObject)[-1].retained++;
3.这里说明一下 ((obj)anObject)[-1]GNUstep用占用头部内存实现,就是obj返回的指针-1就是指向retained的内存地址,苹果用哈希表实现会有点不同
3.release方法的实现
-在base/Source/Foundation/NSObject.m的- (oneway void) release方法里,最后调用
static void release_fast(id anObject)
1.weak修饰的时候返回本身不做处理
2.引用计数--((obj)anObject)[-1].retained--
3.引用计数为0时调用dealloc方法
4.dealloc方法的实现
-在base/Source/Foundation/NSObject.m的- (void) dealloc方法里
NSDeallocateObject(id anObject)
// 简化后代码
NSDeallocateObject(id anObject)
{
// 获取类对象
Class aClass = object_getClass(anObject);
// 判断 当前类对象 元类对象不为空
if ((anObject != nil) && !class_isMetaClass(aClass))
{
//ARC下
obj o = &((obj)anObject)[-1]; // 获取对象指针
NSZone *z = NSZoneFromPointer(o); // tagePoint寻址
if (NSZombieEnabled == YES)
{
// 如果僵尸对象散列表不为空 放进僵尸对象散列表
if (0 != zombieMap)
{
pthread_mutex_lock(&allocationLock);
NSMapInsert(zombieMap, (void*)anObject, (void*)aClass);
pthread_mutex_unlock(&allocationLock);
}
// 释放僵尸对象 或者 添加僵尸对象
if (NSDeallocateZombies == YES)
{
object_dispose(anObject);
}
else
{
object_setClass(anObject, zombieClass);
}
}
else
{
object_dispose(anObject);
}
}
return;
}
- 上面注释写的很清楚了,我主要把非ARC下的代码删了,retain和release主要的说清楚,其他的大家可以自己看看,然后发现object_dispose()方法没有了,不急因为object_dispose()是runtime的东西
- 在runtime源码objc/Source/objc-runtime-new.mm里面的
id object_dispose(id obj)
- 释放之前还调用了objc_destructInstance(obj)方法
void *objc_destructInstance(id obj)
{
if (obj) {
// Read all of the flags at once for performance.
// 判断是否有C++析构函数
bool cxx = obj->hasCxxDtor();
// 是否有关联对象
bool assoc = obj->hasAssociatedObjects();
// This order is important.
// 清除有C++析构函数
if (cxx) object_cxxDestruct(obj);
// 清除关联对象表 AssociationsManager对应的value
if (assoc) _object_remove_assocations(obj);
obj->clearDeallocating();
}
return obj;
}
-
最后调用了obj->clearDeallocating()可以自己进去看看,思维导图里面注释的比较清楚如图:
- 至于从何得知void *objc_destructInstance(id obj) 的方法
// 判断是否有C++析构函数
bool cxx = obj->hasCxxDtor();
// 是否有关联对象
bool assoc = obj->hasAssociatedObjects();
- 可以根据isa指针联合体里面有记录的,大致如下:
union isa_t
struct {
// 1代表优化后的使用位域存储更多的信息。
uintptr_t nonpointer : 1;
// 是否有设置过关联对象
uintptr_t has_assoc : 1;
// 是否有C++析构函数
uintptr_t has_cxx_dtor : 1;
// 存储着Class对象的内存地址信息
uintptr_t shiftcls : 33;
// 用于在调试时分辨对象是否未完成初始化
uintptr_t magic : 6;
// 是否有被弱引用指向过。
uintptr_t weakly_referenced : 1;
// 对象是否正在释放
uintptr_t deallocating : 1;
// 引用计数器是否过大无法存储在isa中
// 如果为1,那么引用计数会存储在一个叫SideTable的类的属性中
uintptr_t has_sidetable_rc : 1;
// 里面存储的值是引用计数器减1
uintptr_t extra_rc : 19;
};
- 今天就到这了,小子头昏眼花,急需按一波。陆续会更新内存管理的内容,各位看官,回见