Block結(jié)構(gòu)
block的代碼是內(nèi)聯(lián)的荐吉,效率高于函數(shù)調(diào)用
block對于外部變量默認(rèn)是只讀屬性淑倾,即在Block函數(shù)體里面不能改變Block之外的變量脚猾,只可以讀取力喷。否則會報(bào)錯(cuò)滋尉。
block被Objective-C看成是對象處理自脯,其實(shí)真實(shí)的存儲情況是一個(gè)結(jié)構(gòu)體的形式
struct Block_layout {
void *isa;
int flags;
int reserved;
void (*invoke)(void *, ...);
struct Block_descriptor *descriptor;
/* Imported variables. */
};
1.這里主要是研究下block的調(diào)用順序
typedef void(^myBlockTest)(int);//聲明一個(gè)block塊(block 的重命名)
-(void)testBlock:(NSString *)str andBlock:(myBlockTest)block
{
3. NSLog(@"str===%@",str);
4. //block(100); //將100傳給參數(shù)intParameter,回調(diào)block的實(shí)現(xiàn)函數(shù)
//block塊的回調(diào)敦跌,當(dāng)這行代碼不注釋時(shí)憋沿,
控制臺輸出:NonAtomicTest[2841:113472] str===我是字符竄
NonAtomicTest[2841:113472] 實(shí)現(xiàn)block
NonAtomicTest[2898:116086] 大于10
//當(dāng)這行代碼注釋時(shí)
控制臺輸出:NonAtomicTest[2841:113472] str===我是字符竄
}
-(void)test1
{
1. NSString *str = @"我是字符竄";
2. [self testBlock:str andBlock:^(int intParameter) {
// str = @"1111”;
//這行代碼是錯(cuò)誤的,因?yàn)檫@個(gè)大括號里面是block塊的實(shí)現(xiàn)季稳,它只可以對block塊的參數(shù)intParameter賦值擅这,不可以對testBlock:andBlock:這個(gè)函數(shù)的參數(shù)str賦值。
5. NSLog(@"實(shí)現(xiàn)block");
6. if (intParameter>10) {
NSLog(@"大于10");
}
else
{
NSLog(@"不大于10");
}
}];
}
這個(gè)例子已經(jīng)標(biāo)出調(diào)用順序景鼠,這里將block塊可以看作是內(nèi)聯(lián)函數(shù)
補(bǔ)充:如何在block中修改外部變量
__block int a = 0;
void (^blockTest)(void) = ^{
a = 1;
}
blockTest();
2.block中__strong和__weak的使用
問:為什么使用weakSelf
答:通過 clang -rewrite-objc 源代碼文件名 將代碼轉(zhuǎn)為c++代碼(實(shí)質(zhì)是c代碼)仲翎,可以看到block是一個(gè)結(jié)構(gòu)體,它會將全局變量保存為一個(gè)屬性(是_ _strong的)铛漓,而self強(qiáng)引用了block這會造成循環(huán)引用溯香。所以需要使用__weak修飾的weakSelf。self 持有block浓恶,block持有self玫坛。
問:在block中weak 和strong的使用場景是什么?
答:(摘自http://www.reibang.com/p/36342264d6dfApple )官方的建議是包晰,傳進(jìn) Block 之前湿镀,把 ‘self’ 轉(zhuǎn)換成 weak automatic 的變量,這樣在 Block 中就不會出現(xiàn)對 self 的強(qiáng)引用伐憾。如果在 Block 執(zhí)行完成之前勉痴,self 被釋放了,weakSelf 也會變?yōu)?nil塞耕。
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[weakSelf doSomething];
});
clang 的文檔表示蚀腿,在 doSomething 內(nèi),weakSelf 不會被釋放扫外。但莉钙,下面的情況除外:
__weak __typeof__(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[weakSelf doSomething];
[weakSelf doOtherThing];
});
在 doSomething 中,weakSelf 不會變成 nil筛谚,不過在 doSomething 執(zhí)行完成磁玉,調(diào)用第二個(gè)方法 doOtherThing 的時(shí)候,weakSelf 有可能被釋放驾讲,于是蚊伞,strongSelf 就派上用場了:
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
__strong typeof(self) strongSelf = strongSelf;
[strongSelf doSomething];
[strongSelf doOtherThing];
});
如果僅僅使用__weak去修飾變量席赂,當(dāng)別處把變量釋放后,block中該變量也會被釋放掉时迫。
__strong 確保在 Block 內(nèi)颅停,strongSelf 不會被釋放。當(dāng)加上修飾符strong時(shí)掠拳,當(dāng)別處(別的block 內(nèi))把變量釋放掉癞揉,但調(diào)用該變量的block如果仍然沒有執(zhí)行結(jié)束,那么系統(tǒng)就會等待block執(zhí)行完成后再釋放溺欧,對該變量在block中的使用起到了保護(hù)作用喊熟。當(dāng)block執(zhí)行結(jié)束后會自動釋放掉。
總結(jié)
arc
>當(dāng)在block內(nèi)需要訪問self相關(guān)屬性的時(shí)候姐刁,添加 __weak 防止循環(huán)引用芥牌。
>當(dāng)block內(nèi)的變量不僅僅在block中使用的時(shí)候,為了防止變量提前釋放聂使,添加 __strong壁拉。
>當(dāng)block內(nèi)需要引用外部變量的時(shí)需要添加 __block ,因?yàn)榫植孔兞砍隽朔秶蜁尫叛乙牛莃lock調(diào)用的時(shí)機(jī)是不確定的扇商。所以使用 __ block 將block復(fù)制到堆區(qū)凤瘦,引用的變量也在堆區(qū)宿礁,以后的操作也都在這里進(jìn)行。
>但是__block 本身無法避免循環(huán)引用的問題蔬芥,所以我們可以通過在 block 內(nèi)部手動把 blockObj 賦值為 nil 的方式來避免循環(huán)引用的問題梆靖。另外一點(diǎn)就是 __block 修飾的變量在 block 內(nèi)外都是唯一的,要注意這個(gè)特性可能帶來的隱患笔诵。
注:__block有一點(diǎn):這只是限制在ARC環(huán)境下返吻。在非arc下,__block是可以避免引用循環(huán)的乎婿。
block中各種情況的出現(xiàn)及解決辦法
#define BLog(prefix,obj) {NSLog(@"位置和指針變量名:%@ ,指針內(nèi)存地址:%p, 指針值:%p ,指向的對象:%@ ",prefix,&obj,obj,obj);}
// 強(qiáng)引用
- (void)blockVariableStrongReferenceTest
{
NSLog(@"\n");
NSObject *obj = [[NSObject alloc] init];//+1
BLog(@"StrongRef obj",obj);
void(^testBlock)()= ^(){
BLog(@"StrongRef in block",obj);//+1Block中obj指針已經(jīng)不是外部的obj指針了,它是外部變量obj的拷貝,內(nèi)存引用計(jì)數(shù)加一
};
testBlock();
// Block外部嘗試將obj置為nil
obj = nil;//-1
testBlock(); // 第二次調(diào)用block
}
解釋:block內(nèi)部的obj 指針是外部obj指針的拷貝测僵,有2個(gè)指針指向同一個(gè)NSObject對象,(沒有重新分配空間)但只將外部的obj指針置為nil,NSObject對象的引用計(jì)數(shù)不為0谢翎,無法回收捍靠。
// 弱引用
- (void)blockVariableWeakReferenceTest
{
NSObject *obj = [[NSObject alloc] init];//+1
__weak NSObject *weakObj = obj;//+0 weakObj和obj是2個(gè)不同的指針,指向同一塊內(nèi)存地址森逮,但是因?yàn)槭侨跻谜テ牛砸糜?jì)數(shù)不改變
BLog(@"WeakRef weakObj", weakObj);
void(^testBlock)()= ^(){
BLog(@"weakObj in block",weakObj);
};
testBlock();
obj = nil; // -1=0
testBlock();//
}
解釋:在block中__weak聲明的指針去引用對象 可以避免循環(huán)引用的問題,但是當(dāng)外部對象被釋放了褒侧,block 內(nèi)部會訪問不到這個(gè)對象.
//多線程時(shí)Block生命周期內(nèi)對象安全
- (void)blockVariableMutiThreadTest
{
NSObject *obj = [[NSObject alloc]init]; //obj強(qiáng)引用,<NSObject: 0x7f9413c1c040>對象引用計(jì)數(shù)+1良风,=1
BLog(@"obj", obj);
__weak NSObject *weakObj = obj;//weakObj弱引用,<NSObject: 0x7f9413c1c040>對象引用計(jì)數(shù)不變谊迄,=1
BLog(@"weakObj-0", weakObj);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
__strong NSObject *strongObj = weakObj; //strongObj強(qiáng)引用,<NSObject: 0x7f9413c1c040>對象引用計(jì)數(shù)+1,=2
sleep(3);
BLog(@"weakObj - block", weakObj);
BLog(@"strongObj - block", strongObj);
});
sleep(1);
obj = nil; //obj被置為nil烟央,<NSObject: 0x7f9413c1c040>對象引用計(jì)數(shù)-1统诺,=1
BLog(@"weakObj-1", weakObj); //沒被釋放
sleep(4); //block在異步線程中執(zhí)行完畢(在另一塊內(nèi)存中執(zhí)行),block內(nèi)存被釋放疑俭,<NSObject: 0x7f9413c1c040>對象引用計(jì)數(shù)-1篙议,=0;ARC開始把0x7f9413c1c040對象內(nèi)存回收怠硼,把弱引用weakObj置為nil
BLog(@"weakObj-2", weakObj);
}
總結(jié) :
多線程的時(shí)候鬼贱,在 block 外部用__weak聲明的變量指向一個(gè)對象, 通過把weak聲明的變量值賦值給block內(nèi)部的```strong變量,實(shí)現(xiàn)在block內(nèi)對該對象進(jìn)行強(qiáng)引用香璃,這樣可以在block生命周期內(nèi)保留該對象不被釋放这难,在block生命周期結(jié)束后,對象內(nèi)存被釋放葡秒。
(void)blockVariable
{
NSObject *obj = [[NSObject alloc]init]; //指向的對象:<NSObject: 0x7fb4039063b0>
BLog(@"obj",obj);
__block NSObject *blockObj = obj; //blockObj指向?qū)ο?0x7fb4039063b0
obj = nil;
BLog(@"blockObj -1",blockObj); //blockObj,0x7fff52365c90
void(^testBlock)() = ^(){
BLog(@"blockObj - block",blockObj); //blockObj,0x7fb401c7d7f8,指向的對象:<NSObject: 0x7fb4039063b0>
NSObject *obj2 = [[NSObject alloc]init]; // obj2 ,0x7fff52365bc8,指向的對象:<NSObject: 0x7fb401c83c40>
BLog(@"obj2",obj2);
blockObj = obj2;
BLog(@"blockObj - block",blockObj); //blockObj,0x7fb401c7d7f8,指向?qū)ο?<NSObject: 0x7fb401c83c40>
};
NSLog(@"%@",testBlock); //block 的地址 <__NSMallocBlock__: 0x7fb401c07d20>
BLog(@"blockObj -2",blockObj); //blockObj地址發(fā)生變化姻乓,0x7fb401c7d7f8,/指向的對象:<NSObject: 0x7fb4039063b0>
testBlock();
BLog(@"blockObj -3",blockObj); //blockObj,0x7fb401c7d7f8,指向?qū)ο?<NSObject: 0x7fb401c83c40>
}
分析 :
第3處日志打印了一個(gè)testBlock對象,blockObj的地址發(fā)生變化眯牧。此時(shí)蹋岩,block對象 從棧拷貝到堆上学少,__block變量blockObj剪个,也被拷貝到堆上。block對象擁有blockObj指針指向的對象版确。注意:這是個(gè)強(qiáng)引用哦扣囊。
關(guān)注4到8 處日志,用__block關(guān)鍵字聲明blockObj指針后绒疗,block內(nèi)外的變量blockObj都是0x7fa084905838侵歇,也就是block內(nèi)外的blockObj指針是同一個(gè)指針。
block內(nèi)部改變 blockObj指針指向的對象吓蘑,改動在 block外部可見狡赐。
常用的出現(xiàn)循環(huán)使用:(只要你在block里用到了self所擁有的東西块仆!)
block在copy時(shí)都會對block內(nèi)部用到的對象進(jìn)行強(qiáng)引用(ARC)或者retainCount增1(非ARC)靠汁。在ARC與非ARC環(huán)境下對block使用不當(dāng)都會引起循環(huán)引用問題急黎,一般表現(xiàn)為,某個(gè)類將block作為自己的屬性變量棋嘲,然后該類在block的方法體里面又使用了該類本身酒唉,簡單說就是
self.someBlock = ^(Type var){
[self dosomething];
或者self.otherVar = XXX;
或者_(dá)otherVar = ...XXX;
};
//block的這種循環(huán)引用會被編譯器捕捉到并及時(shí)提醒沸移。
- 即使在你的block代碼中沒有顯式地出現(xiàn)"self"痪伦,也會出現(xiàn)循環(huán)引用侄榴!只要你在block里用到了self所擁有的東西!
- 但對于這種情況网沾,我們無法通過加__weak聲明或者_(dá)_block聲明去禁止block對self進(jìn)行強(qiáng)引用或者強(qiáng)制增加引用計(jì)數(shù)癞蚕。但我們可以通過其他指針來避免循環(huán)引用,具體是這么做的:
__weak typeof(self) weakSelf = self;
self.blkA = ^{
__strong typeof(weakSelf) strongSelf = weakSelf;//加一下強(qiáng)引用辉哥,避免weakSelf被釋放掉
NSLog(@"%@", strongSelf->_xxView); //不會導(dǎo)致循環(huán)引用.
};
self.arr
1)ARC環(huán)境下:
ARC環(huán)境下可以通過使用_weak聲明一個(gè)代替self的新變量代替原先的self桦山,我們可以命名為weakSelf。通過這種方式告訴block醋旦,不要在block內(nèi)部對self進(jìn)行強(qiáng)制strong引用:(如果要兼容ios4.3恒水,則用__unsafe_unretained代替__weak,不過目前基本不需考慮這么low的版本)
self.arr = @[@111, @222, @333];
__weak typeof(self) weakSelf=self;
self.block = ^(NSString *name){
NSLog(@"arr:%@", weakSelf.arr);
};
2)MRC環(huán)境下:解決方式與上述基本一致饲齐,只不過將__weak關(guān)鍵字換成__block即可钉凌,這樣的意思是告訴block:小子,不要在內(nèi)部對self進(jìn)行retain了捂人!
委托delegate
一字訣:聲明delegate時(shí)請用assign(MRC)或者weak(ARC)御雕,
對block想要深入研究的 :http://www.reibang.com/p/ee9756f3d5f6 http://www.cocoachina.com/ios/20170527/19308.html
https://yq.aliyun.com/articles/62662