IOS開發-block的使用

這幾天在在工程中總會用到block傳值以及傳方法,今天對block進行了整理.app

block代碼塊主要用於對象之間的通訊(反向傳值和方法傳遞)。post

首先,咱們從內存管理方面來了解一下blockatom

  block:咱們稱代碼塊,他相似一個方法。而每個方法都是在被調用的時候從硬盤到內存,而後去執行,執行完就消失,因此,方法的內存不須要咱們管理,也就是說,方法是在內存的棧區。因此,block不像OC中的類對象(在堆區),他也是在棧區的。若是咱們使用block做爲一個對象的屬性,咱們會使用關鍵字copy修飾他,由於他在棧區,咱們沒辦法控制他的消亡,當咱們用copy修飾的時候,系統會把該 block的實現拷貝一份到堆區,這樣咱們對應的屬性,就擁有的該block的全部權。就能夠保證block代碼塊不會提早消亡。spa

複製代碼
 1 #import "LBS_A_ViewController.h"  2  3 typedef void(^MyBlock)(void);//block寫法比較特殊,通常重命名一下  4  5 @interface LBS_A_ViewController ()  6 @property (nonatomic,copy)MyBlock block;//定義一個MyBlock屬性  7 @end  8  9 @implementation LBS_A_ViewController 10 11 - (void)viewDidLoad { 12  [super viewDidLoad]; 13 14 //實現一個block 這個block實現代碼是在棧區的,也就是說,當viewDidLoad這個方法執行完以後,block就消失了。 15 void(^block)(void) = ^{ 16 17 NSLog(@"block的簡單使用"); 18  };

19 //賦值給屬性_block 此時就完成了copy _block指針指向堆中一塊內存(存放的是block的實現代碼),_block就一直擁有了代碼塊的使用權,直到 LBS_A_ViewCont roller對象消亡。
20 _block = block;

}
複製代碼

  固然,通常咱們不會這樣寫,block的實現都是在另外一個類的對象中實現。代理

 

其次,在block的實現部分要注意一些事情指針

  咱們在實現block的時候,通常都會使用到外部(block大括號以外)變量。咱們知道,局部變量(非靜態)是不能在外部使用的,而block又相似是一個方法,那他爲何可使用外部變量呢。code

複製代碼
- (void)viewDidLoad { [super viewDidLoad]; int a = 10;//a對於_block來講就是一個外部變量  _block = ^{ NSLog(@"a = %d",a);//可是,此時是可使用a的。  }; }
複製代碼

其實,這是由於OC是一種運行時語言,咱們寫的OC代碼最終都是要轉換成C語言的代碼去執行的。咱們經過運行時代碼能夠知道,系統會把使用到的外部變量經過參數列表傳遞給block,也就變成了block內部的局部變量,因此可使用。對象

 

 

  而在傳遞的時候,對於基本數據類型的外部變量來講,系統默認傳遞的僅僅是值,也就是說這個局部變量是不能修改的。若是想修改值,會使用__block來修飾這個變量。這樣一來,系統在傳遞的時候,傳的就是外部變量的地址,這樣咱們就能夠修改值了。blog

複製代碼
    __block int a = 10;//用__block修飾以後,系統會傳遞a的地址(&a)  _block = ^{ a += 20; NSLog(@"a = %d",a);//有地址,固然就能夠修改a的值了。此時a的值是30 };
複製代碼

 

 

  對於對象類型,傳遞的是地址,同時默認對該對象進行了一次強引用。系統進行了強引用,而他又對該對象的內存管理袖手旁觀,也就是說,他只作了強引用,可是沒有作釋放操做。這個時候就會形成內存泄漏。因此,咱們在使用對象的時候,在MRC下,都會使用__block修飾,在ARC下,使用__weak修飾,這樣一來,系統在傳遞的時候就不會對該對象進行強引用,避免了內存泄漏。內存

複製代碼
 1 - (void)viewDidLoad {  2  [super viewDidLoad];  3  4 UIView *view = [[UIView alloc] init];  5  6 __weak typeof(view)_view = view;//_view和view指向同一塊內存,而_view是弱引用,view的retainCount仍是1.  7  8 _block = ^{  9 //view.frame = CGRectMake(0, 0, 100, 100);//在block內部使用view對象,系統會對view強引用,此時會形成內存泄漏。 10 _view.frame = CGRectMake(0, 0, 100, 100); 11  }; 12 13 }
複製代碼

 最後,說說block的通訊。說到對象之間的通訊,咱們通常有三種方式:代理、block、通知。

什麼是通訊呢?就是兩個對象之間,你讓我幹什麼什麼,我讓你幹什麼什麼。

舉個例子,如今有A和B兩個對象,其中A對象包含B對象,A若是想讓B幹什麼,A只須要給B一個消息[B xiaoxi],而此時,若是B對象想讓A對象幹什麼事情呢,確定是但願是給A一個消息[A xiaoxi],可是B中沒有A對象啊。

那咱們能不能給B一個屬性是A對象呢,讓B也包含A?

顯然不行,第一,B對象中的A對象不是 包含B對象的那個A對象,第二 ,你包含我 我包含你 極可能會形成循環引用,最後兩個對象都釋放不了,形成內存泄漏。

此時的解決方案是使用代理,A包含B對象,當建立B對象的時候,A就把本身設爲B的代理。那若是B給他的代理髮消息,就能保證是包含他的那個A對象去接收消息了。同時,代理屬性咱們都是使用關鍵字weak,就是爲了不循環引用。

而block和代理的使用是同樣的,只不過相對簡單,不用制定協議、寫代理方法。同時效率更高。

而通知呢,更靈活,發一個通知,誰都能註冊接收通知,而後作事情。

那何時使用代理、何時使用block、何時使用通知呢?

在使用代理和block的時候,咱們可能意識到了一個事情,就是通訊的兩個對象之間,必定是有關係的(A包含B  或者 B包含A),否則怎麼設置代理,怎麼實現別的對象的block。因此,當兩個須要通訊的對象之間有包含關係的時候,考慮代理和block。好比,上面的A和B對象,若是B想讓A幹不止一件事情,就用代理。若是就是一件事情,不必又制訂協議,有些代理方法,太麻煩,此時能夠考慮使用block。

有的時候,須要通訊的兩個對象之間沒有關係,或者是一個對象要跟多個對象通訊的時候,就要用到通知了。好比,旅遊類app,若是在第一個界面改了城市名,那其餘平行界面也要知道改了城市名,顯示對應的數據,這個時候 就能夠用通知。

相關文章
相關標籤/搜索