iOS之輕鬆上手block

導語 不會使用block的iOS程序員,不是一個合格的程序員 學會了block,你不再想用繁瑣的代理 block沒有你想象中的那麼難,不要懼怕,不要畏懼,勇敢嘗試 筆者入行iOS時已是ARC的天下,因此這裏只說ARC環境下的使用 什麼是block block其實就是一個代碼塊,把你想要執行的代碼封裝在這個代碼塊裏,等到須要的時候再去調用。那block是OC對象嗎?答案是確定的程序員

來自官方文檔數組

筆者以英語3.9級的水平給你們翻譯下,「block是一個OC對象,這意味着它能被添加到集合,好比NSArray、NSDictionary」安全

block的定義 block屬性或變量 格式:返回值類型(^block名稱)(參數列表) /定義屬性,block屬性能夠用strong修飾,也能夠用copy修飾 有小夥伴留言說蘋果官方建議用copy,筆者查了下文檔, 確實是這樣的,不過筆者未測試出copy與strong的區別,你們喜歡啥就用啥吧/ @property (nonatomic, strong) void(^myBlock)();//無參無返回值 @property (nonatomic, strong) void(^myBlock1)(NSString *);//帶參數 @property (nonatomic, strong) NSString *(^myBlock2)(NSString *);//帶參數與返回值 //定義變量 void(^myBlock)() = nil;//無參無返回值 void(^myBlock1)(NSString *) = nil;//帶參數 NSString *(^myBlock2)(NSString *) = nil;//帶參數與返回值 block被當作方法的參數 格式:(block類型)參數名稱測試

  • (void)test:(void(^)())testBlock//無慘無返回值
  • (void)test1:(void(^)(NSString *))testBlock//帶參數
  • (void)test2:(NSString *(^)(NSString *))testBlock//帶參數與返回值 使用typedef定義block typedef void(^myBlock)(); //之後就可使用myBlock定義無參無返回值的block typedef void(^myBlock1)(NSString *); //使用myBlock1定義參數類型爲NSString的block typedef NSString *(^myBlock2)(NSString *); //使用myBlock2定義參數類型爲NSString,返回值也爲NSString的block //定義屬性 @property (nonatomic, strong) myBlock testBlock; //定義變量 myBlock testBlock = nil; //當作參數
  • (void)test:(myBlock)testBlock; block的賦值 格式:block = ^返回值類型(參數列表){}

沒有參數沒有返回值 myBlock testBlock = ^void(){ NSLog(@"test"); }; //沒有返回值,void能夠省略 myBlock testBlock1 = ^(){ NSLog(@"test1"); }; //沒有參數,小括號也能夠省略 myBlock testBlock2 = ^{ NSLog(@"test2"); }; 有參數沒有返回值 myBlock1 testBlock = ^void(NSString *str) { NSLog(str); } //省略void myBlock1 testBlock = ^(NSString *str) { NSLog(str); } 有參數有返回值 myBlock2 testBlock = ^NSString *(NSString *str) { NSLog(str) return @"hi"; } //有返回值時也能夠省略返回值類型 myBlock2 testBlock2 = ^(NSString *str) { NSLog(str) return @"hi"; } 實戰 接下來,咱們就結合一個實例程序,來看看block在實際開發中的簡單使用atom

本案例涉及到兩個控制器與一個Person類 聯繫人列表控制器:使用tableView展現聯繫人列表,稱爲A控制器 新建聯繫人控制器:建立新的聯繫人對象,稱爲B控制器 Person:聯繫人,有兩個屬性,name與phoneNumber 任務需求:點擊A控制器右上角「新建」按鈕跳到B控制器,B控制器添加聯繫人後,點擊「保存」按鈕返回A控制器,並將新添加的聯繫人展現到列表中翻譯

問題來了,如何將B控制器中的數據傳遞給A控制器呢? 那還不簡單,A控制器直接把聯繫人數組傳遞給B控制器,B控制器新建聯繫人後添加到數組中,而後返回A控制器,在A控制器的viewWillAppear方法中刷新表格就OK了。代理

方法可行,可是不得不說,至關low,B控制器是用來添加聯繫人的,至於聯繫人數組什麼狀況,無需關心,因此,不要把數組傳遞給B控制器指針

B控制器要作的僅僅只是,新建聯繫人,而後把聯繫人對象傳遞給A控制器,至於A控制器拿到聯繫人後會作什麼,那是A的事情,與B無關 看到這裏,不少人可能已經想到了代理,沒錯,代理也能夠實現,但...是...,B控制器定義協議,聲明代理方法,A控制器設置代理,遵照協議,而後實現代理方法,B控制器在合適的地方調用代理方法,臥槽,好麻煩有木有,筆者都不想寫代碼了,仍是回家種田去吧對象

好了不廢話了,進入正題內存

使用block傳遞數據 在B控制器的.h文件中定義一個沒有返回值,參數類型爲Person的block屬性 @property (nonatomic, strong) void(^saveBlock)(Person *); 在B控制器「保存」按鈕的點擊方法中調用block

  • (IBAction)save:(id)sender { //使用事先定義好的類方法建立Person對象 Person *person = [Person personWithName:_nameText.text phoneNumber:_phoneNumberText.text]; /*調用block以前最好先判斷block是否爲空,不爲空才調用,不然程序崩潰/ //裝逼寫法 //!self.saveBlock? : self.saveBlock(person); //通常寫法 if (self.saveBlock) { self.seveBlock(person); } [self.navigationController popViewControllerAnimated:YES]; } 在A控制器中,給B控制器的block屬性進行賦值 //「新建」按鈕點擊執行的方法
    • (void)newContact { AddContactViewController *addVC = [[AddContactViewController alloc] init]; addVC.saveBlock = ^(Person *person){ //這裏就能夠拿到B控制器傳遞過來的person對象,添加到數組而後刷新表格 [self.contactList addObject:person]; [self.tableView reloadData]; }; [self.navigationController pushViewController:addVC animated:YES]; } 三步就搞定,很簡單是否是,因此說,block並無大家想一想的那麼複雜,自從筆者學會了block,就再也沒用過代理,除了系統的。 block常見雷區---循環引用 使用block有一個特別要注意的地方,循環引用,何爲循環引用?你引用我,我引用你,誰也不釋放誰,對象沒法銷燬,佔用內存

咱們來看一個循環引用的一個例子

注意看控制檯輸出,當點擊「取消」時,B控制器被銷燬,dealloc方法被調用

把註釋掉的代碼打開,再運行

點擊「取消」按鈕,B被移除,可是dealloc方法沒有調用,因此說,B控制器並無銷燬,why?

block對象賦值給了B控制器的屬性,所以B會對block有一個強引用,而block中又用到了self(B控制器對象),block會對使用到的外部變量進行捕獲,因此,block對B控制器也有一個強引用,最終形成循環引用,誰也沒法釋放

循環引用解決方法 循環引用如何解決?很簡單,一行代碼搞定

使用weakSelf(名稱隨便取的)替代self,block將再也不對self進行強引用 圖中__weak也可以使用__unsafe_unretained,區別就是__weak修飾的指針,當對象銷燬後,指針會被自動置爲nil,而__unsafe_unretained修飾的指針,當對象銷燬後會變成野指針,爲了安全,推薦使用__weak

如此簡單又好用的block,你是否已經學會了呢,若是以爲筆者的文章對你有幫助,請多多關注,大家的關注,將是筆者更新的動力,筆者將在下篇文章中,講述block在內存中的狀況,以及block對外界變量的捕獲

相關文章
相關標籤/搜索