一,概念
内存分布
- 堆:一般由程序员分配释放,若程序员不释放,则可能会引起内存泄漏。其类似于链表。
- 栈:由编译器自动分配释放,存放函数的参数值,局部变量等值。其操作方式类似于数据结构中的栈。
- 全局区(静态区)(static):全局变量和静态变量的存储是放在一块的,初始化的 全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后由系统释放。
- 文字常量区:常量字符串就是放在这里的。 程序结束后由系统释放。
- 程序代码区:存放函数体的二进制代码。
内存管理原理
OC语言使用引用计数
来管理内存,每个对象都有一个可以递增和递减的计数器。如果有其他对象持用该对象的话,那该对象就递增其引用计数;用完以后就递减其计数,当引用计数为0时,就销毁该对象。
一,weak对象内存管理
1,释放机制
在对象dealloc的时候,会将weak属性的值设置为nil,从而销毁整个对象,回收内存空间,
2.如何实现
Runtime维护了一个weak表,用于存储指向某个对象的所有weak指针,对于 weak 对象会放入一个 hash 表中,Key是所指对象的地址,Value是weak指针的地址
(这个地址的值是所指对象的地址)数组。 当此对象的引用计数为0的时候会 dealloc,假如 weak 指向的对象内存地址是a,那么就会以a为键, 在这个 weak 表中搜索,找到所有以a为键的 weak 对象,从而设置为 nil。
注:由于可能多个weak指针指向同一个对象,所以value为一个数组
weak 的实现原理可以概括以下三步:
1)初始化时:runtime会调用objc_initWeak函数,初始化一个新的weak指针指向对象的地址。
我们以下面这行代码为例:
{
id __weak obj1 = obj;
}
当我们初始化一个weak变量时,runtime会调用objc_initWeak函数。这个函数在Clang中的声明如下:
id objc_initWeak(id *object, id value)
{
*object = 0;
return objc_storeWeak(object, value);
}
示例代码轮换成编译器的模拟代码如下
id obj1;
objc_initWeak(&obj1, obj);
因此,这里所做的事是先将obj1初始化为0(nil),然后将obj1的地址及obj作为参数传递给objc_storeWeak函数。
objc_initWeak函数有一个前提条件:就是object必须是一个没有被注册为__weak对象的有效指针。而value则可以是null,或者指向一个有效的对象
2)添加引用时:objc_initWeak函数会调用 objc_storeWeak() 函数。
objc_storeWeak() 的作用是更新指针指向,创建对应的弱引用表。
3)释放时,调用clearDeallocating函数。
clearDeallocating函数首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。
二,NSString的内存分配
NSString内存分为两种类型:
- __NSCFConstantString(常量区)
- __NSCFString(堆区)、NSTaggedPointerString(堆区)
2、两种内存类型的创建时机。
生成一个NSString类型的字符串有三种方法:
- 方法1.直接赋值:
NSString *a = @"str";
- 方法2.类函数初始化生成:
NSString *str2 = [NSString stringWithString:@"my string"];
- 方法3.实例方法初始化生成:
NSString *str3 = [[NSString alloc] initWithString:@"my string"];
NSString *str4 = [[NSString alloc]initWithFormat:@"my string"];
1)对于__NSCFConstantString
这种类型的字符串是常量字符串。该类型的字符串以字面量的方式创建,保存在字符串常量区,是在编译时创建的。
NSString *a = @"str";
NSString *b = [[NSString alloc]init];
NSLog(@"%@ : class = %@",a,NSStringFromClass([a class]));
NSLog(@"%@ : class = %@",b,NSStringFromClass([b class]));
2020-08-19 10:51:38.043088+0800 内存管理[1471:73994] str : class = __NSCFConstantString
2020-08-19 10:51:38.043255+0800 内存管理[1471:73994] : class = __NSCFConstantString
2)对于__NSCFString和NSTaggedPointerString
- __NSCFString 表示对象类型的字符串,在运行时创建,保存在堆区,初始引用计数为1,其内存管理方式就是对象的内存管理方式。
- NSTaggedPointerString是对__NSCFString类型的一种优化,在运行创建字符串时,会对字符串内容及长度作判断,若内容由ASCII字符构成且长度较小(具体要多小暂时不太清楚),这时候创建的字符串类型就是 NSTaggedPointerString
对于不可以变NSString的测试结果:
NSString *e = [[NSString alloc]initWithFormat:@"str"];
NSString *f = [NSString stringWithFormat:@"str"];
NSString *g = [NSString stringWithFormat:@"123456789"];
NSString *h = [NSString stringWithFormat:@"1234567890"];
NSLog(@"%@ : class = %@",e,NSStringFromClass([e class]));
NSLog(@"%@ : class = %@",f,NSStringFromClass([f class]));
NSLog(@"%@ : class = %@",g,NSStringFromClass([g class]));
NSLog(@"%@ : class = %@",h,NSStringFromClass([h class]));
2020-08-19 11:34:51.752657+0800 内存管理[1609:98009] str : class = NSTaggedPointerString
2020-08-19 11:34:51.752796+0800 内存管理[1609:98009] str : class = NSTaggedPointerString
2020-08-19 11:34:51.752889+0800 内存管理[1609:98009] 123456789 : class = NSTaggedPointerString
2020-08-19 11:34:51.752980+0800 内存管理[1609:98009] 1234567890 : class = __NSCFString
对于可变的NSMutableString
NSMutableString *ms1 = [[NSMutableString alloc]init];
NSMutableString *ms2 = [[NSMutableString alloc]initWithString:@"str"];
NSMutableString *ms3 = [[NSMutableString alloc]initWithFormat:@"str"];
NSMutableString *ms4 = [NSMutableString stringWithFormat:@"str"];
NSMutableString *ms5 = [NSMutableString stringWithFormat:@"123456789"];
NSMutableString *ms6 = [NSMutableString stringWithFormat:@"1234567890"];
NSLog(@"%@ : class = %@",ms1,NSStringFromClass([ms1 class]));
NSLog(@"%@ : class = %@",ms2,NSStringFromClass([ms2 class]));
NSLog(@"%@ : class = %@",ms3,NSStringFromClass([ms3 class]));
NSLog(@"%@ : class = %@",ms4,NSStringFromClass([ms4 class]));
NSLog(@"%@ : class = %@",ms5,NSStringFromClass([ms5 class]));
NSLog(@"%@ : class = %@",ms6,NSStringFromClass([ms6 class]));
2020-08-19 11:37:33.258275+0800 内存管理[1627:100329] : class = __NSCFString
2020-08-19 11:37:33.258373+0800 内存管理[1627:100329] str : class = __NSCFString
2020-08-19 11:37:33.258540+0800 内存管理[1627:100329] str : class = __NSCFString
2020-08-19 11:37:33.258928+0800 内存管理[1627:100329] str : class = __NSCFString
2020-08-19 11:37:33.259329+0800 内存管理[1627:100329] 123456789 : class = __NSCFString
2020-08-19 11:37:33.259667+0800 内存管理[1627:100329] 1234567890 : class = __NSCFString
从结果我们可以看出来NSMutableString都是分配在堆区,且是__NSCFString类型,NSString中Format相关方法也是都分配在堆区,但是会根据字符串的长度,区分为__NSCFString和NSTaggedPointerString两种。在分配堆区的这些变量,其实一部分是正常的对象,一部分变成autorelease对象,具体是哪些,我们可以使用_objc_autoreleasePoolPrint()打印出来,比如实例中的g、ms4、ms5、ms6。
网友评论