iOS: ARC & MRC下string内存管理策略探究

简介: ARC & MRC下string内存管理策略探究       前两天跟同事争论一个关于NSString执行copy操作以后是否会发生变化,两个人整了半天,最后写代码验证了一下,发现原来NSString操作没我们想的那么简单,下面就让我们一起看看NSString和NSMutableString在MRC下执行retain,copy,mutableCopy,以及ARC下不同的修饰__weak, __strong修饰赋值究竟发生了什么。

ARC & MRC下string内存管理策略探究 

  

  前两天跟同事争论一个关于NSString执行copy操作以后是否会发生变化,两个人整了半天,最后写代码验证了一下,发现原来NSString操作没我们想的那么简单,下面就让我们一起看看NSString和NSMutableString在MRC下执行retain,copy,mutableCopy,以及ARC下不同的修饰__weak, __strong修饰赋值究竟发生了什么。

 

一、验证代码如下:

- (void)testStringAddress
{
    int a = 0;
    int b = 0;
    
    static int c = 0;
    
    NSString *str = @"Hello World";
    
#if __has_feature(objc_arc)
    __weak   NSString *weakStr = str;
    __strong NSString *strongStr = str;
#else
    NSString *retainStr = [str retain];
#endif
    
    NSString *copyStr = [str copy];
    NSMutableString *mutableCopyStr = [str mutableCopy];
    
    // 验证mutableCopy出来的是否是mutableString,如果不是执行此行会Crash
    [mutableCopyStr appendFormat:@".."];
    str = @"i'm changed";
    
    NSString *str2 = [NSString stringWithFormat:@"Hello world"];
    
#if __has_feature(objc_arc)
    __weak   NSString *weakStr2 = str2;
    __strong NSString *strongStr2 = str2;
#else
    NSString *retainStr2 = [str2 retain];
#endif
    
    NSString *copyStr2 = [str2 copy];
    NSString *copy2Str2 = [str2 copy];
    NSString *mutableCopyStr2 = [str2 mutableCopy];
    NSString *mutableCopy2Str2 = [str mutableCopy];
    str2 = [[NSString alloc] initWithFormat:@"changed"];
    
    NSMutableString *mutableStr = [NSMutableString stringWithString:@"hello world"];
    
#if __has_feature(objc_arc)
    __weak   NSMutableString *weakMutableStr = mutableStr;
    __strong NSMutableString *strongMutableStr = mutableStr;
#else
    NSMutableString *retainMutableStr = [mutableStr retain];
#endif
    
    NSMutableString *copyMutableStr = [mutableStr copy];
    NSMutableString *copy2MutableStr = [mutableStr copy];
    NSString *mutableCopyMutableStr = [mutableStr mutableCopy];
    NSString *mutableCopy2MutableStr = [mutableStr mutableCopy];
    [mutableStr appendFormat:@" apped something"];

#if __has_feature(objc_arc)
    NSLog(@"\r str: %@,\r weakStr: %@,\r strongStr: %@,\r copyStr: %@,\r mutableCopyStr: %@", str, weakStr, strongStr, copyStr, mutableCopyStr);
    NSLog(@"\r str2: %@,\r weakStr2: %@,\r strongStr: %@,\r copyStr2: %@,\r mutableCopyStr2: %@", str2, weakStr2, strongStr2, copyStr2, mutableCopyStr2);
    NSLog(@"\r mutableStr: %@,\r weakMutableStr: %@\r strongMutableStr: %@,\r copyMutableStr: %@,\r mutableCopyMutableStr: %@", mutableStr, weakMutableStr, strongMutableStr, copyMutableStr, mutableCopyMutableStr);
#else 
    NSLog(@"\r str: %@,\r retainStr: %@,\r copyStr: %@,\r mutableCopyStr: %@", str, retainStr, copyStr, mutableCopyStr);
    NSLog(@"\r str2: %@,\r retainStr2: %@,\r copyStr2: %@,\r mutableCopyStr2: %@", str2, retainStr2, copyStr2, mutableCopyStr2);
    NSLog(@"\r mutableStr: %@,\r retainMutableStr: %@,\r copyMutableStr: %@,\r mutableCopyMutableStr: %@", mutableStr, retainMutableStr, copyMutableStr, mutableCopyMutableStr);
#endif
}
testStringAddress

  代码中最开始定义了两个int型的变量,主要是为了打印出当前函数的栈地址,顺便验证了一下内存中栈是从高地址向低地址生长的。使用预编译宏#if __has_feature(objc_arc)来检测当前是否是ARC环境,并根据不同环境写了不同的测试代码。

  通过在testStringAddress最后添加断点,在lldb命令行下通过“p”命令输出变量对应的地址,例如:查看str指向的内存地址和内容,输入"p str"即可。

  调试环境xCode6 beta4,iOS8 SDK。

 

