iOS之内存管理

  1. OC知识--彻底理解内存管理(MRC、ARC)
    有时间的话,看完上面OC部分就差不多了。当引用计数为0时,对象被释放。堆和栈的概念也是要有大概的认识,可以这么认为堆是程序员需要管理的内存,栈是系统管理的内存。如果申请了内存,没有释放,就会造成浪费,造成内存不足,由于内存有限,所以需要管理
  • weak、strong、assign的持有情况,weak的对象被释放,会自动置为nil,strong则是强引用,对象不会被释放,而assign的对象被释放,造成野指针错误。

  • 文件ARC的开启与关闭
    打开ARC:-fobjc-arc
    关闭ARC:-fno-objc-arc

  • __block的作用

    1. 在MRC时代有两个作用:
      说明变量可改
      说明指针指向的对象不做这个隐式的retain操作
    2. 在ARC时代只有第一个作用
  • 在block内如何修改block外部变量?个人觉得比较好的是# 谈Objective-C block的实现其中的一幅图说明了这个问题,__block在ARC中,会让其被允许修改,复制其引用地址来实现访问的。而没有用__block,如果是基础类型的话,只是复制其数据到其数据结构。引用文中的一幅图就是如下:

    iOS之内存管理_第1张图片
    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的总结,也没有很多。

    1. 谁创建谁释放,这个应该不用多说
    2. 谁retain谁释放,这个最多的情况是两种,一种是作为属性被retain,那么需要在delloc的方法里释放。另外一种是赋值的时候,当赋的对象和原来不一样的时候,才需要释放release原来的对象,并持有retain将要设置的对象。
    3. 调用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
  1. 内存管理当然少不了c语言的部分,malloc()和free()的原理
  • 这里用的都是大白话,详细的看上面就够了。malloc会根据参数申请内存,并返回该内存的指针。当然实际申请的内存会更大一些,申请的内存,实际上是记录信息(记录信息包含大小和是否可用的两个数据。)+可用的内存。为什么会是这样呢?先理解一下free的原理,它到底是怎么释放的呢?实际假设我们malloc得到的指针是p的话,那么p就是可用内存,前面还有记录信息。free的过程是很简单的,就是p减去记录信息这个结构体的大小,就获取了记录信息这个结构体,然后让记录信息的是否可用标识修改为可用。所以分配的时候,就是找到可用内存,然后设置记录信息,并返回可用内存。
  • 当然这也是从浅层去看的,底层原理可能还会更加复杂,但是我们暂时用不到那么深的原理。

你可能感兴趣的:(iOS之内存管理)