对象操作与OC方法的对应
对象操作 | OC方法 |
---|---|
生成并持有对象 | alloc/new/copy/mutableCopy |
持有对象 | retain |
释放对象 | release |
废弃对象 | dealloc |
id obj = [[NSObject alloc] init];
id obj = [NSObject new];
指向生成并持有对象的指针被赋值给变量obj
copy方法利用基于NSCopying方法约定,由各类实现的copyWithZone:方法生成并持有对象的副本。
mutableCopy方法利用基于NSMutableCopying方法约定由各类实现的mutableCopyWithZone:方法生成并持有对象的副本。
虽然是对象的副本,但同alloc、new方法一样,在“自己生成并持有对象”这点上没有改变
release:立即释放
autorelease:注册到autoreleasepool中,pool结束时自动调用release
使用NSMutableArray类的array类方法取得的谁都不持有的对象,这些方法都是通过autorelease实现的。
id obj = [Obj object];
取得的对象存在,但自己不持有该对象
可以通过retain方法将调用autorelease方法取得的对象变为自己持有
[obj retain];
retainCount的存在位置
1.内存块头部
2.引用计数器表(散列表,集中管理,苹果实现)
autorelease
C语言的自动变量
{
int a;
}
// 因为超出变量作用域,自动变量a被废弃
autorelease的具体使用方法
1.生成并持有NSAutoreleasePool对象
2.调用已分配对象的autorelease实例方法
3.废弃NSAutoreleasePool对象
在cocoa框架中,使用NSRunLoop对NSAutoreleasePool对象进行生成、持有和废弃处理
尽管如此,但在大量产生autorelease的对象时,只要不废弃autoreleasepool,那么生成的对象就不能被释放,还是会产生内存不足的情况,因此有必要在适当的地方生成、持有或废弃NSAutoreleasePool对象
for (int i=0; i<100000; i++) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//大量产生autorelease对象的方法
[pool drain];
}
Cocoa框架中有很多类方法用于返回autorelease的对象
id array = [NSMutableArray arrayWithCapacity:1];
等同于
id array = [[[NSMutableArray alloc] initWithCapacity:1] autorelease];
autorelease的实现
autorelease实例方法的本质就是调用NSAutoreleasePool对象的addObject类方法
[obj autorelease];
- (id)autorelease{
[NSAutoreleasePool addObject:self];
}
+ (void)addObject:(id)anObj{
NSAutoreleasePool *pool = 获取正在使用的NSAutoreleasePool对象(多个autoreleasepool会使用最内层的)
if (pool != nil) {
[pool addObject:anObj];
} else {
NSLog(@"NSAutoreleasePool对象非存在状态下调用autorelease");
}
}
// 如果调用NSObject类的autorelease实例方法,该对象将被追加到正在使用的NSAutoreleasePool对象中的数组里
- (void)addObject:(id)anObj{
[array addObject:anObj];
}
[pool drain];
- (void)drain{
[self dealloc];
}
- (void)dealloc{
[self emptyPool];
[array release];
}
- (void)emptyPool{
for (id obj in array) {
[obj release];
}
}
苹果的实现 AutoreleasePoolPage
autorelease NSAutoreleasePool 对象会发生异常
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[pool autorelease];
ARC
__strong
id __strong = [[NSObject alloc] init];
// 编译器模拟代码
id obj = objc_msgSend(NSObject, @select(alloc));
objc_msgSend(obj, @select(init));
objc_release(obj);
id __strong obj = [NSMutableArray array];
// 编译器模拟代码
id obj = objc_msgSend(NSMutableArray, @selector(array));
objc_retainAutoreleasedReturnValue(obj);
objc_release(obj);
__weak
1.若附有__weak 修饰符的变量所引用的对象被废弃,则将nil赋值给该变量
2.使用__weak修饰符的变量,即是使用注册到autoreleasepool中的对象
id __weak obj1 = obj;
// 编译器模拟代码
id obj1;
objc_initWeak(&obj1, obj);
objc_destroyWeak($obj1);
- (id)obj_initWeak:(id)obj1{
obj1 = 0;
objc_storeWeak(&obj1, obj);
}
- (void)objc_destroyWeak:(id)obj1{
objc_storeWeak(&obj1, 0);
}
objc_storeWeak函数把第二个参数的赋值对象的地址作为键值,将第一个参数的附有__weak修饰符的变量的地址注册到weak表中,如果第二个参数为0,则把变量的地址从weak表中删除
销毁对象
1.从weak表中获取废弃对象的地址为键值的记录
2.将包含在记录中的所有附有__weak修饰符变量的地址,赋值为nil
3.从weak表中删除该记录
4.从引用技术表中删除废弃对象的地址为键值的记录
__autoreleasing
@autoreleasepool{
id __autoreleasing obj = [[NSObject alloc] init];
}
该源代码主要将NSObject类对象注册到autoreleasepool中
id pool = objc_autoreleasePoolPush();
id obj = objc_msgSend(NSObject, @selector(alloc));
objc_msgSend(obj, @selector(init));
objc_autorelease(obj);
objc_autoreleasePoolPop(pool);
引用计数
id __strong obj = [[NSObject alloc] init];
retainCount = 1;
id __weak o = obj; // 若引用不持有对象
retainCount = 1;
id __strong obj = [[NSObject alloc] init];
@autoreleasepool{
id _autoreleasing o = obj; // retainCount = 2;
} //@autoreleasepool块结束时释放已注册的对象
retainCount = 1;
MRC: 谁创建谁释放
ARC: 编译时特性,自动插入release
向一个对象发送autorelease消息,这个对象不会立即销毁,而是将这个对象放入了自动释放池,待池子释放时,它会向池子中的每一个对象发送一条release消息,以此来释放对象
向一个对象发送release消息,将对象的引用计数器减一,当对象的引用计数为0时,系统会调用dealloc方法,释放对象和对象本身拥有的实例
Autorelease对象什么时候释放
在没有手动加入Autorelease Pool的情况下,Autorelease对象在当前的runloop迭代结束时释放。而它能够释放的原因是系统在每个runloop迭代中都加入了自动释放池push pop
ARC下dealloc的作用
ACR下,系统会帮我们释放该对象,及其包含的对象,但却无法释放不属于该对象的一些东西
- 通知观察者 NSNotification
- KVO观察者
使用容器的block版本的枚举器时,内部会自动添加一个AutoreleasePool
[array enumerateObjectsUsingBlock:^(id obj, NSInteger idx, BOOL *stop){
//这里被一个局部 @autoreleasePool 包围着
}];
ClassA *obj1 = [[ClassA alloc] init];
ClassA *obj2 = obj1;
[obj1 hello]; //输出hello
[obj1 dealloc];
[obj2 hello]; // 能继续执行吗
[obj2 dealloc];
不能,因为obj1和obj2只是指针,他们指向的是同一个对象,[obj1 dealloc]已经销毁了这个对象
OC指针赋值时,retain count 不会自动增加,需要手动retain
ClassA *obj1 = [[ClassA alloc] init]; // retain count =1
ClassA *obj2 = obj1; // 指针赋值,不会自动增加
[obj2 retain]; // retain count = 2
[obj1 hello]; //输出hello
[obj1 release]; // retain count = 1
[obj2 hello]; // 能继续执行吗
[obj2 release]; // retain count = 0 , 对象被销毁