Objective-C中内存管理的一些特例

发布时间:2020-06-25 07:30:57 作者:hailinan
来源:网络 阅读:678

 

     该教程是讨论IOS平台上内存管理规则之外的一些特殊情况,我相信大部分的开发人员可能都没有觉察到。

    我们先普及一下Objectivie-C中的内存管理的基本知识,如果你已经比较熟悉了,可以直接跳过该节。Objective-C使用的是引用计数(Reference Counting),引用计数就是对象用一个变量来保存有几个地方(类、方法等)在使用它。当一个对象被创造出来时,它的引用计数(下面我们用retainCount来表示这个值)为1,在应用程序运行的过程中,可能有很多地方都用到了这个对象,凡是用到这个对象时,就将它的retainCount加1,当不用了时,再将其retainCount减1,当对象的retainCount为0时,表示没有人在用这个对象了,系统就会释放这个对象所占用的内存。

在Objective-C中,关于基于引用计数的内存管理,其实你只需要掌握三条最基本的规则:
    只要你理解了这三条规则基本就足够了。但是要理解下面所讲的一些例外情况,仅有这些知识就不够了。下面就让我们开始这段探索之旅吧。
 
使用Xcode使用“IOS”-“Application”-“Single View Application”模板创建一个名为MemoryTest的工程。在ViewController.m文件的viewDidLoad方法中键入下面的代码:
  1. NSString *emptyStr = [NSString new];  
  2. NSLog(@"emptyStr retainCount: %u", emptyStr.retainCount); 
先想想你认为输出结果是什么?然后看一下运行后Console的输出是什么?我这边的结果是4294967295,一个很大的数,实际上这个数是UINT_MAX,就是无符号整型的最大值。你原先认为它应该输出1对吗?为什么会这样呢?带着这个疑问在看下面的代码,仍旧将其放入viewDidLoad方法中:
 
  1. NSString *emptyStr1 = [NSString new]; 
  2. NSString *emptyStr2 = [NSString new]; 
  3. NSLog(@"emptyStr1 address: %p", emptyStr1); 
  4. NSLog(@"emptyStr2 address: %p", emptyStr2); 
  5. NSLog(@"emptyStr1 retainCount: %u", emptyStr1.retainCount); 
  6. NSLog(@"emptyStr2 retainCount: %u", emptyStr2.retainCount); 
这段代码创建了两个新的空字符串对象,接着输出这两个对象的在内存中的地址和它们的引用计数值。运行一下,查看一下结果,我这边的结果是:

       Objective-C中内存管理的一些特例

这两个对象的地址竟然一样,这出离我们原先的认识对吗?new方法两次创建的对象竟然一样,这在c++中是绝不可能的。但这是Objective-C,编译器在底层做了一些我们看不见的工作。很显然这两个空字符串对象指针指向的是同一个对象。Objective-C为什么会这么处理呢?这是因为NSString类型的不可变性,就是这种类型的对象一旦创建,就不能改变(增加或删除其中的字符),如果你希望改变对象,那就用NSMutableString类型。NSString的不可变性使得空字符串对象一旦创建,就不能改变,永远是空字符串,而所有的空字符串都是一样的。所以出于效率的考量,Objective-C编译器在底层就让所有创建的空字符串指向内存中的同一个空字符串。并且这个空字符串对象是不可release掉的,因此它的引用计数值就为UINT_MAX,表示这个对象是不可release的,那你可能会问,我如果release UINT_MAX次,是不是就释放掉了,不是的,实际上你向这个对象发送release消息是没有任何效果的。
 我们换非空字符串试试,输入下面的代码:
  1. NSString *nonEmptyStr1 = @"Hello"; 
  2. NSString *nonEmptyStr2 = [[NSString alloc] initWithString:@"Hello"]; 
  3. NSString *nonEmptyStr3 = [[NSString alloc] initWithFormat:@"%@", @"Hello"]; 
  4. NSLog(@"nonEmptyStr1 address: %p", nonEmptyStr1); 
  5. NSLog(@"nonEmptyStr2 address: %p", nonEmptyStr2); 
  6. NSLog(@"nonEmptyStr3 address: %p", nonEmptyStr3); 
  7. NSLog(@"nonEmptyStr1 retainCount: %u", nonEmptyStr1.retainCount); 
  8. NSLog(@"nonEmptyStr2 retainCount: %u", nonEmptyStr2.retainCount); 
  9. NSLog(@"nonEmptyStr3 retainCount: %u", nonEmptyStr3.retainCount); 
