OC语言--内存管理

1.内存管理原理的介绍

1.1C的内存管理

char *p = (char *)malloc(100*sizeof (char));

这是C的动态内存分配,我们手动跟系统申请了100个字节的内存;或者说系统在堆里开辟了100个字节的空间,并将这个空间的首地址返回给指针变量p。

strcpy(p,"Hello World!");

将字符串拷贝给指针变量p指向的内存空间。

puts(p);

将p指针指向的内存空间里的字符串打印出来。

free(p);

使用完成后,手动跟系统释放内存空间;或者说系统回收空间。如上就是C里简单的内存管理。

C的内存管理,我们手动申请,手动释放。

这样来看,我们只需要注意两个问题就好了:

1,申请内存,使用完成后需要释放,如果不释放会造成内存泄漏。

2,不能多次释放,如果多次释放,则会崩溃。

但是,如果项目比较复杂,需要有几十上百号人一起分工完成,就很容易出现问题。

比方说我们开辟了一块内存空间,里面存放了一块很有用的数据。但是,这个数据不只有我在这一块代码里用,甚至有多个人,在程序的多个地方使用。这样造成的结果就是,就算我使用完成这块内存,我也不能去释放他,因为我不能确定,别人在别的地方是否还需要使用这块内存。内存泄露在所难免了。

2.OC的内存管理方式:引用计数

2.1引用计数

对于一块动态申请的内存,有一个人(指针)使用,就给这个内存的计数器(该计数器在该对象中)加1,使用完成后,就给这个计数器减1,当这个内存的引用计数为0了,我们则释放他,这样,上面的问题就解决了。OC,就是使用引用计数这种方式来管理内存的。

2.2内存管理的黄金法则

对于引用计数来说,有一套内存管理的黄金法则:

The basic rule to apply is everything that increases the reference counterwith alloc, [mutable]copy[withZone:] or retain is in charge of the corresponding [auto]release.

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

通俗一点的说法就是谁污染谁治理。

2.3MRC和ARC

ARC Automatic Reference Counting,自动引用计数,由xcode,帮我们去管理内存。

MRC Manual  Reference Counting,手动引用计数,由我们手动管理内存。

但就目前来看,很多公司还是使用MRC.

2.4 如何将工程改为MRC

xcode5,工程创建的时候是ARC的,我们如果想要MRC,需要进行如下设置。

选中工程 - >target - >Bulid Settings - >搜索:automatic reference counting或auto,将Objective-C Automatic Reference Counting改为NO。

3.手动内存管理的操作(MRC)

3.1alloc与release

创建一个新的工程,先别将内存管理改为手动

创建一个Dog类

@interface Dog : NSObject

  @end

  @implementation Dog

  - (void)dealloc

  {

    NSLog(@"dog dealloc");

    [super dealloc];

  }

  @end

dealloc里的析构函数,当对象销毁的时候,会自动调用这个方法,我们在这里重写这个方法。

在main函数里,写入如下代码:

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

  {

    @autoreleasepool {

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

     }

    NSLog(@"程序即将退出");

    return 0;

  }

从终端打印信息来看,程序即将退出这条打印之前,已经打印dog dealloc,也就是说在程序运行结束前,dog对象已经销毁了。这个是ARC,由xcode帮我们管理dog对象。

将ARC改为MRC,再执行程序,dog对象并没有销毁,因为我们现在是手动管理了,我们需要遵守内存管理的黄金法则;Dog *dog = [[Dog alloc] init]; 我们需要对dog进行release。

将main函数代码改为如下形式:

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

{

    @autoreleasepool {

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

        [dog release];

     }

    NSLog(@"程序即将退出");

    return 0;

}

再次执行程序,从打印可以看出,dog对象已经销毁。这就是黄金法则,我们对dog进行alloc,就要对dog进行release。

注意,release 并不是销毁对象,而是让对象的引用计数减1,当对象的引用计数快为0的时候,自动调用dealloc方法并销毁对象。

3.2 retain与retainCount

retain,将对象进项保留操作,也就是使对象的引用计数加1。

retainCount,打印一个对象的引用计数。

