美文网首页
iOS内存管理

iOS内存管理

作者: CoderKK | 来源:发表于2020-03-27 15:57 被阅读0次
iOS程序的内存布局(由低到高)
保留段
代码段(_TEXT)
数据段(_DATA)
堆(heap)
栈(stack)
内核区

代码段:编译之后的代码
数据段:按如下由低到高分配

  • 字符串常量,如NSString *str = @"abc" (常量区)
  • 已初始化数据:已初始化的全局变量和静态变量等 (全局区)
  • 未初始化数据未初始化的全局变量和静态变量等 (全局区)

栈:函数调用开销,比如局部变量。内存分配的内存空间越来越小
堆:通过alloc、malloc等动态分配的空间,分配的空间地址越来越大

Tagged Pointer
  • 从64bit开始,iOS引入了Tagged Pointer技术,用于优化NSNumber、NSDate、NSString等小对象的存储
  • 在没有使用Tagged Pointer之前,NSNumber等对象需要动态分配内存、维护引用计数等,NSNumber指针存储的是堆中NSNumber对象的地址值
  • 使用Tagged Pointer之后,NSNumber指针里面存储数据编程了:Tag + Data,也就是将数据直接存储在了指针中
  • 当指针不够存储数据时,才会使用动态分配内存的方式创建对象来存储数据
  • objc_msgSend能识别Tagged Pointer,如果是Tagged Pointer,直接从指针提取数据,节省了以前的调用开销
  • 判断是否Tagged Pointer
    iOS平台,最高有效位是1
    Mac平台,最低有效位是1

判断Tagged Pointer相关源码

#if TARGET_OS_OSX && __x86_64__
    // 64-bit Mac - tag bit is LSB
#   define OBJC_MSB_TAGGED_POINTERS 0
#else
    // Everything else - tag bit is MSB
#   define OBJC_MSB_TAGGED_POINTERS 1
#endif
#if OBJC_MSB_TAGGED_POINTERS
#   define _OBJC_TAG_MASK (1UL<<63)
#else
#   define _OBJC_TAG_MASK 1UL
#endif
static inline bool 
_objc_isTaggedPointer(const void * _Nullable ptr) 
{
    return ((uintptr_t)ptr & _OBJC_TAG_MASK) == _OBJC_TAG_MASK;
}
相关面试题

思考一下以下两段代码会发生什么事?有什么区别?

@property (strong, nonatomic) NSString *name;

dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
for (int i = 0; i < 1000; i++) {
    dispatch_async(queue, ^{
        self.name = [NSString stringWithFormat:@"abcdefghijk"];
    });
}
@property (strong, nonatomic) NSString *name;

dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
for (int i = 0; i < 1000; i++) {
    dispatch_async(queue, ^{
        self.name = [NSString stringWithFormat:@"abc"];
    });
}

第一段会报坏内存访问(EXC_BAD_ACCESS)异常,第二段正常运行
原因:第一段self.name的本质是调用[self setName:]方法,方法实质实现如下

- (void)setName:(NSString *)name{
    if (_name != name) {
        [_name release];
        _name = [name retain];
    }
}

当多个线程同时调用时,就会出现由于_name已经被释放但还继续调用[_name release]方法,造成坏内存访问,可以使用加锁方式解决该异常
第二段由于[NSString stringWithFormat:@"abc"]是个Tagged Ponter,采用的是直接赋值的方式,所以不会造成问题。

OC对象的内存管理
  • 在iOS中,使用引用计数来管理OC对象的内存
  • 一个新创建的OC对象引用计数默认是1,当引用计数减为0,OC对象会销毁,释放其占用的内存空间
  • 调用retain会会让OC对象的引用计数+1,调用release会让OC对象的引用计数-1
  • 内存管理经验总结:
    当调用alloc、new、copy、mutableCopy方法返回一个对象,在不需要这个对象时,要调用release或者autorelease来释放它
copy和mutableCopy
NSString NSMutableString NSArray NSMutableArray NSDictionary NSMutableDictionary
copy 浅拷贝 深拷贝 浅拷贝 深拷贝 浅拷贝 深拷贝
mutableCopy 深拷贝 深拷贝 深拷贝 深拷贝 深拷贝 深拷贝

深拷贝和浅拷贝的判断依据是有没有生成新的副本对象
总结:不可变对象的copy是浅拷贝,其他的均为深拷贝
自定义对象使用copy,要遵循NSCopying协议,实现copyWithZone:方法

ARC
  • ARC的本质也是MRC, 只不过我们不用手动去管理引用计数,管理引用计数的相关代码由编译器(lvvm)帮我们生成了。ARC是编译器(lvvm)和Runtime机制(weak指针自动置nil)共同协调完成的。
  • 在ARC环境下,我们需注意循环引用问题,一般使用weak或__unsafe_unretained解决循环引用。
  • ARC 只能帮我们管理 Foudation 对象,非 Foundation 框架下的还是需要我们手动管理的,比如我们常用到 CoreFoundation,使用 CoreFoundation 时就需要注意是否分配了内存,如果分配了内存需要手动调用 free进行释放。以及 Foundation 对象转 CoreFoundation下的内存管理权的移交问题。

相关文章

网友评论

      本文标题:iOS内存管理

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