#import <Foundation/Foundation.h> @interface Test : NSObject /** * 默认的就是__strong,这里只是做示范,实际使用时,不用写。 * * @param obj <#obj description#> */ - (void)setObject:(id __strong)obj; @end
#import "Test.h" @interface Test(){ id __strong obj_; /** 默认的就是__strong,这里只是做示范,实际使用时,不用写。*/ } @end @implementation Test - (void)setObject:(id)obj{ //设置成员变量的值 obj_ = obj; } @end
ARC的学习
使用ARC时,id类型和对象类型上必须加上所有权修饰符,所有权修饰符分为下面四种,一.是__strong,强引用(id类型和对象类型默认的就是这个修饰符,如成员变量,所有前面什么也不加,就是__strong)
是__weak弱引用,一般不会持有对象。三.是__unsafe_unretained修饰符。 四. 是__autoreleasing修饰符。
一. __strong 的使用
特点
1.强引用,会持有对象,但会造成两个类或对象之前互相引用,造成循环引用。
2.超出作用域,强引用失效,会被释放。
3. 栗子1,循环引用,循环引用的重点就是最后引用的的那个对象是否为空,如果持有的对象已经废弃了,那个引用还存在,说明内存泄漏了,不能合理的翻译该对象。
id test1 = [[Test alloc] init]; /**test1 持有对象A的强引用*/ id test2 = [[Test alloc] init]; /**test2 持有对象B的强引用*/ [test1 setObject:test2]; /**Test对象A的成员变量obj_持有Test对象B的强引用,此时持有Test对象B的强引用为Test对象A的成员变量obj_和test2*/ [test2 setObject:test1]; /**Test对象B的成员变量obj_持有Test对象A的强引用,此时持有Test对象A的强引用为Test对象A的成员变量obj_和test1*/ /**1.因为test1变量超出作用域时,强引用会失效,会自动释放Test对象A*/ /**2.因为test2变量超出作用域时,强引用会失效,会自动释放Test对象B*/ /**3.此时持有Test对象A的强引用变量为Test对象B的成员变量obj_*/ /**4.此时持有Test对象B的强引用变量为Test对象A的成员变量obj_*/ /**5.发生内存泄漏*/
3.什么是内存泄漏?
/**内存泄漏是应当废弃的对象,在赶出自己的生命周期后继续存在*/
4.自己持有自己,不会造成内存泄漏,因为超出其生命周期时,会正常释放。
id test0 = [[Test alloc] init]; [test0 setObject:test0];
5. 如何避免内存泄漏
二. __weak的使用
特点
1.避免循环引用
2.当一个对象对使用弱引用时,若对象被废弃时,该对象会被置为空
3.不支持iOS4及以下和OS X Snow Leopard
#if 0 //强引用的话,最终obj1还会持有对象。 id obj1 = nil; //默认的是用__strong修饰 { id obj0 = [[NSObject alloc] init]; obj1 = obj0; /**强引用持有obj0*/ NSLog(@"obj1=%@,obj0=%@",obj1,obj0); //打印结果, obj1=<NSObject: 0x7fbcf3417220>,obj0=<NSObject: 0x7fbcf3417220> } /**obj0已经被废弃了,但obj1还持有它*/ NSLog(@"强引用超出作用域被废弃后obj1=%@",obj1); // 弱引用超出作用域被废弃后obj1=<NSObject: 0x7fbcf3417220> #endif #if 0 //弱引用的话,最终obj1为nil id __weak obj1 = nil; //默认的是用__strong修饰,现在这里弱引用这个对象,并不持有它 { id obj0 = [[NSObject alloc] init]; obj1 = obj0; NSLog(@"obj1=%@,obj0=%@",obj1,obj0); //打印结果, obj1=<NSObject: 0x7fbcf3417220>,obj0=<NSObject: 0x7fbcf3417220> } NSLog(@"弱引用超出作用域被废弃后obj1=%@",obj1); // 打印结果,弱引用超出作用域被废弃后obj1=(null) #endif
三. __unsafe_unretained的使用
特点
1.当使用iOS4及以下和OS X Snow Leopard时,会用到__unsafe_unretained,主要目的是在iOS4及以下和OS X Snow Leopard中代替__weak的。
#if 1 //__unsafe_unretained的话,最终会抛出异常或恰巧正常运行。 id __unsafe_unretained obj1 = nil; //默认的是用__strong修饰,现在这里弱引用这个对象,并不持有它 { id obj0 = [[NSObject alloc] init]; obj1 = obj0; NSLog(@"obj1=%@,obj0=%@",obj1,obj0); //打印结果, obj1=<NSObject: 0x7fbcf3417220>,obj0=<NSObject: 0x7fbcf3417220> } NSLog(@"__unsafe_unretained超出作用域被废弃后obj1=%@",obj1); //objc1表示变量的对象,已经被废弃(悬垂指针)!错误访问!,也就是说,最后一行的NSLog只是碰巧正常运行而已,虽然访问了已经废弃的对象,但应用程序在个别情况下 #endif
四. __autoreleasing 的使用,在ARC中不允许使用autorelease和自动释放池pool,但autorelease是真实存在的。
arc下有效的方法
@autoreleasepool { id __autoreleasing obj = [[NSObject alloc] init]; //obj,超出释放池之后会被自动释放 }
相当于mrc下的NSAutoreleasePool
#if 0 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; //如obj,要做耗时操作,如加载大量图片等等 id obj = [[NSObject alloc] init]; [objc autorelease]; [pool drain]; #endif
除alloc/new/copy/mutableCopy创建的对象不会注册的aureleasePool外,其他的都会注册到,如__weak,其他情况即为“取得非自己生成持有的对象”,这些务必牢记,为了在使用参数取得对象时,贯彻内存管理的思考方式,我们要将参数声明附有__autorelease的修饰符指针
//***__strong修饰符和__weak修饰符类似于C++中的智能指针,std::shared_ptr和weak::shared_ptr,前者也是强引用,后者也是避免循环引用***//
不要显式调用dealloc,如【self dealloc】,下面是可行的
//------------------------ARC下内存管理规则,不能使用或不推荐使用的-----------------------//
1.对象变量不能作为C语言的结构体成员,
struct data{ // NSMutableArray *array; //成员变量,因为这是Objective-c中故有的对象,所以不能作为成员变量。 NSMutableArray __unsafe_unretained *array; // 这样的话可以在Objective中使用,但__unsafe_unretained不属于内存管理的对象,所以会造成内存泄漏。 NSInteger name; }data1,data2; //data1,data2是变量名 data1.name = 123; data2.name = 456; NSLog(@"结构体打印%ld,%ld",data1.name,data2.name); //2.显式转换id与void*的转换, 通过 __bridge 可以显式转换id与void*的转换 id obj = [[NSObject alloc] init]; void *p =(__bridge void *)obj; id obje = (__bridge id)p;
__bridge 还有两种转换,__bridge_retained(使用后对象还存在相当于retain),__bridge_transfer(使用后对象被释放,相当于release),ARC中不推荐使用
但这样是可以转换,其安全性与__unsafe_unretained类似甚至会更低,如果管理者不注意赋值对象的所有者就会因悬垂指针而导致程序崩溃
//*** 悬垂指针 :指向曾经存在的对象,但该对象已经不再存在了,此类指针称为悬垂指针。结果未定义,往往导致程序错误,而且难以检测。***/