黑马程序员-多对象,set方法,@property的内存管理,以及模型设计练习

---------------------- ASP.Net+Unity开发.Net培训、期待与您交流! ----------------------

 

多对象内存管理

 

内存管理原则:

1.原则分析

QQ堂开房间原理:只要房间还有人在用,就不会解散

 

只要还有人在用某个对象,那么这个对象就不会被回收

只要你想用这个对象,就让对象的计数器+1

当你不再使用这个对象时,就让对象的计数器-1

 

2.谁创建,谁release

如果你通过allocnew[mutable]copy来创建一个对象,那么你必须调用releaseautorelease

换句话说,不是你创建的,就不用你去[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对象,调用Bookdealooc方法

       NSLog(@"Person被回收");

       [super dealloc];

}

@end

 

/*

1.你想使用(占用)某个对象,就应该让对象的计数器+1.(让对象做一次retain操作)

2.你不想想使用(占用)某个对象,就应该让对象的计数器-1.(让对象做一次release操作)

3,谁retainrelease

4,谁allocrelease

*/

 

 

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对象-1b-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(@"速度为%dCar对象被回收了"_speed);

       [super dealloc];

}

@end

 

/*

内存管理代码规范:

1.只要调用了alloc必须有releaseautorelease

  如果对象不是通过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 : 同时生成settergetter的声明实现 (默认)

 

@property (readonlyassign) int height :不同类型可以都写在两个括号里

 

3.多线程管理

 

*nonatomic :性能高  :只要记得它性能高就行了,一般就用这个

*atomic    :性能低(默认)

 

4settergetter方法的名称

 

@property (setter = setAbc: ,getter = abc) int weight;set方法是要有:(冒号)的

改完后,p.weight = 100p.abc = 100是等价的。但是只能[p abc:100]不能用原来的[p weight:100].不过一般不用来改settergetter一般是用来的场合是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 CardNSObject

 

@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对象retainB对象,B对象retainA对象

这样会导致A对象和B对象永远无法释放

 

 解决方案

 1> 一端用retain

 2> 一端用assign

 

 代码效果

 

@property (nontomic, assign) Person *person;

@property (nontomic, retain) Card *card;

 

- (void)dealloc

{

    NSLog(@"Car被销毁了");

   

    // [_person release];被注释,因为既然选择了不retainassign。就不必要在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.自动释放池的创建方式

 

1ios5.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.系统自带的方法里面么有包含allocnewcopy,说明返回对象都是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] inwithInt100]

              [num release];

       }

       return 0;

}

---------------------- ASP.Net+Unity开发.Net培训、期待与您交流! ----------------------详细请查看: www.itheima.com
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值