一、簡述:
a.每一個方法都是在被調用的時候從硬盤到內存,然後去執行,執行完就消失。在內存的棧區,不需要我們去管理。
b.block,我們成爲代碼塊,它類似一個方法,也是在棧區的。
c.如果我們使用block作爲一個對象的屬性,則用copy關鍵詞修飾。這樣系統會把block的實現拷貝一份到堆區,保證代碼塊不會提前消亡,使我們對應的屬性擁有該block的所有權。
d.block常用於對象之間的通信(反向傳值和方法傳遞)。
二、使用:
#import "ViewController.h"
typedef void (^MyBlock) (void);//初始化
@interface ViewController ()
@property (nonatomic,copy)MyBlock block;//定義一個block屬性
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//實現一個block 這個block實現代碼是在棧區的,也就是說,當viewDidLoad這個方法執行完之後,block就消失了。
void(^block)(void) = ^{
NSLog(@"block的使用");
};
//賦值給屬性_block 此時就完成了copy _block指針指向堆中一塊內存(存放的是block的實現代碼),_block就一直擁有了代碼塊的使用權,直到 viewcontroller
_block = block;
//一般我們不會這樣寫,block的實現都是在另一個類的對象中實現。
}
三、使用block傳值時需要注意的幾點:
1.我們在實現block的時候,一般都會使用到外部(block大括號之外)變量。我們知道,局部變量(非靜態)是不能在外部使用的,而block又類似是一個方法,那他爲什麼可以使用外部變量呢。
- (void)viewDidLoad {
[super viewDidLoad];
int a = 10;//a對於_block來說就是一個外部變量
_block = ^{
NSLog(@"a = %d",a);//但是,此時是可以使用a的。
};
}
其實,這是因爲OC是一種運行時語言,我們寫的OC代碼最終都是要轉換成C語言的代碼去執行的。我們通過運行時代碼可以知道,系統會把使用到的外部變量通過參數列表傳遞給block,也就變成了block內部的局部變量,所以可以使用。
2.而在傳遞的時候,對於基本數據類型的外部變量來說,系統默認傳遞的僅僅是值,也就是說這個局部變量是不能修改的。如果想修改值,會使用__block來修飾這個變量。這樣一來,系統在傳遞的時候,傳的就是外部變量的地址,這樣我們就可以修改值了。
__block int a = 10;//用__block修飾之後,系統會傳遞a的地址(&a)
_block = ^{
a += 20;
NSLog(@"a = %d",a);//有地址,當然就可以修改a的值了。此時a的值是30
};
3.對於對象類型,傳遞的是地址,同時默認對該對象進行了一次強引用。系統進行了強引用,而他又對該對象的內存管理袖手旁觀,也就是說,他只做了強引用,但是沒有做釋放操作。這個時候就會造成內存泄漏。所以,我們在使用對象的時候,在MRC下,都會使用__block修飾,在ARC下,使用__weak修飾,這樣一來,系統在傳遞的時候就不會對該對象進行強引用,避免了內存泄漏。
- (void)viewDidLoad {
[super viewDidLoad];
UIView *view = [[UIView alloc] init];
__weak typeof(view)_view = view;//_view和view指向同一塊內存,而_view是弱引用,view的retainCount還是1.
_block = ^{
//view.frame = CGRectMake(0, 0, 100, 100);//在block內部使用view對象,系統會對view強引用,此時會造成內存泄漏。
_view.frame = CGRectMake(0, 0, 100, 100);
};
}