OC进阶(一)
1.OC的内存管理
---任何对象都有可能拥有一个或者多个拥有者,只要一个对象至少还有一个拥有者,它就会继续存在
/* 1 引用计数器: 用来保存当前对象有几个东西在使用它(数字) 2 引用计数器的作用: 用来判断对象是否应该回收内存空间(如果对象不等于nil,当引用计数器为0,此时要回收对象的内存空间) 3 引用计数器的操作: retain 使得引用计数器+1 release 使的引用计数器-1 retainCount 得到引用计数器的值 4 如果一个对象被释放的时候,会有"临终遗言"(会调用该对象的dealloc方法) 注意: 1)dealloc方法是NSObject 的,一般我们要重写dealloc方法 2)在dealloc 方法的内部,要调用 [super dealloc]; 5 手动内存管理: MRC 手动内存管理 ARC 自动内存管理 注意,我们创建一个项目,此时默认的是ARC的(自动内存管理) 把项目从ARC模式改成 MRC模式 */
#import <Foundation/Foundation.h> #import "Person.h" int main(int argc, const char * argv[]) { @autoreleasepool { //用Person 类实例化一个实例对象 Person *p = [Person new]; // 对象有没有所有者? 有 // [p dealloc]; //证明有一个所有者 NSUInteger count = [p retainCount]; NSLog(@"count = %lu",count); // 1 //使用引用计数器+1 // Person *p2 = p; // //Person *p2 = [p retain]; [p retain]; NSLog(@"p.retainCount = %lu",[p retainCount]); //2 //如果要回收对象? 应该想办法 retatinCount = 0 [p release]; NSLog(@"p.retainCount = %lu",[p retainCount]); //1 [p release]; //此处执行后,p的空间被回收 //0 //证明p的空间被释放了,可以在在Person类中,重写dealloc方法 } return 0; }
@implementation Person //dealloc方法,是对象的临终遗言的方法 //对象被销毁的时候,会默认的调用该方法 //注意:dealloc 方法是系统根据引用计数器的值,自动调用的, //不需要手动调用 - (void)dealloc { //1 先释放子类自己的对象的空间 NSLog(@"Person已经挂了"); //2 再释放父类的 [super dealloc]; } @end
2.内存管理的原则
/* 内存管理: 对象如果不在使用了,就应该回收它的空间,防止造成内存泄露 内存管理的范围: 所有的集成了NSObject的对象的内存管理 基本数据类型(int double float char struct enum )的数据内存不需要我们进行管理 内存管理的原则: 一. 如果对象有人使用,就不应该回收 如果你想使用这个对象,应该让这个对象 retain一次 如果你不想使用这个对象了,应该让这个对象 relase一次 二. 谁创建 谁release 三. 谁 retain 谁 release 内存管理研究的内容: 1)野指针: 1)定义的指针变量没有初始化 2)指向的空间已经被释放了 2)内存泄露: { Person *p = [Person new]; } p 栈区 [Person new]; 堆区 如果栈区的p已经释放了,而堆区的空间还没有释放,堆区的空间就被泄露了 */ #import <Foundation/Foundation.h> #import "Dog.h" int main(int argc, const char * argv[]) { @autoreleasepool { //创建: new alloc init copy Dog *bigYellowDog = [Dog new]; //1 NSLog(@"bigYellowDog.retainCount = %lu",[bigYellowDog retainCount]); //1 // Dog *jd = [bigYellowDog retain]; //jd 2 bigYellowDog 2 // [jd run]; NSLog(@"bigYellowDog.retainCount = %lu",[bigYellowDog retainCount]); //2 NSLog(@"jd.retainCount = %lu",[jd retainCount]); //2 [bigYellowDog release]; //1 //只有保证谁创建谁release 才能保证对象能够释放 [bigYellowDog release]; //0 } return 0; }
3.野指针
/* 一定要注意: 每个Target创建完成以后,默认的都是ARC模式 */ #import <Foundation/Foundation.h> #import "Dog.h" int main(int argc, const char * argv[]) { @autoreleasepool { //创建一个对象 //对象创建完成以后,默认的所有者有一个,是自己,所以引用计数为1 Dog *byd = [Dog new]; //1 [byd eat]; // NSLog(@"byd.retainCount = %lu",byd.retainCount); //如果一个对象已经被释放了,这个对象就称之为僵尸对象 // [byd release]; //0 // NSLog(@"byd.retainCount = %lu",byd.retainCount); //值已经没有意义了 //这句话默认情况下不报错, //如果要让他报错,要开启僵尸对象检测 //byd指针也就是野指针 // [byd eat]; //野指针访问 // [byd retain]; //byd 已经是僵尸对象了,不能复生 } return 0; }
4.单个对象的内存管理
#import <Foundation/Foundation.h> #import "Dog.h" //避免僵尸对象的使用报错 void test(){ Dog *d = [[Dog alloc] init]; //1 [d release]; //1--->0 //nil 给nil发送任何消息,都没有效果 // [nil run]; //避免使用僵尸对象的方法是,对象释放了以后,给对象赋值为nil //健壮性 d = nil; //nil 给对象赋值 Nil 类对象 // [d retain]; // } int main(int argc, const char * argv[]) { @autoreleasepool { //单个对象的内存泄露问题 //内存泄露情况1: // 创建完成 使用之后,没有release // Dog *d = [[Dog alloc] init]; //1 // NSLog(@"%lu",d.retainCount); //使用d对象 //内存泄露情况2: //没有遵守内存管理的原则 // Dog *d = [[Dog alloc] init]; //1 // [d retain]; //2 //// [d release]; // [d release]; //1 //内存泄露的情况3: //不当的使用了nil // Dog *d = [[Dog alloc] init]; //1 // d = nil; // // [d eat]; //nil eat // [d release]; // nil release //内存泄露的情况4: //在方法中对传入的对象进行了retain Dog *d = [[Dog alloc] init]; //1 NSLog(@"d.retainCount = %lu",d.retainCount); //对象依然被泄露了 [d compareColorWithOther:d]; //2 NSLog(@"d.retainCount = %lu",d.retainCount); [d release]; } return 0; }
5.多个对象的野指针错误
#import <Foundation/Foundation.h> #import "Person.h" int main(int argc, const char * argv[]) { @autoreleasepool { //创建对象 Person *fengjie = [Person new]; //1 Car *bigBen = [Car new]; //1 bigBen.speed = 100; //给凤姐一辆车 [fengjie setCar:bigBen]; [fengjie goLasa]; [fengjie goLasa]; // [bigBen release]; //1->0 car销毁 车已经毁了 [fengjie goLasa]; // 车已经毁了 人就不能去拉萨了 所以报错 [fengjie release]; } return 0; }
6.多个对象的内存管理
#import "Person.h" @implementation Person - (void)dealloc { //为什么要让车先挂 //_car ---> 1 [_car release]; //_car 0 销毁 NSLog(@"人已经挂了"); [super dealloc]; } -(void)goLasa{ //开车 // [nil run]; [_car run]; } -(void)setCar:(Car *) car{ //bigBen 1 _car = [car retain]; //benBen _car 2 } @end
7.set方法的内存管理
//set方法的内存管理 //原则: //如果在一个类中,有其他类的对象(关联关系) //set方法书写的时候,要:判断是否是同一个对象,release 旧值,retain新值 -(void)setCar:(Car *) car{ //为了解决set方法的内存泄露,我们在新的retain之前,先release旧值 // [_car release]; // _car = [_car retain]; NSString //car bmw // 如果 _car == car 这是同一个对象 // 如果是同一个对象就不需要在release if (_car != car) { [_car release]; //[bmw release] 1-->0 _car = [car retain]; // _car bmw 2 } } @end
8.@property参数
#import <Foundation/Foundation.h> #import "Car.h" @interface Person : NSObject @property(nonatomic,retain) Car *car; /* assign 直接赋值 -(void)setCar:(Car *)car{ _car = car; } 验证: assign retain release 旧值,retain新值 //什么时候使用retain //在一个类中有关联其他对象的时候,这个对象的 @property (nonatomic,retain) //什么时候使用assign //实例变量是基本数据类型的时候 -(void)setCar:(Car *)car{ if(_car != car){ [_car realase]; _car = [car retain]; } } */ @property(nonatomic,assign) int age; -(void)driver; @end
#import <Foundation/Foundation.h> @interface Person : NSObject @property (nonatomic,assign,setter=isVip:,getter=isVip) BOOL vip; //_vip @end
#import <Foundation/Foundation.h> #import "Dog.h" #import "Person.h" int main(int argc, const char * argv[]) { @autoreleasepool { Dog *d = [Dog new]; // d.tuiNum ----> [d setTuiNum:4]; // d.tuiNum = 4; //调用set方法 NSLog(@"tuiNum = %d",d.tuiNum); //调用get方法 Person *p = [Person new]; //isVip //setVip // p.vip = YES; //1)替换set方法的名称 @property (nonatomic,setter = isVip:) // [p setVip:YES]; //----- [p isVip:YES]; //2)替换get方法的名称 @property (nonatomic,setter = isVip:,getter=isVip) [p isVip:YES]; //[p isVip] if (p.isVip) { NSLog(@"这时VIP客户"); } } return 0; }
9.@class的使用
/* @class的使用: @class的使用格式: @class 类名; @class XXXX; 含义: 告诉编译器,XXXX 是一个类,至于类有哪些属性和方法,此处不去检测 好处: 如果XXXX文件内容发生了改变,而不需要重新编译 @class 的使用注意: 1) .h @class XX; 2) .m #import "XX.h" @class的特殊用法: 可以解决循环引入问题 */
10.循环retain
/* 循环的retain 会导致两个对象都会内存泄露 防止方法: 1) 让某个对象多释放一次 (注意顺序) 2) 推荐的方法: 一端使用 assign 一端使用retain */
11.NSSting的内存管理
#import <Foundation/Foundation.h> int main(int argc, const char * argv[]) { @autoreleasepool { //定义字符串 //字符串的常量池, //如果你需要的字符串在常量池中已经存在了,不会分配内存空间 //使用字符串的时候, // @"abc" stringWithString alloc initWithString 都在常量区/\ //0x100001030 小 NSString *str1 = @"abc"; //@"abc" 字符串的常量 NSString *str3 = [NSString stringWithString:@"abc"]; //常量区 NSString *str5 = [[NSString alloc] initWithString:@"abc"]; //也在常量区 NSString *str6 = [[NSString alloc] init];//常量区 str6 = @"abc"; //0x100202030 大 //如果在常量区 str2 str4 地址应该是一样的 //实际上不一样的,所以 str2 str4都在堆区 NSString *str2 = [NSString stringWithFormat:@"abc"]; //不是在栈区,在堆区 NSString *str4 = [[NSString alloc] initWithFormat:@"abc"];//不是在栈区,在堆区 //0x7fff5fbff764 int a = 10; //栈区 NSLog(@"str1 = %@,%p,%lu",str1,str1,str1.retainCount); NSLog(@"str2 = %@,%p,%lu",str2,str2,str2.retainCount); NSLog(@"str3 = %@,%p,%lu",str3,str3,str3.retainCount); NSLog(@"str4 = %@,%p,%lu",str4,str4,str4.retainCount); NSLog(@"str5 = %@,%p,%lu",str5,str5,str5.retainCount); NSLog(@"str6 = %@,%p,%lu",str6,str6,str6.retainCount); NSLog(@"a = %p",&a); } return 0; }
11.autorelease的基本使用
/* 自动释放池 :特殊的栈结构 特点: 对象可以加入到自动释放池中 自动释放池结束的时候,会给池中的对象发送一条 release消息 自动释放池的使用: 1) 创建自动释放池 @autoreleasepool { } 2) 加入自动释放池 在自动释放池中 [对象 autorelease]; */ #import <Foundation/Foundation.h> #import "Person.h" int main(int argc, const char * argv[]) { //1 创建自动释放池 Person *p = [Person new]; // p 1 @autoreleasepool {//自动释放池开始 [p run]; NSLog(@"%lu",p.retainCount); // 1 // [p autorelease] 把对象p加入到自动释放池中 // 注意:加入到自动释放池中以后, 引用计数不会变化 [p autorelease]; //加入自动释放池, NSLog(@"%lu",p.retainCount); // 1 [p run]; }//自动释放池结束 [p release]; [p run]; return 0; }