将main函数代码改为如下形式:

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

  {

    @autoreleasepool {

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

//此时打印的结果,retainCount值为1,

//也就是我们alloc,创建dog对象时,对象的引用计数为1

NSLog(@"dog retainCount = %lu",[dog retainCount]);

//dog1指针要使用(引用)dog对象,

//此时,为避免dog对象进行release,

//使得引用计数减1变为0,销毁对象,

//我们进行了retain操作。

Dog *dog1 = [dog retain];

//此时打印的结果,retainCount值为2

NSLog(@"dog retainCount = %lu",[dog retainCount]);

Dog *dog2 = [dog retain];

//此时打印的结果,dog,dog1,dog2,retainCount值都为3,

//因为这三个指针指向同一个对象。

NSLog(@"dog retainCount = %lu",[dog retainCount]);

NSLog(@"dog1 retainCount = %lu",[dog1 retainCount]);

NSLog(@"dog2 retainCount = %lu",[dog2 retainCount]);

 //release 并不是销毁对象,让对象的引用计数减1

 [dog release];

//此时打印的结果,dog,dog1,dog2,retainCount值都为2,

//虽然dog执行了release,但dog指针还是指向那个对象。

//此时dog对对象只有使用权,而没有拥有权。

  NSLog(@"dog retainCount = %lu",[dog retainCount]);

  NSLog(@"dog1 retainCount = %lu",[dog1 retainCount]);

  NSLog(@"dog2 retainCount = %lu",[dog2 retainCount]);

  [dog1 release];

  [dog2 release];

//执行完上面两句话的时候,dog对象就销毁了。

//虽然这里我们可以写两句[dog release];

//也能达到同样的效果,但是,务必不要这样写,

//我们要遵守内存管理的黄金法则:

Dog *dog = [[Dog alloc] init]; // 这是对dog指针进行alloc,需要对应[dog release];

Dog *dog1 = [dog retain]; //这是对dog1指针进行retain,需要对应[dog1 retain];

//这时候打印dog的retainCount是错误的用法!!

//因为对象已经销毁了!! 对一个已经销毁的对象发送消息是逻辑错误的!

//会造成程序的崩溃,

//因为dog对象已经销毁了,没法调用dog对象的方法。

//注意,如果上面不加两行打印的话,可能不会崩溃。

        NSLog(@"dog retainCount = %lu",[dog retainCount]);

     }

    NSLog(@"程序即将退出");

    return 0;

  }

3.3 类的复合中使用

在上面代码中,增加Person类

@interface Person : NSObject {

  // 一个人,养了一条狗

    Dog *_dog;

  }

  - (void)setDog:(Dog *)dog;

  - (Dog *)dog;

  @end

setDog方法形式:

@implementation Person

  /* 人并没有真正持有狗,

如果在main函数里[dog release],让dog的引用计数减1,就变为0,

dog就销毁了。

    - (void)setDog:(Dog *)dog

  {

    _dog = dog;

  }

    */

  /* 如果人再持有别的狗,

就会造成第一条狗得不到释放,内存泄露。

  - (void)setDog:(Dog *)dog

  {

    _dog = [dog retain];

  }

    */

  /* 如果本来持有一条狗,又重新设置这条狗,先进行release,

这个时候,很可能dog就销毁了,然后,就没法再次retain了。

  - (void)setDog:(Dog *)dog

  {

    [_dog release];

    _dog = [dog retain];

  }

    */

  // 标准写法

  - (void)setDog:(Dog *)dog

  {
           if (_dog  !=  dog) {

        [_dog release];

        _dog = [dog retain];

           }
  }

  - (Dog *)dog

  {
     return _dog;
  }

  - (void)dealloc
  {
   NSLog(@"person dealloc");

// 人在销毁的时候,一并将持有的dog对象销毁

    [_dog release];

    [super dealloc];

  }
@end

错误方法分析:

