1.2.2
内存管理原则:
- 自己生成的对象,自己所持有
- 非自己生成的对象,自己也能持有
- 不再需要自己持有的对象时释放
- 非自己持有的对象无法释放
自己生成的对象,自己所持有
//自己生成并持有对象
id obj = [[NSObject alloc] init];
//自己持有对象
使用NSObject类的alloc类方法就能自己生成并持有对象。指向生成并持有对象的指针
使用一下名称开头的方法名意味着自己生成的对象只有自己持有:
- alloc
- new
- copy
- mutableCopy
根据以上规则,下列名称也意味着自己生成并持有对象
- allocMyObject
- newThatObject
但是以下规则无效 - allocate
- newer
非自己生成的对象,自己也能持有
//非自己生成并持有的对象
id obj = [NSMutableArray array];
//取得的对象存在,但自己不持有对象
源码中,NSMutableArray类对象呗赋值给变量obj,但变量obj自己并不持有该对象。使用retain方法可以持有对象
//在后面多加一句
[obj retain];
//自己持有对象
不再需要自己持有的对象时释放
释放时使用release方法
//自己生成并持有对象
id obj = [[NSObject alloc] init];
//自己持有对象
[obj release];
//释放对象。指向对象的指针任然被保留在变量obj中,貌似能够访问
//但是对象一经释放绝对不可访问
如果要用某个方法生成对象,并将其返还给该方法的调用方,那么它的源代码又是怎样的呢
-(id)allocObject
{
//自己生成并持有对象
id obj = [[NSObject alloc] init];
//自己持有对象
return obj;
}
如上,原封不动的返回用alloc方法生成并持有的对象,就能让调用方也持有改对象。请注意allocObject这个名称是符合前文命名规则的
//取得非自己生成并持有的对象
id obj1 = [ojb0 allocObject];
//自己持有对象
所以,如果要自己不持有对象,就不能使用alloc/new/copy/mutableCopy开头的方法名,实现如下
-(id)object
{
id obj = [[NSObject alloc] init];
//自己持有对象
[obj autorelease];
//取得对象存在,但自己不持有
return obj;
}
上例中,我们使用了autorelease方法,该方法可以取得对象存在,但自己不持有对象。autorelease提供使得对象在超出指定的生存范围时能够自动并正确得释放的功能
当然,也能够通过retain方法将调用autorelease方法取得的对象变为自己持有
id obj1 = [obj0 object];
//取得但自己不持有
[obj1 retain];
//自己持有对象
无法释放非自己持有的对象
id obj = [[NSObject alloc] init];
[obj release];
[obj release];
//这里这里就崩溃了
id obj1 = [obj0 object];
[obj1 release];
//这里也崩溃
1.3.3所有权修饰符
__strong
ARC时,id类型的对象类型同C语言其他类型不同,其类型上必须附加所有权修饰符。所有权修饰符一共有4种
__strong __weak __unsafe __unretained __autoreleasing
__strong修饰符是id类型的和对象类型默认的所有权修饰符。也就是说,以下源代码中的id变量,实际上被附加了所有权修饰符
id obj = [[NSObject alloc] init];
其与以下相同
id __strong obj = [[NSObject alloc] init];
MRC下:
id obj = [[NSObject alloc] init];
再看以下代码
{
id __strong obj = [[NSObject alloc] init];
}
MRC下:
{
id obj = [[NSObject alloc] init];
[obj release];
}
为了释放生成并持有的对象,增加了release方法的代码
如此源码所示,附有__strong修饰符的变量obj在超出其变量作用域时,即在该变量被废弃时,会释放其被赋予的对象
__strong、__weak、__autoreleasing,可以保证附有这些修饰符的自动变量初始化为nil
id __strong obj0;
id __weak obj1;
id __autoreleasing obj2;
同:
id __strong obj0 = nil;
id __weak obj1 = nil;
id __autoreleasing obj2 = nil;
id __weak obj = [[NSObject alloc] init];
NSLog(@"弱引用自身地址:%p",&obj);
NSLog(@"弱引用指向地址:%p",obj);
id __strong obj0 = [[NSObject alloc] init];
id __weak obj1 = obj0;
NSLog(@"强引用自身地址:%p",&obj0);
NSLog(@"弱引用自身地址:%p",&obj1);
NSLog(@"强引用指向地址:%p",obj0);
NSLog(@"弱引用指向地址:%p",obj1);
// obj1 = nil;
// obj0 = nil;
NSLog(@"弱引用销毁时强类型变量指向地址:%p",obj0);
NSLog(@"弱引用销毁时弱类型变量指向地址:%p",obj1);
由此可见,强引用时对变量拥有,弱引用时只是指向该变量而不拥有
非自己生成的对象,即alloc/new/copy/mutableCopy以外的创建方法取得对象,并且赋值给非strong变量时,对象将会自动注册至autoreleasepool,从而不至于立刻被析构; (3.1)
当赋值给给strong变量时,编译器作自动优化插入objc_retainAutoreleaseedReturnValue, 而不是插入retain,以避免注册至autoreleasePool.(3.3)
访问weak变量时,将会把指向对象注册至autoreleasePool.(3.3)。另外,对weak变量再访问会再注册,访问几次则会注册几次,所以这里,最好把weak变量赋给一个strong变量,避免重复注册到aotureleasepool
获取引用计数数值的函数uintptr_t objc_rootRetainCount(id obj)
例子:
{
id __strong obj = [[NSObject alloc] init];
id __weak o = obj;
NSLog(@"retain count = %d", _objc_rootRetainCount(obj));//结果为1
}
dispatch_sync函数。它意味着“同步”,也就是将制定Block“同步”追加到指定的Dispatch Queue中。在追加Block结束之前,dispatch_sync函数会一直等待
sync引起线程死锁
sync 会等到 后面block 执行完成才返回, sync 又再 dispatch_get_main_queue() 队列中,
它是串行队列,sync 是后加入的,前一个是主线程,
所以 sync 想执行 block 必须等待主线程执行完成,主线程等待 sync 返回,去执行后续内容。
dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"=================2"); });
以下正面例子
- (void)viewDidLoad{
[super viewDidLoad];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"=================1");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"=================2"); });
NSLog(@"=================3"); });
}
程序会完成执行,为什么不会出现死锁。
首先: async 在主线程中 创建了一个异步线程 加入 全局并发队列,async 不会等待block 执行完成,立即返回,
1,async 立即返回, viewDidLoad 执行完毕,及主线程执行完毕。2,同时,全局并发队列立即执行异步 block , 打印 1, 当执行到 sync 它会等待 block 执行完成才返回, 及等待
dispatch_get_main_queue() 队列中的 mianThread 执行完成, 然后才开始调用block 。因为1 和 2 几乎同时执行,因为2 在全局并发队列上, 2 中执行到sync 时 1 可能已经执行完成或 等了一会,mainThread 很快退出, 2 等已执行后续内容。