堆棧的區(qū)別:經(jīng)典解釋
原作者不詳,未詳細查詢胜卤,從其它地方轉(zhuǎn)載并修改部分?jǐn)⑹觯卮苏f明
- 預(yù)備知識:程序的內(nèi)存分配
一個由C/C++編譯的程序占用的內(nèi)存分為以下幾個部分- 棧(stack):由編譯器自動分配釋放 葛躏,存放函數(shù)的參數(shù)值澈段,局部變量的值等舰攒。其操作方式類似于數(shù)據(jù)結(jié)構(gòu)中的棧。
- 堆(heap): 一般由程序員分配釋放芒率, 若程序員不釋放,程序結(jié)束時可能由OS回收 偶芍。注意它與數(shù)據(jù)結(jié)構(gòu)中的堆是兩回事充择,分配方式類似于鏈表匪蟀。
- 全局區(qū)(靜態(tài)區(qū)static):全局變量和靜態(tài)變量的存儲是放在一塊的,初始化的全局變量和靜態(tài)變量在一塊區(qū)域材彪, 未初始化的全局變量和未初始化的靜態(tài)變量在相鄰的另一塊區(qū)域琴儿,程序結(jié)束后由系統(tǒng)釋放。
- 文字常量區(qū):常量字符串放在這里造成, 程序結(jié)束后由系統(tǒng)釋放
- 程序代碼區(qū):存放函數(shù)體的二進制代碼。
- 例子程序
//main.cpp
int a = 0; //全局初始化區(qū)
char *p1; //全局未初始化區(qū)
main()
{
int b; //棧
char s[] = "abc"; //棧
char *p2; //棧
char *p3 = "123456"; //123456\0在常量區(qū)雄嚣,p3在棧上。
static int c =0缓升;//全局(靜態(tài))初始化區(qū)
p1 = (char *)malloc(10);
p2 = (char *)malloc(20);
//分配得來得10和20字節(jié)的區(qū)域就在堆區(qū)。
strcpy(p1, "123456"); //123456\0放在常量區(qū)港谊,編譯器可能會將它與p3所指向的"123456"優(yōu)化成一個地方。
}
Block基本語法
//As a local variable:
returnType (^blockName)(parameterTypes) = ^returnType(parameters) {...};
As a property:
//As a property:
@property (nonatomic, copy) returnType (^blockName)(parameterTypes);
As a method parameter:
//As a method parameter:
- (void)someMethodThatTakesABlock:(returnType (^)(parameterTypes))blockName;
As an argument to a method call:
//As an argument to a method call:
[someObject someMethodThatTakesABlock:^returnType (parameters) {...}];
As a typedef:
//As a typedef:
typedef returnType (^TypeName)(parameterTypes);
TypeName blockName = ^returnType(parameters) {...};
修飾Block成員變量
Block 成員需要使用 copy 進行修飾歧寺,需要考慮Block是否線程安全,必要情況下使用atomic參數(shù)成福,當(dāng)使用atomic參數(shù)也不能百分百確保線程安全,因此在使用時最好將block屬性賦值給本地變量在使用奴艾,以防止其它線程將self.block置空。實際上蕴潦,我們使用修飾符 copy 是因為將存在棧區(qū)上的block轉(zhuǎn)移到堆區(qū)上,這個習(xí)慣是在MRC下的潭苞,現(xiàn)在在ARC下使用 copy 和 strong 是相同的。
2015/10/15更新
:為什么ARC下使用copy和strong是相同的
要解釋這個問題必須要知道block的實現(xiàn)原理此疹,具體的實現(xiàn)原理參閱『參考3』僧诚。我在這里直接給出解釋蝗碎,實際上Block是有不同類型的,這些類型的區(qū)別在于Block是存儲在哪一個內(nèi)存區(qū)間蹦骑。
在MRC下有三種類型(見名知意):
- _NSConcreteGlobalBlock 全局的靜態(tài) block慈省,不會訪問任何外部變量眠菇。
- _NSConcreteStackBlock 保存在棧中的 block袱衷,當(dāng)函數(shù)返回時會被銷毀。
- _NSConcreteMallocBlock 保存在堆中的 block笑窜,當(dāng)引用計數(shù)為 0 時會被銷毀。
但在ARC下只有兩種怖侦,也就是第一種和第三種谜叹,也就是說本來需要在MRC下使用copy所做的操作實際上ARC幫我們做了,因此使用copy和strong也就無所謂了荷腊。
循環(huán)引用(ARC)
使用:
__weak __typeof(self) weakSelf = self;
解決循環(huán)引用self的問題艳悔。
** AFNetworking 作者的使用技巧:**
MyObject *obj = [[MyObject alloc]init];
obj.text = @"string";
__weak MyObject *weakObj = obj;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
__strong MyObject *strongObj = weakObj;
sleep(3);
});
sleep(1);
obj = nil;
sleep(4);
把變量在 block 外先用 __weak
聲明女仰,在 block 內(nèi)把前面 __weak
聲明的變量在賦值給 __strong
修飾的變量。這種寫法的好處就是可以讓變量在 block 內(nèi)部安全使用疾忍,即使外部對象釋放了,也會在 block 的生命周期內(nèi)保留該變量一罩。這種寫法非常巧妙杨幼,既避免了循環(huán)引用的問題聂渊,又可以在 block 內(nèi)部持有該變量。
參考:
正確使用Block避免Cycle Retain和Crash
How Do I Declare A Block in Objective-C?
談Objective-C Block的實現(xiàn)
block沒那么難(一):block的實現(xiàn)
若有錯誤請不吝指教