一、引用计数
- 在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
函数, 并清空了引用计数
转载于:https://juejin.im/post/5c9380bbf265da610c068abe