二、MRC下执行情况

  执行结果如下图所示:

  解释一下上图的内容:

  1、第一部分

  “p &str”表示打印str这个变量本身的地址,“p str”表示打印str指向的内容的地址。如上图所示,"p &str"打印出来的结果是0xbff0aff8,局部变量b的地址为0xbff0affc,我们可以看出这两者的内存地址是相连,因为他们都是函数体内部局部变量。而“p str”打印出来的结果是0x000a7048,说明str指向的内容的存储区域和函数局部变量不在一起。

  2、第二部分

  c是我在函数中定义的一个static变量,“p &c”打印出来的地址为0x000a778c,观察可知变量c和函数变量也不在一起。static变量在程序运行过程中只会有一个,位于程序的静态变量区。

  3、第三部分

  str的定义为“NSString *str = @"Hello World";”,通过调试发现str指向的内容为0x000a7048,且此时对str执行的retain和copy操作得到的结果地址均为0x000a7028。而执行mutableCopy后得到的NSString的地址为0x7974a110,和str以及retain,copy操作得到的地址不在一起。

  总结:形如@“Hello World”形式的变量在程序的常量区,而NSString在针对常量区的对象首次执行retain和copy时创建新对象,之后执行同类操作则发挥之前创建的对象,mutableCopy操作则在其他地方创建了一个对象,并使用str完成了初始化操作。

  4、第四部分

  str2定义为“NSString *str2 = [NSString stringWithFormat:@"Hello world"];”,打印结果发现str2指向的内容的地址为0x79749000执行retain操作得到的string的指向内容的地址为0x7974a5c0,copy操作得到的地址也是0x7974a5c0,测试发现之后再对str2执行copy操作均会得到相同的地址。这块儿貌似跟我们平时常念的“retain增加引用技术,copy创建新的对象”的观念不符。对str2执行mutableCopy得到的地址为0x7974a380,重新创建了一个对象,符合我们的预期。

  总结:使用stringWithFormat方式创建的NSString对象默认在堆上,对这一类对象首次执行retain或copy操作时会创建一份拷贝,后续所有的retain和copy均会指向之前创建的同一个拷贝,无论何时执行mutableCopy操作均会创建新的对象。

  5、第五部分

  mutableStr的定义为“[NSMutableString stringWithString:@"hello world"];”,打印结果发现对mutableStr执行retain操作得到对象的地址和mutableStr相同,执行copy操作会创建新的对象,执行mutableCopy操作也会创建新的对象。

  总结:使用stringWithString方式创建的NSMutableString对象默认在堆上,对NSMutableString执行retain时不会创建新对象,执行copy和mutableCopy均会创建新的对象。

  观察以上对象地址,大致可以分为四个区间0x000a70xx0x000a78xx0x7974xxxx0xbff5xxxx,其实它们分别依次代表四个不同的内存段常量区,静态变量区,堆区,栈区。

 

三、ARC下执行情况

  执行结果如下图:

  分析方法和第一部分一样,这里就不重复了。

  总结:

  1、针对常量区的NSString对象,执行weak,strong赋值活着copy操作只会生成一份拷贝,每次执行mutableCopy时均会创建新的对象。

  2、针对堆上的NSString对象执行weak,strong,copy操作时只会创建一份拷贝,后续所有操作均得到相同的对象,每次执行mutableCopy时均会创建新的对象。

  3、针对NSMutableString对象首次执行weak,strong操作只会创建一份拷贝,后续所有操作均得到相同对象,每次执行copy和mutableCopy操作均会创建新的对象。

 

四、总结

  综上ARC和MRC下NSString,NSMutableString执行retain,copy,mutableCopy,weak,strong操作时内存情况见下表格:

 

注:smileEvday保留本文的一切权利

  转载请著名出处,有什么问题欢迎留言

  如果觉得本文帮到了你,请推荐给身边的朋友

  


