内存管理释义

  • 在iOS中,使用引用计数来管理OC对象的内存
  • 一个新创建的OC对象引用计数默认是1,当引用计数减为0,OC对象就会销毁,释放其占用的内存空间
  • 调用retain会让OC对象的引用计数+1,调用release会让OC对象的引用计数-1
  • 内存管理的经验总结
    • 当调用alloc、new、copy、mutableCopy方法返回了一个对象,在不需要这个对象时,要调用release或者autorelease来释放它
    • 想拥有某个对象,就让它的引用计数+1;不想再拥有某个对象,就让它的引用计数-1
  • 可以通过以下私有函数来查看自动释放池的情况
    1
    extern void _objc_autoreleasePoolPrint(void);

深拷贝、浅拷贝

  • 拷贝的目的:产生一个副本对象,跟源对象互不影响
    • 修改了源对象,不会影响副本对象
    • 修改了副本对象,不会影响源对象
  • iOS提供了2个拷贝方法
    • copy,不可变拷贝,产生不可变副本
    • mutableCopy,可变拷贝,产生可变副本
  • 深拷贝和浅拷贝
    • 深拷贝:内容拷贝,产生新的对象
    • 浅拷贝:指针拷贝,没有产生新的对象
copy mutableCopy
NSString NSString 浅拷贝 NSMutableString 深拷贝
NSMutableString NSString 深拷贝 NSMutableString 深拷贝
NSArray NSArray 浅拷贝 NSMutableArray 深拷贝
NSMutableArray NSArray 深拷贝 NSMutableArray 深拷贝
NSDictionary NSDictionary 浅拷贝 NSMutableDictionary 深拷贝
NSMutableDictionary NSDictionary 深拷贝 NSMutableDictionary 深拷贝
1
2
3
4
5
6
7
8
9
NSString *str1 = [[NSString alloc] initWithFormat:@"test111111111"];
NSString *str2 = [str1 copy]; // 浅拷贝,指针拷贝,没有产生新对象
NSMutableString *str3 = [str1 mutableCopy]; // 深拷贝,内容拷贝,有产生新对象

NSMutableString *str1 = [[NSMutableString alloc] initWithFormat:@"test"]; // 1
NSString *str2 = [str1 copy]; // 深拷贝
NSMutableString *str3 = [str1 mutableCopy]; // 深拷贝

//数组和字典类同

引用计数的存储

  • 在64bit中,引用计数可以直接存储在优化过的isa指针中,也可能存储在SideTable类中
  • refcnts是一个存放着对象引用计数的散列表

weak

  • weak 是弱引用,用 weak 来修饰、描述所引用对象的计数器并不会增加,而且weak会在引用对象被释放的时候自动置为 nil,这也就避免了野指针访问坏内存而引起奔溃的情况,
  • weak表其实是一个hash(哈希)表 (字典也是hash表),Key是所指对象的地址,Value是weak指针的地址集合。weak通常用于解决循环引用问题。

weak 的实现原理:

  • 初始化时:runtime会调用objc_initWeak函数,初始化一个新的weak指针指向对象的地址。
  • 添加引用时:objc_initWeak函数会调用 objc_storeWeak() 函数, objc_storeWeak() 的作用是更新指针指向,创建对应的弱引用表。
  • 释放时,调用clearDeallocating函数。clearDeallocating函数首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,清理对象的记录。

源码解析

NSObject.mm

1
2
3
4
5
6
7
8
struct SideTable {
// 保证原子操作的自旋锁
spinlock_t slock;
// 引用计数的 hash 表
RefcountMap refcnts;
// weak 引用全局 hash 表
weak_table_t weak_table;
}

objc-weak.h

weak_table_t,weak表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 全局的弱引用表, 保存object作为key, weak_entry_t作为value
* The global weak references table. Stores object ids as keys,
* and weak_entry_t structs as their values.
*/
struct weak_table_t {
// 保存了所有指向特地对象的 weak指针集合
weak_entry_t *weak_entries;
// weak_table_t中有多少个weak_entry_t
size_t num_entries;
// weak_entry_t数组的count
uintptr_t mask;
// hash key 最大偏移值,
// 采用了开放定制法解决hash冲突,超过max_hash_displacement说明weak_table_t中不存在要找的weak_entry_t
uintptr_t max_hash_displacement;
};

