内存管理



一、基本原理

1、内存管理

(1)什么是内存管理

内存管理是指软件运行时对计算机内存资源的分配和使用技术,其最主要的目的是如何高效快速的分配,并且在

适当的时候释放和回收内存资源。

(2)为什么需要内存管理

移动设备的内存极其有限,每个app所能占用的内存也是有限制的,当app所占用的内存较多时,系统会发出内

存警告,这时得回收一些不需要再使用的内存空间。比如回收一些不需要使用的对象、变量等。

(3)管理范围的范围

任何继承了NSObject的对象,对其他基本数据类型(int、char、float、double、struct、enum等)无效。

2、对象的基本结构


每个OC对象内部专门有4个字节的存储空间来存储引用计数器,该计数器为整数,表示对象被引用次数,即有多

少人正在使用这个OC对象。

3、引用计数工作原理

(1)什么是引用计数器

Object-C语言使用引用计数器来管理内存,也就是说每个对象都有个可以递增或递减的计数器,如果想使某个对

象继续存活,那就递增其引用计数;用完之后,就递减其计数。当计数为0的时候,就表示没人关注此对象来,于是

就可以把对象所占的内存回收。

(2)引用计数器的作用

当使用alloc、new或者copy创建一个新对象时,新对象的引用计数器默认就是1,当一个对象的引用计数器值为0

时,对象所占用内存就会被系统回收,也就是说对象的计数器不为0,那么在整个程序运行过程,它占用的内存就不

可能被回收,除非整个程序已经退出。

(3)引用计数器的操作

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

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

retainCount:获得当前的引用计数器值

    如下图演示了对象自创造出来之后经历一次retain及两次release操作的过程


在对象的生命周期中,其引用计数时而递增,时而递减,最终归零。

4、对象的销毁

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

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

(3)一般会重写dealloc方法,在这里释放相关资源。

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

(5)运行期系统会在适当的时候调用dealloc方法,绝不能直接调用该方法。

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

5、僵尸对象、野指针和空指针的概念

(1)僵尸对象:所占用内存已经被回收的对象,该对象已经不可用

(2)野指针:当指针指向僵尸对象时即为野指针,给野指针发送消息会报错(EXC_BAD_ACCESS 坏的访问)

(3)空指针:没有指向任何东西的指针,空指针不会报错

6、Xcode的设置

(1)取消ARC


(2)开启僵尸对象监控


7、内存管理原则

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

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

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

(4)如果你通过alloc、new或[mutable]copy来创建一个对象,那么你必须调用release或autorelease,换句话说,不

是你创建的,就不用你去release或者是autorelease。

(5)只要你调用了retain,无论这个对象是如何生成的,你都要调用release

8、set方法的内存管理

如果有个OC对象类型的成员变量,就必须管理这个成员变量的内存。比如有个Book *_book

set方法的实现:

- (void)setBook:(Book *)book{
	if (book != _book) {
		[_book release];
		_book = [book retain];
	}
}



dealloc方法的实现:

- (void)dealloc {
	[_book release];
	[super dealloc];
}



代码示例:

#import <Foundation/Foundation.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
{
    //book占用Book对象,所以Book对象计数器+1
    _book = [book retain];
}
- (Book *)book
{
    return _book;
}
-(void)dealloc
{
    //对Person对象内存被释放时,它占有的对象计数器要-1
    [_book release];
    NSLog(@"Person对象释放内存");
    [super dealloc];
}
@end

#import <Foundation/Foundation.h>

@interface Book : NSObject

@end

#import "Book.h"

@implementation Book
-(void)dealloc
{
    NSLog(@"Book对象被释放");
    [super dealloc];
}
@end

#import <Foundation/Foundation.h>
#import "Person.h"
#import "Book.h"
int main()
{
    //Person对象的计数器p=1
    Person *p = [[Person alloc] init];
    
    //Book对象的计数器b=1
    Book *b = [[Book alloc] init];
    
    //此时b+1=2
    [p setBook:b];
    
    //p不再占用Person对象了,所以p=1-1=0,计数器等于0后,该对象的内存要被释放,所以它所占有的对象的计数器也要—1,于是b=2-1=1
    [p release];
    
    //变量b也不再占用该对象了,所以b=1-1=0,对象内存释放
    [b release];

    return 0;
}