部门招人: 高级iOS、Android、前端开发,有意私聊,博主请你喝️
如果觉得本文帮到了你,记得点赞哦,当然也可以请博主喝一杯豆浆
o_IMG_2722.PNG o_qq.PNG
微信二维码 QQ二维码
目录
相关文章
|
26天前
|
编解码 算法 Java
构建高效的Android应用:内存优化策略详解
随着智能手机在日常生活和工作中的普及,用户对移动应用的性能要求越来越高。特别是对于Android开发者来说,理解并实践内存优化是提升应用程序性能的关键步骤。本文将深入探讨针对Android平台的内存管理机制,并提供一系列实用的内存优化技巧,以帮助开发者减少内存消耗,避免常见的内存泄漏问题,并确保应用的流畅运行。
|
2天前
|
移动开发 Android开发 开发者
构建高效Android应用:采用Kotlin进行内存优化的策略
【4月更文挑战第18天】 在移动开发领域,性能优化一直是开发者关注的焦点。特别是对于Android应用而言,由于设备和版本的多样性,确保应用流畅运行且占用资源少是一大挑战。本文将探讨使用Kotlin语言开发Android应用时,如何通过内存优化来提升应用性能。我们将从减少不必要的对象创建、合理使用数据结构、避免内存泄漏等方面入手,提供实用的代码示例和最佳实践,帮助开发者构建更加高效的Android应用。
5 0
|
4天前
|
缓存 移动开发 Java
构建高效的Android应用:内存优化策略
【4月更文挑战第16天】 在移动开发领域,尤其是针对资源有限的Android设备,内存优化是提升应用性能和用户体验的关键因素。本文将深入探讨Android应用的内存管理机制,分析常见的内存泄漏问题,并提出一系列实用的内存优化技巧。通过这些策略的实施,开发者可以显著减少应用的内存占用,避免不必要的后台服务,以及提高垃圾回收效率,从而延长设备的电池寿命并确保应用的流畅运行。
|
8天前
|
算法 安全 Java
内存分配与回收策略
内存分配与回收策略
15 0
内存分配与回收策略
|
11天前
|
NoSQL 安全 Redis
redis内存限制与淘汰策略
Redis内存管理包括限制和淘汰策略。`maxmemory`配置参数决定内存上限,无设置时64位系统默认不限制,可能导致系统资源耗尽,生产环境建议设定合理值。当内存满时,未设置淘汰策略会导致写入错误。Redis提供8种淘汰策略,如LRU(最近最少使用)和LFU(最不经常使用),以及随机或基于过期时间的删除。需根据数据重要性、访问频率和一致性选择合适策略。
13 0
|
18天前
|
存储 缓存 NoSQL
Redis的内存淘汰策略是什么?
【4月更文挑战第2天】Redis内存淘汰策略在内存满时,通过删除旧数据为新数据腾空间。策略包括:volatile-lru/LFU(基于LRU/LFU算法淘汰有过期时间的键),volatile-random/ttl(随机/按TTL淘汰),allkeys-lru/LFU(所有键的LRU/LFU),allkeys-random(随机淘汰所有键),以及noeviction(不淘汰,返回错误)。选择策略要考虑访问模式、数据重要性和性能需求。
|
23天前
|
缓存 Java Android开发
安卓应用开发中的内存优化策略
在移动应用开发领域,性能一直是衡量应用质量的重要指标之一。特别是对于安卓平台,由于设备的硬件配置多样化,内存管理成为开发者面临的重大挑战。本文将深入探讨针对安卓平台的内存优化技巧,包括内存泄漏的预防、合理使用数据结构和算法、以及高效的资源释放机制。通过这些方法,开发者可以显著提升应用的性能和用户体验。
|
30天前
|
监控 算法 Android开发
安卓应用开发中的内存优化策略
【2月更文挑战第30天】随着移动设备性能的不断提升,用户对应用程序的体验要求越来越高。在安卓应用开发中,内存管理是影响应用性能和用户体验的关键因素之一。本文将探讨针对安卓平台的内存优化技巧,包括避免内存泄漏、合理使用数据结构和算法、优化图片资源处理等策略,旨在帮助开发者提升应用性能和稳定性。
19 1
|
1月前
|
监控 Java 编译器
Go语言内存与并发性能综合优化策略
【2月更文挑战第11天】Go语言以其高效的并发处理能力和简洁的内存管理机制成为了现代软件开发中的热门选择。然而,在实际应用中,如何综合优化Go程序的内存使用和并发性能,仍然是一个值得探讨的话题。本文将深入探讨Go语言内存与并发性能的综合优化策略,包括内存布局优化、并发模式设计、资源池化以及性能监控与分析等方面,旨在帮助开发者全面提升Go程序的整体性能。
|
2月前
|
存储 缓存 Java
Go语言中的内存分配与释放策略
【2月更文挑战第5天】本文旨在深入探讨Go语言中的内存分配与释放策略,包括其背后的设计理念、内存分配器的实现细节以及内存释放的时机和方式。通过了解这些内容,读者可以更好地理解Go语言的内存管理特点,并在实际开发中更好地利用这些特性优化程序性能。