objc利用block實現鏈式編程方法java
由於很差讀。block和其餘語言的匿名函數同樣,不少程序員剛開始很難主動去用他。git
本文描述block做爲屬性的實際使用,看懂block,並講解如何利用block實現鏈式編程方法。程序員
//【New】支持多對象鏈式編程 //1.若是使用多個對象的鏈式編程,須要取值的時候請調用ends()獲取多個結果 //2.若是調用end()只會取得第一個對象的鏈條返回值 //3.使用endsAt()能夠獲取指定index對象的鏈條返回值 //4.若是鏈條結尾返回的是「值類型」,那麼該值是第一個對象的鏈條返回值 //5.使用項目中的「方法」對多對象的鏈式編程不起做用 //寫法一 UIView* viewA = UIViewNew.viewSetFrame(45,100,20,20); UIView* viewB = UIViewNew.viewSetFrame(120,100,20,20); linkObjs(viewA, viewB).viewAddToView(self.view).viewBGColor([UIColor lightGrayColor]); //寫法二 NSMutableArray* arrA = [NSMutableArray arrayWithObjects:@"A", nil]; NSMutableArray* arrB = [NSMutableArray arrayWithObjects:@"B", nil]; NSMutableArray* arrC = [NSMutableArray arrayWithObjects:@"C", nil]; NSArray* linkResults = @[arrA,arrB,arrC].makeLinkObjs.m_arrAddObj(@"E").ends(); //寫法三 linkResults = arrA.linkAnd(arrB).linkAnd(arrC).m_arrAddObj(@"D").ends(); //寫法四,簡單粗暴的重複執行以後鏈條100次,這種狀況不須要for循環了 linkResults = arrA.linkLoop(100).m_arrAddObj(@"F").ends();
【一】遭遇github
到今天iOS開發中最經常使用的語言仍是objc,市場就像泰坦尼克號,人雖然在上樓,可是船在下沉,因此人仍是在下沉。雖然swift出現的迅猛,可是大部分開發者面對的仍是objc。何時swift替代objc,今天的objc開發人員也不用太着急,說不定船都沉了(蘋果保佑)。編程
objc最使人頭疼的是他看起來像部落語言同樣的表達。好比下面這行新手代碼,可是就像求偶訊號同樣晦澀:swift
[[NSMutableDictionary alloc] initWithContentsOfURL:[NSURL URLWithString:@"a.txt"]][@"persons"][12];
但它並無告訴別人任何有意義的事情,分解一下語法就像下面同樣:框架
NSMutableDictionary* dict = [[NSMutableDictionary alloc] initWithContentsOfURL:[NSURL URLWithString:@"a.txt"]]; NSArray* arr = dict[@"persons"]; arr[12];
語言發展到今天,代碼的普世價值不是效率,而是開發效率。這就是爲何java可以成爲王者的緣由。由於java寫起來快,好讀。函數
【二】探索oop
objc比起java又慢又很差讀。那麼如何讓objc看起來像java同樣呢。縱觀objc所有語法,只有block可以辦到。咱們來看block的定義atom
returnType (^name) (val1, val2, ...); 返回類型 變量名 參數列表 //咱們能夠從表面認爲他是一根指向函數的指針,這個函數的樣子以下 (returnType)name(val1, val2, ...) { //...... }
block具備一個函數的外觀,又被看成一個變量。那麼block就具有兩個功能,第一:能夠做爲類的屬性被'點'出來。第二:能夠看成函數直接調用。下面逐個解釋,第一個類的屬性能夠點出來,好比person.name;這很好理解,你必定見過,str.length;對吧。第二個呢,block做爲一個變量,可是又能夠把它看成指向函數的指針同樣調用。
NSString*(^myBlock)() = ^(){ return @"菊樂"; };//定義一個返回值爲NSString類型,無參的,而且名字叫作myBlock的block myBlock();//這一行就是調用 NSString result = myBlock();//這是取出block執行後拿到的返回值,也就是@"菊樂"
這裏再解釋一次block的定義:上面的myBlock能夠認爲是指向一個定義以下的函數的指針,這個函數是
(NSString*) myBlock() { //... } //指向函數的指針 NSString* (*p) (); //能夠認爲指針p就是myBlock(其實block的真實身份更加複雜)
【三】推理
咱們來幻想一下objc寫起來是這樣的
//設置視圖位置和大小,設置視圖背景色 view.setFrame(0,0,50,50).setBackgroundColor( @"#0c0c0c".toColor() ); //移除空格並在控制檯打印字詩句 @" 白 日 依 山 盡 , 黃 河 入 海 流 ".removeStr(@" ").nslog();
而實際上要寫5行的代碼,一行3秒,3行15秒。就這樣錯過了一次搖一搖的機會,人生在緩慢的艱難,而咱們卻還一無所知。
view.frame = CGRectMake(0, 0, 50, 50); view.backgroundColor= [UIColor colorWithRed:0.5 green:0.5 blue:0.5 alpha:1.0]; NSString* str= @" 白 日 依 山 盡 , 黃 河 入 海 流 "; str= [str stringByReplacingCharactersInRange:@" " withString:@""]; NSLog(str);
如今咱們來倒着分析如何使用block來實現鏈式編程,再來觀察這句話:
view.setFrame(0,0,50,50).setBackgroundColor( @"#0c0c0c".toColor() );
其中的setFrame和setBackgroundColor他們是什麼?你們必定知道是屬性。但是屬性是不帶括號的,setFrame()他是屬性嗎?那他又是什麼呢?setFrame()是屬性,而且是block類型的,由於block能夠加括號調用。就像這樣調用block();那麼還有一個問題很關鍵:view.setFrame().setBackgroundColor();怎麼能夠連續點出來,這剛開始很很差理解。這並不難,咱們往前看那句話:
NSString result = myBlock();
細心的去發現,block是能夠有返回值的,那麼咱們就依靠這個返回值就能夠點出一堆屬性,一直點下去,這樣咱們的鏈式編程的思路就通了。
【四】實踐
咱們如今就來實現view.setFrame().setBackgroundColor();
首先準備一個分類。頭文件定義以下:
// UIView+Extension.h #import <UIKit/UIKit.h> @interface UIView(Extesion) @property (nonatomic,copy) UIView* (^setFrame)(CGFloat x, CGFloat y, CGFloat w, CGFloat h); @property (nonatomic,copy) UIView* (^setBackgroundColor)(UIColor* color); @end
咱們在UIView類上擴展了兩個額外屬性setFrame和setBackgroundColor,這意味着只要是繼承自UIView的對象就能夠點出來這兩個屬性。
如今咱們須要理解一下兩個的區別:
view.setFrame;//這是獲取屬性,它返回一個block view.setFrame();//這是獲取屬性,它返回一個block,最後咱們使用括號調用了這個block,這個block的返回值是UIView類型的對象,那麼他就能夠繼續點出下一個屬性了
view.setFrame()這句話的末尾由於加上了括號因此執行了block,而他的返回類型是一個UIView對象,因此能夠調用setBackgroundColor();
這裏解釋一下爲何用copy,block這種類型原本是值類型的,他原本是在棧上的,在ARC環境下若是被任何強指針過了一次,編譯器就會把他進行一次copy,放到堆內存中。block的retain行爲默認是用copy的行爲實現的,由於block變量默認是聲明爲棧變量的,爲了可以在block的聲明域外使用應該使用copy。
接下來實現.m文件內的代碼:
- (UIView *(^)(CGFloat, CGFloat, CGFloat, CGFloat))setFrame { return ?; }
不難分析出,這個屬性的類型是block,具體是UIView* (^)(CGFloat , CGFloat , CGFloat , CGFloat )類型。那麼‘?’就是這樣一個類型的對象。但是這個對象從哪裏得到呢,若是是個NSString我還存儲的有,而這個對象只能無從獲取。這並不重要,由於咱們並不要要block對象自己,咱們須要的是:block對象他可以執行一些功能就夠了。因此屬性內部返回一個臨時block對象,這個block內部呢去執行一些功能,最重要的是block內部必定要返回UIView類型的對象。編譯器會對block的類型進行苛刻的檢測。
- (UIView *(^)(CGFloat, CGFloat, CGFloat, CGFloat))setFrame { return ^(CGFloat x, CGFloat y, CGFloat w, CGFloat h){//返回臨時變量的block self.frame = CGRectMake(x, y, w, h);//block執行一些功能 return self;//block執行完畢的返回值 }; }
下面是.m文件的實現
// UIView+Extension.m #import "UIView+Extension.h" @implementation UIView(Extesion) - (UIView *(^)(CGFloat, CGFloat, CGFloat, CGFloat))setFrame { return ^(CGFloat x, CGFloat y, CGFloat w, CGFloat h){ self.frame = CGRectMake(x, y, w, h); return self; }; } - (void)setSetFrame:(UIView *(^)(CGFloat, CGFloat, CGFloat, CGFloat))setFrame{};//該屬性不須要從外部設置,不想報警告就寫空方法 - (UIView *(^)(UIColor *))setBackgroundColor { return ^(UIColor* color){ self.backgroundColor= color; return self; }; } - (void)setSetBackgroundColor:(UIView *(^)(UIColor *))setBackgroundColor{}; @end
【五】崩潰
objc中向nil對象發送任何消息都不會崩潰,可是發送他不能處理的消息類型就會崩潰,這也是最常常遇到了狀況。還有這樣也會崩潰:nil.length; view.length;調用不存在的屬性也會崩潰。那麼這是咱們要處理的一個重要的狀況。
試想,一行代碼中間忽然有一個處理返回了nil給下一個環節,一調用就崩潰,關鍵是斷點並不能明確告訴你在哪一行。咱們只能從控制檯中得到調試信息。這也是鏈式編程的弊端之一。
爲了處理這種狀況,我寫了一個鏈式編程框架LinkBlock來統一處理,針對幾乎全部經常使用功能進行了鏈式的封裝,並無什麼門檻,但願你們幫我點一顆星星,支持天朝作良好的程序員。
【六】效率
關於效率,確定比原生低那麼一丁點的,幾乎能夠忽略的,只是多了一次屬性調用,一次一臨時block的建立,一次block的執行。博主的觀點是爲了提升開發效率,而下降一些運行效率。有時候也是值得的。最後蘋果也是大力提倡咱們使用block的。祝愉快。