• Object-c 内存管理


                                  内存管理

    主要内容

    1.内存管理的概念

    2.引用计数

    3.如何持有对象所有权

    4.自动释放池

    5.@property的使用

    什么是内存管理

    内存管理是关于如何管理对象生命周期的编程原则。

    int main(int argc, char *argv[])

    {

           int value = 100;

           Dog *mydog = [[Dog alloc]init];

           [dog setNum:@"蓝眼儿"];

           return 0

    }

    栈区

    value 100

    person 0x12345

     

    堆区

     

    Dog对象

    _name

    oc中的内存管理只针对oc中的对象,所有对象都继承NSObject

    基本数据类型不需要管理内存。

    当一个对象没有人再使用,该对象应该从内存中销毁掉。

    引用计数

    所有oc对象都有一个计数器,这个计数器我们称为引用计数。

    引用计数表示有几个人在使用当前对象。

    NSObject对象

    retainCount

    引用计数

    每个对象都有一个retainCount引用计数器,表示当前对象被引用的数量。

    怎么是引用计数器加减呢?

    1.调用retain方法, 计数+1,调用release方法,计数-1

    2.当引用计数为0时,说明没有人使用此对象,此对象会被系统销毁。

    alloc   retain       release

    alloc用来创建对象,创建完成后,引用计数为1,只调用一次。

    retain使引用计数+1,可以调用多次。

    release使引用计数-1,可以调用多次。

    当引用计数为0时,对象会被系统从内存中销毁,销毁之前,会自动调用此对象的dealloc方法。

    retain, release示例

    Person *person = [[Person alloc]init];//计数器为1

    [person retain];//计数器为2

    [person retain];//计数器为3

    [person release];//计数器为2

    [person release];//计数器为1

    [person release];//计数器为0,对象从内存中销毁,调用dealloc方法。

    为什么使用引用计数

                                                            遛狗的例子

    对象所有权

    当一个所有者(owner,本身是一个oc对象)做了如下动作,它就拥有了一个对象的所有权(ownership)

    alloc                          retain                [mutable]copy

    使用如下方法

    release             autorelease

    黄金法则

    如果对一个对象使用了alloc,[mutable]copy, retain, 那么你必须使用相应的release或者autorelease释放。

    如何持有对象

    实例分析:如何让人持有狗的对象所有权

    1.set方法持有对象的所有权

    -(void)setDog: (Dog *)dog
    
    {
    
           if (_dog!=dog) {
    
                  [_dog release];
    
                  _dog = [dog retain];
    
           }
    
    }

    2.自定义初始化方法,持有对象的所有权

     1 -(id) initWithDog: (Dog *)dog
     2 
     3 {
     4 
     5        self = [super init];
     6 
     7        if (self != nil) {
     8 
     9               _dog = [dog retain];
    10 
    11        }
    12 
    13        return self;
    14 
    15 }

    dealloc方法

    当对象的引用计数为0时,系统调用此对象dealloc。

    我们应该在dealloc方法中,释放他持有的对象。

    -(void)dealloc

    {

           [_dog release];

           [_car release];

           [super release];//为了释放父类持有的对象所有权。

    property的使用

    有一个类有如下属性

    @interface User:NSObject

    {

           int userId;

           NSString *_userName;

           NSString *_passwd;

           NSString *_email;

           NSString *_birthday;

    }

    设置器

     1 -(void) setUserName: (NSString *)name
     2 
     3 {
     4 
     5        _name = name;
     6 
     7 }
     8 
     9 -(NSString *)userName{
    10 
    11        return _name;
    12 
    13 }

    每个属性都这样写set和get方法是很麻烦的。

    property可以自动生成一个get和set方法,不需要我们手动编写

    @interface User : NSObject

    {

           //不必在定义,@property自动生成

    }

    @property(nonatomic, retain) NSString *userName;

    使用方法

    @property(<1>nonatomic,<2>retain,<3>readwrite)NSString *userName;

    1.原子性:

    atomic:多线程模式下存在线程保护,默认

    nonatomic:多线程模式下不存在线程保护

    2.赋值

    assign:直接赋值,默认

    -(void)setUserName: (NSString *)name

    {

           _name = name;

    }

    retain:保留原对象

    -(void)setUserName: (NSString *)name

    {

           if (_name != name) {

                  [_name release];

                  _name = [name retain];

           }

    }

    copy:拷贝对象

    -(void)setUserName: (NSString *)name

    {

           if (_name != name) {

                  [_name release];

                  _name = [name copy];

           }

    }

    3.读写性

    readwrite:生成getter,setter方法,默认

    readonly:只生成getter方法

    注意,我们也可以重新实现set和get方法,优先调用自己实现的set和get方法。

    实战

    1.理解引用计数,对象所有权,理解oc内存管理的机制。

    2.设计如下几个类,Car自定义初始化方法,初始化方法传入引擎对象和车灯对象。当车启动的时候,会调用引擎转动,车灯亮灯,当车停止的时候调用引擎停止转动,车灯熄灭。(注意内存管理)

    Car(汽车)

    Engine(引擎)

    lamp(车灯)

    属性:

    属性:

    属性:

    名字(name)

    型号(model)

    瓦数(wattage)

    车牌号(licence)

    排量(capacity)

    方法:

    引擎(engine)

    方法:

    亮(light)

    车灯(lamp)

    转动(turn)

    熄灭(dark)

    方法:启动(run)

    停止转动(stopTurn)

    停止(stop)

     

    数组的内存管理

    如果一个对象被添加到数组,那么这个对象的引用计数会变化吗?

    NSMutableArray *mutableArray = [NSMutableArray array];

    Dog *dog1 = [[Dog alloc]init];

    Dog *dog2 = [[Dog alloc]init];

    [array addObject: dog1]; //dog1会被数组retain,计数+1

    [array addObject:dog2];dog2会被数组retain计数+1

    [dog1 release];

    [dog2 release];

    数组销毁,或者removeAllObjects,会给每一个元素发送release消息。

    [array release];

    //移除下标为1的元素,同时向它发送release消息

    [array removeObjectAtIndext:1];

    自动释放池

    自动释放池是oc的一种内存自动管理机制。

    当自动释放池销毁时,它会对池子中每一个对象调用一次release方法。

    当我们向一个对象autorelease消息时,这个对象就会被放入自动释放池。

    老的写法

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];

    User *user = [[user alloc]init];

    //加入自动释放池

    [user autorelease];

    //自动释放池销毁

    [pool release];

    新语法的写法

    @autorelesepool {

           User *user = [[User alloc]init];

           //加入自动释放池

           [user autorelease];   

    }

    自动释放池的原理

    自动释放池就像一个数组,容纳了许多加入池的对象。

    对象1

    对象2

    对象3

    自动释放池可以嵌套使用

    autorelease会将对象添加到离他最近的自动释放池中。

    NSAutoreleasePool pool1 = [[NSAutoreleasePool alloc]init];

    Person *person = [[Person alloc]init];

    //添加到离他最近的pool1中

    [person autorelease];

    NSAutoreleasePool pool2 = [[NSAutoreleasePool alloc]init];

    Dog *dog = [[Dog alloc]init];

    [dog autorelease];

    [pool2 release];

    [pool1 release];

    典型错误例子,如下代码有内存泄漏

    [person setDog:[[Dog]alloc]init];

    正确的写法是:

    Dog *dog = [[[Dog alloc]init]release];

    [person setDog:dog];

    //这里不能[dog release]

    错误的典型例子

    -(NSString *)getInfo {

           NSString *info = [[NSString alloc]initWithFormat:@"姓名:%s年龄:%d", _name, _age];

           [info release];//返回的对象已经销毁

           return info;

    }

    NSString *infoString = [person getInfo];

    [infoString release];//违背了黄金法则

    正确的写法是

    -(NSString *)getInfo {

           NSString *info = [[NSString alloc]initWithFormat:@"姓名:%s年龄:%d", _name, _age];

           return [info autorelease];

    }

    NSString *infoString = [person getInfo];

    //自动释放池销毁的时候,会向infoString对象发送release消息。

    举例

    for (i = 0; i < 1000000; i++) {

           NSMutableArray *array = [[NSMutableArray alloc]init];

           [array autorelease];

    }

    循环100万次,创建100万个对象,这些对象在该循环体内不会自动销毁,只会加到最近的自动释放池中。只有当自动释放池销毁了,100万个对象才会受到release消息。这样100万个对象同时存在内存当中,占用内存很大。

    更好的写法是

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];

    for (i = 0; i < 1000000; i++) {

           if (i % 1000 == 0) {

           [pool release];

           NSAutorelease *pool = [[NSAutorelease alloc]init];

           }

           NSMutableArray *array = [[NSMutableArray alloc]init];

           [array autorelease];

    }

    该代码每执行1000次就会把pool释放一次,同时在穿件一个新的自动释放池。也就是累计不会超过1000个对象。

    类方法创建的内存管理

    +(Dog *)dog {

           Dog *dog = [[Dog alloc]init];

           return [dog autorelease];

    };

    Dog *dog = [Dog dog];

    [person setDog: dog];

    //这里不能[dog release];

    类方法创建的内存管理

    Foundation中的类可以通过alloc创建和类方法创建,区别在于内存管理不一样。

    类方法创建的对象是加入了自动释放池

    //alloc创建

    NSArray *array = [[NSArray alloc]init];

    //其他方法

    [array release];

    //类方法创建, 加入了自动释放池

    NSArray *arr = [NSArrary array];

    //不能再用[arr release];

    新语法创建的内存管理

    新语法创建的对象和类方法创建的对象相同,由自动释放池管理

    NSArray *array = @[@“zhngsan”,  @”lisi”];

    NSNumber *number = @123;

    常见的错误

    1.错误

    +(id) person{

           Person *person = [[Person alloc]init];

           [person release];

    return person;

    }

    正确的写法

    +(id)person {

           Person *person = [[Person alloc]init];

           return [person autorelease];

    }

    2.错误

    +(void)description

    {

           NSString *string = [NSString stringWithFormat:@”lrz”];

           [name release];

           NSLog(“name:%@”, string);

    }

    正确

    +(void)description

    {

           NSString *string = [NSString stringWithFormat:@”lrz”];

           NSLog(“name:%@”, string);

    }

    3.错误

    Laptop *laptop = [[Laptop alloc]init];

    laptop.name = [NSString stringWithFormat:@”apple”];

    NSArray *array = [NSArray arrayWithObject: laptop];

    [array release];

    正确

    Laptop *laptop = [[Laptop alloc]init];

    laptop.name = [NSString stringWithFormat:@”apple”];

    NSArray *array = [NSArray arrayWithObject: laptop];

    [laptop release];

    4.错误

    -(void)memory

    {

           Car *car = [[Car alloc]init];

           if (gas == 0) {

                  return;

    }

    [car release];

    }

    正确

    -(void)memory

    {

           Car *car = [[[Car alloc]init]autorelease];

           if (gas == 0) {

                  return;

    }

    }

    ARC

    自动引用计数(automatic reference counting),提供自动管理内存的功能。

    不需要手动管理引用计数,不需要也不允许retain, release, autorelease

    注意版本的支持是在IOS4(不支持弱引用),ios5上

    循环引用

    对象A retain对象B。同时对象B retain对象A,这种情况我们称之为循环引用。

    循环引用会导致两个对象都无法销毁掉。

    -(void)setA: (A *a)
    
    {
    
           if (_a!=a) {
    
    [_a release];
    
    _a = [a retain];
    
    }
    
    }
    
    -(void)setB:(B *b)
    
    {
    
           if (_b != b) {
    
                  [_b release];
    
                  _b = [b retain];
    
    }
    
    }
  • 相关阅读:
    jquery.cookie.js 的使用
    2013年工作中遇到的20个问题:141-160
    提高生产力:文件和IO操作(ApacheCommonsIO-汉化分享)
    提高生产力:文件和IO操作(ApacheCommonsIO-汉化分享)
    我的网站恢复访问了,http://FansUnion.cn
    我的网站恢复访问了,http://FansUnion.cn
    噩梦遇地震,醒后忆岁月
    噩梦遇地震,醒后忆岁月
    2013年工作中遇到的20个问题:121-140
    2013年工作中遇到的20个问题:121-140
  • 原文地址:https://www.cnblogs.com/yanzifamily/p/4983044.html
Copyright © 2020-2023  润新知