二、@property参数

1、控制set方法的内存管理

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

assign:直接赋值,不做任何内存管理(默认,用于非OC对象类型)

copy:release旧值,copy新值(一般用于NSString*)


2、控制是否生成set方法

readwrite:同时生成set和get方法(默认)

readonly:只会生成getter方法


3、多线程管理

atomic:性能低(默认)

nonatomic:性能高


4、控制set和get方法的名称

setter:设置set方法的名称,一定要有个冒号“:”

getter:设置get方法的名称

5、set方法完善

代码示例:

#import <Foundation/Foundation.h>

@interface Car : NSObject
{
    int _speed;
}
- (void)setSpeed:(int)speed;
- (int)speed;
@end

#import "Car.h"

@implementation Car
- (void)setSpeed:(int)speed
{
    _speed = speed;
}
- (int)speed
{
    return _speed;
}

-(void)dealloc
{
    NSLog(@"速度为%d的Car对象内存释放",_speed);
    //调用父类的dealloc
    [super dealloc];
}
@end

#import <Foundation/Foundation.h>
#import "Car.h"
@interface Person : NSObject
{
    Car *_car;
}
- (void)setCar:(Car *)car;
- (Car *)car;
@end

#import "Person.h"

@implementation Person
- (void)setCar:(Car *)car
{
    //对旧车对象进行判断,如果仍为旧车,计数器不用再—1
    if (_car != car) {
        //旧车对象计数器-1
        [_car release];
        //新车对象计数器+1
        _car = [car retain];
    }
}
- (Car *)car
{
    return _car;
}

-(void)dealloc
{
    //车对象计数器-1
    [_car release];
    NSLog(@"Person对象内存释放");
    [super dealloc];
}

@end

#import <Foundation/Foundation.h>
#import "Person.h"
#import "Car.h"

int main()
{
    //计数器p1-1
    Person *p1 = [[Person alloc] init];
    
    //c1-1
    Car *c1 = [[Car alloc] init];
    //c2-1
    Car *c2 = [[Car alloc] init];
    
    c1.speed = 200;
    c2.speed = 300;
    
    //c1-2
    p1.car = c1;
    //c1-1
    [c1 release];
    
    //c2-2,c1-0
    p1.car = c2;
    
    //p1-0,c2-1
    [p1 release];
    //c2-0
    [c2 release];

    return 0;
}


三、循环引用

1、什么是循环引用

两个对象通过彼此之间的强引用而构成一个保留环,比如A类引用B类,同时B类也引用了A类,如图所示:


他们的代码如下:

#import "B.h"
@interface A : NSObject
  B *b;
@end

#import "A.h"
@interface B :NSObject
  A *b
@end

这种代码编译会报错。当使用@class在两个类相互声明,就不会出现编译报错,例如将#import "B.h"改为

@class B,它会告诉编译器有这个类,但是不知道类中的具体方法。


2、@class和#import的区别

(1)#import方式会包含被引用类的所有信息,包括被引用类的变量和方法;@class方式只是告诉编译器在A.h文件

中 B *b 只是类的声明,具体这个类里有什么信息,这里不需要知道,等实现文件中真正要用到时,才会真正去查看B

类中信息

(2)如果有上百个头文件都#import了同一个文件,或者这些文件依次被#improt,那么一旦最开始的头文件稍有改

动,后面引用到这个文件的所有类都需要重新编译一遍,这样的效率也是可想而知的,而相对来 讲,使用@class方

式就不会出现这种问题了

(3)在.m实现文件中,如果需要引用到被引用类的实体变量或者方法时,还需要使用#import方式引入被引用类


3、循环retain

比如A对象retain了B对象,B对象retain了A对象,这样会导致A对象和B对象永远无法释放,解决方案:当两端互

