内存管理

对象操作与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下,系统会帮我们释放该对象,及其包含的对象,但却无法释放不属于该对象的一些东西

  1. 通知观察者 NSNotification
  2. 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 , 对象被销毁

你可能感兴趣的:(内存管理)