黑马程序员——内存管理

来源:互联网 发布:软件性能指标有哪些 编辑:程序博客网 时间:2024/05/17 17:56
---------------------- ASP.Net+Unity开发、.Net培训、期待与您交流! ----------------------

终于将最难的内存管理看完了,真心觉得内存管理学起来比较抽象,其实内存管理说难也不是很难,只有一个retain和release,只要能够正确的判断什么时候应该retain,什么时候应该release那么就说明已经掌握内存管理的机制了。但是这也是最难的就是如何判断对象什么时候应该release什么时候应该retain。

MJ老师说的一般看见alloc就必须在后面加上一个release,而且操作都要写在relese前面,防止一个很神奇的错误,就是野指针错误。

说到这里又需要讲一下什么是野指针错误。其实就是一个指针指向了一个被释放的回收的对象。那个对象被称为僵尸对象,而且僵尸对象一旦称为僵尸之后就不能再复活了。

不能说你先relese将对象回收完后对象已经变为僵尸对象了你再一个retain对象又可以重新复活,这明显是不行的。


还有每个对象里面都有一个计数器,当计数器为0的时候对象就会被回收,而且计数器是用4个字节来存放的。

方法的基本使用

retain:计数器+1,会返回对象本身

release:计数器-1,没有返回值

retainCount:获取当前的计数器

dealloc

当一个对象要被回收的时候,就会调用

一定要调用[super dealloc],这句调用要放在最后面

 

概念

僵尸对象 :所占用内存已经被回收的对象,僵尸对象不能再使用

野指针 :指向僵尸对象(不可用内存)的指针,给野指针发送消息会报错(EXC_BAD_ACCESS

空指针 :没有指向任何东西的指针(存储的东西是nilNULL0),给空指针发送消息不会报错

#import <Foundation/Foundation.h>#import "Person.h"int main(){    // 1    Person *p = [[Person alloc] init];        //NSUInteger c = [p retainCount];        //NSLog(@"计数器:%ld", c);            // 2 retain方法返回的是对象本身    [p retain];        // 1    [p release];        // 0 野指针:指向僵尸对象(不可用内存)的指针    [p release];        [p retain];        // -[Person setAge:]: message sent to deallocated instance 0x100109a10    // 给已经释放的对象发送了一条-setAge:消息:    p.age = 10;    //[p setAge:10];        // 指针p变成空指针    //p = nil;        // EXC_BAD_ACCESS : 访问了一块坏的内存(已经被回收、已经不可用的内存    // 野指针错误    // OC不存在空指针错误,给空指针发送消息,不报错    [p release];    [p release];    [p release];    [p release];        [nil release];        return 0;}

当出现一个对象需要使用另外一个对象的时候,那么就要在这个对象中重写他的delloc方法。

因为当你自己这个对象被销毁回收之后,你使用或者调用的对象如果没被释放那么就会出现野指针错误,因此在delloc方法中就要将调用的对象release下才行

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

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

retain,谁release

alloc,谁release

还有在调用对象的set方法的时候当传入的参数是一个类的时候就需要将当期使用的类release然后将传入的类retain,因为retain方法功能是将计数器加一并且还有一个返回值就是放回对象本身,所以可以用当前对象来接受


/*内存管理代码规范: 1.只要调用了alloc,必须有release(autorelease)   对象不是通过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]; }

记得之前在讲set,get方法的时候,讲了一个关键字@property 数据类型 类型名

已这种方式来写的xcode的就会自动为我们生成一个set和get方法

现在要遵循oc的内存管理机制,每次在使用alloc的时候都需要release这样写就很麻烦。

所以我们还是可以用@property这个关键字来对变量名进行声明,但是需要在@property后面加上两个参数 (nonatomic,retain)起中nonatomic是起到一个优化的作用mj老师说的必须加,然后retain参数就是实现一个生成的set方法里面,release旧值,retain新值。

但是在implementation中还是需要重写delloc方法,用来release调用过的对象


Person.h

#import <Foundation/Foundation.h>#import "Book.h"@interface Person : NSObject@property int age;// retain : 生成的set方法里面,release旧值,retain新值@property (nonatomic,retain) Book *book;@property (nonatomic,retain) NSString *name;@end

Person.m

#import "Person.h"@implementation Person- (void)dealloc{    [_book release];    [_name release];    [super dealloc];}@end


set方法内存管理相关的参数

retain : release旧值,retain新值(适用于OC对象类型)

assign : 直接赋值(默认,适用于非OC对象类型)

copy   : release旧值,copy新值

是否要生成set方法

readwrite :同时生成settergetter的声明、实现(默认)

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

多线程管理

nonatomic :性能高 (一般就用这个)

atomic    :性能低(默认)

settergetter方法的名称

setter :决定了set方法的名称,一定要有个冒号 :

getter :决定了get方法的名称(一般用在BOOL类型)


循环引用

循环引用就是两个对象相互调用

such as:学生有一个学号,学号也对应一个学生。那么在对象的相互调用的过程中,如果在声明变量时还是用@property(nonatomic,retain)的话,相互调用那么内存永远不会被释放。

那么解决这个循环引用的方法就是一段用retain一段用assign。


@class的作用:仅仅告诉编译器,某个名称是一个类

@class Person; //仅仅告诉编译器,Person是一个类

开发中引用一个类的规范

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

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

两端循环引用解决方案

一端用retain

一端用assign


#import <Foundation/Foundation.h>#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;}

Person.h

#import <Foundation/Foundation.h>#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

card.h

#import <Foundation/Foundation.h>@class Person;@interface Card : NSObject@property (nonatomic, assign) Person *person;@end

最后一个讲的时autorelease

光理解这个单词就能知道这个关键字大概有什么用 自动释放

autorelease其实就是一个类,在IOS 5.0前就是调用的NSAutoreleasePool这个类方法来创建autorelease自动释放池

在IOS 5.0以后就直接使用@autoreleasepool{}中的类需要利用autorelease的就将对象写在里面,在init之后在调用autorelease方法就行了。这样写了之后我们就不用在手动释放内存了,不用在不停的写release retain之类的,大大增加了程序员编写是的效率。


#import <Foundation/Foundation.h>#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];                    } // } 结束代表销毁释放池}

最后就是oc历史性的功能ARC机制。那么后面再说。。

---------------------- ASP.Net+Unity开发、.Net培训、期待与您交流! ----------------------
0 0
原创粉丝点击