//第一个setDog:方法对应的错误

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

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

             [xiaoXin setDog:xiaoBai];

  //引用计数为1

             NSLog(@"count = %lu",xiaoBai.retainCount);

             [xiaoBai release];

  //此时狗已经销毁了,因此,xiaoXin需要持有这条狗。

             [xiaoXin release];

 // 第二个setDog:方法对应的错误

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

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

                [xiaoXin setDog:xiaoBai];

  //引用计数为2

               NSLog(@"count = %lu",xiaoBai.retainCount);

               [xiaoBai release];

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

               [xiaoXin setDog:xiaoHei];

               [xiaoHei release];

               [xiaoXin release];

  //此时xiaoBai这条狗没有释放

  //第三个setDog:方法对应的错误

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

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

                [xiaoXin setDog:xiaoBai];

  //引用计数为2

               NSLog(@"count = %lu",xiaoBai.retainCount);

               [xiaoBai release];

  //这样设置是不对的,因为在setDog:里,将dog进行release的时候,

  //引用计数为0,dog就销毁了,无法再retain了。

               [xiaoXin setDog:xiaoBai];

               [xiaoXin release];

  //另外,这里还要说明,类里,类外,都需要遵守内存管理。

3.4 @property retain,assign,copy展开

i.) retain展开

如上代码里,Person的setter和getter方法,也可以用property,写成如下形式:

@property (nonatomic, retain) Dog *dog;

进行如上测试,都没有问题。

因此,实际如果写成这样@property (nonatomic, retain) Dog *dog;,

则会展开如下:

- (void)setDog:(Dog *)dog

{

if (_dog != dog) {

[_dog release];

_dog = [dog retain];

}

}

- (Dog *)dog

{

return _dog;

}

ii.) assign展开

@property (nonatomic, assign) Dog *dog;,assign是直接复制,

则会展开如下:

- (void)setDog:(Dog *)dog

{

_dog = dog;

}

- (Dog *)dog

{

return _dog;

}

iii.) copy展开

@property (nonatomic, copy) Dog *dog;,copy,拷贝,

将原来的对象拷贝一份出来,展开如下:

- (void)setDog:(Dog *)dog

{

if (_dog != dog) {

[_dog release];

_dog = [dog copy];

}

}

- (Dog *)dog

{

return _dog;

}

3.5 字符串内存管理

i.) 字符串的内存管理

对于字符串而言,非常不遵守黄金法则! (如果从字符串的引用计数来看,乱七八糟!)这只是一个表象! 其实内部还是遵循的!!

我们要做的是,我们依旧遵守我们的黄金法则!

NSString *str = [[NSString alloc] initWithFormat:@"%d %s",1,"hello"];

        NSLog(@"count1 = %lu",str.retainCount);

        NSString *str2 = @"hello";

         NSLog(@"count2 = %lu",str2.retainCount);

        NSString *str3 = [str retain];

        NSString *str4 = [str2 retain];

        NSLog(@"count3 = %lu",str.retainCount);

        NSLog(@"count4 = %lu",str2.retainCount);

        NSString *str5 = [[NSString alloc] initWithString:str];

        NSString *str6 = [[NSString alloc] initWithString:str2];

        NSLog(@"count5 = %lu",str5.retainCount);

        NSLog(@"count6 = %lu",str6.retainCount);

        // NSString *str7 = [NSString stringWithFormat:@"%d",5];

       [str release];

       [str3 release];

       [str4 release];

       [str5 release];

       [str6 release];

       // str7不用release!!

因此,如果是NSString,我们的property格式写成如下: @property (nonatomic, copy) NSString *name;

ii.) copy和mutableCopy

NSMutableString *string = [[NSMutableString alloc] initWithString:@"hello"];

        // 这样写会崩溃,看对象,不看指针

        // copy 将(可变或不可变)字符串拷贝成不可变字符串,string2 实际是不可变字符串

        // NSMutableString *string2 = [string copy];

        // [string2 appendString:@"world"];

        NSString *string2 = [string copy];

        NSLog(@"string2 = %@",string2);

        // mutableCopy 将(可变或不可变)字符串拷贝成可变字符串

        NSMutableString *string3 = [string2 mutableCopy];

        [string3 appendString:@"world"];

        NSLog(@"string3 = %@",string3);

        // 不用管它的引用计数是多少,我们遵守我们自己的黄金法则就够了

        [string release];

        [string2 release];

        [string3 release];

        // UI里,也不要随便打印retainCount, 各人顾各人

        // new 相当于alloc init,在OC或IOS里几乎不用!!

