经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 移动开发 » iOS » 查看文章
iOS底层原理(八)内存管理(上)
来源:cnblogs  作者:FunkyRay  时间:2021/4/12 9:49:32  对本文有异议

iOS程序的内存布局

iOS程序的内存布局顺序如以下所示

我们可以通过打印内存地址来验证

  1. int a = 10;
  2. int b;
  3. int main(int argc, char * argv[]) {
  4. @autoreleasepool {
  5. static int c = 20;
  6. static int d;
  7. int e;
  8. int f = 20;
  9. NSString *str = @"123";
  10. NSObject *obj = [[NSObject alloc] init];
  11. NSLog(@"\n&a=%p\n&b=%p\n&c=%p\n&d=%p\n&e=%p\n&f=%p\nstr=%p\nobj=%p\n",
  12. &a, &b, &c, &d, &e, &f, str, obj);
  13. return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
  14. }
  15. }
  16. // 输出结果
  17. &a=0x100c81e58
  18. &b=0x100c81f24
  19. &c=0x100c81e5c
  20. &d=0x100c81f20
  21. &e=0x7ffeeef80c2c
  22. &f=0x7ffeeef80c28
  23. str=0x100c81070
  24. obj=0x6000024900a0

经过排序的内存地址大小如下

  1. 字符串常量
  2. str=0x10dfa0068
  3. 已初始化的全局变量、静态变量
  4. &a =0x10dfa0db8
  5. &c =0x10dfa0dbc
  6. 未初始化的全局变量、静态变量
  7. &d =0x10dfa0e80
  8. &b =0x10dfa0e84
  9. obj=0x608000012210
  10. &f =0x7ffee1c60fe0
  11. &e =0x7ffee1c60fe4

注意: 字符串常量严格来说应该是存储在__TEXT段,只是我们习惯把他归于数据段

OC对象的一些特殊类型的内存管理

64bit开始,iOS引入了Tagged Pointer技术,用于优化NSNumber、NSDate、NSString等小对象的存储

Tagged Pointer的使用

  • 在没有使用Tagged Pointer之前, NSNumber等对象需要动态分配内存、维护引用计数等,NSNumber指针存储的是堆中NSNumber对象的地址值
  • 使用Tagged Pointer之后,NSNumber指针里面存储的数据变成了:Tag + Data,也就是将数据直接存储在了指针中
  • 当指针不够存储数据时,才会使用动态分配内存的方式来存储数据
  • objc_msgSend能识别Tagged Pointer,比如NSNumberintValue方法,直接从指针提取数据,节省了以前的调用开销

下面我们来举例说明,看示例代码

  1. NSNumber *number1 = @4;
  2. NSNumber *number2 = @5;
  3. NSNumber *number3 = @(0xFFFFFFFFFFFFFFF);
  4. NSLog(@"%p %p %p", number1, number2, number3);
  5. // 输出地址分别为:0x5a05f784d24b4325 0x5a05f784d24b4225 0x100515970

我们知道,如果要是动态分配内存,由于OC对象都有isa指针,所以最少分配16个字节,换算成十六进制后末位都是0;由此可以推断使用了Tagged Pointer的内存地址末位不为0

然后再看上面示例代码的打印,前两个个值的末位都不为0,所以使用了Tagged Pointer来做优化;最后一个内存地址末位为0,证明由于需要存储的数据太大了,才会采用动态分配内存的方式

源码分析

我们可以从objc4objc-object.h找到isTaggedPointer的实现,是通过和一个掩码_OBJC_TAG_MASK进行按位与运算来判断是否使用了TaggedPointer

  1. objc_object::isTaggedPointer()
  2. {
  3. return _objc_isTaggedPointer(this);
  4. }
  5. static inline bool
  6. _objc_isTaggedPointer(const void * _Nullable ptr)
  7. {
  8. return ((uintptr_t)ptr & _OBJC_TAG_MASK) == _OBJC_TAG_MASK;
  9. }