下面是在机器上运行结果:

       Objective-C中内存管理的一些特例

前两个指针仍然是指向同一个对象,原因上面已经解释了。但是第三个不一样,你可以从NSString的不可变性和对象的初始化方式的不同出发想想原因,相信你很快就可以得出结论的。
在Objective-C中不唯NSString是不可变对象,还有NSArray和NSDictionary。同样你可以试试下面的代码:
 
  1. NSArray *emptyArray1 = [[NSArray alloc] init]; 
  2. NSArray *emptyArray2 = [[NSArray alloc] init]; 
  3. NSArray *emptyArray3 = [[NSArray alloc] initWithArray:emptyArray1]; 
  4. NSLog(@"emptyArray1 address: %p", emptyArray1); 
  5. NSLog(@"emptyArray2 address: %p", emptyArray2); 
  6. NSLog(@"emptyArray3 address: %p", emptyArray3);     
  7. NSLog(@"emptyArray1 retainCount: %d", emptyArray1.retainCount); 
  8. NSLog(@"emptyArray2 retainCount: %d", emptyArray2.retainCount); 
  9. NSLog(@"emptyArray3 retainCount: %d", emptyArray3.retainCount); 
  10.  
  11. NSArray *nonEmptyArray1 = [[NSArray alloc] initWithObjects:@"1", @"2", nil]; 
  12. NSArray *nonEmptyArray2 = [[NSArray alloc] initWithObjects:@"1", @"2", nil]; 
  13. NSLog(@"nonEmptyArray1 address: %p", nonEmptyArray1); 
  14. NSLog(@"nonEmptyArray2 address: %p", nonEmptyArray2); 
  15. NSLog(@"nonEmptyArray1 retainCount: %d", nonEmptyArray1.retainCount); 
  16. NSLog(@"nonEmptyArray2 retainCount: %d", nonEmptyArray2.retainCount); 
  17.  
  18. NSDictionary *emptyDict1 = [[NSDictionary alloc] init]; 
  19. NSDictionary *emptyDict2 = [[NSDictionary alloc] init]; 
  20. NSLog(@"emptyDict1 address: %p", emptyDict1); 
  21. NSLog(@"emptyDict2 address: %p", emptyDict2); 
  22. NSLog(@"emptyDict1 retainCount: %d", emptyDict1.retainCount); 
  23. NSLog(@"emptyDict2 retainCount: %d", emptyDict2.retainCount); 
通过运行结果来进一步体会一下Objective-C编译器底层的工作机理。
按照上面提到过的三条内存管理规则,你new、init、copy得到一个对象,你就必须负责release掉它,但实际上前面提到过的这些语句是个例外:
 
  1. NSString *s1 = [NSString new]; 
  2. NSString *s2 = [NSString alloc] initWithString:@"Hello"]; 
  3. NSArray *a = [NSArray alloc] init];  
  4. NSDictionary *dict = [NSDictionary alloc] init]; 

就算你不调用release或autorelease方法释放也不会造成内存泄漏,你可以用Instruments检测一下看看是否有内存泄漏。但是虽然如此,我仍强烈建议你按照内存管理三规则来处理。一致的规则不容易让人迷惑,尤其是对阅读你代码的人。
 
本教程的工程文件:MemoryTest.zip
 
本文作者:安海林,软件工程师,诺基亚北京研究院。他的格言是:学问深时意气平!

  

附件:http://down.51cto.com/data/2360642
推荐阅读:
  1. Objective-C中的属性机制
  2. Objective-C(9)内存管理之ARC

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

objective-c iphone开发 ios开发

上一篇:Android 最火的快速开发框架XUtils

下一篇:添加或删除用户

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》