小碼哥iOS學習筆記第二十五天: OC對象的內存管理

1、引用計數

  • 在iOS中,使用引用計數來管理OC對象的內存
    • 一個新建立的OC對象引用計數默認是1,當引用計數減爲0,OC對象就會銷燬,釋放其佔用的內存空間
    • 調用retain會讓OC對象的引用計數+1,調用release會讓OC對象的引用計數-1
  • 內存管理的經驗總結
    • 當調用allocnewcopymutableCopy方法返回了一個對象,在不須要這個對象時,要調用release或者autorelease來釋放它
    • 想擁有某個對象,就讓它的引用計數+1;不想再擁有某個對象,就讓它的引用計數-1

MRC下的setter方法

  • MRC下, 對象須要持有成員變量, 當銷燬時釋放成員變量
@interface Person : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int age;

@end

@implementation Person

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

- (void)setAge:(int)age {
    _age = age;
}

- (void)dealloc {
    self.name = nil;
    [super dealloc];
}

@end
複製代碼

2、copy和mutableCopy

  • 經過copymutableCopy, 能夠生成一個副本, 與源代碼分隔開, 二者之間互不干擾
  • mutableCopy爲例, 有以下代碼
  • 深拷貝: 產生一個新的副本, 與源對象相互獨立
  • 淺拷貝: 指針拷貝, 指向源對象

自定義對象的拷貝

  • 自定義對象的拷貝須要實現NSCopying協議
#import <Foundation/Foundation.h>

@interface Person : NSObject <NSCopying>

@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int age;

@end

@implementation Person

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

- (void)setAge:(int)age
{
    _age = age;
}

- (id)copyWithZone:(nullable NSZone *)zone
{
    Person *person = [[Person allocWithZone:zone] init];
    person.age = self.age;
    person.name = self.name;
    return person;
}
@end
複製代碼

3、引用計數的存儲

  • 能夠經過retainCount方法, 查看引用計數的存儲
  • 經過runtime源碼, 查看- (NSUInteger)retainCount方法, 以下圖

  • 進入rootRetainCount函數, 能夠看到引用計數的查找過程
    • 若是指針是Tagged Pointer, 那麼直接返回, 不然進入下一步
    • 判斷isa是否優化過, 若是優化過, 那麼最後isa的最後19位存儲的是引用計數
    • 若是最後19位不足以存儲, 那麼多餘的引用計數會存儲到sidetable中, 同時將倒數第20位的值置爲1, 就是has_sidetable_rc的值爲1
    • 若是has_sidetable_rc的值爲1, 就會從sidetable_getExtraRC_nolock函數中取出sidetable中存儲的引用計數

  • sidetable_getExtraRC_nolock中的代碼以下圖

  • 若是isa沒有優化過, 那麼就會進入sidetable_retainCount函數, 獲取sidetable中的引用計數

  • Sidetable結構以下

  • 咱們也能夠從sidetable_retainsidetable_release函數中, 看到對引用技術的操做

4、weak的原理是什麼?

  • 在程序中寫入下面一段代碼
__weak NSObject *obj;
NSLog(@"1");
{
    obj = [[NSObject alloc] init];
}
NSLog(@"2 - %@", obj);
複製代碼
  • 能夠看到, 當對象釋放時, 被__weak修飾的指針會執行nil

  • 咱們能夠經過-dealloc的源碼, 查看weak的實現過程
  • 下圖是-dealloc方法的底層實現

  • 進入_objc_rootDealloc函數

  • 進入rootDealloc函數, 在這裏能夠看到兩種狀況
    • isa是優化過的指針, 對象沒有被弱引用, 沒有關聯對象, 沒有c++析構函數, 沒有將引用計數存到Sidetable中, 就會當即釋放
    • 不然調用object_dispose函數

  • 進入object_dispose函數, 能夠看到調用了objc_destructInstance函數

  • 進入objc_destructInstance函數, 能夠看到對objc的處理, 是在clearDeallocating函數中將弱指針置爲nil的

  • 進入clearDeallocating函數, 又能夠看到兩種狀況
    • 對象的isa沒有優化過
    • 和優化過, 而且被弱指針引用 或者 將引用計數存放到了Sidetable

  • 當isa沒有被優化過, 進入sidetable_clearDeallocating函數, 能夠看到weak引用是存放到SideTable中的

  • 存放在了SideTableweak_table_t

  • 查看weak_table_t, 以下圖, 即weak會被存放到一個全局的散列表中

  • 會經過weak_clear_no_lock函數, 對弱指針置爲nil, 同時移除刪列表中的weak記錄

  • 若是isa被優化過, 而且對象被弱引用或者將引用計數存到Sidetable中, 就會調用clearDeallocating_slow函數

  • 進入clearDeallocating_slow函數, 能夠看到在函數中, 調用了weak_clear_no_lock函數, 並清空了引用計數

相關文章
相關標籤/搜索