_OBJC_TAG_MASK掩码的值

  1. #if __arm64__
  2. # define OBJC_SPLIT_TAGGED_POINTERS 1
  3. #else
  4. # define OBJC_SPLIT_TAGGED_POINTERS 0
  5. #endif
  6. #if (TARGET_OS_OSX || TARGET_OS_MACCATALYST) && __x86_64__
  7. // 64-bit Mac - tag bit is LSB
  8. # define OBJC_MSB_TAGGED_POINTERS 0
  9. #else
  10. // Everything else - tag bit is MSB
  11. # define OBJC_MSB_TAGGED_POINTERS 1
  12. #endif
  13. #if OBJC_SPLIT_TAGGED_POINTERS
  14. # define _OBJC_TAG_MASK (1UL<<63) // 指针的最高有效位为1
  15. #elif OBJC_MSB_TAGGED_POINTERS
  16. # define _OBJC_TAG_MASK (1UL<<63)
  17. #else
  18. # define _OBJC_TAG_MASK 1UL // 指针的最低有效位为1
  19. #endif

OC对象的内存管理

在iOS中,使用引用计数来管理OC对象的内存

引用计数的原则

  • 一个新创建的OC对象引用计数默认是1,当引用计数减为0,OC对象就会销毁,释放其占用的内存空间
  • 调用retain会让OC对象的引用计数+1,调用release会让OC对象的引用计数-1- 调用retain会让OC对象的引用计数+1,调用release会让OC对象的引用计数-1- 当调用alloc、new、copy、mutableCopy方法返回了一个对象,在不需要这个对象时,要调用release或者autorelease来释放它
  • 想拥有某个对象,就让它的引用计数+1;不想再拥有某个对象,就让它的引用计数-1

MRC环境下的内存管理

MRC环境下的内存管理使用,见下面代码

  1. // Car
  2. @interface Car : NSObject
  3. @end
  4. @implementation Car
  5. @end
  6. // Dog
  7. @interface Dog : NSObject
  8. - (void)run;
  9. @end
  10. @implementation Dog
  11. - (void)run
  12. {
  13. NSLog(@"%s", __func__);
  14. }
  15. - (void)dealloc
  16. {
  17. [super dealloc];
  18. NSLog(@"%s", __func__);
  19. }
  20. @end
  21. // Person
  22. @interface Person : NSObject
  23. {
  24. Dog *_dog;
  25. Car *_car;
  26. int _age;
  27. }
  28. - (void)setAge:(int)age;
  29. - (int)age;
  30. - (void)setDog:(Dog *)dog;
  31. - (Dog *)dog;
  32. - (void)setCar:(Car *)car;
  33. - (Car *)car;
  34. @end
  35. @implementation Person
  36. - (void)setAge:(int)age
  37. {
  38. _age = age;
  39. }
  40. - (int)age
  41. {
  42. return _age;
  43. }
  44. - (void)setDog:(Dog *)dog
  45. {
  46. if (_dog != dog) {
  47. [_dog release];
  48. _dog = [dog retain];
  49. }
  50. }
  51. - (Dog *)dog
  52. {
  53. return _dog;
  54. }
  55. - (void)setCar:(Car *)car
  56. {
  57. if (_car != car) {
  58. [_car release];
  59. _car = [car retain];
  60. }
  61. }
  62. - (Car *)car
  63. {
  64. return _car;
  65. }
  66. - (void)dealloc
  67. {
  68. // [_dog release];
  69. // _dog = nil;
  70. self.dog = nil;
  71. self.car = nil;
  72. NSLog(@"%s", __func__);
  73. // 父类的dealloc放到最后
  74. [super dealloc];
  75. }
  76. @end
  77. int main(int argc, const char * argv[]) {
  78. @autoreleasepool {
  79. Dog *dog = [[Dog alloc] init]; // 1
  80. Person *person = [[Person alloc] init]; // 1
  81. [person setDog:dog]; // 2
  82. [person release]; // 0 // dog -1 = 1
  83. [dog release]; // 0
  84. }
  85. return 0;
  86. }

加上@property后,编译器会自动生成setter和getter

  1. @property (nonatomic, assign) int age;
  2. @property (nonatomic, retain) Dog *dog;

类工厂方法创建的对象不需要进行retain操作,其内部已经对应做了处理

  1. NSMutableArray *data = [NSMutableArray array];

copy

深拷贝和浅拷贝

拷贝的目的:产生一个副本对象,跟源对象互不影响

  • 修改了源对象,不会影响副本对象
  • 修改了副本对象,不会影响源对象

iOS提供了2个拷贝方法

  • copy:不可变拷贝,产生不可变副本
  • mutableCopy:可变拷贝,产生可变副本

深拷贝和浅拷贝

  • 深拷贝:内容拷贝,产生新的对象
  • 浅拷贝:指针拷贝,没有产生新的对象

