美文网首页
ios内存管理

ios内存管理

作者: 晓晓521 | 来源:发表于2017-07-27 15:36 被阅读0次

    目录:

    一、字符串(String)

    1.1、字符串的创建

    1.2、字符串的isa

    二、拷贝(copy)

    2.1、immutable对象的copy

    2.2、mutable对象的copy

    2.3、浅拷贝与深拷贝

    2.4 、单层深拷贝

    三、 集合(Collections)

    3.1、NSMapTable

    3.2、NSHashTable

    3.3、NSPointerArray

    一、字符串(String)

    看到好几篇文章都在说这道面试题,字符串差不多是每个高级语言必有的,在实际项目中也的确是使用的最多类型之一。本文就以此题开始我们的内存管理的讨论。```
    dsfdsl

    //第一个
    
    1.NSString *name1 = [NSString stringWithFormat:@"stringTestOne"];
    
    __weak NSString *name2 = name1;
    
    NSLog(@"name1:%@", name1);
    
    NSLog(@"name2:%@", name2);
    
    name1 = nil;
    
    NSLog(@"name1:%@", name1);
    
    NSLog(@"name2:%@", name2);
    
    //第二个
    
    NSString *a1 = [[NSString alloc] initWithFormat:@"stringTestTwo"];
    
    __weak NSString *a2 = a1;
    
    NSLog(@"a1:%@", a1);
    
    NSLog(@"a2:%@", a2);
    
    a1 = nil;
    
    NSLog(@"a1:%@", a1);
    
    NSLog(@"a2:%@", a2);
    
    //第三个
    
    NSString *b1 = @"stringTestThree";
    
    __weak NSString *b2 = b1;
    
    NSLog(@"b1:%@", b1);
    
    NSLog(@"b2:%@", b2);
    
    b1 = nil;
    
    NSLog(@"b1:%@", b1);
    
    NSLog(@"b2:%@", b2);
    
    打印结果:
    
    name1:stringTestOne
    
    name2:stringTestOne
    
    name1:(null)
    
    name2:stringTestOne
    
    a1:stringTestTwo
    
    a2:stringTestTwo
    
    a1:(null)
    
    a2:(null)
    
    b1:stringTestThree
    
    b2:stringTestThree
    
    b1:(null)
    
    b2:stringTestThree
    

    1.1、字符串的创建

     NSString *string1 = [NSString stringWithFormat:@"TestString1_BAXIANG"];
        NSLog(@"%p",string1);
        NSString *string2 = [[NSString alloc] initWithFormat:@"TestString2_BAXIANG"];
        NSLog(@"%p",string2);
        NSString *string3 = @"TestString3_BAXIANG";
        NSLog(@"%p",string3);
    打印结果:
     0x7fb6e7d58a40
     0x7fb6e7d1c3a0
     0x10e6a7280
    

    (1) 关于stringWithFormat和initWithFormat的区别如果同学是从MRC开发者一路过来的话理解这个很简单,但是ARC已经彻底主导了如今的开发,对引用计数这个概念不需要理解那么苛刻,stringWithFormat实际上创建的是一个加入自动释放池的 (autoreleased)的对象,主要目的就是延迟释放,而initWithFormat的对象就需要遵循我们常唠叨的内存管理黄金法则 谁创建谁释放。也就是MRC中的release。

    通过po _objc_autoreleasePoolPrint()打印当前自动释放池的中对象(Autorelease pools),刚才我们通过stringWithFormat创建的字符串对象0x7fa65a50fdc0 在当前释放池中。

    objc[24294]: ##############
    objc[24294]: AUTORELEASE POOLS for thread 0x10e6533c0
    objc[24294]: 798 releases pending.
    objc[24294]: [0x7fa65b800000]  ................  PAGE (full)  (cold)
    objc[24294]: [0x7fa65b800038]  ################  POOL 0x7fa65b800038
    objc[24294]: [0x7fa65b800040]    0x7fa65a702820  __NSCFString
    objc[24294]: [0x7fa65b800048]  ################  POOL 0x7fa65b800048
    ........
    // 
    objc[24294]: [0x7fa65b010958]    0x7fa65a50fdc0  __NSCFString
    

    所以第一个字符name1 = nil 只是去掉了对当前0x7fa65a50fdc0 字符的指针。name2还是指向了0x7fa65a50fdc0字符串,所以name2还可以打印出当前的字符数据。而关于通过打印内存地址会发现字符串3(0x10e6a7280)会明显小于上面二者,因为它是创建在字符串常量区的,而我们的第一二字符串是创建在堆区的。所以b2是照样可以打印出字符串的。但是如果我们修改第二个字符串内容为string,在64位的苹果设备上调试,打印的结果变成跟我们的第一个和第二个字符串结果一样的:

      NSString *a1 = [[NSString alloc] initWithFormat:@"string"];
        __weak NSString *a2 = a1;
        NSLog(@"a1:%@", a1);
        NSLog(@"a2:%@", a2);
        a1 = nil;
        NSLog(@"a1:%@", a1);
        NSLog(@"a2:%@", a2);
    打印结果:
    a1:string
    a2:string
    a1:(null)
    a2:string
    
    143845-ecbf28371592578a.png 143845-f3b6612ed88f7eab.png

    我们只是缩短了字符串的长度,当前的字符串的类就变了 ,更让人奇怪的是字符缩短后的对象没有isa是空。也就是当前字符串对象没有类。这就涉及到apple的Tagged Pointer。

    1.2字符串的isa

    (1)NSTaggedPointerString

    NSTaggedPointerString 用指针地址的富余位存储当前变量值,若对象指针的最低有效位为1(即奇数),则该指针为Tagged Pointer。这种指针不通过解引用isa来获取其所属类,而是通过接下来三位的一个类表的索引。该索引是用来查找所属类是采用Tagged Pointer的哪个类。剩下的60位则存储数据。对于NSNumber,小于2^60-1的整数就都采用Tagged Pointer来存储,对于字符串来说所需内存小于60位的,它可以创建一个Tagged Pointer,所以NSTaggedPointerString是一个伪装的对象,里面存储的不是指针地址而是字符串值,这样不需要一次真正对象的内存分配,不需要一次间接取值。同时引用计数可以是空指令,因为没有内存需要释放,所以会对性能有显著的提升。

    (2)__NSCFConstantString

    字符串常量,是一种编译时常量,它的 retainCount 值很大,在控制台打印出的数值则是 18446744073709551615==2^64-1,测试证明,即便对其进行 release 操作,retainCount 也不会产生任何变化。相同内容的 NSCFConstantString 对象的地址相同,也就是说常量字符串对象是一种单例。这种对象一般通过字面值 @"..."、CFSTR("...") 或者 stringWithString: 方法(需要说明的是,这个方法在 iOS6 SDK 中已经被称为redundant,使用这个方法会产生一条编译器警告。这个方法等同于字面值创建的方法)产生。这种对象存储在字符串常量区。

    (3)NSCFString

    对象被存储在堆上。 __NSCFString 对象是在运行时创建的一种 NSString 子类,他并不是一种字符串常量。所以和其他的对象一样在被创建时获得了 1 的引用计数。通过 NSString 的 stringWithFormat 等方法创建的 NSString 对象一般都是这种类型。

    相关文章

      网友评论

          本文标题:ios内存管理

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