一、引用计数
- 在iOS中,使用引用计数来管理OC对象的内存
- 一个新创建的OC对象引用计数默认是
1,当引用计数减为0,OC对象就会销毁,释放其占用的内存空间
- 调用
retain会让OC对象的引用计数+1,调用release会让OC对象的引用计数-1
- 内存管理的经验总结
- 当调用
alloc、new、copy、mutableCopy方法返回了一个对象,在不需要这个对象时,要调用release或者autorelease来释放它
- 想拥有某个对象,就让它的引用计数
+1;不想再拥有某个对象,就让它的引用计数-1
MRC下的setter方法
MRC下, 对象需要持有成员变量, 当销毁时释放成员变量
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int age;
@end
@implementation Person
- (void)setName:(NSString *)name {
if (_name != name) {
[_name release];
_name = [name retain];
}
}
- (void)setAge:(int)age {
_age = age;
}
- (void)dealloc {
self.name = nil;
[super dealloc];
}
@end
二、copy和mutableCopy
- 通过
copy和mutableCopy, 可以生成一个副本, 与源代码分隔开, 两者之间互不干扰
- 以
mutableCopy为例, 有如下代码
- 深拷贝: 产生一个新的副本, 与源对象相互独立
- 浅拷贝: 指针拷贝, 指向源对象
自定义对象的拷贝
#import <Foundation/Foundation.h>
@interface Person : NSObject <NSCopying>
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int age;
@end
@implementation Person
- (void)setName:(NSString *)name
{
if (_name != name) {
[_name release];
_name = [name retain];
}
}
- (void)setAge:(int)age
{
_age = age;
}
- (id)copyWithZone:(nullable NSZone *)zone
{
Person *person = [[Person allocWithZone:zone] init];
person.age = self.age;
person.name = self.name;
return person;
}
@end
三、引用计数的存储
- 可以通过
retainCount方法, 查看引用计数的存储
- 通过
runtime源码, 查看- (NSUInteger)retainCount方法, 如下图
- 进入
rootRetainCount函数, 可以看到引用计数的查找过程
- 如果指针是
Tagged Pointer, 那么直接返回, 否则进入下一步
- 判断
isa是否优化过, 如果优化过, 那么最后isa的最后19位存储的是引用计数
- 如果最后
19位不足以存储, 那么多余的引用计数会存储到sidetable中, 同时将倒数第20位的值置为1, 就是has_sidetable_rc的值为1
- 如果
has_sidetable_rc的值为1, 就会从sidetable_getExtraRC_nolock函数中取出sidetable中存储的引用计数
sidetable_getExtraRC_nolock中的代码如下图
- 如果
isa没有优化过, 那么就会进入sidetable_retainCount函数, 获取sidetable中的引用计数
- 我们也可以从
sidetable_retain和sidetable_release函数中, 看到对引用技术的操作
四、weak的原理是什么?
__weak NSObject *obj;
NSLog(@"1");
{
obj = [[NSObject alloc] init];
}
NSLog(@"2 - %@", obj);
- 可以看到, 当对象释放时, 被
__weak修饰的指针会执行nil
- 我们可以通过
-dealloc的源码, 查看weak的实现过程
- 下图是
-dealloc方法的底层实现
- 进入
rootDealloc函数, 在这里可以看到两种情况
- isa是优化过的指针, 对象没有被弱引用, 没有关联对象, 没有c++析构函数, 没有将引用计数存到
Sidetable中, 就会立即释放
- 否则调用
object_dispose函数
- 进入
object_dispose函数, 可以看到调用了objc_destructInstance函数
- 进入
objc_destructInstance函数, 可以看到对objc的处理, 是在clearDeallocating函数中将弱指针置为nil的
- 进入
clearDeallocating函数, 又可以看到两种情况
- 对象的isa没有优化过
- 和优化过, 并且被弱指针引用 或者 将引用计数存放到了
Sidetable中
- 当isa没有被优化过, 进入
sidetable_clearDeallocating函数, 可以看到weak引用是存放到SideTable中的
- 存放在了
SideTable的weak_table_t中
- 查看
weak_table_t, 如下图, 即weak会被存放到一个全局的散列表中
- 会通过
weak_clear_no_lock函数, 对弱指针置为nil, 同时移除删列表中的weak记录
- 如果
isa被优化过, 并且对象被弱引用或者将引用计数存到Sidetable中, 就会调用clearDeallocating_slow函数
- 进入
clearDeallocating_slow函数, 可以看到在函数中, 调用了weak_clear_no_lock函数, 并清空了引用计数