看下面示例代码,内存地址分别是什么

  1. NSString *str1 = [NSString stringWithFormat:@"test"];
  2. NSString *str2 = [str1 copy]; // 返回的是NSString,浅拷贝
  3. NSMutableString *str3 = [str1 mutableCopy]; // 返回的是NSMutableString,深拷贝
  4. NSMutableString *str4 = [[NSMutableString alloc] initWithFormat:@"test"];
  5. NSString *str5 = [str4 copy]; // 返回的是NSString,深拷贝
  6. NSMutableString *str6 = [str4 mutableCopy]; // 返回的是NSMutableString,深拷贝
  7. NSLog(@"%p %p %p", str1, str2, str3);
  8. NSLog(@"%p %p %p", str4, str5, str6);
  9. // copy、mutablecopy相当于进行了一次retain,要对应进行一次release
  10. [str6 release];
  11. [str5 release];
  12. [str4 release];
  13. [str3 release];
  14. [str2 release];
  15. [str1 release];

通过打印发现str1str2的内存地址是一样的,所以为浅拷贝,没有产生新的对象;而其他的都产生了新的对象,为深拷贝

常用的几个可变不可变的类型进行copy的操作如下图所示

copy修饰属性

1.我们看下面代码,运行结果是怎样,为什么

  1. @interface Person : NSObject
  2. @property (strong, nonatomic) NSString * text;
  3. @end
  4. @implementation Person
  5. @end
  6. int main(int argc, const char * argv[]) {
  7. @autoreleasepool {
  8. Person *person = [[Person alloc] init];
  9. NSMutableString * str1 = [NSMutableString stringWithString:@"dddddddddddd"];
  10. person.text = str1;
  11. [str1 appendString:@"33"];
  12. NSLog(@"%@ %@", str1, person.text);
  13. }
  14. return 0;
  15. }
  16. // 输出:dddddddddddd33 dddddddddddd33

我们发现str1的值改变后,person.text也会被影响到,因为它们指向的是同一块内存空间

所以像NSString、NSArray、NSDictionary这几个类型作为属性,都是用copy来修饰的,这样意味着属性是不可改变的

  1. @property (copy, nonatomic) NSArray *data;
  2. @property (copy, nonatomic) NSString *text;
  3. @property (copy, nonatomic) NSDictionary *dict;

2.我们再看下面代码,运行结果是怎样,为什么

  1. @interface Person : NSObject
  2. @property (copy, nonatomic) NSMutableArray *data;
  3. @end
  4. @implementation Person
  5. @end
  6. int main(int argc, const char * argv[]) {
  7. @autoreleasepool {
  8. Person *p = [[Person alloc] init];
  9. p.data = [NSMutableArray array];
  10. [p.data addObject:@"jack"];
  11. [p.data addObject:@"rose"];
  12. }
  13. return 0;
  14. }

结果会报错。

因为用copy修饰NSMutableArray,生成的setter会通过深拷贝变成NSArray类型的对象,然后NSArray在进行添加元素自然会报错

所以像NSMutableString、NSMutableArray、NSMutableDictionary这几个类型作为属性,都是用strong来修饰的,这样意味着属性是不可改变的

  1. @property (strong, nonatomic) NSMutableArray *data;
  2. @property (strong, nonatomic) NSMutableString *text;
  3. @property (strong, nonatomic) NSMutableDictionary *dict;

自定义copy

我们的自定义类型也可以通过遵守NSCopying协议实现拷贝功能

使用代码如下

  1. @interface Person : NSObject <NSCopying>
  2. @property (assign, nonatomic) int age;
  3. @property (assign, nonatomic) double weight;
  4. @end
  5. @implementation Person
  6. - (id)copyWithZone:(NSZone *)zone
  7. {
  8. Person *person = [[Person allocWithZone:zone] init];
  9. person.age = self.age;
  10. person.weight = self.weight;
  11. return person;
  12. }
  13. int main(int argc, const char * argv[]) {
  14. @autoreleasepool {
  15. Person *p1 = [[Person alloc] init];
  16. p1.age = 20;
  17. p1.weight = 50;
  18. Person *p2 = [p1 copy];
  19. // p2.age = 30;
  20. NSLog(@"%@", p1);
  21. NSLog(@"%@", p2);
  22. }
  23. return 0;
  24. }

引用计数的存储

