作者:mobiledu2502889953 | 来源:互联网 | 2024-09-28 12:42
———Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ———
一、引用计数器
每个OC对象都有自己的引用计数器,表示“对象被引用的次数”。
每个OC对象内部都有专门的4个字节的存储空间来存放引用计数器。
1、引用计数器的作用:
1> 当使用alloc、new或copy创建一个新对象时,新对象的引用计数器就为1。
2> 当一个对象的引用计数器的值为0时,对象所占的内存就会被系统回收。
2、引用计数器的基本操作
1> retain 计数器+1 返回对象本身
2> release 计数器-1 没有返回值
3> retainCount 返回当前计数器值
int main()
{
// b-1
Book *b = [[Book alloc] init];
// p-1
Person *p = [[Person alloc] init];
//p想占用b这本书
// b-2
[p setBook:b];
// p-0
// b-1
[p release];
p = nil;
// b-0
[b release];
b = nil;
return 0;
}
3、- dealloc方法
1> 当一个对象销毁时,系统会自动向对象发送一条dealloc的消息。(调用- dealloc方法)
2> 一旦重写了dealloc方法,就必须调用[super dealloc],并且放在最后面。
3> 一旦对象被回收了,它占用的内存就不再可用。
- (void)dealloc
{ NSLog(@"Person对象被回收!!!"); //super的dealloc一定要调用,而且放在最后面 [super dealloc];
}
4、僵尸对象、野指针、空指针
1> 僵尸对象:所占内存已经被回收的对象,僵尸对象不能再使用
2> 野指针:指向僵尸对象(不可用内存)的指针,给野指针发送消息会报错
3> 空指针:没有指向任何东西的指针(存储的东西是nil,null,0),给空指针发送消息不会报错
二、setter方法的内存管理
1、set方法实现
1> 基本数据类型:直接赋值
- (void)setAge:(int)age
{
_age = age;
}
2> OC对象类型
- (void)setCar:(Car *)car
{
//1.先判断是不是新传递进来的对象
if(car != _car)
{
//2.对旧对象做一次release
[_car release];
//3.对新对象做一次retain
_car = [car retain];
}
}
2、dealloc方法实现
// 对当前对象所拥有的其他对象做一次release
- (void)dealloc
{
[_car release];
[super dealloc];
}
三、@property
@property 参数:
1、set方法内存管理相关的参数
1> retain:release旧值,retain新值(适用于OC对象类型)
2> assign:直接赋值(默认,适用于非OC对象类型)
3> copy:release旧值,copy新值(一般用于NSString *)
2、是否要生成set方法
1> readonly:只会生成getter的声明和实现
2> readwrite:同时生成setter和getter的声明和实现
3、多线程管理
1> nonatomic:性能高(推荐)
2> atomic:性能低(默认)
4、setter和getter方法的名称
1> setter:决定了set方法的名称,一定要由冒号:
2> getter:决定了get方法的名称
@interface Person : NSObject
@property int age;
@property (nonatomic, readonly) double height;
@property (setter = setAbc:, getter = aaa) double weight;
// 返回BOOL类型的方法中,方法名一般以is开头
@property (getter = isRich) BOOL rich;
// retain参数:生成的set方法中,release旧值,retain新值
@property (retain) Book *book;
@end
四、循环引用
A类引用了B类,同时B类也引用了A类,导致A对象和B对象永远无法释放。
解决方法:一端用retain,一端用assign。
开发中一个类引用另一个类的规范:
1> 在.h文件中用@class来声明类
2> 在.m文件中用#import来包含类的所有东西
@class的作用:仅仅告诉编译器,某个名称是一个类
格式:@class 类名;
@class和#import的区别:
1> #import方式会包含被引用类的所有信息,包括被引用类的变量和方法;@class方式只是告诉编译器在A.h文件中B *b只是类的声明,具体这个类里面有什么信息,编译器不需要知道。等实现文件.m中真正用到的时候,才会真正去查看B类里面的信息。
2> 如果很多头文件都#import了同一个文件,那么一旦修改了最开始的头文件,后面所有#import引用到这个文件的所有的类都需要重新编译一遍,@class就不会有这种问题。
3> 使用了@class方式,在.m实现文件中,如果需要用到被引用的实体变量或方法,我们还需要使用#import方式引入被引用类。#import方式则不用。
五、autorelease
1、给某个对象发送一条autorelease消息时,就会将这个对象加到一个自动释放池中
2、调用autorelease方法时并不会改变对象的计数器,并且会返回对象本身
3、当自动释放池销毁时,会给池子里面的所有对象发送一条release消息
4、使用注意
1> 占用内存较大的对象不要随便使用autorelease(一直占着,直到pool销毁才释放,太耗内存)
2> 占用内存较小的对象使用autorelease,没有太大的影响
5、常见错误
1> alloc之后调用了autorelease,又调用release,池释放时出现野指针错误
2> 连续调用autorelease,池释放时也会出现野指针错误
@autoreleasepool {
// p-1
// 若调用2次autorelease方法,释放池的时候会进行2次release操作,出现野指针错误
Person *p = [[[Person alloc] init] autorelease];
// p-0
[p release];
} // 野指针错误
6、系统方法
1> 一般来说,除了alloc、new或copy之外的方法创建的对象都被声明了autorelease
比如下面的对象都已经是autorelease的,不需要再release
NSNumber *n = [NSNumber numberWithInt:100];
NSString *s = [NSString stringWithFormat:@"jack"];
NSString *s2 = @"rose";
2> 开发中经常写一些类方法快速创建一个autorelease的对象。创建对象的时候不要直接使用类名,用self。
六、ARC
ARC的判断准则:只要没有强指针指向对象,就会释放对象
1、指针分2种
1> 强指针:默认情况下,所有的指针都是强指针 __strong
2> 弱指针:__weak
2、ARC特性
1> 不允许调用release、retain、retainCount
2> 允许重写dealloc,但不允许调用[super dealloc]
3、@property参数
1> strong:成员变量是强指针,相当于原来的retain(适用于OC对象)
2> weak:成员变量是弱指针,相当于原来的assign(还是适用于OC对象)
3> assign:适用于非OC对象类型
#import
@class Dog;
@interface Person : NSObject
@property (nonatomic, weak) Dog *dog;
@property (nonatomic, strong) NSString *name;
@end
/* 对象一被创建出来就被释放了(错误写法)
__weak Person *pp = [[Person alloc] init];
*/
__strong Person *p = [[Person alloc] init];
__weak Person *p2 = p;
p = nil; // 没有强指针指向对象,对象之后被释放
// 弱指针指向的对象都不存在了,弱指针被清空,不会发生野指针
p2 = nil;// 弱指针不能决定是否释放对象
4、ARC的循环功能
循环引用解决方案:
ARC:
一端用strong,一端用weak
非ARC:
一端用retain,一端用assign