美文网首页
由autorelease用NSString做例子遇到的问题

由autorelease用NSString做例子遇到的问题

作者: 程序狗 | 来源:发表于2017-12-26 11:29 被阅读26次

    我们知道一个对象在用__weak修饰后,创建之后在当前作用域结束之后会被置为nil,就如以下

    __weak id reference = nil;
    
    - (void)viewWillAppear:(BOOL)animated
    {
        [super viewWillAppear:animated];
        NSLog(@"string: %@",reference);
    }
    
    - (void)viewDidAppear:(BOOL)animated
    {
        [super viewDidAppear:animated];
        NSLog(@"string: %@",reference);
    }
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        NSObject *obj = [[NSObject alloc]init];
        reference = obj;
         NSLog(@"string: %@",reference);
    }
    
    

    打印结果很明显是

    2017-12-25 17:47:48.336248+0800 chanbendong[5975:2288145] string: <NSObject: 0x608000006d10>
    2017-12-25 17:47:48.336486+0800 chanbendong[5975:2288145] string: (null)
    2017-12-25 17:47:48.339677+0800 chanbendong[5975:2288145] string: (null)
    

    但是如果我们换成NSString,得到的结果会是怎样的呢?

    __weak id reference = nil;
    
    - (void)viewWillAppear:(BOOL)animated
    {
        [super viewWillAppear:animated];
        NSLog(@"string: %@",reference);
    }
    
    - (void)viewDidAppear:(BOOL)animated
    {
        [super viewDidAppear:animated];
        NSLog(@"string: %@",reference);
    }
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        NSString *obj = @"chanbendong";
        reference = obj;
         NSLog(@"string: %@",reference);
    }
    

    得到的结果还是一样吗?
    结果是

    2017-12-25 17:51:10.090734+0800 chanbendong[6029:2315646] string: chanbendong
    2017-12-25 17:51:10.090963+0800 chanbendong[6029:2315646] string: chanbendong
    2017-12-25 17:51:10.094109+0800 chanbendong[6029:2315646] string: chanbendong
    

    结果是NSString一直还在,这个原因是NSString初始化的时候放在常量区,所以没有释放

    我们继续看这个NSString
    我们在赋值的时候打个断点


    image.png

    利用lldb命令watchpoint set variable obj 来观察,可以看到obj由0x0000000000000000变成0x0000000108aa49a8
    然后点击继续程序,会发现obj变成0x0000000000000000,控制台就打印了一次obj的值,也就是说viewWillAppear还没执行


    image.png

    这里定义一个log语句

    #define log(_var) ({ NSString *name = @#_var;NSLog(@"%@: %@ -> %p: %@",name,[_var class],_var, _var);})
    

    看以下代码

    - (void)viewDidLoad {
        [super viewDidLoad];
        NSString *str = @"123456789";
        NSString *bstr = [NSString stringWithFormat:@"123456789"];
        NSString *cstr = [str mutableCopy];
        NSNumber *xnum = [NSNumber numberWithInteger:8];
        reference = str;
        self.num = xnum;
        log(str);
        log(bstr);
        log(cstr);
    }
    

    打印如下

    2017-12-26 10:39:11.285394+0800 chanbendong[1741:94708] str: __NSCFConstantString -> 0x100f949a8: 123456789
    2017-12-26 10:39:11.285567+0800 chanbendong[1741:94708] bstr: NSTaggedPointerString -> 0xa1ea1f72bb30ab19: 123456789
    2017-12-26 10:39:11.285646+0800 chanbendong[1741:94708] cstr: __NSCFString -> 0x6040002456a0: 123456789
    2017-12-26 10:39:11.285789+0800 chanbendong[1741:94708] string: (null)
    2017-12-26 10:39:11.289097+0800 chanbendong[1741:94708] string: (null)
    

    这里可以看到有三种String,其中reference指向了CFString,所以为null

    这里来看这三种String
    ·NSCFConstantString:字符串常量,放在常量区,对它retain或者release都没有用,程序结束后释放,所以在viewDidLoad结束之后还能打印出来,其中指针str也是一个对象储存在栈中,由系统释放
    · NSTaggedPointerString:Tagged Point,标签指针,苹果在64位环境下对NSString和NSNumber做了优化,把对象的内容放在了指针里面,这样就不需要在堆内存里面开辟一块空间存放对象了,一般是用来优化长度较小的内容。
    举个例子

     NSNumber *n1 = @1;
        NSNumber *n2 = @2;
        NSNumber *n3 = @3;
        NSNumber *nf = @(0xFFFF);
        NSLog(@"n1 : %p",n1);
        NSLog(@"n2 : %p",n2);
        NSLog(@"n3 : %p",n3);
        NSLog(@"nf : %p",nf);
    

    打印结果如下

    2017-12-26 10:53:13.235424+0800 chanbendong[2002:141469] n1 : 0xb000000000000012
    2017-12-26 10:53:13.235561+0800 chanbendong[2002:141469] n2 : 0xb000000000000022
    2017-12-26 10:53:13.235693+0800 chanbendong[2002:141469] n3 : 0xb000000000000032
    2017-12-26 10:53:13.235784+0800 chanbendong[2002:141469] nf : 0xb0000000000ffff2
    

    可以看到除去最后的数字末尾的2和最开头的0xb,其他数字正好表示了NSNumber的值
    这方便不是本文重点,感兴趣可以去看唐巧的博客,例子也是选取唐巧博客里面的深入理解Tagged Pointer

    对于NSString,如果不是由字面量直接赋值(即[NSString alloc]initWithString:或者@"")英文字母字符串小于等于9的时候会自动成为NSTaggedPointerString类型,即bstr再加一位或者有中文则变成NSCFString,而NSTaggedPointerString也是不会释放的,它的内容就在本身的指针里,所以即使把reference指向它,在viewWillAppear和viewDidAppear也是能打印出来的

    ·NSCFString:这种String就类似普通的NS对象了,储存在堆上,有正常的引用计数,需要程序员分配和释放。所以在viewWillAppear和viewDidAppear为null。

    然而

     NSString *bstr = [NSString stringWithFormat:@"12345678900"];
    reference = bstr;
    

    在viewWillAppear和viewDidAppear打印结果却是

    2017-12-26 11:07:38.277489+0800 chanbendong[2197:197549] string: 12345678900
    2017-12-26 11:07:38.280625+0800 chanbendong[2197:197549] string: (null)
    

    根据黑幕背后的autorelease里面说viewDidLoad和viewWillAppear是在同一个runloop调用的,如果是这样的话cstr应该也能打印出来,普通的NSObject对象应该也能打印出来。

    说到底其实不是runloop的问题,问题是stringWithFormat这个工厂方法上。

    以 alloc, copy, init,mutableCopy和new这些方法打头的方法,返回的都是 retained return value,例如[[NSString alloc] initWithFormat:],而其他的则是unretained return value,例如 [NSString stringWithFormat:]。对于前者调用者是要负责释放的,对于后者就不需要了。而且对于后者ARC会把对象的生命周期延长,确保调用者能拿到并且使用这个返回值,但是并不一定会使用 autorelease,在worst case 的情况下才可能会使用,因此调用者不能假设返回值真的就在 autorelease pool中。从性能的角度,这种做法也是可以理解的。如果我们能够知道一个对象的生命周期最长应该有多长,也就没有必要使用 autorelease 了,直接使用 release 就可以。如果很多对象都使用 autorelease 的话,也会导致整个 pool 在 drain 的时候性能下降。

    我们可以看看array的方法

     NSObject *object = [NSObject new];
        NSArray *arr = @[object];//1
    //    NSArray *arr = [NSArray arrayWithObjects:object, nil];//2
        reference = arr;
    

    1打印如下

    2017-12-26 11:26:44.945028+0800 chanbendong[2354:251369] string: (null)
    2017-12-26 11:26:44.948504+0800 chanbendong[2354:251369] string: (null)
    
    

    2打印如下

    2017-12-26 11:27:30.558241+0800 chanbendong[2387:255117] string: (
        "<NSObject: 0x60c000008050>"
    )
    2017-12-26 11:27:30.561603+0800 chanbendong[2387:255117] string: (null)
    

    可以看到通过工厂方法创建的array生命周期变长了。

    总结
    ·方法里的临时变量是会通过autoreleasepool释放的
    ·NSCFString跟普通对象是一样的
    ·NSString和NSArray,NSDictionary的工厂方法可以延长对象的生命周期

    相关文章

      网友评论

          本文标题:由autorelease用NSString做例子遇到的问题

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