看一个例子
1 | @property (nonatomic, copy) NSString *testStr; |
- 执行结果:正常
- [self.testStr class] => NSTaggedPointerString
1 | dispatch_queue_t queue = dispatch_get_global_queue(0, 0); |
- 执行结果:EXC_BAD_ACCESS
- [self.testStr class] => __NSCFString
原因
NSTaggedPointerString
- 生成的对象占用内存小,iOS采用了TaggedPointer(指针)来进行存储
- 本质上就是赋值操作,不存在release内存的操作。因此不会存在多线程问题
__NSCFString
1
2
3
4
5
6- (void)setTestStr:(NSString *)testStr {
if (_testStr != testStr) {
[_testStr release];
_testStr = [testStr copy];
}
}第二段代码代码,其实是在调用如上的set方法,涉及多线程访问,同时执行release操作,会导致EXC_BAD_ACCESS
- 当然我们可以通过for循环加锁 or 重写set方法加锁 or atomic 来解决EXC_BAD_ACCESS问题
TaggedPointer
- iOS用于优化NSNumber,NSDate,NSString等小对象的存储,将对象指针拆分,一部分保留数据,一部分作特殊标。不够存数据时,会动态分配内存进行存储,维护引用计数,指针存储对象地址值,其实就是上面的方式二,传统方式
当String的内容有中文或者特殊字符(非ASCII字符)时,那么就只能存储为String指针
TaggedPointer与objc_msgSend
- 讲讲个人的理解:如果一个对象是TaggedPointer,就不存在isa指针了,不算真正的OC对象,只是看似对象的普通变量而已。要避免设计对isa的操作
- 在进行方法调用时,objc_msgSend会进行识别,不会从isa查找cache、父类查找、消息转发的过程了,直接从指针中取出值进行操作
- 优化:节省内存,使用上也优化了,不需要经历消息发送这类的过程了