---------------------- ASP.Net+Unity开发、.Net培训、期待与您交流! ----------------------
多对象内存管理
内存管理原则:
1.原则分析
QQ堂开房间原理:只要房间还有人在用,就不会解散
只要还有人在用某个对象,那么这个对象就不会被回收
只要你想用这个对象,就让对象的计数器+1
当你不再使用这个对象时,就让对象的计数器-1
2.谁创建,谁release
如果你通过alloc、new或[mutable]copy来创建一个对象,那么你必须调用release或autorelease
换句话说,不是你创建的,就不用你去[auto]release
3.谁retain,谁release
只要你调用了retain,无论这个对象是如何生成的,你都要调用release
4.总结
有始有终,有加就有减
曾经让对象的计数器+1,就必须在最后让对象计数器-1
代码示例:
@interface Book : NSObject
{
int _price;
}
- (void)setPrice:(int)price;
- (int)price;
@end
@implementation Book
- (void)setPrice:(int)price
{
_price = price;
}
- (int)price
{
return _price;
}
- (void)dealloc
{
NSLog(@"Book被回收");
[super dealloc];
}
@end
@interface Person : NSObject
{
Book *_book;
}
- (void)setBook:(Book *);
- (Book *)book;
@end
@implemetation Person
- (void)setBook:(Book *)
{
//_book = book;对象数不合理,未曾加1
_book = [book retain];//人使用书加1
}
- (Book *)book
{
return _book;
}
- (void)dealloc
{
[_book release];//人不使用书,然后回收book对象,调用Book的dealooc方法
NSLog(@"Person被回收");
[super dealloc];
}
@end
/*
1.你想使用(占用)某个对象,就应该让对象的计数器+1.(让对象做一次retain操作)
2.你不想想使用(占用)某个对象,就应该让对象的计数器-1.(让对象做一次release操作)
3,谁retain谁release
4,谁alloc谁release
*/
int main()
{
Book *book = [[Book alloc] init];//b-1计数器是1
Person *p = [[Person alloc] init];//p-1
//p想占用这本书。b-2
[p setBook:b];
[b release];//book对象-1,b-1`
b = nil; //指针设置为空.
[p release];//p-0,b-0
p = nil;
return 0;
}
set方法的内存管理
@interface Person : NSObject
{
Car *_car;
int _age;
}
- (void)setCar:(Car *)car;
- (Car *)car;
- (void)setAge:(int)age;
- (int)age;
@end
@implementation Person
- (void)setCar:(Car *)car
{
if(car != _car)//如果是当前一样的车就不执行
{
//对当前使用的车(旧车)做一次release
[_car release];
_car = [car retain];//对新车做一次retain
}
}
- (Car *)car
{
return _car;
}
- (void)setAge:(int)age
{ //基本数据类型不需要管理内存
_age = age;
}
- (int)age
{
return _age;
}
- (void)dealloc
{
[_car release];// 当人不在了不用车对车做一次release操作
NSLog(@"Person被回收");
[super dealloc];
}
@end
@interface Car : NSObject
{
int wheels;
int speed;
}
- (void)setSpeed:(int)speed;
- (int)speed;
@end
@implementation Car
- (void)setSpeed:(int)speed
{
_speed = speed;
}
- (int)speed
{
return _speed;
}
- (void)dealloc
{
/*
_speed : 直接访问成员变量
self->_speed : 直接访问成员变量
self.speed : get方法
[self speed] : get方法
*/
NSLog(@"速度为%d的Car对象被回收了",_speed);
[super dealloc];
}
@end
/*
内存管理代码规范:
1.只要调用了alloc必须有release(autorelease)
如果对象不是通过alloc产生的。就不需要release。比如stu.name = @"jack";创建出对象,但是没
有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];
}
*/
// 速度为100的车子: c1, p1->_car从思想上没有人用了就销毁。
// 速度为200的车子
int main()
{
Person *p = [[Person alloc] init];
p.age = 20;
Car *c1 = [[Car alloc] init];
c1.speed = 100;
p1.car = c1;
Car *c2 = [[Car alloc] init];
c2.speed = 300;
p2.car = c2;
[[Car alloc] init].speed = 100;//有内存泄露,还是那句话有alloc必须有release
[c2 release];
[c1 release];
[p release];
return 0;
}
void test1()
{
Person *p = [[Person alloc] init];
p.age = 20;
Car *c1 = [[Car alloc] init];
c1.speed = 253;
p.car = c1;
[c1 release];//
p.car = c1;//报错,僵尸对象。
/*
- (void)setCar:(Car *)car
{
[_car release];//原因是这句话后c1-0
_car = [car retain];//c1已经不存在了。再retain无意义。所以修改为在set方法加if
}
*/
[p release];
return 0;
}
void test()
{
//p-1
Person *p = [[Person alloc] init];
p.age = 20;
//c1-1
Car *c1 = [[Car alloc] init];
c1.speed = 253;
// p想拥有c1车
//c1-2
p.car = c1; //[p setCar:c1]
//新对象c2-1
Car *c2 = [[Car alloc]init];
c2.speed = 290;
// p将车换成了c2
//c1-1
//c2-2
p.car =c2;
//c2-1
[c2 release];
//c1-0
[c1 release];
//p-0,当前对象的car.c2-0,c1无消除
//修改后p-0.c2-0.
[p release];
return 0;
}
@property的内存管理
1.同时生成setget方法的时候,@property不会帮你生成下划线成员变量
2.@property (retain) Book *book;会自动生成下面代码
// retain ;生成的set方法里面,release旧值,retain新值
- (void)setBook:(Book *)
{
//_book = book;对象数不合理,未曾加1
_book = [book retain];//人使用书加1
}
/*
1.set方法内存管理相关参数(只能选一种)
*retain :release旧值。retain新值(适用于OC对象类型)
*assign :直接复制(默认。适用是非OC对象类型)
*copy : release旧值,copy新值
2.是否要生成set方法(当只读的时候)
@property (readonly) int height :只好生成getter的声明实现
@readwrite : 同时生成setter和getter的声明实现 (默认)
@property (readonly,assign) int height :不同类型可以都写在两个括号里
3.多线程管理
*nonatomic :性能高 :只要记得它性能高就行了,一般就用这个
*atomic :性能低(默认)
4、setter和getter方法的名称
@property (setter = setAbc: ,getter = abc) int weight;set方法是要有:(冒号)的
改完后,p.weight = 100与p.abc = 100是等价的。但是只能[p abc:100]不能用原来的[p weight:100].不过一般不用来改setter和getter一般是用来的场合是BOOL类型的getter方法:
// 返回BOOL类型的方法名一般以is开头
@property (getter = isRich) BOOL rich;
最后来个复杂的总结:
@property (nonatomic, assign, readwrite) int weight;
*/
代码示例:
@interface Student : NSObject
@property (retain) Book *book;
@property (retain) Name *name;
@end
@implementation
- (void)dealloc
{
[_book release];
[_name release];
[super dealloc];
}
@end
模型设计练习
模型设计练习:微博
#import <Foundatition/Foundation.h>
// 姓名、微博号码、密码、头像、性别、手机、生日
typedef enum{
SexMan,
SexWoman
} Sex;
typedef struct{
int year;
int month;
int day;
} Date;
@interface User : NSObject
@property (nontomic, retain) NSString *name;
@property (nontomic,retain) NSString *account ;
@property (nontomic,retain) NSString *password;
@property (nontomic,retain) NSString *icon;
@property (nontomic,assign) Sex sex;
//不是oc对象也要写上assign,方便同事之间交流
@property (nontomic,assign) NSString *phone;//有破折号一般用字符串来保存
@property (nontomic,assign) Date birthday;
@end
@implementation User
- (void)dealloc
{
[_name release];
[_account release];
[_icon release];
[_password release];
[_phone release];
[super dealloc];
}
@end
// 微博内容、微博配图、发送时间、微博发送人、转发的微博、被评论数、被转发数
typedef struct{
int year;
int month;
int day;
} Date;
import"User.h"
@interface Status : NSObject
@property (nontomic, retain) NSString *txt;
@property (nontomic, retain) NSString *icon;
// 1970-1-1 00:00:00开始,一共读过了多少秒
@property (nontomic,assign) long time;
//@property (nontomic, retain) NSString *user;只能获取围脖用户名,不能知道用户名,内容,头
像
@property (nontomic, retain) User *user;//用户对象,面向对象思想考虑
@property (nontomic, retain) Status *retweetStatus;//被转发的围脖也是微博
@property (nontomic,assign) int commentsCount;
@property (nontomic,assign) int retweetsCount;
@end
@implementation Status
- (void)dealloc
{
[_user release];
[_retweetStatus release];
[_icon release];
[_txt release];
[super dealloc];
}
@end
int main()
{
User *u = [[User alloc] init];
u.name = @"SB";
User *u2 = [[User alloc] init];
u2.name = @"2B";
Status *s = [[Status alloc] init];
s.txt = @"今天天气真好";
s.user = u;
Status *s2 = [[Status alloc] init];
s2.retweetStatus = s;
s2.txt = @"今天天气真的很好";
s2.user = u2;
[u2 release];
[u release];
[s2 release];
[s release];
return 0;
}
循环引用的问题
//#import "Card.h"
// @class仅仅是告诉编译器,Card是一个类,这个类里包含的方法,成员变量都不知道。
@class Card;
@interface Person :NSObject
//会对其报错,card类型有问题,不可以你包含我我包含你,去掉#import "Card.h",都用@class
@property (nontomic, retain) Card *card;
@end
#import "Card.h"
@implementation Person
- (void) dealloc
{
NSLog(@"Person被销毁了");
[_card release];
[super dealloc];
}
@end
//#import "Person.h"
@class Person;
@interface Card:NSObject
@property (nontomic, retain) Person *person;
@end
在Card.m文件里用到了再包含#import "Person.h"
@implementation Card
- (void) dealloc
{
NSLog(@"Car被销毁了");
[_person release];//要不这句person的方法无实现
[super dealloc];
}
@end
/*
1.@class的作用
@class的作用:仅仅告诉编译器,某个名称是一个类
@class Person; // 仅仅告诉编译器,Person是一个类
#import 头文件,100个引用类需要都拷贝,一旦改动,需要100个类重新COPY。降低了编译效率
2,开发中引用一个类的规范
1》在.h文件中用@class来声明类
2》在 .m文件中用#import来包含类的所有东西
@class和#import的区别
#import方式会包含被引用类的所有信息,包括被引用类的变量和方法;@class方式只是告诉编译器在A.h文件中 B *b 只是类的声明,具体这个类里有什么信息,这里不需要知道,等实现文件中真正要用到时,才会真正去查看B类中信息如果有上百个头文件都#import了同一个文件,或者这些文件依次被#improt,那么一旦最开始的头文件稍有改动,后面引用到这个文件的所有类都需要重新编译一遍,这样的效率也是可想而知的,而相对来讲,使用@class方式就不会出现这种问题了在.m实现文件中,如果需要引用到被引用类的实体变量或者方法时,还需要使用#import方式引入被引用类
3.两端循环引用
循环retain
比如A对象retain了B对象,B对象retain了A对象
这样会导致A对象和B对象永远无法释放
解决方案
1> 一端用retain
2> 一端用assign
代码效果
@property (nontomic, assign) Person *person;
@property (nontomic, retain) Card *card;
- (void)dealloc
{
NSLog(@"Car被销毁了");
// [_person release];被注释,因为既然选择了不retain而assign。就不必要在release
[super dealloc];
}
*/
int main()
{
Person *p = [[Person alloc] init];
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 :
@interface Person : NSObject
@property (nontomic, assign) int age;
@end
@implementation Person
- (void)dealloc
{
NSLog(@"Person 被销毁");
[super dealloc];
}
@end
/*
autorelease的基本用法
1》会将atuorelease会将对象放到一个自动释放池中
2》当自动释放池被销毁时,会对池子里面所有的对象做一次release操作
3》会放回对象本身
4》调用完autorelease方法后,对象计数器不变
autorelease的好处
1》不用再关心对象释放时间
2》不用再关心什么时候调用release
autorelease的使用注意
1》占用内存较大的对象不要随便使用autorelease
2》占用内存较小的对象使用autorelease,没有太大影响
缺点:不能精确的把握内存释放时间,比如说子弹击中在屏幕上要立刻销毁。
应用场合:在一些占用内存比较小的类,里面对象比较少,基本上都是基本类型
而像一个Person里面包含很多对象的大型类,就不要用autorelease,用release精确把握销毁时间。
4.错误写法
alloc之后调用了autorelease又调用了release
@autoreleasepool
{
Person *p = [[[Person alloc] init] atuorelease];
// [p release] 错误的,要满足一对一关系
}
连续多次调用autorelease
[[[[Person alloc] init] atuorelease] autorelease]
5.自动释放池
1》在IOS程序运行过程中,会创建出无数个池子,这些池子都是以栈结果存在(先进后出)
2》当一个对象调用autorelease方法时,会将这个对象放到栈顶的释放池。
6.自动释放池的创建方式
1》ios5.0以前
NSAtuoreleasePool *pool = [[NSAutoreleasePool alloc] init];
Person *p = [[[Person alloc] init] atuorelease];
[pool release];[pool drain]
2>ios5.0开始
@autoreleasepool
{
Person *p = [[[Person alloc] init] atuorelease];
}
*/
int main()
{
@autoreleasepool
{
Person *p = [[[Person alloc] init] atuorelease];
// [p release] 错误的,要满足一对一关系
}
return 0;
}
void test ()
{
@autoreleasepool
{// “{”开始代表创建了释放池
// atuorelease方法会返回对象本身
//调用完autorelease方法后,对象计数器不变
// atuorelease会将对象放到一个自动释放池中
// 当自动释放池被销毁时,会对池子里面所有的对象做一次release操作
Person *p = [[[Person alloc] init] atuorelease];
p.age = 10;
@autoreleasepool
{
Person *p2 = [[[Person alloc] init] atuorelease];
p2.age = 10;
}
}// “}”结束代表销毁释放池
}
autorelease 实例练习
@interface Person : NSObject
@property (nontomic, assign) int age;
+ (id)person;
+ (id)personWithAge:(int)age;
@end
@implementation
+ (id)person //开发中经常写一个类方法,快速返回一个autorelease对象
{
return [[[self alloc] init] autorelease];
//用self满足子类调用需求,不必都返回父类对象
}
- (void)dealloc// dealloc方法不要直接调用。系统自动调用
{
NSLog(@"%d岁的人被销户了",_age);
[super dealloc];
}
+ (id)personWithAge:(int)age
{
Person *p = [self person];
p.age = 100;
return p;
}
@end
/*
1.系统自带的方法里面么有包含alloc,new,copy,说明返回对象都是autorelease的(老师一再强调注意原则)
2.NS是个前缀,是个叫next step公司写出来的。类名相同的情况下再加前缀
3.开发中经常会提供一些类方法,快速创建一个已经autorelease过的对象
1》创建对象时不要直接用类名一般用self,好处是:谁调用就返回谁对象
+ (id)person
{
return [[[self alloc] init] autorelease];
//用self满足子类调用需求,不必都返回父类对象
}
*/
int main()
{
autoreleasepool
{
Person *p = [[[Person alloc] init] autorelease];
p.age = 20;
//Person *p2 = [Person person];
//p2.age = 100;
上两行代码等价于:Person *p2 = [Person personWithAge:100];
NSString *str = @"145234";//默认是autorelease
NSString *str2 = [NSString stringWithFormat:@"age is %d",10];
NSnumeber *num = [[NSNumber alloc] inwithInt:100];
[num release];
}
return 0;
}
---------------------- ASP.Net+Unity开发、 .Net培训、期待与您交流! ----------------------详细请查看: www.itheima.com