OC内存管理:
什么是内存管理?
移动设备的内存极其有限,每个app所能占用的内存是有限制的
当app所占用的内存较多时,系统会发出内存警告,这时得回收一些不需要再使用的内存空间。比如回收一些不需要使用的对象、变量等
管理范围:任何继承了NSObject的对象,对其他基本数据类型(int、char、float、double、struct、enum等)无效
对象的基本结构:
每个OC对象都有自己的引用计数器,是一个整数,表示“对象被引用的次数”,即有多少人正在使用这个OC对象
每个OC对象内部专门有4个字节的存储空间来存储引用计数器
引用计数器的作用:
当使用alloc、new或者copy创建一个新对象时,新对象的引用计数器默认就是1
当一个对象的引用计数器值为0时,对象占用的内存就会被系统回收。换句话说,如果对象的计数器不为0,那么在整个程序运行过程,它占用的内存就不可能被回收,除
非整个程序已经退出
引用计数器的操作:
给对象发送一条retain消息,可以使引用计数器值+1(retain方法返回对象本身)
给对象发送一条release消息,可以使引用计数器值-1
可以给对象发送retainCount消息获得当前的引用计数器值
对象的销毁:
当一个对象的引用计数器值为0时,那么它将被销毁,其占用的内存被系统回收
当一个对象被销毁时,系统会自动向对象发送一条dealloc消息
一般会重写dealloc方法,在这里释放相关资源,dealloc就像对象的遗言
一旦重写了dealloc方法,就必须调用[super dealloc],并且放在最后面调用
不要直接调用dealloc方法
一旦对象被回收了,它占用的内存就不再可用,坚持使用会导致程序崩溃(野指针错误)
retain、release:
retain方法给对象的引用计数器+1
release方法给对象的引用计数器-1
retain方法会返回对象本身,release方法没有返回值
dealloc方法:
当一个对象要被回收的时候会被调用
重写的时候,一定要调用[super dealloc]方法,这句调用一定要放到最后面
空指针
OC中没有空指针异常
没有指向任何东西的指针(存储的东西是nil、NULL、0),给空指针发送消息不会报错
野指针
指向僵尸对象(不可用的内存)的指针,给野指针发送消息会报错(EXC_BAD_ACCESS)
僵尸对象
所占用内存已经被回收的对象,僵尸对象不能再使用
retain、release使用注意:
谁retain,谁release
只要你调用了retain,无论这个对象是如何生成的,你都要调用release
有始有终,有加就有减
曾经让对象的计数器+1,就必须在最后让对象计数器-1
如果你通过alloc、new或[mutable]copy来创建一个对象,那么你必须调用release或autorelease
你想使用某个对象,就应该让对象的计数器+1(让对象做一次retain操作)
当你不想在使用某个对象时,我们就该让对象的计数器-1(让对象做一次release操作)
谁retain,谁就release
谁alloc,谁就release
set方法和dealloc的内存管理
例如:
Person.h
#import
@interface Person : NSObject
@property (nonatomic, assign) int age;
@end
Person.m
#import "Person.h"
@implementation Person
- (void)dealloc
{
NSLog(@"Person---dealloc");
[super dealloc];
}
@end
main.m
/*
1.autorelease的基本用法
1> 会将对象放到一个自动释放池中
2> 当自动释放池被销毁时,会对池子里面的所有对象做一次release操作
3> 会返回对象本身
4> 调用完autorelease方法后,对象的计数器不变
2.autorelease的好处
1> 不用再关心对象释放的时间
2> 不用再关心什么时候调用release
3.autorelease的使用注意
1> 占用内存较大的对象不要随便使用autorelease
2> 占用内存较小的对象使用autorelease,没有太大影响
4.错误写法
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];
}
5.自动释放池
1> 在iOS程序运行过程中,会创建无数个池子。这些池子都是以栈结构存在(先进后出)
2> 当一个对象调用autorelease方法时,会将这个对象放到栈顶的释放池
6.自动释放池的创建方式
1> iOS 5.0前
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[pool release]; // [pool drain];
2> iOS 5.0 开始
@autoreleasepool
{
}
*/
#import
#import "Person.h"
int main()
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
Person *pp = [[[Person alloc] init] autorelease];
[pool release]; // [pool drain];
@autoreleasepool
{
// 1
Person *p = [[[[Person alloc] init] autorelease] autorelease];
// 0
// [p release];
}
return 0;
}
void test()
{
@autoreleasepool
{// { 开始代表创建了释放池
// autorelease方法会返回对象本身
// 调用完autorelease方法后,对象的计数器不变
// autorelease会将对象放到一个自动释放池中
// 当自动释放池被销毁时,会对池子里面的所有对象做一次release操作
Person *p = [[[Person alloc] init] autorelease];
p.age = 10;
@autoreleasepool
{
// 1
Person *p2 = [[[Person alloc] init] autorelease];
p2.age = 10;
}
Person *p3 = [[[Person alloc] init] autorelease];
} // } 结束代表销毁释放池
}
只要调用alloc,必须有release(autorelease)
set方法的内存管理:
如果你有个OC对象类型的成员变量,就必须管理这个成员变量的内存。比如有个Book *_book
1. set方法的实现
- (void)setBook:(Book *)book{
if (book != _book) {
[_book release];
_book = [book retain];
}
}
- (void)dealloc {
[_book release];
[super dealloc];
}
set方法代码规范
基本数据类型:直接赋值
OC对象类型:
- (void)setCar:(Car *)car
{
//1.先判断是不是新传进来对象
if(car != _car){
//2.对旧对象做一次release -1
[_car release];
//3.对新对象做一次retain +1
_car = [car retain];
}
}
- (void)dealloc
{
/*
_speed //直接访问成员变量
self -> _speed //直接访问成员变量
self.speed //get方法
[self speed] //get方法
*/
NSLog(@"速度为%d的Car对象被回收了",_speed);
[super dealloc];
}
通过@property可以自动生成带内存管理的setter方法
下面时不带参数声明
@property(retain) Book *book;
声明过后,需要到dealloc方法中去release需要release的成员变量
@property的参数
set方法内存管理相关的参数
retain:release旧址,retain新值(适用于OC对象类型)
assign:直接复制,默认的,可以不写(适用于非OC对象类型)
copy: release旧值,copy新值
是否要生成get方法
readonly:只生成getter方法的声明、实现
readwrite:同时生成setter和getter的声明、实现(默认的)
多线程管理
nonatomic :性能高(一般就用这个)
atomic :性能低(默认)
setter和getter的名称(一般用到BOOL类型)
getter:设置getter的方法名称
setter:设置setter的方法名称,必须带冒号:
返回BOOL类型的方法名一般以is开头
@property (getter = isRich) BOOL rich;
循环引用:
@class的作用,仅仅告诉编译器,某个名称是一个类
@class Person;
开发中引用一个类的规范:
在.h文件中用@class来声明类
在.m文件中用#import来包含类的所有东西
@class和#import的区别
#import方式会包含被引用类的所有信息,包括被引用类的变量和方法;@class方式只是告诉编译器在A.h文件中 B *b 只是类的声明,具体这个类里有什么信息,这里不需要知
道,等实现文件中真正要用到时,才会真正去查看B类中信息
如果有上百个头文件都#import了同一个文件,或者这些文件依次被#improt,那么一旦最开始的头文件稍有改动,后面引用到这个文件的所有类都需要重新编译一遍,
这样的效率也是可想而知的,而相对来 讲,使用@class方式就不会出现这种问题了
在.m实现文件中,如果需要引用到被引用类的实体变量或者方法时,还需要使用#import方式引入被引用类
两端循环引用解决方案
一端@property用retain
一端@property用assign
例如:
Person.h
#import
#import "Card.h"
// @class仅仅是告诉编译器,Card是一个类
//@class Card;
@interface Person : NSObject
@property (nonatomic, retain) Card *card;
@end
Person.m
#import "Person.h"
#import "Card.h"
@implementation Person
- (void)dealloc
{
NSLog(@"Person被销毁了");
[_card release];
[super dealloc];
}
@end
main.m
/*
1.@class的作用:仅仅告诉编译器,某个名称是一个类
@class Person; // 仅仅告诉编译器,Person是一个类
2.开发中引用一个类的规范
1> 在.h文件中用@class来声明类
2> 在.m文件中用#import来包含类的所有东西
3.两端循环引用解决方案
1> 一端用retain
2> 一端用assign
*/
#import
#import "Card.h"
#import "Person.h"
int main()
{
// p - 1
Person *p = [[Person alloc] init];
// c - 1
Card *c = [[Card alloc] init];
// c - 2
p.card = c;
// p - 1
c.person = p;
// c - 1
[c release];
// p - 0 c - 0
[p release];
return 0;
}
autorelease:
autorelease的基本用法
会讲对象放到一个自动释放池中
当自动释放池被销毁时,会对池子里面的所有对象做一次release操作
会返回对象本身
调用完autorelease方法后,对象的计数器不变
autorelease的好处
不用再关心对象释放的时间
不用再关心什么时候调用release方法
autorelease的使用注意
占用内存较大的对象不要随便使用autorelease
占用内存较小的对象使用autorelease,没有太大影响
错误写法:
alloc之后调用了autorelease,又调用release
@autoreleasepool {
//1
Person *p = [[[Person alloc]init]autorelease];
//0,野指针错误
//[p release];
}
@autorelease
{
Person *p = [[[[Person alloc]init]autorelease]autorelease];
}
在ios程序运行过程中,会创建无数个池子,这些池子都是以栈结构存在(先进后出)
当一个对象调用autorelease方法时,会将这个对象方到栈顶得释放池
自动释放池的创建方式:
@autorelease
{
}
系统自带的方法里面没有包含alloc、new、copy,说明返回的对象都时autorelease的
开发中经常会提供一些类方法,快速创建一个autorelease过的对象
创建对象时,不要直接用类名,一般用self
+ (id)person
{
return [[[self alloc] init]autorelease];
}