iOS的內存管理和引用計數規則objective-c
內存管理的思考方式express
ARC有效時,id類型和對象類型必須加上全部權修飾符,一共有四種安全
__strong函數
id和對象類型若是不加全部權修飾符那麼默認爲__strong類型學習
id obj = [[NSObject alloc]init] id __strong obj = [[NSObject alloc]init] //以上兩種在ARC有效狀況下是相同的
//ARARC { id __strong obj = [[NSObject alloc]init] } //ARC無效時 { id obj = [[NSObject alloc]init] [obj release] } //ARC無效時執行release操做
__strong修飾符表示對對象的強引用,持有強引用的變量在超出其做用域時被廢棄,它強引用的對象也會被釋放指針
含有__strong修飾的變量,不單單在做用域上,在賦值上也能正確管理對象的生命週期code
__weak對象
__unsafe_unretained生命週期
__autoreleasing內存
屬性聲明的屬性修飾符與全部權修飾符之間的關係
屬性修飾符 | _unsafe_unretained |
---|---|
assign | _unsafe_unretained |
copy | _strong(指針變量指向的是新的被複制的對象) |
retain | _strong |
strong | _strong |
unsafe_unretained | _unsafe_unretained |
weak | _weak |
爲屬性添加各類修飾符就至關於給變量添加各類對應的全部權修飾符
@property (strong) id obj; -------------------------- id __strong obj = [[NSObject alloc]init]
ARC的規則
Block
block是什麼
//一句話歸納,block是帶有自動變量的匿名函數 ^(int param){ NSLog(@"%d",param); } ------------------------------------- //上面的爲簡寫的,完整的block形式爲 //^返回值類型 (參數列表){表達式} ^void (int param){ NSLog(@"%d",param); } //完整形式的block語法與C語言函數定義相比,僅有兩點不一樣 //1.沒有函數名---沒有函數名由於它是匿名函數 //2.帶有"^"符號---^是插入記號,方便查找
block的返回值類型能夠省略,省略返回值類型時
block的參數類型也能夠省略
^(void)(void){expression} ------------------------- ^{expression}
//C語言的函數指針的寫法 int func(int a){ printf("%d",a); } int (*funcptr)(int) = &func //將func函數的地址賦值給函數指針funcptr //block的寫法 int (^blk)(int a); //僅僅是將c語言函數指針的"*"更換成了"^"
block類型變量與c語言變量的用法徹底相同,能夠做爲如下用途使用
block變量的使用
//將block賦值給block變量 int (^blk)(int) = ^(int){}; //將block變量賦值給block變量 int (^blk1)(int) = blk; //兩個block變量互相賦值 int (^blk2)(int); blk2 = blk1;
//在函數參數中使用block類型變量能夠向函數傳遞block void func((int)(^blk)(int)); //在函數返回值中使用block能夠將返回值指定爲block void func(){ return ^{return;}; }
//使用typedef定義block typedef (int)(^blk)(int); //定義一個block,後面該block的類型就爲blk //經過block類型變量調用block與在c語言中一般的函數執行沒什麼區別 blk func(blk block,int rate){ return blk(rate); }
//這裏blk截獲的是Array對象的實例指針,經過這個實例指針調用該對象的方法是徹底沒問題的,可是若是向Array指針賦值的話就會編譯錯誤(能夠用__block解決) id Array = [NSMutableArray new]; void (^blk)(void) = ^{ id obj = [NSObject new]; [Array addObject:obj]; };
在block中若是須要改變被截獲的外部變量的值,可使用__block說明符(__block存儲域類說明符)來解決
block代碼轉換爲cpp代碼分析(待完善)
//block對外部變量捕獲的原理,使用cpp代碼查看 //這裏寫一個block捕獲外部變量val的值 int val = 10; void (^blk)(void) = ^{ printf("%d",val); }; //block本質也是一個OC的對象,oc對象都是結構體,其中含有指向父類的isa指針 struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; int val; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _val, int flags=0) : val(_val) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; //這裏使用clang -rewrite-objc將.m代碼轉換成.cpp代碼,這裏的_cself是block的自身的結構體指針,能夠看到在block的函數中是將val從新建立了一個變量進行輸出 static void __main_block_func_0(struct __main_block_impl_0 *__cself) { int val = __cself->val; // bound by copy printf("%d",val); }
三種block的形式
NSConcreteGlobalBlock(全局Block,存放在全局區[數據區])
NSConcreteStackBlock(棧Block)
NSConcreteMallocBlock(堆Block)
配置在全局區的block在變量做用域外也能夠經過指針安全的訪問,可是配置在棧上的block一旦其做用域結束就會被系統回收
Block提供了將Block和__Block變量複製到堆上的方法來解決這個問題
複製到堆上的block將類對象NSMallocBlock賦值給isa指針
struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _val, int flags=0) : val(_val) { //注意這裏就是講isa指針指向堆Block impl.isa = &_NSConcreteStackBlock; } };