內存管理最重要的就是
誰建立誰釋放的原則
,基本上能夠解決咱們90%以上(筆者憑藉經驗猜想的數字,不要太較真)的問題,可是有時候,系統作優化,會不遵循這個原則。git
下面咱們經過一個例子來介紹:github
Mark
類@interface Mark : NSObject
+ (Mark *)newMark;
+ (Mark *)createMark;
+ (Mark *)getMark;
@end
@implementation Mark
+ (Mark *)newMark {
return [[Mark alloc] init];
}
+ (Mark *)createMark {
return [[Mark alloc] init];
}
+ (Mark *)getMark {
return [[Mark alloc] init];
}
@end
複製代碼
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
Mark * mark = [Mark createMark];
Mark * mark2= [Mark getMark];
Mark * mark3= [Mark newMark];
self.mark = mark;
self.mark2 = mark2;
self.mark3 = mark3;
}
複製代碼
Hopper
來查看這些代碼。在
[Mark createMark]
和[Mark getMark]
的內部其實 會調用autoreleaseReturnValue
來放入到自動釋放池中。[Mark newMark]
會自動返回,給持有的人swift
void * +[Mark newMark](void * self, void * _cmd) {
rax = [Mark alloc];
rax = [rax init];
return rax;
}
void * +[Mark createMark](void * self, void * _cmd) {
rax = [[Mark alloc] init];
rax = [rax autorelease];
return rax;
}
void * +[Mark getMark](void * self, void * _cmd) {
rax = [[Mark alloc] init];
rax = [rax autorelease];
return rax;
}
複製代碼
在控制器中,咱們能夠知道bash
對於
[[Mark createMark] retain]
和[[Mark getMark] retain]
其實僞代碼有些錯誤
,咱們經過查看彙編代碼能夠知道會有個imp___stubs__objc_retainAutoreleasedReturnValue
,其實就是把autoRelease
和retain
合併起來,這樣就不用再注入到自動釋放池中
.數據結構
void -[AutoReleaseVC viewDidLoad](void * self, void * _cmd) {
rax = var_20;
[[rax super] viewDidLoad];
var_28 = [[Mark createMark] retain];
var_30 = [[Mark getMark] retain];
var_38 = [Mark newMark];
[self setMark:var_28];
[self setMark2:var_30];
[self setMark3:var_38];
objc_storeStrong(var_38, 0x0);
objc_storeStrong(var_30, 0x0);
objc_storeStrong(var_28, 0x0);
return;
}
複製代碼
首先咱們要搞清兩個概念,一個是
autoreleasepool
,另一個是AutoreleasePoolPage
,帶着這兩個問題,咱們看下面例子。iphone
-rewrite-objc
查看cpp代碼- (void)testAutoPool {
@autoreleasepool {
NSObject * obj = [[NSObject alloc] init];
NSLog(@"obj = %@",obj);
}
}
複製代碼
經過命令 xcrun -sdk iphoneos clang -rewrite-objc -F Foundation -arch arm64 Auto.m
, 咱們看看 @autoreleasepool
究竟是什麼東西?優化
static void _I_AutoModel_testAutoPool(AutoModel * self, SEL _cmd) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
NSObject * obj = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_zx_py2__3n12pd_dj7nkk36jzqm0000gn_T_AutoModel_b08238_mi_0,obj);
}
}
struct __AtAutoreleasePool {
__AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
void * atautoreleasepoolobj;
};
複製代碼
再經過Hopper
咱們能夠更加清楚看到ui
void -[AutoModel testAutoPool](void * self, void * _cmd) {
var_28 = objc_autoreleasePoolPush();
var_18 = [[NSObject alloc] init];
NSLog(@"obj = %@", var_18);
objc_storeStrong(var_18, 0x0);
objc_autoreleasePoolPop(var_28);
return;
}
複製代碼
其實
@autoreleasepool{}
內部就是 經過var_28 = objc_autoreleasePoolPush()
和objc_autoreleasePoolPop(var_28);
來做用的spa
經過查找源碼objc4-723
中的NSObject.mm
,咱們能夠查看到 AutoreleasePoolPage
,而後咱們查看一下它的數據結構和一些方法線程
class AutoreleasePoolPage {
magic_t const magic;
id *next;
// 說明 一個 autoreleasePool 對應一個線程
pthread_t const thread;
AutoreleasePoolPage * const parent;
AutoreleasePoolPage *child;
uint32_t const depth;
uint32_t hiwat;
};
void *objc_autoreleasePoolPush(void) {
return AutoreleasePoolPage::push();
}
void objc_autoreleasePoolPop(void *ctxt) {
AutoreleasePoolPage::pop(ctxt);
}
複製代碼
咱們應該很清楚的知道,AutoreleasePoolPage 是一個 雙向鏈表,爲何要設置多張Page,其實系統仍是爲了節省空間,相似咱們的內存分頁的思想。
經過
autpreleasePoolPush
設置哨兵nil
也就是begain
, 裏面有個next
指針指定了下個obj的位置,也就是end
。那麼在調用autoreleasePoolPop
的時候其實就是釋放end - begain
的空間,同時 給這裏的每一個對象都發送一個release
方法。
橋接主要設計到下面幾個方法,其實CFBridgingRetain
內部是調用 __bridge_retained
的; CFBridgingRelease
內部也是調用 __bridge_transfer
;
__bridge
: 不會改變原來的ownership
;p = (__bridge_retained void *)obj
和 p = (void *) CFBridgingRetain(obj)
: 把OC裏面的ARCownership
全部權,給CF類型的對象
;(id)CFBridgingRelease(p)
和 (__bridge_transfer id)p
: 把CF的ownership
全部權,交給 OC對象
;CF對象 = (__bridge void *)OC對象
經過
__bridge
把OC 轉換成CF
- (void)brigeTest1 {
void * p = 0;
{
id obj = [NSObject new];
p = (__bridge void *)obj;
}
// 直接崩潰,由於p 不持有 obj 對象, 當 {} 結束的時候,obj被釋放了, 因此致使崩潰
NSLog(@" p = %@",p);
}
複製代碼
CF對象 = (__bridge_retained void *)OC對象
或者 CF對象 = (void *) CFBridgingRetain(OC對象)
- (void)brigeTest2 {
void * p = 0;
{
id obj = [NSObject new];
p = (__bridge_retained void *)obj;
}
NSLog(@" p = %@",p);
CFBridgingRelease(p);
}
- (void)brigeTest3 {
void * p = 0;
{
id obj = [NSObject new];
p = (void *) CFBridgingRetain(obj);
}
// 直接崩潰,由於p 不持有 obj 對象, 當 {} 結束的時候,obj被釋放了, 因此致使崩潰
NSLog(@" p = %@",p);
CFBridgingRelease(p);
}
複製代碼
id o = (__bridge id)p
- (void)test {
// 生成 CF p
void * p = 0;
id obj = [NSObject new];
p = (void *) CFBridgingRetain(obj);
// OC 對象
// 無論理p的內存
//Use __bridge to convert directly (no change in ownership)
//使用 __bridge 不會改變 p的全部權
id o = (__bridge id)p;
}
複製代碼
id o1 = (id)CFBridgingRelease(p)
和 id o2 = (__bridge_transfer id)p
- (void)test {
// 生成 CF p
void * p = 0;
id obj = [NSObject new];
p = (void *) CFBridgingRetain(obj);
// OC 對象
//Use CFBridgingRelease call to transfer ownership of a +1 'void *' into ARC
// 把p的全部權轉移給 oc對象持有
id o1 = (id)CFBridgingRelease(p);
// 和 CFBridgingRelease 等價
id o2 = (__bridge_transfer id)p;
}
複製代碼
參考