您的位置:首页 > 其它

OC内存管理

2015-03-11 18:22 218 查看
一、OC内存管理

1、管理范围:任何继承了NSObject的对象,对基本数据类(int、char、float、double、struct、enum等)无效

2、每个OC对象都有自己的引用计数器,是一个整数,表示“对象被引用的次数”,即有多少人正在使用这个OC对象,每个OC对象内部专门有4个字节的存储空间来存储引用计数器

3、当使用alloc、new或者copy创建一个新对象时,新对象的引用计数器默认就是1,当一个对象的引用计数器值为0时,对象占用的内存就会被系统回收。换句话说,如果对象的计数器不为0,那么在整个程序运行过程,它占用的内存就不可能被回收,除非整个程序已经退出

4、引用计数器的操作

1>.给对象发送一条retain消息,可以使引用计数器值+1(retain方法返回对象本身)

2>.给对象发送一条release消息,可以使引用计数器值-1

3>.可以给对象发送retainCount消息获得当前的引用计数器值

5、对象的销毁

1>.当一个对象的引用计数器值为0时,那么它将被销毁,其占用的内存被系统回收

2>.当一个对象被销毁时,系统会自动向对象发送一条dealloc消息

3>.一般会重写dealloc方法,在这里释放相关资源,dealloc就像对象的遗言

4>.一旦重写了dealloc方法,就必须调用[super dealloc],并且放在最后面调用

5>.不要直接调用dealloc方法

6>.一旦对象被回收了,它占用的内存就不再可用,坚持使用会导致程序崩溃(野指针错误)

#import "Person.h"
@implementation
-(void)dealloc{
NSLog(@"Person对象被回收");
[super dealloc];//super的dealloc一定要调用,而且放最后面
}
@end
#import <Foudation/Foudation.h>
#import "Person.h"
int main(){
Person *p = [[Person alloc] init];
NSUInteger c = [p retainCount];
NSLog(@"计数器:%d",c);
[p retain]; //retain方法返回对象本身,计数器的值+1=2
[p release]; //计数器的值-1=1
[p release]; //计数器的值-1=0,对象被回收,这时的指针p就是野指针,指向僵尸对象(不可用内存)
p = nil; //nil=0,所以把p=0,p变为空指针,OC里面不存在空指针错误
[p release];//EXC_BAD_ACCESS:访问了一块坏的内存(已被回收,不可用),野指针错误
return 0;
}
僵尸对象:对象的计数器指针为0,所占内存被回收,不能再使用,这样的对象就叫僵尸对象

野指针:指向该僵尸对象的指针就是野指针

空指针:没有指向任何对象(存储的东西是nil、NULL、0)

6、多对象内存管理

1.谁创建,谁release,如果你通过alloc、new或[mutable]copy来创建一个对象,那么你必须调用release或autorelease,换句话说,不是你创建的,就不用你去[auto]release

2.谁retain,谁release,只要你调用了retain,无论这个对象是如何生成的,你都要调用release

3、总结,有始有终,有加就有减,曾经让对象的计数器+1,就必须在最后让对象计数器-1

#import <Foudation/Foudation.h>
@interface Book : NSObject{
int _price
}
-(void)setPrice:(int)price;
-(int)price;
@end
#import "Book.h"
@implementation Book
-(void)setPrice:(int)price{
_price = price;
}
-(int)price{
return _price;
}
-(void)dealloc{
NSLog(@"Book对象被回收");
[super dealloc];
}
@end
#import <Foudation/Foudation.h>
#import "Book.h"
@interface Person : NSObject{
Book *_book
}
-(void)setBook:(Book *)book;
-(Book *)book;
@end
#import "Person.h"
@implementation Person
-(void)setBook:(Book *)book{
if(car != _car){//判断对象是不是新对象
[_book release];//对当前使用的旧书做一次release
_book = [book retain];//book的计数器+1
}
}
-(Book *)book{
return _book;
}
-(void)dealloc{
[_book release];
NSLog(@"Person对象被回收");
[super dealloc];
}
@end
#import <Foudation/Foudation.h>
#import "Book.h"
#import "Person.h"
int main(){
Book *b = [[Book alloc] init];//b=1
Person *p1 = [[Person alloc] init];p=1
[p1 setBook:b];//p1想用这本书,调用Person的setBook方法b=2
[b release];//只要有alloc就有一个release,计数器b-1=1
b = nil;*b指针置空
[p1 release];//调用Person的dealloc操作,b=0
p1 = nil;
return 0;
}
1>.你想使用某个对象,就让对象的计数器+1(让对象做一次retain操作)

2>.你不想再使用某个对象,就让对象的计数器-1

3>.谁使用谁retain,谁release

7、内存管理代码规范

1>.只要调用了alloc就必须要有release,只要对象不是通过alloc产生的舅不需要release

2>.set方法的代码规范

基本数据类型,直接复制 :_age=age;

OC对象类型

-(void)setBook:(Book *)book{
if(car != _car){//先判断传进来的对象是不是新对象
[_book release];//对当前使用的旧对象做一次release
_book = [book retain];//对新对象做一次retain,计数器+1
}
}
3>.dealloc方法代码规范

一定要[super dealloc]放最后面

对self(当前)所拥有的其他对象做一次realease

-(void)dealloc{
[_book release];
NSLog(@"Person对象被回收");
[super dealloc];
}
4>.@property(里面的参数)

只需要在@interface里面写

@property(retain) Bool *book;//对象类型变量可以直接这样写

在@implementation里面写dealloc

1.内存管理相关的参数

retain:release旧值,retain新值(是对象就用retain)

assign:直接赋值(默认,不是对象就用)

copy:release旧值,copy新值

2.是否要生成set方法

