黑马程序员——内存管理

------ Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

 内存管理

内存管理基本原理

 •    移动设备的内存极其有限,每个app所能占用的内存是有限制的
 •    当app所占用的内存较多时,系统会发出内存警告,这时得回收一些不需要再使用的内存空间。   比如回收一些不需要使用的对象、变量等

<1>回顾c语言中的内存管理

int fun(int x)
{
    return x+10;
}
int main()
{
    char *str=malloc(100);
    int a=100;
    fun(a);
}
栈空间中(系统函数开始的时候自动申请,结束的时候自动释放)
    局部变量,函数参数
静态存储区(程序开始时为这些拜年了申请空间,结束时释放)
    全局变量,静态变量
堆空间(malloc手工申请,使用free手工释放)
    malloc申请的空间在堆中
如果malloc申请的内存没有free?
    会出现什么问题:内存泄漏

<2>oc中内存管理方式简介

    两类内存管理方式:
        手动内存管理和arc(自动引用计数)
    为什么学习手动内存管理
        (1)面试的时候可能考察手动内存管理
        (2)有些公司使用手动内存管理,以前的代码使用手动内存管理
        (3)部分开源库也是使用手动内存管理的

<3>引用计数的内存管理方式

    遇到的问题:多个指针指向同一个对象
         小技巧:如何切换程序的内存管理方式
         Project-->Build Setting--->搜索counting 找到选项 设置为YES或者NO
         YES 表示使用自动引用计数
          NO表示使用手动内存管理
       思路:
       第一件事:定义一个对象指针dog
       第二件事:创建一个对象
       这个对象的内存的哪?  alloc 申请的内存一般都在堆上
       alloc 也需要释放
1. oc中使用的内存管理方式基于引用计数的内存管理方式
      (1)解决了多个指针指向同一个对象的释放问题
        (2)oc中对象都有一个引用次数retainCount
        (3)每次一个指针指向了这个对象 则这个对象的引用次数+1
        (4)如果一个指针不在引用这个对象,则这个对象的引用次数-1
        (5)最后,没有指针引用这个对象,则释放对象
       
        Dog *dog=[[Dog alloc] init];
        创建对象的时候retainCount==1 表示有一个指针引用了这个对象
        NSLog(@"%lu",dog.retainCount);
       
2.如何释放?使用release方法释放对象
        release作用:
         (1)如果这个对象retainCount>1 retainCount-1
         (2)如果这个对象retainCount==1 需要释放这个对象
                
        Dog *anDog=[dog retain];
        retain方法作用:使对象的引用次数+1;
        [anDog retain];
        NSLog(@"%lu",dog.retainCount);
        [dog release];
        NSLog(@"%lu",dog.retainCount);
        [anDog release];
        不要这样使用,这样使用时有问题的
        NSLog(@"%lu",dog.retainCount);
        思考:什么叫释放内存???
         放弃了这块内存的使用权
        什么是申请内存???
          获取了这块内存的使用权


#import <Foundation/Foundation.h>
@interface Dog : NSObject
{
    NSString *_name;
    int _age;
}
@property (copy)NSString *name;
@property int age;
@end

#import "Dog.h"
@implementation Dog
//重写dealloc方法,验证对象释放被释放
//  实现的 时候有特殊要求,方法的最下面执行父类的dealloc
//作用:对象被释放的时候由系统自动调用
-(void)dealloc
{
    NSLog(@"dog dealloc");
    [super dealloc];
}
@end

