iOS之内存管理
2018-03-04 本文已影响45人
RasonWu
-
OC知识--彻底理解内存管理(MRC、ARC)
有时间的话,看完上面OC部分就差不多了。当引用计数为0时,对象被释放。堆和栈的概念也是要有大概的认识,可以这么认为堆是程序员需要管理的内存,栈是系统管理的内存。如果申请了内存,没有释放,就会造成浪费,造成内存不足,由于内存有限,所以需要管理堆
。
-
weak、strong、assign的持有情况,weak的对象被释放,会自动置为nil,strong则是强引用,对象不会被释放,而assign的对象被释放,造成野指针错误。
-
文件ARC的开启与关闭
打开ARC:-fobjc-arc
关闭ARC:-fno-objc-arc -
__block的作用
- 在MRC时代有两个作用:
说明变量可改
说明指针指向的对象不做这个隐式的retain操作 - 在ARC时代只有第一个作用
- 在MRC时代有两个作用:
-
在block内如何修改block外部变量?个人觉得比较好的是# 谈Objective-C block的实现其中的一幅图说明了这个问题,__block在ARC中,会让其被允许修改,复制其引用地址来实现访问的。而没有用__block,如果是基础类型的话,只是复制其数据到其数据结构。引用文中的一幅图就是如下:
image.png
-
Objective-C类型的对象和Core Foundation类型的对象的相互转换
- __bridge 改变指针的索引,在Objective-C和Core Foundation之间,但不改变所有权
- __bridge_retained或者CFBridgingRetain将Objective-C指针类型转变为Core Foundation指针类型,并且改变所有权,必须调用CFRelease来释放
- __bridge_transfer或者CFBridgingRelease将一个非non-Objective-C指针转变为Objective-C指针类型,并且改变所有权,启用ARC,即不需要自己去Release。
-
ARC下有种会占用大量内存的情况,需要注意
for (int i=0; i<100000; i++) {
@autoreleasepool{\\如果obj在后面没有再使用,要及时添加autoreleasepool可以及时减少内存的占用
NSMutableString *obj = [NSMutableString stringWithFormat:@"sdfkjlas;dfj%i",i];
}
}
- ARC下打印引用计数retainCount方法如下:
-
CFGetRetainCount((__bridge CFTypeRef)(obj))
- ARC下有个地方是需要注意的error被提前释放了,因为NSError **会默认添加__autoreleasing
- (void)loopThroughDictionary:(NSDictionary *)dict error:(NSError **)error
{
[dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop){
@autoreleasepool // 被隐式创建
{
if (there is some error && error != nil)
{
*error = [NSError errorWithDomain:@"MyError" code:1 userInfo:nil];
}
}
}];
// *error 在这里已经被dict的做枚举遍历时创建的autorelease pool释放掉了 :(
}
-
MRC的总结,也没有很多。
- 谁创建谁释放,这个应该不用多说
- 谁retain谁释放,这个最多的情况是两种,一种是作为属性被retain,那么需要在delloc的方法里释放。另外一种是赋值的时候,当赋的对象和原来不一样的时候,才需要释放release原来的对象,并持有retain将要设置的对象。
- 调用autorelease会将对象放到自动释放池autoreleasepool,在autoreleasepool的结束时会释放对象。
-
最后需要注意的是字符串如果是常量的话,引用计数是无限大,根据CFGetRetainCount可以得知是LONG_MAX,其中str9是没有打印的,因为野指针崩了,实例如下:
__weak NSString *str1 = @"my string33333sdfssdstring22222f";
__weak NSString *str2 = [NSString stringWithString:@"my string22222ststring22222ring22222"];
__weak NSString *str3 = [[NSString alloc] initWithString:@"my strinstring22222string22222g22222"];
NSString *str4 = [[NSString alloc]initWithFormat:@"my stri"];
NSMutableString *str5 = [NSMutableString stringWithString:@"my string"];
NSMutableString *str6 = [[NSMutableString alloc] initWithString:@"my string"];
NSMutableString *str7 = [[NSMutableString alloc]initWithFormat:@"my string"];
NSLog(@"str1:%p----%ld--@\"my string\"",str1,[self getRetainCount:str1]);
NSLog(@"str2:%p----%ld--stringWithString",str2,[self getRetainCount:str2]);
NSLog(@"str3:%p----%ld--initWithString",str3,[self getRetainCount:str3]);
NSLog(@"str4:%p----%ld--initWithFormat",str4,[self getRetainCount:str4]);
NSLog(@"str5:%p----%ld--stringWithString",str5,[self getRetainCount:str5]);
NSLog(@"str6:%p----%ld--initWithString",str6,[self getRetainCount:str6]);
NSLog(@"str7:%p----%ld--initWithFormat",str7,[self getRetainCount:str7]);
//跟str4做对照,NSString的initWithFormat会根据字符串长短,来判断是否配置无限大的引用计数
NSString *str8 = [[NSString alloc]initWithFormat:@"my stri234sdfafasdfasadfjklj;lkjsdf"];
NSLog(@"str8:%p----%ld--initWithFormat",str8,[self getRetainCount:str8]);
//马上被释放,会崩溃
__weak NSString *str9 = [[NSString alloc]initWithFormat:@"my stri2ssssssssssssss"];
NSLog(@"str9:%p----%ld--initWithFormat",str9,[self getRetainCount:str9]);
}
下面是结果,引用计数居然出现1152921504606846975其实就是LONG_MAX的,但是就是这样的。常量不符合ARC的一般规则,是无限大,并且引用计数不受retain和release的影响。
2018-03-03 18:22:10.290594+0800 RSRuntimeMethodDemo[4021:479475] str1:0x1026bb1d8----1152921504606846975--@"my string"
2018-03-03 18:22:10.290754+0800 RSRuntimeMethodDemo[4021:479475] str2:0x1026bb1f8----1152921504606846975--stringWithString
2018-03-03 18:22:10.290892+0800 RSRuntimeMethodDemo[4021:479475] str3:0x1026bb218----1152921504606846975--initWithString
2018-03-03 18:22:10.290991+0800 RSRuntimeMethodDemo[4021:479475] str4:0xa6972747320796d7----9223372036854775807--initWithFormat
2018-03-03 18:22:10.291123+0800 RSRuntimeMethodDemo[4021:479475] str5:0x604000447ef0----2--stringWithString
2018-03-03 18:22:10.291243+0800 RSRuntimeMethodDemo[4021:479475] str6:0x604000447f80----1--initWithString
2018-03-03 18:22:10.291361+0800 RSRuntimeMethodDemo[4021:479475] str7:0x604000447f50----1--initWithFormat
2018-03-03 18:22:10.291503+0800 RSRuntimeMethodDemo[4021:479475] str8:0x608000074740----1--initWithFormat
- 内存管理当然少不了c语言的部分,malloc()和free()的原理
- 这里用的都是大白话,详细的看上面就够了。malloc会根据参数申请内存,并返回该内存的指针。当然实际申请的内存会更大一些,申请的内存,实际上是记录信息(记录信息包含大小和是否可用的两个数据。)+可用的内存。为什么会是这样呢?先理解一下free的原理,它到底是怎么释放的呢?实际假设我们malloc得到的指针是p的话,那么p就是可用内存,前面还有记录信息。free的过程是很简单的,就是p减去记录信息这个结构体的大小,就获取了记录信息这个结构体,然后让记录信息的是否可用标识修改为可用。所以分配的时候,就是找到可用内存,然后设置记录信息,并返回可用内存。
- 当然这也是从浅层去看的,底层原理可能还会更加复杂,但是我们暂时用不到那么深的原理。