我们之前学习的isa指针里有一个位域的值是用来存储引用计数器的

当引用计数器过大就会存储在一个叫SideTable的类中

  1. struct SideTable {
  2. spinlock_t slock;
  3. RefcountMap refcnts;
  4. weak_table_t weak_table; // 弱引用表
  5. SideTable() {
  6. memset(&weak_table, 0, sizeof(weak_table));
  7. }
  8. ~SideTable() {
  9. _objc_fatal("Do not delete SideTable.");
  10. }
  11. void lock() { slock.lock(); }
  12. void unlock() { slock.unlock(); }
  13. void forceReset() { slock.forceReset(); }
  14. // Address-ordered lock discipline for a pair of side tables.
  15. template<HaveOld, HaveNew>
  16. static void lockTwo(SideTable *lock1, SideTable *lock2);
  17. template<HaveOld, HaveNew>
  18. static void unlockTwo(SideTable *lock1, SideTable *lock2);
  19. };

RefcountMap refcnts是一个散列表,用来存储着引用计数的

  1. typedef objc::DenseMap<DisguisedPtr<objc_object>,size_t,RefcountMapValuePurgeable> RefcountMap;

通过源码分析

retain的源码分析

我们在objc-object.h中可以对应看到引用计数存储的相关代码

我们通过retain来分析,其内部会去调用rootRetain

  1. inline id
  2. objc_object::retain()
  3. {
  4. ASSERT(!isTaggedPointer());
  5. return rootRetain(false, RRVariant::FastOrMsgSend);
  6. }
  7. ALWAYS_INLINE id
  8. objc_object::rootRetain(bool tryRetain, objc_object::RRVariant variant)
  9. {
  10. if (slowpath(isTaggedPointer())) return (id)this;
  11. bool sideTableLocked = false;
  12. bool transcribeToSideTable = false;
  13. isa_t oldisa;
  14. isa_t newisa;
  15. oldisa = LoadExclusive(&isa.bits);
  16. if (variant == RRVariant::FastOrMsgSend) {
  17. // These checks are only meaningful for objc_retain()
  18. // They are here so that we avoid a re-load of the isa.
  19. if (slowpath(oldisa.getDecodedClass(false)->hasCustomRR())) {
  20. ClearExclusive(&isa.bits);
  21. if (oldisa.getDecodedClass(false)->canCallSwiftRR()) {
  22. return swiftRetain.load(memory_order_relaxed)((id)this);
  23. }
  24. return ((id(*)(objc_object *, SEL))objc_msgSend)(this, @selector(retain));
  25. }
  26. }
  27. if (slowpath(!oldisa.nonpointer)) {
  28. // a Class is a Class forever, so we can perform this check once
  29. // outside of the CAS loop
  30. if (oldisa.getDecodedClass(false)->isMetaClass()) {
  31. ClearExclusive(&isa.bits);
  32. return (id)this;
  33. }
  34. }
  35. do {
  36. transcribeToSideTable = false;
  37. newisa = oldisa;
  38. // 如果不是nonpointer,直接操作散列表+1
  39. if (slowpath(!newisa.nonpointer)) {
  40. ClearExclusive(&isa.bits);
  41. if (tryRetain) return sidetable_tryRetain() ? (id)this : nil;
  42. else return sidetable_retain(sideTableLocked);
  43. }
  44. // don't check newisa.fast_rr; we already called any RR overrides
  45. if (slowpath(newisa.isDeallocating())) {
  46. ClearExclusive(&isa.bits);
  47. if (sideTableLocked) {
  48. ASSERT(variant == RRVariant::Full);
  49. sidetable_unlock();
  50. }
  51. if (slowpath(tryRetain)) {
  52. return nil;
  53. } else {
  54. return (id)this;
  55. }
  56. }
  57. uintptr_t carry;
  58. // 执行引用计数加1操作
  59. newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry); // extra_rc++
  60. // 判断extra_rc是否满了,carry是标识符
  61. if (slowpath(carry)) {
  62. // newisa.extra_rc++ overflowed
  63. if (variant != RRVariant::Full) {
  64. ClearExclusive(&isa.bits);
  65. return rootRetain_overflow(tryRetain);
  66. }
  67. // Leave half of the retain counts inline and
  68. // prepare to copy the other half to the side table.
  69. // 如果extra_rc满了,则拿出一半存储到side table散列表中
  70. if (!tryRetain && !sideTableLocked) sidetable_lock();
  71. sideTableLocked = true;
  72. transcribeToSideTable = true;
  73. newisa.extra_rc = RC_HALF;
  74. newisa.has_sidetable_rc = true;
  75. }
  76. } while (slowpath(!StoreExclusive(&isa.bits, &oldisa.bits, newisa.bits)));
  77. if (variant == RRVariant::Full) {
  78. if (slowpath(transcribeToSideTable)) {
  79. // Copy the other half of the retain counts to the side table.
  80. sidetable_addExtraRC_nolock(RC_HALF);
  81. }
  82. if (slowpath(!tryRetain && sideTableLocked)) sidetable_unlock();
  83. } else {
  84. ASSERT(!transcribeToSideTable);
  85. ASSERT(!sideTableLocked);
  86. }
  87. return (id)this;
  88. }