objc-runtime-new.mm

weak_entry_t

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct weak_entry_t {
// 所有weak指针指向的特定对象
DisguisedPtrobjc_object> referent;
// 共用体,保存weak指针的集合,
// 小于等于4个时为数组(下面的结构体), 超过4个时为hash表(上面的结构体)
union {
struct {
weak_referrer_t *referrers;
uintptr_t out_of_line : 1;
uintptr_t num_refs : PTR_MINUS_1;
uintptr_t mask;
uintptr_t max_hash_displacement;
};
struct {
// out_of_line=0 is LSB of one of these (don't care which)
weak_referrer_t inline_referrers[WEAK_INLINE_COUNT];
};
};
}

NSObject.mm

objc_clear_deallocating

1
2
3
4
5
6
7
8
void 
objc_clear_deallocating(id obj)
{
assert(obj);

if (obj->isTaggedPointer()) return;
obj->clearDeallocating();
}

objc_clear_deallocating该函数的动作如下:

  • 1、从weak表中,以dealloc对象为key, 找到对应的weak_entry_t,
  • 2、将weak_entry_t中的所有附有weak修饰符变量的地址,赋值为nil
  • 3、将weak表中该对象移除

强、弱指针引用

1
2
3
4
5
6
7
8
9
10
11
// ARC是LLVM编译器和Runtime系统相互协作的一个结果
__strong HXPerson *person1;
__weak HXPerson *person2;
__unsafe_unretained HXPerson *person3;

NSLog(@"111");
{
MJPerson *person = [[MJPerson alloc] init];
person3 = person;
}
NSLog(@"222 - %@", person3);//访问person3会出现crash,因为__unsafe_unretained指向野指针

dealloc

  • 当一个对象要释放时,会自动调用dealloc,接下的调用轨迹是
    • dealloc
    • _objc_rootDealloc
    • rootDealloc
    • object_dispose
    • objc_destructInstance
    • erase

源码解析

NSObject.mm

dealloc

1
2
3
4
// Replaced by NSZombies
- (void)dealloc {
_objc_rootDealloc(self);
}

NSObject.mm

_objc_rootDealloc

1
2
3
4
5
6
7
void
_objc_rootDealloc(id obj)
{
assert(obj);

obj->rootDealloc();
}

objc-object.h

rootDealloc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
inline void
objc_object::rootDealloc()
{
if (isTaggedPointer()) return; // fixme necessary?

if (fastpath(isa.nonpointer &&
!isa.weakly_referenced &&
!isa.has_assoc &&
!isa.has_cxx_dtor &&
!isa.has_sidetable_rc))
{
assert(!sidetable_present());
free(this);
}
else {
object_dispose((id)this);
}
}

objc-runtime-new.mm

object_dispose

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/***********************************************************************
* object_dispose
* fixme
* Locking: none
**********************************************************************/
id
object_dispose(id obj)
{
if (!obj) return nil;

objc_destructInstance(obj);
free(obj);

return nil;
}

objc-runtime-new.mm

objc_destructInstance

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/***********************************************************************
* objc_destructInstance
* Destroys an instance without freeing memory.
* Calls C++ destructors.
* Calls ARC ivar cleanup.
* Removes associative references.
* Returns `obj`. Does nothing if `obj` is nil.
**********************************************************************/
void *objc_destructInstance(id obj)
{
if (obj) {
// Read all of the flags at once for performance.
bool cxx = obj->hasCxxDtor();
bool assoc = obj->hasAssociatedObjects();

// This order is important.
if (cxx) object_cxxDestruct(obj);//清除成员变量
if (assoc) _object_remove_assocations(obj);
obj->clearDeallocating();//将指向当前对象的弱指针置为nil
}

return obj;
}

参考