|
| 1 | +--- |
| 2 | +layout: post |
| 3 | +title: 从一道网易面试题浅谈 Tagged Pointer |
| 4 | +subtitle: 浅谈 Tagged Pointer |
| 5 | +date: 2017-12-26 |
| 6 | +author: BY |
| 7 | +header-img: img/post-bg-universe.jpg |
| 8 | +catalog: true |
| 9 | +tags: |
| 10 | + - iOS |
| 11 | +--- |
| 12 | + |
| 13 | + |
| 14 | +## 前言 |
| 15 | + |
| 16 | +这篇博客九月就想写了,因为赶项目拖了到现在,抓住17年尾巴写吧~ |
| 17 | + |
| 18 | + |
| 19 | +## 正文 |
| 20 | + |
| 21 | +上次看了一篇 [《从一道网易面试题浅谈OC线程安全》](https://www.jianshu.com/p/cec2a41aa0e7) 的博客,主要内容是: |
| 22 | + |
| 23 | +作者去网易面试,面试官出了一道面试题:下面代码会发生什么问题? |
| 24 | + |
| 25 | +```objc |
| 26 | +@property (nonatomic, strong) NSString *target; |
| 27 | +//.... |
| 28 | +dispatch_queue_t queue = dispatch_queue_create("parallel", DISPATCH_QUEUE_CONCURRENT); |
| 29 | +for (int i = 0; i < 1000000 ; i++) { |
| 30 | + dispatch_async(queue, ^{ |
| 31 | + self.target = [NSString stringWithFormat:@"ksddkjalkjd%d",i]; |
| 32 | + }); |
| 33 | +} |
| 34 | +``` |
| 35 | + |
| 36 | +答案是:会 crash。 |
| 37 | + |
| 38 | +我们来看看对`target`属性(`strong`修饰)进行赋值,相当与 MRC 中的 |
| 39 | + |
| 40 | +``` |
| 41 | +- (void)setTarget:(NSString *)target { |
| 42 | + if (target == _target) return; |
| 43 | + id pre = _target; |
| 44 | + [target retain];//1.先保留新值 |
| 45 | + _target = target;//2.再进行赋值 |
| 46 | + [pre release];//3.释放旧值 |
| 47 | +} |
| 48 | +``` |
| 49 | + |
| 50 | +因为在 *并行队列* `DISPATCH_QUEUE_CONCURRENT` 中*异步* `dispatch_async` 对 `target`属性进行赋值,就会导致 target 已经被 `release`了,还会执行 `release`。这就是向已释放内存对象发送消息而发生 crash 。 |
| 51 | + |
| 52 | + |
| 53 | +### 但是 |
| 54 | + |
| 55 | +我敲了这段代码,执行的时候发现并不会 crash~ |
| 56 | + |
| 57 | +```objc |
| 58 | +@property (nonatomic, strong) NSString *target; |
| 59 | +dispatch_queue_t queue = dispatch_queue_create("parallel", DISPATCH_QUEUE_CONCURRENT); |
| 60 | +for (int i = 0; i < 1000000 ; i++) { |
| 61 | + dispatch_async(queue, ^{ |
| 62 | + // ‘ksddkjalkjd’删除了 |
| 63 | + self.target = [NSString stringWithFormat:@"%d",i]; |
| 64 | + }); |
| 65 | +} |
| 66 | +``` |
| 67 | + |
| 68 | +原因就出在对 `self.target` 赋值的字符串上。博客的最后也提到了 - *‘上述代码的字符串改短一些,就不会崩溃’*,还有 `Tagged Pointer` 这个东西。 |
| 69 | + |
| 70 | +我们将上面的代码修改下: |
| 71 | + |
| 72 | + |
| 73 | +```objc |
| 74 | +NSString *str = [NSString stringWithFormat:@"%d", i]; |
| 75 | +NSLog(@"%d, %s, %p", i, object_getClassName(str), str); |
| 76 | +self.target = str; |
| 77 | +``` |
| 78 | +
|
| 79 | +输出: |
| 80 | +
|
| 81 | +``` |
| 82 | +0, NSTaggedPointerString, 0x3015 |
| 83 | +``` |
| 84 | +
|
| 85 | +发现这个字符串类型是 `NSTaggedPointerString`,那我们来看看 Tagged Pointer 是什么? |
| 86 | +
|
| 87 | +### Tagged Pointer |
| 88 | +
|
| 89 | +Tagged Pointer 详细的内容可以看这里 [深入理解Tagged Pointer](http://www.infoq.com/cn/articles/deep-understanding-of-tagged-pointer)。 |
| 90 | +
|
| 91 | +Tagged Pointer 是一个能够提升性能、节省内存的有趣的技术。 |
| 92 | +
|
| 93 | +- Tagged Pointer 专门用来存储小的对象,例如 **NSNumber** 和 **NSDate**(后来可以存储小字符串) |
| 94 | +- Tagged Pointer 指针的值不再是地址了,而是真正的值。所以,实际上它不再是一个对象了,它只是一个披着对象皮的普通变量而已。 |
| 95 | +- 它的内存并不存储在堆中,也不需要 malloc 和 free,所以拥有极快的读取和创建速度。 |
| 96 | +
|
| 97 | +
|
| 98 | +
|
| 99 | +
|
| 100 | +### 参考: |
| 101 | +
|
| 102 | +- [从一道网易面试题浅谈OC线程安全](https://www.jianshu.com/p/cec2a41aa0e7) |
| 103 | +
|
| 104 | +- [深入理解Tagged Pointer](http://www.infoq.com/cn/articles/deep-understanding-of-tagged-pointer) |
| 105 | +
|
| 106 | +- [【译】采用Tagged Pointer的字符串](http://www.cocoachina.com/ios/20150918/13449.html) |
| 107 | +
|
0 commit comments