retain的流程可以用下图来概述

release的源码分析

然后我们再看release,会对应调用rootRelease

  1. inline void
  2. objc_object::release()
  3. {
  4. ASSERT(!isTaggedPointer());
  5. rootRelease(true, RRVariant::FastOrMsgSend);
  6. }
  7. ALWAYS_INLINE bool
  8. objc_object::rootRelease(bool performDealloc, objc_object::RRVariant variant)
  9. {
  10. if (slowpath(isTaggedPointer())) return false;
  11. bool sideTableLocked = false;
  12. isa_t newisa, oldisa;
  13. oldisa = LoadExclusive(&isa.bits);
  14. if (variant == RRVariant::FastOrMsgSend) {
  15. // These checks are only meaningful for objc_release()
  16. // They are here so that we avoid a re-load of the isa.
  17. if (slowpath(oldisa.getDecodedClass(false)->hasCustomRR())) {
  18. ClearExclusive(&isa.bits);
  19. if (oldisa.getDecodedClass(false)->canCallSwiftRR()) {
  20. swiftRelease.load(memory_order_relaxed)((id)this);
  21. return true;
  22. }
  23. ((void(*)(objc_object *, SEL))objc_msgSend)(this, @selector(release));
  24. return true;
  25. }
  26. }
  27. // 判断nonpointer是不是共用体类型的指针
  28. if (slowpath(!oldisa.nonpointer)) {
  29. // a Class is a Class forever, so we can perform this check once
  30. // outside of the CAS loop
  31. if (oldisa.getDecodedClass(false)->isMetaClass()) {
  32. ClearExclusive(&isa.bits);
  33. return false;
  34. }
  35. }
  36. retry:
  37. do {
  38. newisa = oldisa;
  39. // 判断是否为nonpointer
  40. if (slowpath(!newisa.nonpointer)) {
  41. ClearExclusive(&isa.bits);
  42. // 不是则直接操作散列表-1
  43. return sidetable_release(sideTableLocked, performDealloc);
  44. }
  45. if (slowpath(newisa.isDeallocating())) {
  46. ClearExclusive(&isa.bits);
  47. if (sideTableLocked) {
  48. ASSERT(variant == RRVariant::Full);
  49. sidetable_unlock();
  50. }
  51. return false;
  52. }
  53. // don't check newisa.fast_rr; we already called any RR overrides
  54. uintptr_t carry;
  55. // 进行引用计数-1操作(extra_rc--)
  56. newisa.bits = subc(newisa.bits, RC_ONE, 0, &carry); // extra_rc--
  57. if (slowpath(carry)) {
  58. // don't ClearExclusive()
  59. // 如果此时extra_rc的值为0了,则走到underflow
  60. goto underflow;
  61. }
  62. } while (slowpath(!StoreReleaseExclusive(&isa.bits, &oldisa.bits, newisa.bits)));
  63. // 此时extra_rc中值为0,散列表中也是空的,触发析构函数
  64. if (slowpath(newisa.isDeallocating()))
  65. goto deallocate;
  66. if (variant == RRVariant::Full) {
  67. if (slowpath(sideTableLocked)) sidetable_unlock();
  68. } else {
  69. ASSERT(!sideTableLocked);
  70. }
  71. return false;
  72. underflow:
  73. // newisa.extra_rc-- underflowed: borrow from side table or deallocate
  74. // abandon newisa to undo the decrement
  75. newisa = oldisa;
  76. // 判断散列表中是否存储了一半的引用计数
  77. if (slowpath(newisa.has_sidetable_rc)) {
  78. if (variant != RRVariant::Full) {
  79. ClearExclusive(&isa.bits);
  80. return rootRelease_underflow(performDealloc);
  81. }
  82. // Transfer retain count from side table to inline storage.
  83. if (!sideTableLocked) {
  84. ClearExclusive(&isa.bits);
  85. sidetable_lock();
  86. sideTableLocked = true;
  87. // Need to start over to avoid a race against
  88. // the nonpointer -> raw pointer transition.
  89. oldisa = LoadExclusive(&isa.bits);
  90. goto retry;
  91. }
  92. // Try to remove some retain counts from the side table.
  93. // 从SideTable中移除存储的一半引用计数
  94. auto borrow = sidetable_subExtraRC_nolock(RC_HALF);
  95. bool emptySideTable = borrow.remaining == 0; // we'll clear the side table if no refcounts remain there
  96. if (borrow.borrowed > 0) {
  97. // Side table retain count decreased.
  98. // Try to add them to the inline count.
  99. bool didTransitionToDeallocating = false;
  100. // 进行-1操作,然后存储到extra_rc中
  101. newisa.extra_rc = borrow.borrowed - 1; // redo the original decrement too
  102. newisa.has_sidetable_rc = !emptySideTable;
  103. bool stored = StoreReleaseExclusive(&isa.bits, &oldisa.bits, newisa.bits);
  104. if (!stored && oldisa.nonpointer) {
  105. // Inline update failed.
  106. // Try it again right now. This prevents livelock on LL/SC
  107. // architectures where the side table access itself may have
  108. // dropped the reservation.
  109. uintptr_t overflow;
  110. newisa.bits =
  111. addc(oldisa.bits, RC_ONE * (borrow.borrowed-1), 0, &overflow);
  112. newisa.has_sidetable_rc = !emptySideTable;
  113. if (!overflow) {
  114. stored = StoreReleaseExclusive(&isa.bits, &oldisa.bits, newisa.bits);
  115. if (stored) {
  116. didTransitionToDeallocating = newisa.isDeallocating();
  117. }
  118. }
  119. }
  120. if (!stored) {
  121. // Inline update failed.
  122. // Put the retains back in the side table.
  123. ClearExclusive(&isa.bits);
  124. sidetable_addExtraRC_nolock(borrow.borrowed);
  125. oldisa = LoadExclusive(&isa.bits);
  126. goto retry;
  127. }
  128. // Decrement successful after borrowing from side table.
  129. if (emptySideTable)
  130. sidetable_clearExtraRC_nolock();
  131. if (!didTransitionToDeallocating) {
  132. if (slowpath(sideTableLocked)) sidetable_unlock();
  133. return false;
  134. }
  135. }
  136. else {
  137. // Side table is empty after all. Fall-through to the dealloc path.
  138. }
  139. }
  140. // 进行析构,发送dealloc消息
  141. deallocate:
  142. // Really deallocate.
  143. ASSERT(newisa.isDeallocating());
  144. ASSERT(isa.isDeallocating());
  145. if (slowpath(sideTableLocked)) sidetable_unlock();
  146. __c11_atomic_thread_fence(__ATOMIC_ACQUIRE);
  147. if (performDealloc) {
  148. ((void(*)(objc_object *, SEL))objc_msgSend)(this, @selector(dealloc));
  149. }
  150. return true;
  151. }

