1、引用計數
- 在iOS中,使用引用計數來管理OC對象的內存
- 一個新建立的OC對象引用計數默認是
1
,當引用計數減爲0
,OC對象就會銷燬,釋放其佔用的內存空間
- 調用
retain
會讓OC對象的引用計數+1
,調用release
會讓OC
對象的引用計數-1
- 內存管理的經驗總結
- 當調用
alloc
、new
、copy
、mutableCopy
方法返回了一個對象,在不須要這個對象時,要調用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
- 經過
copy
和mutableCopy
, 能夠生成一個副本, 與源代碼分隔開, 二者之間互不干擾
- 以
mutableCopy
爲例, 有以下代碼
- 深拷貝: 產生一個新的副本, 與源對象相互獨立
- 淺拷貝: 指針拷貝, 指向源對象
自定義對象的拷貝
#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_retain
和sidetable_release
函數中, 看到對引用技術
的操做
4、weak的原理是什麼?
__weak NSObject *obj;
NSLog(@"1");
{
obj = [[NSObject alloc] init];
}
NSLog(@"2 - %@", obj);
複製代碼
- 能夠看到, 當對象釋放時, 被
__weak
修飾的指針會執行nil
- 咱們能夠經過
-dealloc
的源碼, 查看weak
的實現過程
- 下圖是
-dealloc
方法的底層實現
- 進入
rootDealloc
函數, 在這裏能夠看到兩種狀況
- isa是優化過的指針, 對象沒有被弱引用, 沒有關聯對象, 沒有c++析構函數, 沒有將引用計數存到
Sidetable
中, 就會當即釋放
- 不然調用
object_dispose
函數
- 進入
object_dispose
函數, 能夠看到調用了objc_destructInstance
函數
- 進入
objc_destructInstance
函數, 能夠看到對objc
的處理, 是在clearDeallocating
函數中將弱指針置爲nil的
- 進入
clearDeallocating
函數, 又能夠看到兩種狀況
- 對象的isa沒有優化過
- 和優化過, 而且被弱指針引用 或者 將引用計數存放到了
Sidetable
中
- 當isa沒有被優化過, 進入
sidetable_clearDeallocating
函數, 能夠看到weak
引用是存放到SideTable
中的
- 存放在了
SideTable
的weak_table_t
中
- 查看
weak_table_t
, 以下圖, 即weak
會被存放到一個全局的散列表中
- 會經過
weak_clear_no_lock
函數, 對弱指針置爲nil, 同時移除刪列表中的weak
記錄
- 若是
isa
被優化過, 而且對象被弱引用
或者將引用計數存到Sidetable
中, 就會調用clearDeallocating_slow
函數
- 進入
clearDeallocating_slow
函數, 能夠看到在函數中, 調用了weak_clear_no_lock
函數, 並清空了引用計數