【一】遭遇
到今天iOS開發(fā)中最常用的語言還是objc,市場(chǎng)就像泰坦尼克號(hào),人雖然在上樓,但是船在下沉,所以人還是在下沉。雖然swift出現(xiàn)的迅猛,但是大部分開發(fā)者面對(duì)的還是objc。什么時(shí)候swift替代objc,今天的objc開發(fā)人員也不用太著急,說不定船都沉了(蘋果保佑)。
objc最令人頭疼的是他看起來像部落語言一樣的表達(dá)。比如下面這行新手代碼,但是就像求偶訊號(hào)一樣晦澀:
[[NSMutableDictionary alloc] initWithContentsOfURL:[NSURL URLWithString:@"a.txt"]][@"persons"][12];
但它并沒有告訴別人任何有意義的事情,分解一下語法就像下面一樣:
NSMutableDictionary* dict = [[NSMutableDictionary alloc] initWithContentsOfURL:[NSURL URLWithString:@"a.txt"]];
NSArray* arr = dict[@"persons"];
arr[12];
語言發(fā)展到今天,代碼的普世價(jià)值不是效率,而是開發(fā)效率。這就是為什么java能夠成為王者的原因。因?yàn)閖ava寫起來快,好讀。
【二】探索
objc比起java又慢又不好讀。那么如何讓objc看起來像java一樣呢。縱觀objc全部語法,只有block能夠辦到。我們來看block的定義
returnType? (^name)? (val1, val2, ...);
返回類型? ? 變量名? ? ? ? 參數(shù)列表//我們可以從表面認(rèn)為他是一根指向函數(shù)的指針,這個(gè)函數(shù)的樣子如下(returnType)name(val1, val2, ...)
{//......}
block具有一個(gè)函數(shù)的外觀,又被當(dāng)作一個(gè)變量。那么block就具備兩個(gè)功能,第一:可以作為類的屬性被'點(diǎn)'出來。第二:可以當(dāng)作函數(shù)直接調(diào)用。下面逐個(gè)解釋,第一個(gè)類的屬性可以點(diǎn)出來,比如person.name;這很好理解,你一定見過,str.length;對(duì)吧。第二個(gè)呢,block作為一個(gè)變量,但是又可以把它當(dāng)作指向函數(shù)的指針一樣調(diào)用。
NSString*(^myBlock)() = ^(){return@"菊樂"; };//定義一個(gè)返回值為NSString類型,無參的,并且名字叫做myBlock的blockmyBlock();//這一行就是調(diào)用NSString result= myBlock();//這是取出block執(zhí)行后拿到的返回值,也就是@"菊樂"
這里再解釋一次block的定義:上面的myBlock可以認(rèn)為是指向一個(gè)定義如下的函數(shù)的指針,這個(gè)函數(shù)是
(NSString*) myBlock()
{//...}//指向函數(shù)的指針NSString* (*p) ();//可以認(rèn)為指針p就是myBlock(其實(shí)block的真實(shí)身份更加復(fù)雜)
【三】推理
我們來幻想一下objc寫起來是這樣的
//設(shè)置視圖位置和大小,設(shè)置視圖背景色view.setFrame(0,0,50,50).setBackgroundColor(@"#0c0c0c".toColor() );//移除空格并在控制臺(tái)打印字詩句@"白 日 依 山 盡 , 黃 河 入 海 流".removeStr(@"").nslog();
而實(shí)際上要寫5行的代碼,一行3秒,3行15秒。就這樣錯(cuò)過了一次搖一搖的機(jī)會(huì),人生在緩慢的艱難,而我們卻還一無所知。
view.frame = CGRectMake(0,0,50,50);
view.backgroundColor= [UIColor colorWithRed:0.5green:0.5blue:0.5alpha:1.0];
NSString* str=@"白 日 依 山 盡 , 黃 河 入 海 流";
str= [str stringByReplacingCharactersInRange:@""withString:@""];
NSLog(str);
現(xiàn)在我們來倒著分析如何使用block來實(shí)現(xiàn)鏈?zhǔn)骄幊?,再來觀察這句話:
view.setFrame(0,0,50,50).setBackgroundColor(@"#0c0c0c".toColor() );
其中的setFrame和setBackgroundColor他們是什么?大家一定知道是屬性??墒菍傩允遣粠Юㄌ?hào)的,setFrame()他是屬性嗎?那他又是什么呢?setFrame()是屬性,并且是block類型的,因?yàn)閎lock可以加括號(hào)調(diào)用。就像這樣調(diào)用block();那么還有一個(gè)問題很關(guān)鍵:view.setFrame().setBackgroundColor();怎么可以連續(xù)點(diǎn)出來,這剛開始很不好理解。這并不難,我們往前看那句話:
NSString result = myBlock();
細(xì)心的去發(fā)現(xiàn),block是可以有返回值的,那么我們就依靠這個(gè)返回值就可以點(diǎn)出一堆屬性,一直點(diǎn)下去,這樣我們的鏈?zhǔn)骄幊痰乃悸肪屯恕?/p>
【四】實(shí)踐
我們現(xiàn)在就來實(shí)現(xiàn)view.setFrame().setBackgroundColor();
首先準(zhǔn)備一個(gè)分類。頭文件定義如下:
//UIView+Extension.h#import@interfaceUIView(Extesion)@PRoperty (nonatomic,copy) UIView* (^setFrame)(CGFloat x, CGFloat y, CGFloat w, CGFloat h);
@property (nonatomic,copy) UIView* (^setBackgroundColor)(UIColor*color);@end
我們?cè)赨IView類上擴(kuò)展了兩個(gè)額外屬性setFrame和setBackgroundColor,這意味著只要是繼承自UIView的對(duì)象就可以點(diǎn)出來這兩個(gè)屬性。
現(xiàn)在我們需要理解一下兩個(gè)的區(qū)別:
view.setFrame;//這是獲取屬性,它返回一個(gè)blockview.setFrame();//這是獲取屬性,它返回一個(gè)block,最后我們使用括號(hào)調(diào)用了這個(gè)block,這個(gè)block的返回值是UIView類型的對(duì)象,那么他就可以繼續(xù)點(diǎn)出下一個(gè)屬性了
view.setFrame()這句話的末尾因?yàn)榧由狭死ㄌ?hào)所以執(zhí)行了block,而他的返回類型是一個(gè)UIView對(duì)象,所以可以調(diào)用setBackgroundColor();
這里解釋一下為什么用copy,block這種類型本來是值類型的,他本來是在棧上的,在ARC環(huán)境下如果被任何強(qiáng)指針過了一次,編譯器就會(huì)把他進(jìn)行一次copy,放到堆內(nèi)存中。block的retain行為默認(rèn)是用copy的行為實(shí)現(xiàn)的,因?yàn)閎lock變量默認(rèn)是聲明為棧變量的,為了能夠在block的聲明域外使用應(yīng)該使用copy。
接下來實(shí)現(xiàn).m文件內(nèi)的代碼:
- (UIView *(^)(CGFloat, CGFloat, CGFloat, CGFloat))setFrame
{return?;
}
不難分析出,這個(gè)屬性的類型是block,具體是UIView* (^)(CGFloat , CGFloat , CGFloat , CGFloat )類型。那么‘?’就是這樣一個(gè)類型的對(duì)象??墒沁@個(gè)對(duì)象從哪里獲得呢,如果是個(gè)NSString我還存儲(chǔ)的有,而這個(gè)對(duì)象只能無從獲取。這并不重要,因?yàn)槲覀儾⒉灰猙lock對(duì)象本身,我們需要的是:block對(duì)象他能夠執(zhí)行一些功能就夠了。所以屬性內(nèi)部返回一個(gè)臨時(shí)block對(duì)象,這個(gè)block內(nèi)部呢去執(zhí)行一些功能,最重要的是block內(nèi)部一定要返回UIView類型的對(duì)象。編譯器會(huì)對(duì)block的類型進(jìn)行苛刻的檢測(cè)。
- (UIView *(^)(CGFloat, CGFloat, CGFloat, CGFloat))setFrame
{return^(CGFloat x, CGFloat y, CGFloat w, CGFloat h){//返回臨時(shí)變量的block
self.frame=CGRectMake(x, y, w, h);//block執(zhí)行一些功能returnself;//block執(zhí)行完畢的返回值
};
}
下面是.m文件的實(shí)現(xiàn)
//UIView+Extension.m#import"UIView+Extension.h"@implementationUIView(Extesion)- (UIView *(^)(CGFloat, CGFloat, CGFloat, CGFloat))setFrame
{return^(CGFloat x, CGFloat y, CGFloat w, CGFloat h){
self.frame=CGRectMake(x, y, w, h);returnself;
};
}- (void)setSetFrame:(UIView *(^)(CGFloat, CGFloat, CGFloat, CGFloat))setFrame{};//該屬性不需要從外部設(shè)置,不想報(bào)警告就寫空方法- (UIView *(^)(UIColor *))setBackgroundColor
{return^(UIColor*color){
self.backgroundColor=color;returnself;
};
}- (void)setSetBackgroundColor:(UIView *(^)(UIColor *))setBackgroundColor{};@end
【五】崩潰
objc中向nil對(duì)象發(fā)送任何消息都不會(huì)崩潰,但是發(fā)送他不能處理的消息類型就會(huì)崩潰,這也是最經(jīng)常遇到了情況。還有這樣也會(huì)崩潰:nil.length; view.length;調(diào)用不存在的屬性也會(huì)崩潰。那么這是我們要處理的一個(gè)重要的情況。
試想,一行代碼中間突然有一個(gè)處理返回了nil給下一個(gè)環(huán)節(jié),一調(diào)用就崩潰,關(guān)鍵是斷點(diǎn)并不能明確告訴你在哪一行。我們只能從控制臺(tái)中獲得調(diào)試信息。這也是鏈?zhǔn)骄幊痰谋锥酥弧?/p>
【六】效率
關(guān)于效率,肯定比原生低那么一丁點(diǎn)的,幾乎可以忽略的,只是多了一次屬性調(diào)用,一次一臨時(shí)block的創(chuàng)建,一次block的執(zhí)行。博主的觀點(diǎn)是為了提高開發(fā)效率,而降低一些運(yùn)行效率。有時(shí)候也是值得的。最后蘋果也是大力提倡我們使用block的。祝愉快。