一、基本原理
1.内存管理内存管理管理范围:任何继承了NSObject的对象,对其他基本数据类型(int、char、float、double、struct、enum等)无效;
栈:放局部变量,会自动回收;
堆:放一些动态产生的。
2.对象的基本结构
每个OC对象都有自己的引用计数器,是一个整数,表示“对象被引用的次数”;
每个OC对象内部专门有4个字节的存储空间来存储引用计数器。
3.引用计数器的作用
当使用alloc、new或者copy创建一个新对象时,新对象的引用计数器默认就是1;
当一个对象的引用计数器值为0时,对象占用的内存就会被系统回收。
4.方法的基本使用
retain :计数器+1,会返回对象本身
release:计数器-1,没有返回值
retainCount:获取当前的计数器
dealloc:
*当一个对象要被回收的时候就会调用
*一定要调用[super dealloc],这句调用要放在最后面
5.概念
1>僵尸对象:所占用内存已经被回收的对象,僵尸对象不能再使用
2>野指针:指向僵尸对象(不可用内存)的指针,给野指针发送消息会报错(EXC_BAD_ACCES)
3>没有指向任何东西的指针(存储的东西时nil、null、0),给空指针发送消息不会报错
给对象发送一条retain消息,引用计数器值+1(retain方法返回对象本身);
给对象发送一条release消息,引用计数器值-1(没有返回值);
给对象发送retainCount消息获得当前的引用计数器值。
7.对象的销毁
当一个对象的引用计数器值为0时,它就会将被销毁,其占用的内存被系统回收;
当一个对象被销毁时,系统会自动向对象发送一条dealloc消息(系统自动调用,不能直接调用dealloc方法);
一般会重写dealloc方法,在这里释放相关资源;
一旦重写了dealloc方法,就必须调用[super dealloc];,并且放在最后面调用(重写dealloc方法,验证一个对象是否回收);
一旦对象被回收了,它占用的内存就不在可用,坚持使用会导致程序崩溃(野指针错误)。
#import <Foundation/Foundation.h>
#import "Person.h"
int main()
{
Person *p = [[Person alloc]init];
NSUInteger c =[p retainCount];
NSLog(@"计数器%lu",(unsigned long)c);
//0 野指针:指向僵尸对象(不可用内存)的指针
[p release];
p = nil;
//EXC_BAD_ACCESS 访问了一块坏的内存(已被回收,不可用的内存)
//野指针错误
//OC中不存在空指针错误,给空指针发送消息,不报错
[p release];
return 0;
}
@interface Person : NSObject
@end
@implementation Person
- (void)dealloc
{
NSLog(@"Person对象被回收");
[super dealloc];
}
@end
二、内存管理原则
1.原则分析只要还有人在用某个对象,那么这个对象就不会被回收;
只要你想使用(占用)某个对象,就应该让对象的计数器+1(让对象做一次retain操作);
当你不想使用(占用)某个对象,就应该让对象的计数器-1(让对象做一次release操作)。
2.谁创建,谁release
如果你通过alloc、new或[mutable]copy来创建一个对象,那么你必须调用release或autorelease;
不是你创建的,就不用你去[auto]release。
3.谁retain,谁release
只要你调用了retain,无论这个对象是如何生成的,你都要调用release。
4.总结
有始有终,有加就有减;
曾经让对象的计数器+1,就必须在最后让对象计数器-1。
三、内存管理代码规范
1> 只要调用了alloc,必须有release(autorelease),对象不是通过alloc产生的,就不用调用release。2> set方法的代码规范
基本数据类型:直接赋值;
OC对象类型:
先判断是不是新传进来的对象;
对旧对象做一次release;
对新对象做一次retain。
- (void)setCar:(Car *)car
{
if ( car != _car )
{
[_car release];
_car = [car retain];
}
}
3.dealloc方法的代码规范
一定要[super dealloc];,而且放在最后面;
对self(当前)所拥有的其他对象都做一次release;
- (void)dealloc
{
[_car release];
[super dealloc];
}
四、@property参数
1.控制set方法的内存管理retain:release旧值,retain新值(用于OC对象)(自动生成管理内存的set方法);
assign:直接赋值,不做任何内存管理(默认,用于非OC对象类型);
copy:release旧值,copy新值(一般用于NSString *)。
@interface Person : NSObject
@property int age;
// retain : 生成的set方法里面,release旧值,retain新值
@property (retain) Book *book;
@property (retain) NSString *name;
@end
@implementation Person
//- (void)setBook:(Book *)book
//{
// if (_book != book)
// {
// [_book release];
//
// _book = [book retain];
// }
//}
- (void)dealloc
{
[_book release];
[_name release];
[super dealloc];
}
@end
注意:dealloc的重写仍需手动进行。
2.set方法内存管理相关的参数
* retain : release旧值,retain新值(适用于OC对象类型)
* assign : 直接赋值(默认,适用于非OC对象类型)
* copy : release旧值,copy新值
* assign : 直接赋值(默认,适用于非OC对象类型)
* copy : release旧值,copy新值
3.是否要生成set方法
* readwrite : 同时生成setter和getter的声明、实现(默认)
* readonly : 只会生成getter的声明、实现
4.多线程管理
* nonatomic : 性能高 (一般就用这个)
* atomic : 性能低(默认)
4.setter和getter方法的名称
* setter : 决定了set方法的名称,一定要有个冒号 :
* getter : 决定了get方法的名称(一般用在BOOL类型)
对于循环依赖关系来说,比如A类引用B类,同时B类也引用A类,当使用@class在两个类相互声明,就不会出现编译报错;
使用@class 类名;就可以引用一个类,说明一下它是一个类。
2.@class和#import的区别(面试)
#import方式会包含被引用类的所有信息,包括被引用类的变量和方法;@class方式只是告诉编译器在A.h文件中B *b只是类的声明;
如果有上百个文件都#import了同一个文件,或者这些文件依次被#import,那么一旦最开始的头文件稍有改动,后面引用到这个文件的所有类都需要重新编译一遍;而相对来讲,使用@class方式就不会出现这种问题了(关乎性能);
在.m实现文件中,如果需要引用到被引用类的实例变量或者方法时,用@class方式,还需要使用#import方式引入被引用类。
3.开发中引用一个类的规范
在.h文件用@class来声明类;
在.m文件中用#import来包含类的所有东西。
4.两端循环retain引用的解决方案
比如A对象retain了B对象,B对象retain了A对象,这样会导致A对象和B对象永远无法释放;
解决方案:一端用retain,一端用assign(用了assign,重写dealloc方法就不要写它的release)。
* readwrite : 同时生成setter和getter的声明、实现(默认)
* readonly : 只会生成getter的声明、实现
4.多线程管理
* 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;
// setWeight:
// weight
//
@property (readwrite, assign) int height;
@property (nonatomic, assign) int age;
@property (retain) NSString *name;
@end
五、循环引用
1.@class对于循环依赖关系来说,比如A类引用B类,同时B类也引用A类,当使用@class在两个类相互声明,就不会出现编译报错;
使用@class 类名;就可以引用一个类,说明一下它是一个类。
2.@class和#import的区别(面试)
#import方式会包含被引用类的所有信息,包括被引用类的变量和方法;@class方式只是告诉编译器在A.h文件中B *b只是类的声明;
如果有上百个文件都#import了同一个文件,或者这些文件依次被#import,那么一旦最开始的头文件稍有改动,后面引用到这个文件的所有类都需要重新编译一遍;而相对来讲,使用@class方式就不会出现这种问题了(关乎性能);
在.m实现文件中,如果需要引用到被引用类的实例变量或者方法时,用@class方式,还需要使用#import方式引入被引用类。
3.开发中引用一个类的规范
在.h文件用@class来声明类;
在.m文件中用#import来包含类的所有东西。
4.两端循环retain引用的解决方案
比如A对象retain了B对象,B对象retain了A对象,这样会导致A对象和B对象永远无法释放;
解决方案:一端用retain,一端用assign(用了assign,重写dealloc方法就不要写它的release)。
六、autorelease
1.基本用法给某个对象发送一条release消息时,就会将这个对象加到一个自动释放池中;
当自动释放池销毁时,会给池子里面的所有对象发送一条release消息;
调用autorelease方法时并不会改变对象的计数器,并且会返回对象本身;
autorelease实际上只是把对release的调用延迟了,对于每一次autorelease,系统只是把该对象放入了当前的autorelease pool中,当该pool被释放时,该pool中的所有对象会被调用release。
2.自动释放池的创建方式
1> iOS 5.0前
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[pool release]; // [pool drain];
2> iOS 5.0 开始
@autoreleasepool
{
}
3.autorelease的好处
1> 不用再关心对象释放的时间
2> 不用再关心什么时候调用release
4.autorelease的使用注意
1> 占用内存较大的对象不要随便使用autorelease
2> 占用内存较小的对象使用autorelease,没有太大影响
5.错误写法
1> alloc之后调用了autorelease,又调用release
2> 连续调用多次autorelease
1> 不用再关心对象释放的时间
2> 不用再关心什么时候调用release
4.autorelease的使用注意
1> 占用内存较大的对象不要随便使用autorelease
2> 占用内存较小的对象使用autorelease,没有太大影响
5.错误写法
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];
}
6.应用实例
开发中一般可以为类添加一个快速创建一个autorelease对象的类方法(代码封装)
4.规律
一般来说,除了alloc、new或copy之外的方法创建的对象都被声明了autorelease
开发中一般可以为类添加一个快速创建一个autorelease对象的类方法(代码封装)
+ (id)对象名
{
return [[[self alloc] init] autorelease];
4.规律
一般来说,除了alloc、new或copy之外的方法创建的对象都被声明了autorelease
七、内存管理总结
计数器的基本操作
1> retain : +1
2> release :-1
3> retainCount : 获得计数器
set方法的内存管理
1> set方法的实现
- (void)setCar:(Car *)car
{
if ( _car != car )
{
[_car release];
_car = [car retain];
}
}
2> dealloc方法的实现(不要直接调用dealloc)
- (void)dealloc
{
[_car release];
[super dealloc];
}
三、@property参数
1> OC对象类型
@property (nonatomic, retain) 类名 *属性名;
@property (nonatomic, retain) Car *car;
@property (nonatomic, retain) id car;
// 被retain过的属性,必须在dealloc方法中release属性
- (void)dealloc
{
[_car release];
[super dealloc];
}
2> 非OC对象类型(int\float\enum\struct)
@property (nonatomic, assign) 类型名称 属性名;
@property (nonatomic, assign) int age;
四、autorelease
1.系统自带的方法中,如果不包含alloc、new、copy,那么这些方法返回的对象都是已经autorelease过的
[NSString stringWithFormat:....];
[NSDate date];
2.开发中经常写一些类方法快速创建一个autorelease的对象
* 创建对象的时候不要直接使用类名,用self
1> retain : +1
2> release :-1
3> retainCount : 获得计数器
set方法的内存管理
1> set方法的实现
- (void)setCar:(Car *)car
{
if ( _car != car )
{
[_car release];
_car = [car retain];
}
}
2> dealloc方法的实现(不要直接调用dealloc)
- (void)dealloc
{
[_car release];
[super dealloc];
}
三、@property参数
1> OC对象类型
@property (nonatomic, retain) 类名 *属性名;
@property (nonatomic, retain) Car *car;
@property (nonatomic, retain) id car;
// 被retain过的属性,必须在dealloc方法中release属性
- (void)dealloc
{
[_car release];
[super dealloc];
}
2> 非OC对象类型(int\float\enum\struct)
@property (nonatomic, assign) 类型名称 属性名;
@property (nonatomic, assign) int age;
四、autorelease
1.系统自带的方法中,如果不包含alloc、new、copy,那么这些方法返回的对象都是已经autorelease过的
[NSString stringWithFormat:....];
[NSDate date];
2.开发中经常写一些类方法快速创建一个autorelease的对象
* 创建对象的时候不要直接使用类名,用self