readwrite:同时生成setter和getter方法的声明和实现

readonly:只会生成getter的声明和实现

3.多线程管理

nonatomic:性能高

atomic:性能低(默认)

4.setter和getter方法的名称

@property(setter = setA:,getter = adc) int weight

set方法的名称就是setA:,get方法的名称就是adc

返回BOOL类型的方法名一般以is开头,例:

@property(getter = isRich) BOOL rich;

模型设计练习

姓名、微博号码、密码、头像、性别、手机、生日

#import <Foundation/Foundation.h>
typedef enum {
SexMan, // 男
SexWoman // 女
} Sex;
typedef struct {
int year;
int month;
int day;
} Date;
@interface User : NSObject
@property (nonatomic, retain) NSString *name;//一定要带上nonatomic提高效率
@property (nonatomic, retain) NSString *account;
@property (nonatomic, retain) NSString *password;//http://weibo.com/a.png  URL
@property (nonatomic, retain) NSString *icon;
@property (nonatomic, assign) Sex sex;
@property (nonatomic, retain) NSString *phone;
@property (nonatomic, assign) Date birthday;
@end


#import "User.h"
@implementation User
- (void)dealloc{
[_name release];
[_account release];
[_icon release];
[_password release];
[_phone release];
[super dealloc];
}
@end


微博内容、微博配图、发送时间、微博发送人、转发的微博、被评论数、被转发数

<pre name="code" class="objc">#import <Foundation/Foundation.h>
#import "User.h"
@interface Status : NSObject
@property (nonatomic, retain) NSString *text;
@property (nonatomic, retain) NSString *icon;// 从1970-01-01 00:00:00 开始,一共度过了多少毫秒
@property (nonatomic, assign) long time;//@property (nonatomic) time_t time;
@property (nonatomic, retain) User *user;//获取用户所有信息
@property (nonatomic, retain) Status *retweetStatus;
@property (nonatomic, assign) int commentsCount;
@property (nonatomic, assign) int retweetsCount;
@end


#import "Status.h"
@implementation Status
- (void)dealloc{
[_text release];
[_user release];
[_retweetStatus release];
[_icon release];
[super dealloc];
}
@end


#import <Foundation/Foundation.h>
#import "User.h"
#import "Status.h"
int main(){// 新建2个用户
User *u = [[User alloc] init];
u.name = @"2B";
User *u2 = [[User alloc] init];
u2.name = @"傻B";
// 新建2条微博
Status *s = [[Status alloc] init];
s.text = @"今天天气真好!";
s.user = u;
Status *s2 = [[Status alloc] init];
s2.text = @"今天天气真的很好!";
s2.retweetStatus = s;
s2.user = u2;
[u2 release];
[u release];
[s2 release];
[s release];
return 0;
}
8、循环引用@class

1.使用场景:对于循环依赖关系来说,比方A类引用B类,同时B类也引用A类

2.格式:

@class + Card//@class仅仅是告诉编译器,Card是一个类

3.在实际开发中引用类的规范

1>.在.h文件中用@class来声明类

2>.在.m文件中用#import来包含类的所有东西

4.@class和#import的区别

1>.#import方式会包含被引用类的所有信息,包括被引用类的变量和方法;@class方式只是告诉编译器在A.h文件中 B *b 只是类的声明,具体这个类里有什么信息,这里不需要知道,等实现文件中真正要用到时,才会真正去查看B类中信息

2>.如果有上百个头文件都#import了同一个文件,或者这些文件依次被#improt,那么一旦最开始的头文件稍有改动,后面引用到这个文件的所有类都需要重新编译一遍,这样的效率也是可想而知的,而相对来 讲,使用@class方式就不会出现这种问题了,在.m实现文件中,如果需要引用到被引用类的实体变量或者方法时,还需要使用#import方式引入被引用类

3>.两端循环引用(无法释放对象)解决方案:一端用retain,一端用assign

9、autorelease方法基本使用

1>.autorelease方法会返回对象本身,会将对象放到一个自动释放池,例:Person *p = [[[Person alloc] init] autorelease];调用autorelease方法后,对象计数器不变,不会-1,只有当自动释放池被销毁时,会对池子里面所有对象做一次release操作

int main(){
@autoreleasepool{//开始代表创建了释放池
Person *p = [[[Person alloc] init] autorelease];
}//结束代表销毁释放池
}
注:@autoreleasepool{}可以无限使用,嵌套

2>.在实际开发中需要精确控制对象内存管理、对于占用内存较大的对象,不要随便用autorelease方法

3>.在IOS程序运行过程中,会创建无数个池子,这些池子都是以栈的结构存在,当一个对象调用autorelease方法时,会将这个对象放到栈顶的释放池

说明:系统自带的方法里面没有包含alloc\new\copy,说明返回的对象都是autorelease的,在开发中经常会提供一个类方法,快速创建一个已经autorelease过的对象,创建对象时不要直接使用类名,一般用self,例:

+(id)person{
return[[[self alloc] init] autorelease];
}
二、内存管理小结

1、计数器的基本操作

1> retain : +1

2> release :-1

3> retainCount : 获得计数器

2、set方法的内存管理

1> set方法的实现

- (void)setCar:(Car *)car{
if ( _car != car ){
[_car release];
_car = [car retain];
}
}
2> dealloc方法的实现(不要直接调用dealloc)

- (void)dealloc{
[_car release];
[super dealloc];
}
3、@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;

4、autorelease

1.系统自带的方法中,如果不包含alloc、new、copy,那么这些方法返回的对象都是已经autorelease过的[NSString stringWithFormat:....];[NSDate date];

2.开发中经常写一些类方法快速创建一个autorelease的对象* 创建对象的时候不要直接使用类名,用self
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: