// 第十章 属性的实现原理
// 引用计数: (retainCount) 指向同一块内存的引用个数; 仍然需要release
// 1.引用计数只对堆区的对象有意义,每个对象都有引用计数
// 1)字符串引用计数问题
// 情况1:常量区
NSString *str = @"程序员";
NSLog(@"%@ %lu", str, str.retainCount);//打印结果:18446744073709551615
//%lu溢出,打印出很大的书,说明是个负数, 用 %d 打印
NSLog(@"%@, %d", str, str.retainCount);//打印结果:-1
// 情况2:常量区
NSString *str1 = [NSString alloc]initWithString:@"产品狗"];
NSLog(@"%@ %lu", str1, str1.retainCount);
// 情况3:堆区
NSString *str2 = [[NSString alloc]initWithFormat:@"架构狮"];
NSLog(@"%@ %lu", str2, str2.retainCount);
//注: 当[[NSString alloc]initWithFormat:@"架构狮"];中字符串为中文, retainCount ++; 若为中文或数字 10 个以内为 - 1, 之后为 1;
// 2)setter和getter方法 属性的内部实现
// assign:
// 内部实现:
@synthesize age = _age;
//非对象类型 直接写
-(void)setAge:(NSInteger)age{
_age = age;
}
-(NSInteger)age{
return _age;
}
// copy、retain:
// (1)(setter): 释放实例变量上一次的retain, 为这次赋值retain
// (上次为0则retainCount ++, 否则不变)
// (2)(getter): 帮别人retain, 自动释放(retainCount ++)
// 情况1:setter方法的野指针异常
// 原理:
Person *per = [[Person alloc]init];
per.name = [str1 retain];//不加retain, 访问per.name会奔溃
per.name = [str2 retain];//每一次给属性命名, 都要加上retain; 因此需要在属性内部加上 retain
[str2 release];
NSLog(@"%@", per.name);
[per.name release];
[per release];
[per sayHello];//给一个已经被alloc的对象发消息, 会造成crash
NSLog(@"%lu", per.retainCount);
// setter方法的内部实现
@synthesize name = _name;
-(void)setName:(NSString *)name{
//_name = name;单纯这样写会造成Crash
if (_name != name) {
[_name release];//给nil对象发送消息不会造成奔溃; 但是给一个释放内存的对象发送消息就会产生野指针异常
_name = [name retain];
}
}
// 情况2: getter方法的野指针异常
// 原理:
NSString *str3 = @"小笨猪";
Person *per3 = [[Person alloc]init];
per3.name = str3;//此时per3.name指向str3
[str3 release];//str3释放
NSString *str4 = per.name;//此时如果str被释放, 会造成Crash
[per3 release];
NSLog(@"%@", str4);
// 2.对象定义的内存管理: 被赋值的实例变量retainCount++
// 1)便利构造器:
// 原理:
NSArray *arr = [NSArray arrayWithObjects:@"2", @"3", @"4", nil];
NSLog(@"%lu", arr.retainCount);//arr的引用计数
[arr release];
// 内部实现:
+(instancetype)personWithName:(NSString *)name{
Person *per = [[Person alloc]initWithName:name];
return [per autorelease];
}
// 2)初始化:
// 原理:
NSString *str = [[NSString alloc]initWithString:@"产品狗"];
Person *per = [[Person alloc]initWithName:str age:23];//在这里面需要_name retain
per.name = @"CTO";//这里会使引用计数变为 -1
NSLog(@"Str 的引用计数: %lu", str.retainCount);//str的引用计数为3
NSLog(@"per.name 的引用计数: %lu", per.name.retainCount);//per.name的引用计数也为3, 因为它们指向同一块内存空间
// 内部实现:
-(instancetype)initWtihName:(NSString *)name age:(NSInteger)age{
if ([super init]) {
self.name = name;//因为set方法已经为name retain 了一次, 所以直接赋值
self.age = age;
}
return self;
}
// 例子1:分析打印结果: str的引用计数:1 per.name的引用计数:3
NSString *str = [[NSString alloc]initWithFormat:@"产品狗"];
Person *per = [[Person alloc]initWtihName:str age:23];
per.name = [[NSString alloc]initWithFormat:@"逗比"];//先释放使str.retainCount --
NSLog(@"str 的引用计数: %lu", str.retainCount);
NSLog(@"per.name 的引用计数: %lu", per.name.retainCount);
// 例子2:分析打印结果: 1 , - 1 , -1
NSString * aString = [[NSString alloc] initWithFormat:@"程序员"];
NSLog(@"%d",aString.retainCount);
aString = @"456";
NSLog(@"%d",aString.retainCount);
[aString release];
aString = @"789";
NSLog(@"%d",aString.retainCount);
// 3)集合的内存管理: 数组、集合、字典:
Person *per = [[Person alloc]init];
Person *per1 = [[Person alloc]init];
Person *per2 = [[Person alloc]init];
NSLog(@"%lu", per.retainCount);//引用计数为1
NSMutableArray *arr = [[NSMutableArray alloc]initWithObjects:per, per1, per2, nil];//对象添加到数组中的时候,引用计数 +1
NSLog(@"%lu", per.retainCount);//引用计数为2
[per release];
NSLog(@"%@", arr[0]);
[arr removeObject:per];//将对象从数组中移除的时候, 引用计数减1
NSLog(@"%lu",per.retainCount);
[arr release];//数组中被(dealloc)释放的时候, 数组中的每个元素引用计数减1
NSLog(@"%lu",per2.retainCount);
// 4)类的回收
// 内部实现:
-(void)dealloc{
[_name release];//当Person被回收时, 同时释放_name
[super dealloc];//一定要写在最下面
}
打下手 -> 程序员 -> Teemo队长 -> 项目经理 -> 架构师 -> CTO