3.6 数组的内存管理

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

{

    @autoreleasepool {

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

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

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

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

        NSLog(@"dog1.retainCount = %lu",dog1.retainCount);

        NSMutableArray *array = [NSMutableArray arrayWithObjects:dog1, 

dog2, dog3, dog4, nil];

        NSLog(@"dog1.retainCount = %lu",dog1.retainCount);

        [array addObject:dog1];

        NSLog(@"dog1.retainCount = %lu",dog1.retainCount);

        // NSLog(@"array = %@",array);

        [array removeLastObject];

        NSLog(@"dog1.retainCount = %lu",dog1.retainCount);

        // 新的array不是我们alloc new... 的,我们不需要release

        // [array release];

        NSLog(@"dog1.retainCount = %lu",dog1.retainCount);

        [dog1 release];

        [dog2 release];

        [dog3 release];

        [dog4 release];

    }

    //for @autoreleasepool

    return 0;

}

结论

1)当我们创建数组的时候,数组会对每个对象进行引用计数加1

2)当数组销毁的时候,数组会对每个对象进行引用计数减1

3)当我们给数组添加对象的时候,会对对象进行引用计数加1

4)当我们给数组删除对象的时候,会对对象进行引用计数减1

总之,谁污染谁治理,管好自己就可以了。

3.7 autorelease与 autoreleasepool

autoreleasepool是一个对象

autorelease 是一个方法

在main函数里写如下代码:

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

  {

    @autoreleasepool {

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

//dog并没有马上销毁,而是延迟销毁,

//将dog对象的拥有权交给了autoreleasepool

    [dog autorelease];

//这个是可以打印的,因为打印完dog的引用计数后,

//dog对象才销毁

@autoreleasepool{

     [dog autorelease]

}

    NSLog(@"retainCount = %lu",dog.retainCount);

     }

    NSLog(@"程序即将退出");

    return 0;

  }

注意: autoreleasepool相当于一个数组,如果哪个对象发送autorelease消息,实际将对象的拥有权交给了autoreleasepool;当autoreleasepool销毁的时候,将向autoreleasepool里持有的所有对象都发送一个release消息。

3.8 加方法的内存管理

我们用加方法创建的对象,不用我们release,是因为类内部的实现使用了autorelease,延迟释放。

在Dog类的声明里增加一个加方法

+ (id)dog;

在Dog类的实现里进行实现

+ (id)dog

{

/*注意,这里不要写成release,如果是release,那么刚创建就销毁了,

使用autorelease,使得将对象的拥有权交给了自动释放池,

只要自动释放池没有销毁,dog对象也就不会销毁。*/

return [[[Dog alloc] init] autorelease];

}

在main函数里代码如下:

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

  {

    @autoreleasepool {

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

             Dog *dog1 = [Dog dog];

             NSLog(@"count = %lu",dog.retainCount);

             NSLog(@"count1 = %lu",dog1.retainCount);

      }

  }
时间: 2024-10-13 15:10:04

OC语言--内存管理的相关文章

黑马程序员——OC的内存管理概述

内存管理在Objective-C中的重要性就像指针在C语言中的重要程序一样. 虽然作为一门高级语言,但OC却没有内存回收机制.这就需要开发者来对动态内存进行管理.OC中内存管理的范围是:任何继承了NSObjective的对象,对基本数据类型是无效的.这和它的管理原理有关. 每个对象内部都保存了一个与之关联的整数,称为引用计数器,当使用alloc.new.或copy创建一个对象时,对象的引用计数器被设置为1.给对象发送一条retain信息,可以使引用计数器的值加1,给对象发送一条release信息

OC基础 内存管理

OC基础  内存管理 我们所了解的c语言内存管理,如下: (1)c语言的内存分配:char *p = (char*)malloc(100*sizeof(char)); (2)c语言的内存释放:free(p); c语言内存管理存在的缺陷: (1)不释放就会造成内存泄露. (2)多次释放就会造成崩溃. (3)大型项目中多人对一块内存进行操作时,不能释放内存,因为不知道别人是否还在使用. oc语言的内存管理 1.内存管理 (1)引用计数. (2)内存管理的黄金法则:谁创建谁释放. 让我们来看一下测试例