release的流程可以用下图来概述

dealloc源码分析

retainrelease的实现中,都涉及到dealloc析构函数,我们来分析下dealloc的底层实现

通过调用轨迹dealloc -> _objc_rootDealloc -> rootDealloc,最终会调用到rootDealloc

  1. inline void
  2. objc_object::rootDealloc()
  3. {
  4. if (isTaggedPointer()) return; // fixme necessary?
  5. if (fastpath(isa.nonpointer && // 普通的isa
  6. !isa.weakly_referenced && // 弱指针引用
  7. !isa.has_assoc && // 关联对象
  8. #if ISA_HAS_CXX_DTOR_BIT
  9. !isa.has_cxx_dtor && // c++析构函数
  10. #else
  11. !isa.getClass(false)->hasCxxDtor() &&
  12. #endif
  13. !isa.has_sidetable_rc)) // 引用计数散列表
  14. {
  15. assert(!sidetable_present());
  16. free(this); // 直接释放
  17. }
  18. else {
  19. object_dispose((id)this);
  20. }
  21. }

进一步调用到object_dispose

我们可以看到如果没有上述判断中需要处理的条件,对象会释放的更快

  1. // object_dispose
  2. id
  3. object_dispose(id obj)
  4. {
  5. if (!obj) return nil;
  6. // 销毁实例而不释放内存
  7. objc_destructInstance(obj);
  8. // 释放内存
  9. free(obj);
  10. return nil;
  11. }
  12. // objc_destructInstance
  13. void *objc_destructInstance(id obj)
  14. {
  15. if (obj) {
  16. // Read all of the flags at once for performance.
  17. bool cxx = obj->hasCxxDtor();
  18. bool assoc = obj->hasAssociatedObjects();
  19. // This order is important.
  20. // 调用c++析构函数,清除成员变量
  21. if (cxx) object_cxxDestruct(obj);
  22. // 删除关联对象
  23. if (assoc) _object_remove_assocations(obj, /*deallocating*/true);
  24. // 将指向当前对象的弱指针置为nil
  25. obj->clearDeallocating();
  26. }
  27. return obj;
  28. }
  29. // clearDeallocating
  30. inline void
  31. objc_object::clearDeallocating()
  32. {
  33. // 判断是否为nonpointer
  34. if (slowpath(!isa.nonpointer)) {
  35. // Slow path for raw pointer isa.
  36. // 如果不是,则直接释放散列表
  37. sidetable_clearDeallocating();
  38. }
  39. else if (slowpath(isa.weakly_referenced || isa.has_sidetable_rc)) {
  40. // Slow path for non-pointer isa with weak refs and/or side table data.
  41. // 如果是,清空弱引用表 + 散列表
  42. clearDeallocating_slow();
  43. }
  44. assert(!sidetable_present());
  45. }
  46. // clearDeallocating_slow
  47. NEVER_INLINE void
  48. objc_object::clearDeallocating_slow()
  49. {
  50. ASSERT(isa.nonpointer && (isa.weakly_referenced || isa.has_sidetable_rc));
  51. SideTable& table = SideTables()[this];
  52. table.lock();
  53. if (isa.weakly_referenced) {
  54. // 清空弱引用表
  55. weak_clear_no_lock(&table.weak_table, (id)this);
  56. }
  57. if (isa.has_sidetable_rc) {
  58. // 清空引用计数
  59. table.refcnts.erase(this);
  60. }
  61. table.unlock();
  62. }

