首先咱們來看一下有哪幾種Block:編程
那麼這3種Block分別在哪裏體現出來呢?接下來,咱們經過代碼來看一下:void(^block)(void) = ^{
NSLog(@"123456");
};
block();
NSLog(@"%@",block);
//萬物皆對象,block也不例外
//運行代碼,能夠看到打印的內容是:
//BlockDemo[34002:5702720] 123456
//BlockDemo[34002:5702720] <__NSGlobalBlock__: 0x107c8b088>
//此時這個block就是GlobalBlock
複製代碼
咱們稍微修改下上面的代碼,讓block去截獲變量的值,bash
int a = 10;
void(^block)(void) = ^{
NSLog(@"a=%d",a);
};
block();
NSLog(@"%@",block);
//運行代碼,能夠看到打印的內容是:
//BlockDemo[34061:5705903] a=10
//BlockDemo[34061:5705903] <__NSMallocBlock__: 0x6000021ccd50>
//此時的block就是MallocBlock
複製代碼
咱們再改變一下block的打印,函數
int a = 10;
void(^block)(void) = ^{
NSLog(@"a=%d",a);
};
block();
NSLog(@"%@",^{
NSLog(@"a=%d",a);
});
//運行代碼,能夠看到打印的內容是:
//BlockDemo[34108:5708687] a=10
//BlockDemo[34108:5708687] <__NSStackBlock__: 0x7ffee9f71970>
//此時打印出的就是咱們看似不經常使用,不常見到,卻可能在不知不覺中使用的StackBlock
複製代碼
NSGlobalBlock
:最初建立一個block
的時候,他是存在於5大內存區(棧區,堆區,靜態區,常量區,代碼區(全局區))的全局區,當咱們引入外部變量,block
會自動捕獲變量,當前這個變量a
由棧區捕獲到堆區,由於a
和block
必須在同一個內存空間才能夠進行相應的操做,因此block
會由全局區遷移到堆區上去,因此block
就存放到棧區(NSStackBlock)
,可是因爲棧區空間是由系統自動分配的,自動銷燬的,因此咱們再寫void(^block)(void) =
這句代碼的時候其實是作了一個copy
操做,將存放在棧區上的block
拷貝到了堆區(NSMallocBlock)
。ui
首先咱們來看下什麼是正常的釋放: this
那麼B何時會釋放呢? 當A調用析構函數dealloc
給B發送
release
信息的時候,A不會再持有B,而後B的
retainCount
會減1,當B的
retainCount=0
的時候,B就會調用本身的析構函數,從而B就能夠釋放掉。
再來看一下什麼是循環引用:
下面咱們用代碼來實現看一下:
@property (nonatomic, copy) ZHBlock block;
@property (nonatomic, copy) NSString *name;
self.name = @"ZH";
self.block = ^{
NSLog(@"%@",self.name);
};
self.block();
複製代碼
在這裏 self-->block-->self
形成循環引用,其實代碼這樣寫,Xcode會在這裏給咱們一個警告,告訴咱們會有循環引用, Capturing 'self' strongly in this block is likely to lead to a retain cycle
這裏,你們都會利用__weak typeof(self) weakSelf = self;
來解決循環引用,這樣完成以後,你們確定會以爲這樣寫不完善,應該還有一個__strong
,那麼爲何會要用這個__strong
呢?對的就是防止「提早釋放」,那麼下面咱們用代碼來演示一下提早釋放:atom
self.name = @"ZH";
__weak typeof(self) weakSelf = self;
self.block = ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@",weakSelf.name);
});
};
self.block();
複製代碼
運行代碼就會發現,weakSelf.name
打印出來的是<null>
,由於當前控制器pop
回去的時候就會調用析構函數dealloc
,而後控制器就被釋放掉了,這個name就沒有任何意義,因此就會被置爲空。spa
那麼還有沒有其餘的方法來解決循環引用呢?code
__block ViewController *vc = self;
self.block = ^{
NSLog(@"%@",vc.name);
vc = nil;
};
self.block();
複製代碼
還有沒有方法呢? 這個循環引用的實質就是在block
裏面用了self
,那麼不用self
就能夠了啊,可是在這裏咱們要打印name
屬性,這個name
又是當前vc
的屬性,那麼就是說咱們要在block
裏面使用vc
,那麼若是把vc
當作參數,傳入block
是否是能夠呢,咱們來試一下cdn
typedef void(^ZHBlock)(ViewController *);
self.block = ^(ViewController *vc){
NSLog(@"%@", vc.name);
};
self.block(self);
複製代碼
運行代碼能夠成功哦!對象
首先寫兩個方法,以下:
-(ViewController *)where
{
NSLog(@"從東土大唐而來");
return self;
}
-(void)there
{
NSLog(@"去往西天拜佛求經");
}
複製代碼
在調用這兩個方法的時候,能夠這樣寫: [[self where] there];
若是換成點語法呢,能夠這樣寫:self.where.there;
一樣能夠打印出兩個語句,這種點語法的格式就有點相似於鏈式編程。
此時若是想往-(void)there
方法傳入一個參數該怎麼作呢,能夠看到這是一個getter
方法,沒法傳入參數,但是又要作到通信的目的:那麼試着在there
方法中返回一個block
,
-(ViewController *)where
{
NSLog(@"從東土大唐而來");
return self;
}
-(void(^)(NSString *))there
{
NSLog(@"去往西天拜佛求經");
void (^block)(NSString *) =^(NSString *name)
{
NSLog(@"%@",name);
};
return block;
}
複製代碼
此時再用點語法調用就能夠傳入參數:self.where.there(@"三藏語錄");
打印能夠看到語句輸出正確, ---這就是面向鏈式編程最基礎的寫法。