1、Blocks簡(jiǎn)介
Block字面意思就是代碼塊(含義) iOS4.0缩多、Mac OS X 10.6開始Apple引入的特性(引入時(shí)間)
Block是Objective C語(yǔ)言中的對(duì)象 但是與NSObject有所區(qū)別 Block是特殊的Objective C對(duì)象 (block是OC的特殊對(duì)象)
Block 對(duì)象提供了一個(gè)使用 C 語(yǔ)言和 C 派生語(yǔ)言(如 Objective-C 和 C++)來(lái)創(chuàng)建表達(dá)式作為一個(gè)特別的函數(shù)嗦董。(block特殊之處)
在其他語(yǔ)言和環(huán)境中,一個(gè)block對(duì)象有時(shí)候被稱為“閉包(closure)”曹宴。在這里,它們通常被口語(yǔ)化為”塊(blocks)”,除非在某些范圍它們?nèi)菀缀蜆?biāo)準(zhǔn) C 表達(dá)式的塊代碼混淆。
對(duì)于閉包(closure),有很多定義往弓,其中閉包就是能夠讀取其它函數(shù)內(nèi)部變量的函數(shù)
“^”符號(hào)可以稱為caret ['k?r?t] 也叫脫字符 插入符
返回值(^塊對(duì)象名稱)(參數(shù)列表類型) = ^(參數(shù)列表){塊對(duì)象中的代碼};
2粒梦、用處
1)簡(jiǎn)單的回調(diào)過(guò)程亮航,不用再實(shí)現(xiàn)并調(diào)用某個(gè)函數(shù) (UIView動(dòng)畫)
2)代碼簡(jiǎn)潔,減少冗余代碼
3)與GCD結(jié)合使用 爽爆了
使用:UIView動(dòng)畫匀们、presentViewController缴淋、ASI
3、聲明和創(chuàng)建Block
聲明Block引用 無(wú)參無(wú)返回 無(wú)參有返回 有參無(wú)返回 有參有返回
定義Block
使用Block
typedef聲明 簡(jiǎn)稱typedef 為現(xiàn)有類型創(chuàng)建一個(gè)新的名字泄朴,或稱為類型別名重抖,在結(jié)構(gòu)體定義,還有一些數(shù)組等地方都會(huì)用到
返回值或參數(shù)為Block的
snippet 代碼片段
4祖灰、Block對(duì)變量存取管理
1)局部變量
局部變量钟沛,在Block中只讀。Block定義時(shí)copy變量的值局扶,在Block中作為常量使用恨统,所以即使變量的值在Block外改變,也不影響它在Block中的值
2)__Block修飾的變量
如果要在block內(nèi)修改block外聲明的局部變量三妈,那么一定要對(duì)該變量加__block標(biāo)記
3)Static修飾符的或全局變量
因?yàn)槿肿兞炕蜢o態(tài)變量在內(nèi)存中的地址是固定的畜埋,Block在讀取該變量值的時(shí)候是直接從其所在內(nèi)存讀出,獲取到的是最新值畴蒲,而不是在定義時(shí)copy的常量.
Block變量悠鞍,被__Block修飾的變量稱作Block變量。 基本類型的Block變量等效于全局變量或靜態(tài)變量 但對(duì)象的block變量不會(huì)
5模燥、Block的內(nèi)存管理
非ARC下
Block是默認(rèn)建立在棧上, 所以如果離開方法作用域, Block就會(huì)被丟棄
Block的copy咖祭、retain、release操作 不同于NSObject的copy蔫骂、retain么翰、release操作:
只要實(shí)現(xiàn)一個(gè)對(duì)周圍變量沒(méi)有引用的Block,就會(huì)顯示為是NSGlobalBlock
如果其中加入了對(duì)局部變量的引用辽旋,就是NSStackBlock
如果你對(duì)一個(gè)NSStackBlock對(duì)象使用了Block_copy()或者發(fā)送了copy消息硬鞍,就會(huì)得到NSMallocBlock
1)NSGlobalBlock:retain、copy、release操作都無(wú)效固该;
2)NSStackBlock:retain锅减、release操作無(wú)效,必須注意的是伐坏,NSStackBlock在函數(shù)返回后怔匣,Block內(nèi)存將被回收。即使retain也沒(méi)用桦沉。容易犯的錯(cuò)誤是[mutableAarry addObject:stackBlock]每瞒,(補(bǔ):在ARC中不用擔(dān)心此問(wèn)題,因?yàn)锳RC中會(huì)默認(rèn)將實(shí)例化的Block拷貝到堆上)在函數(shù)出棧后纯露,從mutableAarry中取到的stackBlock已經(jīng)被回收剿骨,變成了野指針。正確的做法是先將[stackBlock copy]到堆上埠褪,然后加入數(shù)組:[mutableAarry addObject:[[stackBlock copy] autorelease]]浓利。支持copy,copy之后生成新的NSMallocBlock類型對(duì)象钞速。
3)NSMallocBlock支持retain贷掖、release,雖然retainCount始終是1渴语,但內(nèi)存管理器中仍然會(huì)增加苹威、減少計(jì)數(shù)。copy之后不會(huì)生成新的對(duì)象驾凶,只是增加了一次引用牙甫,類似retain;
4)Block_copy與copy等效调违,Block_release與release等效窟哺;
5)對(duì)Block不管是retain、copy翰萨、release都不會(huì)改變引用計(jì)數(shù)retainCount,retainCount始終是1糕殉;
6)盡量不要對(duì)Block使用retain操作,不方便管理亩鬼。
Block的使用:UIView動(dòng)畫、presentViewController阿蝶、ASI
6雳锋、Block對(duì)objc對(duì)象的內(nèi)存管理
staticObj、instanceObj羡洁、localObj玷过、blockObj多種類型obj對(duì)象
主要是block被copy時(shí)其塊中用到的變量的引用計(jì)數(shù)
1)非ARC
staticObj在內(nèi)存中的位置是確定的,所以Block copy時(shí)引用計(jì)數(shù)不會(huì)改變。
instanceObj在Block copy時(shí)并沒(méi)有直接讓instanceObj對(duì)象本身引用計(jì)數(shù)加1辛蚊,但卻讓self引用計(jì)數(shù)加1粤蝎。所以在Block中可以直接讀寫instanceObj變量。
localObj在Block copy時(shí)袋马,系統(tǒng)自動(dòng)增加其引用計(jì)數(shù)初澎。
blockObj在Block copy時(shí)引用計(jì)數(shù)也不會(huì)改變。
使用__block避免循環(huán)引用 __block 類 *對(duì)象 = self
void(^block)(void)= ^{
[blockSelf doSomething];
};
Tips:
內(nèi)存主要分為
1.棧 - 由編譯器自動(dòng)分配釋放 里面的變量通常是局部變量 函數(shù)參數(shù)等
2.堆 - 一般由程序員分配釋放虑凛,若程序員不釋放碑宴,程序結(jié)束時(shí)可能由OS回收 alloc
3.全局區(qū)(靜態(tài)區(qū) static),全局變量和靜態(tài)變量的存儲(chǔ)是放在一塊的桑谍,初始化的全局變量和靜態(tài)變量在一塊區(qū)域延柠,未初始化的全局變量和未初始化的靜態(tài)變量在相鄰的另一塊區(qū)域。- 程序結(jié)束釋放 static
People *p; People *p2 = nil;
4.另外還有一個(gè)專門放常量的地方锣披。- 程序結(jié)束釋放 NSString *lastName = @“xue”;
lastName = @“dkjs”;
5贞间、方法區(qū)
Block的寫法:
1.有返回值有參數(shù)的
int(^myBlock1)(int) = ^(int num){
returen num;
}
//block的調(diào)用
myBlock1(3);
2.無(wú)返回值無(wú)參數(shù)的
//block的聲明 如果沒(méi)有參數(shù)聲明時(shí)必須寫(void) 實(shí)現(xiàn)的時(shí)候可以寫(void)也可以不寫
void(^myblock2)(void) =^(void){
NSLog(@"我是joye");
};
myblock2();
3.無(wú)返回值有參數(shù)的
//無(wú)返回值 有3個(gè)參數(shù)的block 多個(gè)參數(shù)要以逗號(hào)分隔
void (^myblock3)(int,float,NSString*) =^(int a,float b,NSString*name){
NSLog(@"%d----%f----%@",a,b,name);
};
myblock3(5,6.0,@"張三");
4.要定義block的全局變量的方法
#import <UIKit/UIKit.h> //用其代表在.h 文件中
@interface AppDelegate : UIResponder <UIApplicationDelegate>
{
void(^myblock4)(void);
UILabel *(^myblock5)(void);
}
@end
#import "AppDelegate.h" //用其代表在.m 文件中
myblock4= ^{
NSLog(@"123456");
};
myblock4();
myblock5 =^{
UILabel *label =[[UILabel alloc] init];
return label;
};
UILabel *label = myblock5();
5.當(dāng)多次重復(fù)輸入格式相同的塊時(shí) 可以給這個(gè)類型起一個(gè)別名 方便創(chuàng)建相同類型的block 在.h中聲明
//類型定義 相當(dāng)于給某種類型 起了一個(gè)別名 方便我們創(chuàng)建相同類型block
#import <UIKit/UIKit.h>
typedef void(^totalBlock)(void);
@interface AppDelegate : UIResponder <UIApplicationDelegate>
{
totalBlock myblock6;
}
#import "AppDelegate.h"
//使用類型定義創(chuàng)建全局block
myblock6 =^{
};
myblock6();
6.類型定義 如果參數(shù)的位置不同 不能使用同一個(gè)block創(chuàng)建
#import <UIKit/UIKit.h>
//類型定義只能使用定義好的類型 如果參數(shù)位置不一樣 也不能使用同一個(gè)block創(chuàng)建
typedef NSString*(^totalBlock1)(NSString*,int);
typedef NSString*(^totalBlock2)(int, NSString*);
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@end
#import "AppDelegate.h"
totalBlock1 myblock7 =^(NSString *name,int a){
return name;
};
myblock7(@"joye",1)
block內(nèi)存管理
1.在Block內(nèi)部 使用 外部 局部變量 如果一個(gè)外部局部變量沒(méi)有經(jīng)過(guò)__block描述 就在內(nèi)部使用 那么當(dāng)前變量就會(huì)被copy一份新值
int a = 100;
a = 200;
void(^myBlock)(void)=^{
NSLog(@"%d",a);
};
a = 300;
a = 400;
myBlock();
//輸出結(jié)果 a 的值為200
2.如果不想在block內(nèi)部copy 要用__block描述
__block int b = 1111;
b = 2222;
void (^myBlock2)(void)=^{
NSLog(@"%d",b);
};
b = 3333;
myBlock2();
//輸出結(jié)果 a 的值為 3333
3.block 使用全局變量 那么不會(huì)進(jìn)行copy
{
int c;
}
c = 1000;
c = 2000;
void(^myBlock3)(void)=^{
NSLog(@"%d",c);
};
c = 3000;
myBlock3();
//輸出的 c 的值為3000
4.被static描述變量是 不copy 使用原值
static int d = 123;
d = 321;
void(^myBlock4)(void)=^{
NSLog(@"%d",d);
};
d = 456;
myBlock4();
//輸出的 d 的值為123
要在 block 內(nèi)部 修改外部局部變量 要加 ____block__ 或 ____weak__ 描述
在 block 內(nèi)部修改全局變量和靜態(tài)變量 不用加 ____block__
__block int e = 666;
void(^myBlock5)(void)=^{
NSLog(@"%d",e);
e = 999;
};
myBlock5();
總結(jié):當(dāng)變量被 __block 或者 static 描述 或者使用的變量是全局變量時(shí) 在block內(nèi)部使用都不進(jìn)行copy 而是使用原值
內(nèi)存相關(guān)
內(nèi)存分區(qū):
棧:不需要程序員進(jìn)行管理 基本數(shù)據(jù)類型
堆:需要程序員進(jìn)行管理 對(duì)象類型
全局區(qū)(靜態(tài)區(qū)) 全局和靜態(tài)變量
常量區(qū) NSString*str = @“123”;
方法區(qū)
//默認(rèn)是在棧區(qū)創(chuàng)建Block,出了當(dāng)前方法block會(huì)被系統(tǒng)釋放
//__NSGlobalBlock__ 是在沒(méi)有使用任何外部變量時(shí)block的類型
//__NSStackBlock__ 使用了外部變量后block的類型
//不管是 __NSGlobalBlock__ 還是 __NSStackBlock__ 都在棧區(qū)
//__NSGlobalBlock__ 使用retain copy release等內(nèi)存管理的關(guān)鍵字 都是無(wú)效的
//__NSStackBlock__ 使用retain release依然無(wú)效 但是使用copy有效
//__NSMallocBlock__ 當(dāng)block為 __NSStackBlock__ 類型時(shí) 進(jìn)行copy后變成 __NSMallocBlock__ 類型
//__NSMallocBlock__ 是在堆上面創(chuàng)建的 可以對(duì)其retain copy release盈罐,但是進(jìn)行這些內(nèi)存操作后 引用系數(shù)一直為1 但不代表引用計(jì)數(shù)沒(méi)有增加 不建議對(duì)其retain操作