------- android培训、 java培训、期待与您交流! ----------
Objective-C内存管理
基本数据类型存储在栈中,系统会自动释放存储空间,而OC对象存储在堆中,系统不会自动回收对象,故而引起内存管理。
1.引用计数器的基本操作
每一个对象都有一个引用计数器,它在内存中占用4个字节
(1)方法的基本使用
1> retain :计数器+1,会返回对象本身
2> release :计数器-1,没有返回值
3> retainCount :获取当前的计数器
4> dealloc
当一个对象要被回收的时候,就会调用
一定要调用[super dealloc],这句调用要放在最后面
(2).概念
1> 僵尸对象 :所占用内存已经被回收的对象,僵尸对象不能再使用
2> 野指针 :指向僵尸对象(不可用内存)的指针,给野指针发送消息会报错(EXC_BAD_ACCESS)
3> 空指针 :没有指向任何东西的指针(存储的东西是nil、NULL、0),给空指针发送消息不会报错
Person p = [[Person alloc ] init];
[p retain];
[p release];
int size = [p retainCount];
[p release]
// 当一个Person对象被回收的时候,就会自动调用这个方法
- (void)dealloc
{
NSLog(@"Person对象被回收");
// super的dealloc一定要调用,而且放在最后面
[super dealloc];
}
心得体会:
(1)当使用alloc、new或者copy创建一个新对象时,新对象的引用计数器默认就是1
(2)当一个对象的引用计数器值为0时,那么它将被销毁,其占用的内存被系统回收
(3)不要直接调用dealloc方法
(4) 一旦对象被回收了,它占用的内存就不再可用,坚持使用会导致程序崩溃(野指针错误)
(5)OC不存在空指针错误,给空指针发送消息,不报错
2.set方法的内存管理
内存管理代码规范:
1.只要调用了alloc,必须有release(autorelease)对象不是通过alloc产生的,就不需要release
2.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];
}
}
3.dealloc方法的代码规范
1> 一定要[super dealloc],而且放到最后面
2> 对self(当前)所拥有的其他对象做一次release
- (void)dealloc
{
[_car release];
[super dealloc];
}
心得体会:
如果你有个OC对象类型的成员变量,就必须管理这个成员变量的内存。
[email protected]
1.set方法内存管理相关的参数
retain : release旧值,retain新值(适用于OC对象类型)
assign : 直接赋值(默认,适用于非OC对象类型)
copy : release旧值,copy新值
2.是否要生成set方法
readwrite : 同时生成setter和getter的声明、实现(默认)
readonly : 只会生成getter的声明、实现
3.多线程管理
nonatomic : 性能高 (一般就用这个)
atomic : 性能低(默认)
4.setter和getter方法的名称
setter : 决定了set方法的名称,一定要有个冒号 :
getter : 决定了get方法的名称(一般用在BOOL类型)
@interface Person : NSObject
// 返回BOOL类型的方法名一般以is开头
@property (getter = isRich) BOOL rich;
@property (nonatomic, assign, readwrite) int weight;
@property (readwrite, assign) int height;
@property (nonatomic, assign) int age;
@property (retain) NSString *name;
@end
心得体会:
(1)不同类型的参数可以组合使用。
(2)一般都会添加nonatomic参数,提高性能。
4.循环引用
当一个类引用了另外一个类,另一个类又引用了这个类的时候,就叫做循环引用。
@class Person;
@interface Card : NSObject
@property (nonatomic, assign) Person *person;
@end
#import "Card.h"
@interface Person : NSObject
@property (nonatomic, retain) Card *card;
@end
心得体会:这种代码编译会报错。当使用@class在两个类相互声明,就不会出现编译报错
@class
仅仅是告诉编译器,Card是一个类
@class和#include的区别:
(1)#import方式会包含被引用类的所有信息,包括被引用类的变量和方法;@class方式只是告诉编译器在A.h文件中 B *b 只是类的声明,具体这个类里有什么信息,这里不需要知道,等实现文件中真正要用到时,才会真正去查看B类中信息
(2)@class相比#include而言,能提高效率。当#include引入的文件更改时,所有引入的文件都要重新编译一遍
心得体会:
(1)在.m实现文件中,如果需要引用到被引用类的实体变量或者方法时,还需要使用#import方式引入被引用类
(2)两端循环引用的解决方案是,一端用retain一端用assign。
5.autorelease
(1) 给某个对象发送一条autorelease消息时,就会将这个对象加到一个自动释放池中
(2)当自动释放池销毁时,会给池子里面的所有对象发送一条release消息
(3)调用autorelease方法时并不会改变对象的计数器,并且会返回对象本身
@autoreleasepool
{
// 1
Person *p = [[[Person alloc] init] autorelease];
}
1> alloc之后调用了autorelease,又调用release
@autoreleasepool
{
// 1
Person *p = [[[Person alloc] init] autorelease];
// 0
[p release];
}
// 2> 连续调用多次autorelease
@autoreleasepool
{
Person *p = [[[[Person alloc] init] autorelease] autorelease];
}
自动释放池
(1) 在iOS程序运行过程中,会创建无数个池子。这些池子都是以栈结构存在(先进后出)
(2)当一个对象调用autorelease方法时,会将这个对象放到栈顶的释放池
// 自动释放池的创建方式
// 1> iOS 5.0前
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[pool release]; // [pool drain];
// 2> iOS 5.0 开始
@autoreleasepool
{
}
心得体会:
(1)autorelease实际上只是把对release的调用延迟了,对于每一次autorelease,系统只是把该对象放入了当前的autorelease pool中,当该pool被释放时,该pool中的所有对象会被调用Release。
6.autorelease应用
(1)系统自带的方法里面没有包含alloc、new、copy,说明返回的对象都是autorelease的。
NSString *str2 = [NSString stringWithFormat:@"age is %d", 10];
(2)开发中经常会提供一些类方法,快速创建一个已经autorelease过的对象
+ (id)person
{
return [[[self alloc] init] autorelease];
}
心得体会:
(1)创建对象时不要直接用类名,一般用self,这样于子类调用。