简述OC中内存管理机制。

1        简述OC中内存管理机制.与retain配对使用的方法是dealloc还是release,为什么?需要与alloc配对使用的方法是dealloc还是release,为什么?readwrite,readonly,assign,retain,copy,nonatomic .atomic.strong.weak属性的作用? OC使用了一种叫做引用计数的机制来管理对象,如果对一个对象使用了alloc.[Mutable]copy,retain,那么你必须使用相应的realease或者aut

iOS开发OC基础:OC的内存管理

OC内存管理的基础知识 /** //             *  当对一个对象autorelease时,会将该对象放到离它最近的自动释放池,当自动释放池将要销毁时,会对自动释放池中的对象发送release消息,让对象的引用计数减1,(切记,是将对象的引用计数减1,而不是回收空间.) //             */ /** *  如果相对一个对象做copy操作,就必须让该类服从NSCopying协议,并且实现协议中的- (id)copyWithZone:(NSZone *)zone方法 */

OC的内存管理机制

总的来说OC有三种内存管理机制,下面将分别对这三种机制做简要的概述. 1.手动引用计数(Mannul Reference Counting-MRC) mannul:用手的,手工的. 引用计数:retaincount 从英文字面上理解就是在这种机制下,内存需要程序员去手动管理,即通过在代码中调用-retain.-release或者-autorelease去增加和减小对象的引用计数,当引用计数为0时,对象会自动调用-dealloc方法释放所占用的内存. 2.垃圾回收机制(Garbage Collec

(Object-C)学习笔记(三) --OC的内存管理、封装、继承和多态

OC的内存管理 iOS7以前使用的是MRC手动内存管理,现在都使用ARC自动内存管理,一般不会出现内存泄漏问题. 封装 封装就是有选择的保护自己的代码.将给别人使用的接口留出来让人看见,其他的都隐藏起来.增加了代码的可读性.可维护性.可拓展性. 将给别人看的代码放在 interface当中(.h or .m),不让看的放在implementation当中.这就是封装对象. 继承 子类可以继承父类非私有的属性和方法. 继承的优点:1.代码复用 2.制定规则 3.为了多态 继承的缺点:1.提高了代码

简述OC中内存管理机制

1.OC的内存管理机制是引用计数, 内存管理原则是谁开辟谁释放, 有retain就要有release. 2. 分为ARC(自动引用计数)和MRC(非自动引用计数), 在MRC下, 我们需要手动管理内存,需要使用到retain/copy/release/autorelease等方法实现内存管理, ARC下一般不需要程序员手动管理内存, 系统会为程序添加自动释放池以实现内存管理, 当然, 咱们说的一般不需要不能理解为完全不需要考虑内存问题, 比如在解决block循环引用问题的时候, 就需要使用__w

谈谈OC的内存管理 (2013-01-08 09:28:14)

苹果的内存有限,为了更好的用户体验,需要手动管理内存.从网上copy,也 一 基本原理 Objective-C的内存管理机制与Java那种全自动的垃圾回收机制是不同的,它本质上还是C语言中的手动管理方式,只不过稍加了一些自动方法. 1,OC采用了引用计数(retain count)对对象内存进行管理,例如,某个对象alloc了,那么这个对象的引用计数就加1,retain时加1,当对象不需要用时就需要销毁对象,释放内存,需要对象调用release方法,release会让引用计数减1,只有引用计数消

iOS核心语言Objective C语言 —— 内存管理

本分享是面向有意向从事iOS开发的伙伴以及苹果产品的发烧友们,或者已经从事了iOS的开发者,想进一步提升者.如果您对iOS开发有极高的兴趣,可以与我一起探讨iOS开发,一起学习,共同进步.如果您是零基础,建议您先翻阅我之前分享的iOS开发分分钟搞定C语言系列,然后在开始Objective C语言的学习,如果您遇到问题也可以与我探讨,另外将无偿分享自己整理出来的大概400G iOS学习视频及学习资料,都是干货哦!可以新浪微博私信?关注极客James,期待与您的共同学习和探讨!!由于时间有限,每天在