dealloc的流程可以用下图概述

retainCount 源码分析

下面我们再来分析retainCount的实现

  1. - (NSUInteger)retainCount {
  2. return _objc_rootRetainCount(self);
  3. }
  4. uintptr_t
  5. _objc_rootRetainCount(id obj)
  6. {
  7. ASSERT(obj);
  8. return obj->rootRetainCount();
  9. }

内部会进一步调用rootRetainCount

  1. inline uintptr_t
  2. objc_object::rootRetainCount()
  3. {
  4. // 如果是TaggedPointer就返回
  5. if (isTaggedPointer()) return (uintptr_t)this;
  6. sidetable_lock();
  7. // 拿到isa
  8. isa_t bits = __c11_atomic_load((_Atomic uintptr_t *)&isa.bits, __ATOMIC_RELAXED);
  9. if (bits.nonpointer) { // 查看是否为非指针类型
  10. uintptr_t rc = bits.extra_rc; // 拿到isa指针里的extra_rc返回
  11. if (bits.has_sidetable_rc) { // 判断has_sidetable_rc的值是否为1,如果为1就要去SideTable里面取
  12. rc += sidetable_getExtraRC_nolock();
  13. }
  14. sidetable_unlock();
  15. return rc;
  16. }
  17. sidetable_unlock();
  18. return sidetable_retainCount();
  19. }
  20. size_t
  21. objc_object::sidetable_getExtraRC_nolock()
  22. {
  23. // 通过一个key取出SideTable里的散列表refcnts
  24. ASSERT(isa.nonpointer);
  25. SideTable& table = SideTables()[this];
  26. RefcountMap::iterator it = table.refcnts.find(this);
  27. if (it == table.refcnts.end()) return 0;
  28. else return it->second >> SIDE_TABLE_RC_SHIFT;
  29. }

原文链接:http://www.cnblogs.com/funkyRay/p/ios-di-ceng-yuan-li-ba-nei-cun-guan-li-shang.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号