<4>字符串内存管理             

 

        //字符串的内存管理
        //  如果是字符串常量对象,retainCount是一个极大值
        //无需retain也无需release
        NSString *str=@"chang huai yi ke ping chang xin";
        NSLog(@"NSString 的 retainCount 值是:%lu",str.retainCount);
        //输出:18446744073709551615
        
        NSString *str2=[[NSString alloc] initWithString:@"123"];
        NSLog(@"NSString 的 retainCount 值是:%lu",str2.retainCount);
        [str2 release];
        //输出:18446744073709551615

        NSMutableString *str3=[[NSMutableString alloc] initWithString:@"123"];
        NSLog(@"NSString 的 retainCount 值是:%lu",str3.retainCount);
        // 输出:1
        [str3 release];
        
        NSMutableString *str4=[[NSMutableString alloc] initWithUTF8String:"ooooo"];
        NSLog(@"NSString 的 retainCount 值是:%lu",str4.retainCount);
        //输出:1
        [str4 release];

总结:字符串内存管理方式

        (1)如果是字符串常量对象,引用次数是极大值使用的时候不考虑内存管理
        (2)如果这个字符串是新创建的(不是指向一个常量字符串对象)他的引用次数是1需要用    release释放
        以后如何处理:如果这个字符串对象时alloc创建的,使用release释放即可
        不是通过alloc创建的,不需要释放

<5>autorelease方法,自动释放作用    //自动释放池

   
@autoreleasepool {
        //autorealese 的使用
        //autorealese的原理:会把对象加入到自动释放池
        Dog *dog=[[[Dog alloc] init] autorelease];
        dog.age=100;
        NSLog(@"%d",dog.age);
        //[dog release];
        //给一个对象发送了autorelease之后不会立即释放,而是使用完之后释放
        //[dog autorelease];
    }
    NSLog(@"end");
    //输出的结果分别是100,dealloc,end  所以是释放完之后再结束
    return 0;

<6>copy和mutableCopy方法,复制一个对象      

需求:获取一个对象,不让其他指针改变
解决: 使用copy方法或者mutableCopy复制

// copy方法和mutableCopy的使用
        NSMutableString *str=[[NSMutableString alloc] initWithString:@"yanyan"];
       // NSMutableString *name=[str retain];
        //出现的问题:A指针和B指针指向同一个对象
        //      导致A指针修改对象,B指针得到的是修改后的值
        //copy返回一个不可变的对象 mutableCopy返回一个可变的对象
        NSMutableString *name=[str mutableCopy];
        [str setString:@"yanzi"];
        NSLog(@"%@",name);
        [str release];
        [name release];

内存管理原则---黄金法则

原则:如果一个对象使用了alloc   new   retain   copy    mutableCopy 那么你必须使用想要的release和autonrelease
代码中出现的各种情况
(1)如果实在方法的内部定义的对象指针,指向了alloc申请的一个对象
        何时释放: 使用完之后释放,可以放在方法最后
        方法内部申请对象空间,方法结束的时候release释放即可
(2)init中申请对象指针(类的实例变量)的申请对象需要在dealloc方法中释放(重点)
{
    Engine *_engine;
    //包含过个轮胎    
    NSMutableArray *_tireArray;
}
-(id)init
{
    if (self = [super init]) {
        //申请内存
        _engine=[[Engine alloc] init];
        _tireArray = [[NSMutableArray alloc] init];
    }
    return self;
}
-(void)dealloc
{
    NSLog(@"Car dealloc");
    //释放内存
    [_engine release];
    [_tireArray release];
    [super dealloc];
}

(3)对象指针的setter方法

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

//添加设置方法
-(void)setEngine:(Engine *)engine
{
    //避免两次传染的时同一个对象
    if (_engine!=engine) {
        //_engine指向新的对象之前有可能指向一个对象
        [_engine release];
        //类中的实例变量—_engine指向外部传入的对象engine  多个指针指向同一个对象
        _engine=[engine retain];
    }
}
(4)对象制作的 getter方法
-(Engine *)engine
{
    //不能两个指针指向一个对象  同事不能立即释放
    return  [[_engine retain] autorelease];
}

(5)各种变量的property(重点)