相引用时,应该一端用retain、一端用assign


代码示例:

#import <Foundation/Foundation.h>
@class Card;
@interface Person : NSObject
//防止互相应用的对象互相retain,导致内存无法回收
@property (nonatomic,assign) Card *card; // card对象设置为assign,即计数器不会retain
@end

#import "Person.h"
#import "Card.h"
@implementation Person
-(void)dealloc
{
    NSLog(@"Person对象被回收了");
    //[_card release];
    [super dealloc];
}
@end

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

#import "Card.h"
#import "Person.h"
@implementation Card
- (void)dealloc
{
    NSLog(@"Card内存被回收了");
    [_person release];
    [super dealloc];
}
@end

#import <Foundation/Foundation.h>
#import "Person.h"
#import "Card.h"
int main()
{
    //p-1
    Person *p = [[Person alloc] init];
    //c-1
    Card *c = [[Card alloc] init];
    
    //c-1
    p.card = c;
    //p-2
    c.person = p;
    
    //c-0 p-1
    [c release];
    //p-0
    [p release];
    return 0;
}

四、自动释放池(autorelease

1、autorelease

(1)给某个对象发送一条autorelease消息时,就会将这个对象加到一个自动释放池中

(2)当自动释放池销毁时,会给池子里面的所有对象发送一条release消息

(3)调用autorelease方法时并不会改变对象的计数器,并且会返回对象本身

(4)autorelease实际上只是把对release的调用延迟了,对于每一次autorelease,系统只是把该对象放入了当前的

autorelease pool中,当该pool被释放时,该pool中的所有对象会被调用Release

2、autorelease的内部原理

NSAutoreleasePool内部包含一个可变数组(NSMutableArray),用来保存声明为autorelease的所有对象。如果一个对象声明为autorelease, 系统所做的工作就是把这个对象加入到这个数组中去。当NSAutoreleasePool自身释放的时候,会遍历数组中的所有对象,并且调用 release方法。如果对象的retainCount=0 那么系统会释放这些对象,如果retainCount>0,则会内存泄露。

3、自动释放池的创建

 ios 5.0后:

@autoreleasepool
{
    // ....
}

 ios 5.0前:

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// .....
[pool release]; // 或[pool drain];

在程序运行过程中,可以创建多个自动释放池,它们是以栈的形式存在内存中OC对象只需要发送一条autorelease消息,就会把这个对象添加到最近的自动释放池中(栈顶的释放池)


代码示例

#import <Foundation/Foundation.h>
#import "Person.h"
#import "GoodPerson.h"
int main()
{
    
    // 自动释放池里嵌着另一个自动释放池
    @autoreleasepool {
        Person *p = [[[Person alloc] init] autorelease];
        @autoreleasepool {
            Person *p1 = [[[Person alloc] init] autorelease];
            p1.age = 1;
        }   // 在这里释放p1这个对象所占据的内存
    } // 在这里释放p对象的内存
    
    // 自动释放池相当于在创建完一个对象后并不马上释放它,而是另做它用之后再释放
    Person *p2 = [[Person alloc] init];
    p2.age = 10;
    [p2 release];
    
    

    @autoreleasepool {
        Person *p3 = [Person person];
        p3.age = 100;
        
        
        Person *p4 = [Person personWithAge:1000];
       
        
        NSString *str = [NSString stringWithFormat:@"age=%d",p4.age];
        NSLog(@"%@",str);
        
        // 除了alloc、new或copy之外的方法创建的对象都被声明了autorelease,例如NSNumber。。。
        NSNumber *num = [[NSNumber alloc] initWithInt:10];
        [num release];
        
        NSNumber *num1 = [NSNumber numberWithInt:100];
        
    }
    
    
    @autoreleasepool {
        // 调用父类的方法,返回自己的对象
        GoodPerson *g = [GoodPerson personWithAge:19];
        g.money = 1000;
        NSString *str1 = [NSString stringWithFormat:@"age=%d,and money=%f",g.age,g.money];
        NSLog(@"%@",str1);
        
    }
    
    

    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值