美文网首页
iOS之内存管理

iOS之内存管理

作者: RasonWu | 来源:发表于2018-03-04 20:11 被阅读45次
    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,如果是基础类型的话,只是复制其数据到其数据结构。引用文中的一幅图就是如下:

      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之内存管理

          本文链接:https://www.haomeiwen.com/subject/crnoxftx.html