MRC 相关
对象的 Setter 方法
- (void)setName:(NSString *)name {
if (_name != name) { //1
[_name release]; //2
_name = [name retain]; //3
}
}
- 在 MRC 模式下,setter 方法的处理方式为了:
- 防止重复_name 可能会被提前释放,导致调用_name 是僵尸对象而崩溃;
- 将旧对象_name 提前释放,防止内存泄露;
- 将对象 name 的 retain 引用计数加一,防止对象被释放而导致使用报错问题;
对象深拷贝与浅拷贝
- 不可变对象的 copy 方法执行的是浅拷贝操作,可变对象的 copy 方法执行的是深拷贝操作。
- 不可变对象的 mutableCopy 方法执行的是深拷贝操作,可变对象的 mutableCopy 方法执行的是深拷贝操作。
对象使用 copy 或 strong 修饰符
@property (nonatomic, copy) NSMutableArray *array;
@property (nonatomic, strong) NSMutableArray *array;
- 这两行代码的本质区别就是在 setter 方法里面执行的操作不同:
- copy 修饰符的属性,在 setter 方法中会执行[array copy]方法,会导致可变数组变成了不可变数组了;
- strong 修饰符的属性,在 setter 方法中会执行[array retain]方法,只是将引用计数加一,保护当前对象不被释放;
- 一般字符串操作就是使用 copy,而对于可变的对象,都是使用 strong 来进行修饰!!
autoreleasepool
自动释放池
int main(int argc, const char * argv[]) {
@autoreleasepool {
}
return 0;
}
将 autoreleasepool 转换为底层 C++源码,可以知道底层是一个结构体__AtAutoreleasePool 构成的!
struct __AtAutoreleasePool {
__AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
void * atautoreleasepoolobj;
};
AutoreleasePoolPage 结构
class AutoreleasePoolPage
{
static size_t const SIZE = PAGE_MAX_SIZE; //4096
static size_t const COUNT = SIZE / sizeof(id);
magic_t const magic; //检验AutoreleasePoolPage完整性
id *next; //指向下一个存放autorelease对象的地址
pthread_t const thread; //当前页所在线程
AutoreleasePoolPage * const parent; //当前page的parent指针
AutoreleasePoolPage *child; //当前page的child指针
uint32_t const depth;
uint32_t hiwat;
}
- AutoreleasePoolPage 可使用的最大内存空间为 4096 个字节,其中的变量所占用的内存大小为 56 个字节!
- AutoreleasePoolPage 其实是一个双向链表的数据结构存储方式,将每个 page 通过 parent 和 child 进行互相链接,可以通过链表查找的方式找到想要释放的对象!
原理
- 假设地址从 0x1000 开始到 0x2000,总共占用的内存空间为 4096 个字节,其中变量到 0x1038 的字节大小为 56 个字节。
- 如果 AutoreleasePoolPage 中变量所占的字节大于 4096,那将会重新生成一个新的 page 继续存储对象。
runtime 源码
static inline void *push()
{
id *dest;
if (DebugPoolAllocation) {
//创建新的page
dest = autoreleaseNewPage(POOL_BOUNDARY);
} else {
//获取page,没有或者page满了就创建新的
dest = autoreleaseFast(POOL_BOUNDARY);
}
return dest;
}
static inline id *autoreleaseFast(id obj)
{
AutoreleasePoolPage *page = hotPage();
if (page && !page->full()) { //hot的page不满直接将obj入栈
return page->add(obj);
} else if (page) { //创建新的page将obj对象入栈
return autoreleaseFullPage(obj, page);
} else { //没有page则创建新的page
return autoreleaseNoPage(obj);
}
}
//page已经满了,获取未存满的page或创建新的page
id *autoreleaseFullPage(id obj, AutoreleasePoolPage *page)
{
do {
if (page->child) page = page->child; //遍历获取获取最后一个未存满的page
//创建新的page
else page = new AutoreleasePoolPage(page);
} while (page->full());
setHotPage(page);
return page->add(obj);
}
//创建新的page并将obj入栈
id *autoreleaseNoPage(id obj)
{
// 初始化page并将POOL_BOUNDARY传入做为第一个栈底对象
AutoreleasePoolPage *page = new AutoreleasePoolPage(nil);
setHotPage(page);
if (pushExtraBoundary) {
page->add(POOL_BOUNDARY);
}
// 将obj对象入栈
return page->add(obj);
}
//获取新的page
id *autoreleaseNewPage(id obj)
{
AutoreleasePoolPage *page = hotPage(); //获取正在使用的page
if (page) return autoreleaseFullPage(obj, page); //获取新的page
else return autoreleaseNoPage(obj); // 初始化page
}
- 如果当前没有 page,则创建新的 page,并将 POOL_BOUNDARY 做为第一个对象压入栈顶,再将 obj 对象添加到 page 中;
- 如果当前存在 page 并且 page 未存满,则将当前 obj 对存入栈中;若当前 page 已存满,则创建新的 page 并将 POOL_BOUNDARY 做为第一个对象压入栈顶,再将 obj 对象添加到 page 中;
static inline void pop(void *token)
{
AutoreleasePoolPage *page;
id *stop;
page = pageForPointer(token); //根据对象token查找token是在哪一个page中
stop = (id *)token;
page->releaseUntil(stop); //从hotpage释放对象直到stop
//根据不同情况将page进行释放,并将page置nil
if (DebugPoolAllocation && page->empty()) {
AutoreleasePoolPage *parent = page->parent;
page->kill();
setHotPage(parent);
} else if (DebugMissingPools && page->empty() && !page->parent) {
page->kill();
setHotPage(nil);
}
else if (page->child) {
if (page->lessThanHalfFull()) {
page->child->kill();
}
else if (page->child->child) {
page->child->child->kill();
}
}
}
- 调用 pop 传入 token 对象会对 page 进行变量查找在哪一个 page 中,并将对象进行释放知道 token 所在位置,并将多余的 page 进行释放;
对象 dealloc
runtime 源码
- (void)dealloc {
_objc_rootDealloc(self);
}
_objc_rootDealloc(id obj)
{
obj->rootDealloc();
}
objc_object::rootDealloc()
{
if (isTaggedPointer()) return; // 如果是TaggedPointer直接返回
if (fastpath(isa.nonpointer //没使用位域来存放信息 &&
!isa.weakly_referenced //没被弱引用 &&
!isa.has_assoc //没关联对象 &&
!isa.has_cxx_dtor //没析构函数 &&
!isa.has_sidetable_rc //引用计数没存储在sidetable表中))
{
assert(!sidetable_present());
free(this); //直接释放对象
}
else {
object_dispose((id)this);
}
}
object_dispose(id obj)
{
objc_destructInstance(obj);
free(obj);
}
void *objc_destructInstance(id obj)
{
if (obj) {
bool cxx = obj->hasCxxDtor();
bool assoc = obj->hasAssociatedObjects();
if (cxx) object_cxxDestruct(obj);
if (assoc) _object_remove_assocations(obj);
obj->clearDeallocating();
}
return obj;
}
object_cxxDestruct:
- 执行了正要释放的 obj 对象相关对象的释放;
- 执行了父类相关对象的释放;
_object_remove_assocations:
- 从 AssociationsHashMap 哈希表中移除当前对象相关联的 assocation 对象;
clearDeallocating:
- 若对象被其它对象弱引用着,则将弱引用对象置为 nil,并将当前对象从 weak_table 弱引用表中删除;
- 若当前对象有将引用计数存放在 sidetable 表中,则将对象从 sidetable 表中移除;
参考资料: 探索 dealloc 真谛