iOS Objective-C底层 part4:die
生对死,alloc
对dealloc
alloc
经历了一些周折才调用calloc
,开辟内存空间.
dealloc
也一样经历了一些周折才调用free
,释放了内存空间.
1. dealloc流程
dealloc.png调用栈如上图.其实可以将分支合并:
isa.nonpointer&&!isa.weakly_referenced&&!isa.has_assoc&& !isa.has_cxx_dtor&&!isa.has_sidetable_rc
- 条件1:isa.nonpointer
是否是支持nonpointer
的isa
,不支持直接free
;(不支持nonpointer
的isa
,下面的条件都不必看)
- 条件2:isa.has_cxx_dtor
是否有C++的析构函数,为真,则要调用析构函数;
- 条件3:isa.has_assoc
是否有关联属性,为真,则要解除关联属性;
- 条件4:isa.weakly_referenced
是否有weak指针
指向该对象,为真,则要将所有指向该对象的weak指针
全部置为nil;
- 条件5:isa.has_sidetable_rc
是否开启了Sidetable
来储存该对象的retainCount
,为真,则要将该对象对应的Sidetable
内储存的retainCount
清除.
5个条件全部走完,再调用free
,收工.
2. 哪里不对?
问题1:ARC环境下不用书写对象对自己实例变量引用解除的代码,为什么流程中不见对自己实例变量解除引用的代码?
答:一个方法,一个标记.
@interface PGCustomClass : NSObject
@property(nonatomic,copy)NSString * name;
@end
生成PGCustomClass
对象时,看initInstanceIsa
的参数:
@interface PGCustomClass2 : NSObject
@end
生成PGCustomClass
对象时,看initInstanceIsa
的参数:
- 一个标记
很明显,带实例变量的类的obj
->isa.has_cxx_dtor
==1;
很明显,不带实例变量的类的obj
->isa.has_cxx_dtor
==0;
实例变量(是不是属性格式无所谓).
obj
->isa.has_cxx_dtor
,追溯根源还是来自类的bits->flags
.
- 一个方法
在PGCustomClass
和PGCustomClass2
内加入以下代码,打印方法列表:
- (void)logMethods
{
unsigned int count;
Method *methods = class_copyMethodList([self class], &count);
for (int i = 0; i < count; i++)
{
Method method = methods[i];
SEL selector = method_getName(method);
NSString * name = NSStringFromSelector(selector);
NSLog(@"方法名:%@",name);
}
}
PGCustomClass:
logMethods
.cxx_destruct
PGCustomClass2:
logMethods
很明显,带实例变量的类多出一个.cxx_destruct
方法,是编译器加的.
一个标记+一个方法==>如下效果:
isa.has_cxx_dtor==1;
└─object_cxxDestructFromClass
└─.cxx_destruct
isa.has_cxx_dtor==1;
才会调用object_cxxDestructFromClass
,进而调用.cxx_destruct
.
.cxx_destruct
方法内就是做了对象对自己实例变量的引用解除
static void object_cxxDestructFromClass(id obj, Class cls)
{
void (*dtor)(id);
// Call cls's dtor first, then superclasses's dtors.
for ( ; cls; cls = cls->superclass) {
if (!cls->hasCxxDtor()) return;
dtor = (void(*)(id))
lookupMethodInClassAndLoadCache(cls, SEL_cxx_destruct);
if (dtor != (void(*)(id))_objc_msgForward_impcache) {
if (PrintCxxCtors) {
_objc_inform("CXX: calling C++ destructors for class %s",
cls->nameForLogging());
}
(*dtor)(obj);
}
}
}
isa.has_cxx_dtor
除了标记当前类是否有C++的析构函数外,被赋予了其他公用:标记对象是否有实例变量;
为什么有实例变量的类才加标记
和方法
呢?
原因也很简单,因为只有这样的类生成的对象才会有
实例变量,对象有实例变量才需要
解除引用.
问题2:ARC环境下-dealloc
内不能书写[super dealloc]
,为什么流程中也不见调用父类的dealloc
?
由上面的逻辑推算,[super dealloc]
也是编译器加的.详情请戳