@property参数
    •    控制set方法的内存管理
    •    retain : release旧值,retain新值(用于OC对象)
    •    assign : 直接赋值,不做任何内存管理(默认,用于非OC对象类型)
    •    copy   : release旧值,copy新值(一般用于NSString *)
 
    •     控制需不需生成set方法
    •    readwrite :同时生成set方法和get方法(默认)
    •    readonly  :只会生成get方法
 
    •     多线程管理
    •    atomic    :性能低(默认)
    •    nonatomic :性能高
 
    •     控制set方法和get方法的名称
    •    setter : 设置set方法的名称,一定有个冒号:
    •    getter : 设置get方法的名称


#import <Foundation/Foundation.h>
#import "Engine.h"
@interface Car : NSObject
{
    double _speed;
    NSString *_name;
    Engine *_engine;
    //包含过个轮胎
    NSMutableArray *_tireArray;
}
//基本数据类型一般都用 assign
//设置方法中直接设置值
//获取方法中直接返回值
//  assign是默认的,可写可不写
@property (assign)double speed;
//字符串一般都用copy
//  表示吧传染的字符串复制一份
@property (copy)NSString *name;
//对象指针一般用retain
//  设置方法先释放以前指向的对象,再指向新的对象(retain一次)
//获取方法中 先retain再release
@property (retain)Engine *engine;
添加设置方法
//-(void)setEngine:(Engine *)engine;
添加获取方法
//-(Engine *)engine;
@end


#import "Car.h"
@implementation Car
-(id)init
{
    if (self = [super init]) {
        //申请内存
        _engine=[[Engine alloc] init];
        _tireArray = [[NSMutableArray alloc] init];
    }
    return self;
}
//添加设置方法
-(void)setEngine:(Engine *)engine
{
    //避免两次传染的时同一个对象
    if (_engine!=engine) {
        //_engine指向新的对象之前有可能指向一个对象
        [_engine release];
        //类中的实例变量—_engine指向外部传入的对象engine  多个指针指向同一个对象
        _engine=[engine retain];
    }
}
//添加获取方法
-(Engine *)engine
{
    //不能两个指针指向一个对象  同事不能立即释放
    return  [[_engine retain] autorelease];
}
-(void)dealloc
{
    NSLog(@"Car dealloc");
    //释放内存
    [_engine release];
    [_tireArray release];
    [super dealloc];
}
@end

(6)类方法创建对象如何写
    特性:类方法创建对象会自动释放,不需要release
+(id)car
{
    Car *newCar=[[Car alloc] init];
    //(2)返回对象  类方法创建的对象加了autorelease之后会自动释放内存
    return [newCar autorelease];
}
main 中 不用写释放
 Car *car=[Car car];

(7)数组中的添加对象内存管理 NSArry
        
//NSArray 存储对象的内存管理
        Car *car1=[[Car alloc] init];
        Car *car2=[[Car alloc] init];
        //数组中加入一个对象,这个不仅会保存对象的指针 还会把对象引用次数retiancount+1
        NSArray *arr=[[NSArray alloc] initWithObjects:car1,car2,nil];
        NSLog(@"car1 rc=%lu",car1.retainCount);
        //释放数组的时候,数组对其中每个对象发送release消息
        [arr release];
        [car1 release];
        [car2 release];
(8)取消线程保护 nonautomic
多线程再访问同一个实例变量的时候会出现问题

 nonautomic去掉了多线程保护,提高了代码的运行速度


@class和#import的区别
    •    #import方式会包含被引用类的所有信息,包括被引用类的变量和方法;@class方式只是告诉编译器在A.h文件中 B *b 只是类的声明,具体这个类里有什么信息,这里不需要知道,等实现文件中真正要用到时,才会真正去查看B类中信息
    •    如果有上百个头文件都#import了同一个文件,或者这些文件依次被#improt,那么一旦最开始的头文件稍有改动,后面引用到这个文件的所有类都需要重新编译一遍,这样的效率也是可想而知的,而相对来 讲,使用@class方式就不会出现这种问题了
    •    在.m实现文件中,如果需要引用到被引用类的实体变量或者方法时,还需要使用#import方式引入被引用类


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值