iOS之輕鬆上手block

来源:http://www.cnblogs.com/8hao/archive/2016/03/04/5242461.html
-Advertisement-
Play Games

原文出處: codingZero 歡迎分享原創到伯樂頭條 導語 不會使用block的iOS程式員,不是一個合格的程式員學會了block,你再也不想用繁瑣的代理block沒有你想象中的那麼難,不要害怕,不要畏懼,勇敢嘗試筆者入行iOS時已經是ARC的天下,所以這裡只說ARC環境下的使用 什麼是bloc


原文出處: codingZero   歡迎分享原創到伯樂頭條

導語

不會使用block的iOS程式員,不是一個合格的程式員
學會了block,你再也不想用繁瑣的代理
block沒有你想象中的那麼難,不要害怕,不要畏懼,勇敢嘗試
筆者入行iOS時已經是ARC的天下,所以這裡只說ARC環境下的使用

什麼是block

block其實就是一個代碼塊,把你想要執行的代碼封裝在這個代碼塊里,等到需要的時候再去調用。那block是OC對象嗎?答案是肯定的

 

 

來自官方文檔

筆者以英語3.9級的水平給大家翻譯下,“block是一個OC對象,這意味著它能被添加到集合,比如NSArray、NSDictionary”

block的定義

  1. block屬性或變數
    格式:返回值類型(^block名稱)(參數列表) 
       
    1 2 3 4 5 6 7 8 9 10 /*定義屬性,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;//帶參數與返回值
  2. block被當做方法的參數
    格式:(block類型)參數名稱 
       
    1 2 3 - (void)test:(void(^)())testBlock//無慘無返回值 - (void)test1:(void(^)(NSString *))testBlock//帶參數 - (void)test2:(NSString *(^)(NSString *))testBlock//帶參數與返回值
  3. 使用typedef定義block
       
    1 2 3 4 5 6 7 8 9 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 = ^返回值類型(參數列表){}

  1. 沒有參數沒有返回值
       
    1 2 3 4 5 6 7 8 9 10 11 myBlock testBlock = ^void(){      NSLog(@"test"); }; //沒有返回值,void可以省略 myBlock testBlock1 = ^(){      NSLog(@"test1"); }; //沒有參數,小括弧也可以省略 myBlock testBlock2 = ^{      NSLog(@"test2"); };
  2. 有參數沒有返回值
       
    1 2 3 4 5 6 7 myBlock1 testBlock = ^void(NSString *str) {       NSLog(str); } //省略void myBlock1 testBlock = ^(NSString *str) {       NSLog(str); }
  3. 有參數有返回值
       
    1 2 3 4 5 6 7 8 9 myBlock2 testBlock = ^NSString *(NSString *str) {      NSLog(str)      return @"hi"; } //有返回值時也可以省略返回值類型 myBlock2 testBlock2 = ^(NSString *str) {      NSLog(str)      return @"hi"; }

     

    實戰

    接下來,我們就結合一個實常式序,來看看block在實際開發中的簡單使用

     

     

     

    本案例涉及到兩個控制器與一個Person類

    1. 聯繫人列表控制器:使用tableView展示聯繫人列表,稱為A控制器
    2. 新建聯繫人控制器:創建新的聯繫人對象,稱為B控制器
    3. 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傳遞數據

  1. 在B控制器的.h文件中定義一個沒有返回值,參數類型為Person的block屬性
       
    1 @property (nonatomic, strong) void(^saveBlock)(Person *);
  2. 在B控制器“保存”按鈕的點擊方法中調用block
       
    1 2 3 4 5 6 7 8 9 10 11 12 - (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]; }
  3. 在A控制器中,給B控制器的block屬性進行賦值
       
    1 2 3 4 5 6 7 8 9 10    //“新建”按鈕點擊執行的方法    - (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可分為三種
  • NSStackBlock:棧block
  • NSMallocBlock:堆block
  • NSGlobalBlock:全局block
1. 棧block

特點:生命周期由系統控制,函數返回即銷毀
用到局部變數、成員屬性變數,且沒有強指針引用的block都是棧block

a.用到局部變數(圖1),i為局部變數,block直接在NSLog中列印,沒有被指針引用

 

 

圖1

b.用到成員屬性變數(圖2),name為成員屬性

 

 

圖2
2. 堆block

特點:沒有強指針引用即銷毀,生命周期由程式員手動管理
棧block如果有強指針引用或copy修飾的成員屬性引用就會被拷貝到堆中,變成堆block

a.強指針引用(圖3),block被testBlock引用,testBlock就是一個block類型的強指針(ARC環境下預設就是強指針)

 

 

圖3

b.copy修飾的成員屬性引用(圖4)

 

 

圖4
3. 全局block

特點:命長,有多長?很長很長,人在塔在(應用程式在它就在)
沒有用到外界變數,或者只用到全局變數、靜態(static)變數的block就是全局block

對於全局block,有沒有指針引用都不影響,block類型的成員屬性無論是用assign、weak、strong還是copy修飾都無所謂,不過開發中很少用到全局block,所以不要用weak或assign

a.沒有用到外界變數(圖5),下圖中block沒有用到外界變數,所以就算用weak修飾也是全局block(舉個例子而已,開發中不要用weak,用了也別說是筆者教的)

 

 

圖5

b.只用到全局變數、靜態(static)變數(圖6),str為全局變數,str1為靜態變數,只用到其中一個也是全局block

 

 

圖6

分類總結
1.沒有用到外界變數或只用到全局變數、靜態變數的block為全局block,生命周期從創建到應用程式結束
2.用到局部變數、成員屬性變數的block為棧block,生命周期系統控制,函數返回即銷毀
3.有強指針引用或copy修飾的成員屬性引用的block會被覆制一份到堆中成為堆block,沒有強指針引用即銷毀,生命周期由程式員控制

block對外界變數的捕獲

a.基本數據類型—局部變數
block會拷貝該變數的值當做常量使用,外界修改變數的值不會影響block內部,且block內部不能對其修改

block內部修改外界變數i的值直接報錯,如果想要修改,可以在int i = 0前面加上關鍵字__block,此時i等效於全局變數或靜態變數

 

 

 

外界變數i從0變成了1,block內部列印依然是0

 

 

 

b.基本數據類型—靜態變數、全局變數、成員屬性變數
block直接訪問變數地址,在block內部可以修改變數的值,並且外部變數被修改後,block內部也會跟著變

圖中_k為成員屬性變數,初始值i = 10,j = 20,k = 0,block內部只對i、j、k進行一次自增操作,列印結果卻是i = 12,j = 22,k = 2,所以外部的自增操作也影響了內部,即訪問的是同一個記憶體地址

 

 

 

c.指針類型—局部變數
block會複製一份指針並強引用指針所指對象,且內部不能修改指針的指向

圖中被註釋掉的代碼試圖修改指針指向,所以會報錯(如果想要修改,在前面加上__block),但是可以修改所指對象的值,如str從“abc”變成了“abcdef”

 

 

 

d.指針類型—全局變數、靜態變數、成員變數屬性
block不會複製指針,但是會強引用該對象,內部可修改指針指向,block會強引用成員屬性變數所屬的對象,這也是為什麼block屬性內部用到self.xxx會引起迴圈引用的原因

圖中str2為成員屬性,由於NSString是不可變的,所以從列印結果可以看出,在block內部修改了外界指針變數的引用,指向了新的字元串

 

 

 
講到這裡,筆者對block的理解已全部分享給大家,並隨時歡迎各位讀者的補充與糾正

問啊-定製化IT教育平臺,牛人一對一服務,有問必答,開發編程社交頭條 官方網站:www.wenaaa.com

QQ群290551701 聚集很多互聯網精英,技術總監,架構師,項目經理!開源技術研究,歡迎業內人士,大牛及新手有志於從事IT行業人員進入!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • <!DOCTYPE html><html><head lang="en"> <meta charset="UTF-8"> <title></title> <style type="text/css"> body,div,ul,li,iframe{ margin: 0; padding: 0; fon
  • <!DOCTYPE html><html><head lang="en"> <meta charset="UTF-8"> <title>錨點定位</title> <style type="text/css"> *{ margin: 0; padding: 0; } html,body{ width:
  • 轉載博客:http://blog.csdn.net/i_lovefish/article/details/17719081 以下為異常捕捉處理代碼: import java.io.BufferedReader; import java.io.File; import java.io.FileInpu
  • 分類:C#、Android、VS2015; 創建日期:2016-03-04 一、簡介 目前,基於位置的服務發展迅速,已涉及到商務、醫療、定位、追蹤、敏感區域警告、工作和生活等各個方面。定位服務融合了GPS定位、移動通信、導航等多種技術,從而獲取用戶終端設備的位置信息,為移動用戶提供了與空間位置相關的...
  • Android Studio 優秀插件系列: Android Studio 優秀插件(一):GsonFormat Android Studio 優秀插件(二): Parcelable Code Generator -------------------------------------------
  • Notification是Android中很理想的一種顯示提示信息的方法,它可以將應用程式的信息傳遞到我們的Android桌面狀態欄,採用這種消息傳遞方式不會影響到用戶對手機的正常使用。而且Notification不僅僅可以傳遞文字信息,還可以傳遞圖片信息,甚至可以將我們的控制項追加到上面,只要用戶不
  • 自己的學習筆記。
  • 轉載自並做少量添加:http://www.cnblogs.com/playing/archive/2011/04/07/2008620.html Layout對於迅速的搭建界面和提高界面在不同解析度的屏幕上的適應性具有很大的作用。這裡簡要介紹Android的Layout和研究一下它的實現。 Andr
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...