
最近看到了鏈?zhǔn)骄幊毯秃瘮?shù)式編程這兩個概念,網(wǎng)上查了一些資料,但是發(fā)現(xiàn)資料都千篇一律,我心中存在的這幾個疑惑根本沒有幫我解開。
1,為什么可以使用.語法來調(diào)用方法,而不是使用OC里面的[ ]。
2,為什么方法里面明明沒有參數(shù),當(dāng)方法的返回值是一個block時(shí),使用.語法調(diào)用方法可以傳參數(shù)。
不知道有沒有和我遇到一樣的問題的同學(xué),周末的時(shí)間用了一個下午一點(diǎn)一點(diǎn)琢磨。下面我來一一解釋這些問題。
首先,先簡紹下block
block的作用,主要用于回調(diào)傳值,解耦合
1.當(dāng)方法的參數(shù)是block類型,block的參數(shù)用于從內(nèi)向外傳值
2.block的返回值用于從外向內(nèi)返回結(jié)果
這兩句話是能實(shí)現(xiàn)鏈?zhǔn)骄幊痰年P(guān)鍵,好好理解下。
對于block,我們可以這樣理解
舉個例子:大家想必都看過警匪片,在片子里面,壞蛋都喜歡安裝一些遙控的炸彈來威脅警察,如果警察不聽話,壞蛋就會引爆炸彈。這里的炸彈就可以理解為block,而遙控器就可以理解為調(diào)用block。block就是我們提前存儲的一段代碼塊,當(dāng)我們需要使用的時(shí)候才去調(diào)用它。下面來看一段網(wǎng)絡(luò)請求數(shù)據(jù)的代碼來理解下這個遙控炸彈。
@interface ReuuestData : NSObject
//定義無參數(shù)無返回值的block
- (void)requestDataSucc:(void(^)())succBlock fail:(void(^)())failBlock;
@end
@implementation ReuuestData
- (void)requestDataSucc:(void (^)())succBlock fail:(void (^)())failBlock {
//這一塊就相當(dāng)于炸彈的遙控器, 在成功或者失敗里引爆炸彈
if (/* DISABLES CODE */ (YES)) {
succBlock(); //成功的block
}else {
failBlock(); //失敗的block
}
}
@end
下面我們在ViewController里使用下
ReuuestData *obj = [ReuuestData alloc] init];
//這一塊就相當(dāng)于埋了一個炸彈, 我們不管他什么時(shí)候會爆炸,決定他爆炸的時(shí)刻是安裝了這個炸彈接收信號的遙控器
[obj requestDataSucc:^{
//成功的回調(diào)
} fail:^{
//失敗的回調(diào)
}];
因?yàn)槲覀冊谶M(jìn)行網(wǎng)絡(luò)請求時(shí)不知道什么時(shí)候請求會成功, 我們首先讓obj 調(diào)用請求網(wǎng)絡(luò)的方法, 此時(shí),開始網(wǎng)絡(luò)請求, 當(dāng)請求結(jié)束后,會走對應(yīng)的成功或者失敗的方法。
接下來理解了上面的網(wǎng)絡(luò)請求的例子,咱們就以一個例子來講解鏈?zhǔn)骄幊?,這個例子在其他文章中也都有介紹,為了保證文章的完整性,我就再啰嗦一遍這個例子。
1、首先創(chuàng)建一個Person類,并為其增加兩個方法
- (void)name1;
- (void)sex1;
2、實(shí)現(xiàn)方法
- (void) name1 {
NSLog(@"name");
}
- (void) sex1 {
NSLog(@"sex")
}
3、實(shí)例化對象,并調(diào)用方法
Person*person = [[Personalloc]init];
[person name1];
[person sex1];
以上三部很簡單,相比大家都能看的懂。而我們最終的目標(biāo)是
person.name(@"Jason").sex(@"男");
首先我們要實(shí)現(xiàn)
[[person name] sex];
實(shí)現(xiàn)這個目標(biāo)很簡單,只需要調(diào)用[person name]的時(shí)候返回一個Person對象就可以了,下面我們來修改代碼
- (Person *)name2;
- (Person *)sex2;
- (Person *)name2 {
NSLog(@"name");
return self;
}
- (Person *)sex2 {
NSLog(@"sex");
return self;
}
這樣就可以實(shí)現(xiàn)了,
[[person name2] sex2];
這離我們的最終目標(biāo)還有一段距離。
先拆分下目標(biāo),先實(shí)現(xiàn)
[[person name](@"Jason") sex](@"男"); //這個是不是和我們最終的目標(biāo)很像,只需把中括號換成(.)就可以了
person.name(@"Jason").sex(@"男");
我們都知道OC里面調(diào)用方法使用的是[obj func],向一個方法發(fā)送一條消息。而沒有obj.func(param)的形式,要想實(shí)現(xiàn)這個,我們自然而然的會想到OC里面的block,只有block才是以()的方式調(diào)用的。
1、我們可以返回一個block,然后使用()去掉用。
2、那么我們要如何實(shí)現(xiàn)連續(xù)調(diào)用呢?我們再分析,要調(diào)用一個類的實(shí)例方法,必須由這個類的實(shí)例對象才能調(diào)用,如[[person name] sex]; 我們之所以可以連續(xù)使用[ ], 是因?yàn)槲覀兎祷亓艘粋€該實(shí)例對象。如果在返回的block里面返回一個實(shí)例對象不就可以連續(xù)調(diào)用了嗎,而block正好也能返回一個對象。下面我們再改寫代碼。
//返回一個block,而block的返回值是一個Person對象
- (Person * (^)())name3;
- (Person * (^)())sex3;
- (Person * (^)())name3 {
//可以直接返回一個block
return ^Person *(){
return self;
};
}
- (Person * (^)())sex3 {
//也可以定義一個block,接收返回的block,再把block返回
//等號右邊的Person *()可以省略不寫,Person *省略不寫的原因是返回值本身可以不寫,()可以省略不寫的原因是block無參數(shù)時(shí)可以省略()
Person *(^block)() = ^Person *(){
return self;
};
return block;
}
此時(shí)我們調(diào)用方法
[[person name3]() sex3]();
看到如此的寫法是不是很奇怪, OC里面調(diào)用方法沒有這樣寫的呀,不要著急,我們一步一步分解。
[person name3]
這一步大家都很熟悉吧,我們在前面講過,name3方法返回的是一個block,我們打印下這個返回值,看看到底是個什么鬼?
NSLog(@"%@", [person name3]);
//<__NSMallocBlock__: 0x7fa7714193c0>
打印結(jié)果告訴我們,返回值是一個block
//這種寫法就相當(dāng)于我們?nèi)フ{(diào)用了一個block,前面說過,只有block才會有()的這種語
//還記得前面講的定時(shí)炸彈嗎,當(dāng)執(zhí)行完[person name3]這句代碼的時(shí)候,我們就找到了那個炸彈,再執(zhí)行()的時(shí)候,我們就引爆了炸彈。
[person name3]()
前面說過,block也是有返回值的,我們同樣打印下block的返回值
NSLog(@"%@", [person name3]());
<Person: 0x7fd9e9daf980> //返回值是一個對象,很關(guān)鍵
接下來我們換一種寫法來理解這句代碼,把這句代碼分成兩步。
//1.聲明一個返回值是Person類型, 無參數(shù)的block,用于接收返回值
Person * (^returnBlock)() = [person name3];
NSLog(@"%@", [person name3]); //<__NSMallocBlock__: 0x7f93bfc105a0>
NSLog(@"%@", returnBlock); //<__NSMallocBlock__: 0x7f93bfcb7a00>
//2.調(diào)用block
returnBlock(); //注意此處返回的是一個Person對象, 這是我們實(shí)現(xiàn)鏈?zhǔn)骄幊痰年P(guān)鍵
NSLog(@"%@", returnBlock()); //<Person: 0x7fd9e9daf980>
上面的寫法是不是容易理解一點(diǎn)呢,首先聲明一個返回值是Person類型, 無參數(shù)的block,用于接收[person name3]的返回值, 此時(shí)我們找到了我們先前已經(jīng)存好的block(炸彈),然后再去調(diào)用block(引爆炸彈)
下面我們再來看之前的代碼
[[person name3]() sex3]();
//這一步操作分解之后就是這四步, 此處也能看出,鏈?zhǔn)骄幊棠芴岣唛_發(fā)效率和提高代碼的可讀性
Person * (^returnNameBlock)() = [person name3];
Person *personName = returnNameBlock();
Person * (^returnSexBlock)() = [personName sex3];
Person *personSex = returnSexBlock();
此時(shí)離我們最終實(shí)現(xiàn)的效果的前一步還差一點(diǎn),就是沒有參數(shù),我們再改寫代碼,加上參數(shù),實(shí)現(xiàn)下面的效果
[[person name](@"Jason") sex](@"男");
改寫
// 函數(shù)的返回值是一個Block,Block的返回值是當(dāng)前對象,Block有一個參數(shù)
- (Person * (^)(NSString *name))name4; //設(shè)置參數(shù)
- (Person * (^)(NSString *sex))sex4;
- (Person * (^)(NSString *name))name4 {
// 方法的返回是一個”有參有返回值的Block“
return ^Person *(NSString *name){
NSLog(@"%@", name);
// block的返回值是當(dāng)前對象
return self;
};
}
- (Person * (^)(NSString *sex))sex4 {
return ^Person *(NSString *sex) {
NSLog(@"%@", sex);
return self;
};
}
//調(diào)用
//(@"Jason")這個參數(shù)是block的參數(shù),并且是block作為返回值的參數(shù),在文章的開頭已經(jīng)說過,block的返回值參數(shù)用于從外向內(nèi)返回結(jié)果,這就是為什么我們在外面?zhèn)魅雲(yún)?shù)(@"Jason"),而在函數(shù)內(nèi)部會打印我們傳入的參數(shù),這點(diǎn)不要搞錯。-->>解釋文章開頭第二個問題
[[person name4](@"Jason") sex4](@"男");
OK,經(jīng)歷九九八十一難,我們終于到達(dá)了天竺,離求取真經(jīng)只差一步了
我們要的結(jié)果是
person.name(@"Jason").sex(@"男");
而我們現(xiàn)在的結(jié)果和這個目標(biāo)只差一步了,是不是感覺有點(diǎn)小激動呢。
還是文章開頭的第一個問題-->> 為什么可以使用.語法來調(diào)用方法,而不是使用OC里面的[ ]。
首先我們要知道OC里面的屬性聲明默認(rèn)是私有的,外部是不可以直接訪問的,而setter個getter方法是可以間接訪問屬性的。其實(shí)在使用self.propert語法時(shí),不是直接訪問屬性,而是隱士的調(diào)用了setter或者getter方法來訪問屬性的,在編譯與運(yùn)行期間,并不關(guān)心你是否在真正的在獲取一個屬性。要實(shí)現(xiàn)使用.點(diǎn)語法調(diào)用方法,你的方法,必須有一個返回值,并且無參數(shù)。這樣才符合getter方法的書寫規(guī)范,而我們最終使用的方法整好符合這種規(guī)范。所以,你就可以直接使用 person.name(@"Jason").sex(@"男");來調(diào)用方法了。
最終我們實(shí)現(xiàn)了
person.name(@"Jason").sex(@"男");
你可以盡情的 . 下去了
關(guān)于文章的第一個問題 -->> 為什么可以使用.語法來調(diào)用方法,而不是使用OC里面的[ ], 不知道自己的理解是否準(zhǔn)確,如果有錯誤,還請簡友門幫忙糾正。