1.object-C中的block
- 作用:保存一段代碼塊
-
2.聲明
block的寫法:block的寫法: 類型: 返回值類型(^block的名稱)(block的參數(shù)) 值: ^(參數(shù)列表) { // 執(zhí)行的代碼 } //例子 int (^sumOfNumbers)(int a, int b) = ^(int a, int b) { return a + b; };
-
3.定義
block的定義有三種方式
// block定義的三種方式// 方式一 void(^block1)() = ^(){ }; // 方式二 如果沒有參數(shù),參數(shù)可以隱藏,如果有參數(shù),定義的時候必須寫參數(shù),而且必須要有參數(shù)變量名 void(^block2)() = ^{ }; void(^block2_1)(int) = ^(int a){//這里的參數(shù)a不能省略,因為下面的代碼塊要用到這個參數(shù) }; void(^block2_2)(int a) = ^(int a){ }; // 方式三 block的返回是可以省略的,不管有沒有返回值都可以省略 int (^block3)() = ^int{ return 0; }; int (^block3_1)() = ^{ return 0; };
-
4.類型
// 2.block的定義:int(^)(NSString *)<----這就是block的聲明 int(^block4)(NSString *) = ^(NSString *str){ return 1; }; block1();//block的調(diào)用 //inLineBlock:快速創(chuàng)建一個block的快捷方式,如下 <#returnType#>(^<#blockName#>)(<#parameterTypes#>) = ^(<#parameters#>) { <#statements#> };
2.block在開發(fā)中的使用場景
-
1.場景一 (基本沒有實際意義)
目的:在一個方法中定義了代碼塊,可以在另一個方法中去使用;此種方法和直接寫一個方法等價,沒多大意義。
#import "ViewController.h"// BlockName:block類型別名 typedef void(^BlockName)(); @interface ViewController () // block怎么聲明就怎么定義屬性 // 方式一 : 直接用block聲明,block如何聲明就如何定義成屬性 @property (nonatomic,strong) void(^block)(); // 方式二 : 給block起別名,用別名作為類型 @property (nonatomic,strong) BlockName block1; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; void(^block)() = ^{ NSLog(@"直接用block聲明的block 調(diào)用了block"); }; void(^blockName)() = ^{ NSLog(@"使用block起別名的方式 調(diào)用了block"); }; _block = block; _block1 = blockName; } - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { // 調(diào)用block _block1(); _block(); } 打印輸出結(jié)果: 1.使用block起別名的方式 調(diào)用了block 2.直接用block聲明的block 調(diào)用了block
-
2.場景二 (應(yīng)用最多)
目的:在一個類中定義, 在另外一個類中去使用
#import <Foundation/Foundation.h>@interface CellItem : NSObject @property (nonatomic, copy) NSString *title; @property (nonatomic, strong) void(^block)(); // 作為屬性保存到這個模型中 + (instancetype)itemWithTitle:(NSString *)title; @end #import "CellItem.h" @implementation CellItem + (instancetype)itemWithTitle:(NSString *)title { CellItem *item = [[self alloc] init]; item.title = title; return item; } @end
#import "ViewController.h"
#import "CellItem.h"
@interface ViewController ()
@property (nonatomic, strong) NSArray *itemArray;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 初始化tableView
_myTableView = [[UITableView alloc] initWithFrame:[UIScreen mainScreen].bounds style:UITableViewStylePlain];
_myTableView.delegate = self;
_myTableView.dataSource = self;
[self.view addSubview:_myTableView];
CellItem *item1 = [CellItem itemWithTitle:@"打電話"];
item1.block = ^{
NSLog(@"調(diào)用打電話功能");
};
CellItem *item2 = [CellItem itemWithTitle:@"發(fā)短信"];
item2.block = ^{
NSLog(@"調(diào)用發(fā)短信功能");
};
CellItem *item3 = [CellItem itemWithTitle:@"發(fā)郵件"];
item3.block = ^{
NSLog(@"調(diào)用發(fā)郵件功能");
};
_itemArray = @[item1,item2,item3];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return _itemArray.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *ID = @"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:ID];
}
// 此處如果不用數(shù)組就必須使用大量的ifelse語句
CellItem *item = self.itemArray[indexPath.row];
cell.textLabel.text = item.title;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
CellItem *item = self.itemArray[indexPath.row];
// 此處如果不用數(shù)組就必須使用大量的ifelse語句
if (item.block) {// 必須要判斷下block是否有值
item.block();
}
}
-
3.場景三
目的:使用block去傳值,可與delegate互換。(數(shù)據(jù)的逆向傳遞)#pragma mark 這里ModaViewController想把一個字符串傳遞給ViewController, 于是ModaViewController就需要拿到ViewController, 那么就要在ModaViewController中寫一個代理, 讓ViewController去實現(xiàn)這個代理; #import <UIKit/UIKit.h> //@class ModaViewController; // //@protocol ModaViewControllerDelegate <NSObject> //@optional //// 設(shè)計需要實現(xiàn)的方法: 想要代理做什么事情就怎么去設(shè)計 //// 什么時候?qū)崿F(xiàn)代理?---> 這里就簡單的定義為當(dāng)點擊屏幕的時候就將value值傳遞過去 //- (void)modaViewController:(ModaViewController *)vc sendValue:(NSString *)value; //@end @interface ModaViewController : UIViewController // 用block替換下面的代理--哪里用代理,哪里就可以用block去替換 @property (nonatomic, strong) void(^block)(NSString *value); // <---value是block的傳入?yún)?shù) //@property (nonatomic, weak) id<ModaViewControllerDelegate> myDelegate; @end -------------------------------------------------------------- #import "ModaViewController.h" @interface ModaViewController () @end @implementation ModaViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. self.view.backgroundColor = [UIColor blueColor]; } - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { // 傳值給ViewController // if ([_myDelegate respondsToSelector:@selector(modaViewController:sendValue:)]) { // 查看這個代理是否有需要實現(xiàn)的方法 // // 如果有需要實現(xiàn)的方法就讓代理直接去實現(xiàn) // [_myDelegate modaViewController:self sendValue:@"123456"]; // } if (_block) { _block(@"123456"); } } @end -------------------------------------------------------------- #import "ViewController.h" #import "ModaViewController.h" @interface ViewController () //<ModaViewControllerDelegate> @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. } - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { // 推出控制器 ModaViewController *modaVC = [ModaViewController new]; [self presentViewController:modaVC animated:YES completion:nil]; // 在這里需要ViewController控制器拿到ModaViewController的代理方法,代理并實現(xiàn)拿到的代理方法 // modaVC.myDelegate = self; modaVC.block = ^(NSString *value) { NSLog(@"打印block傳過來的值 == %@", value); }; } #pragma mark ModaViewController---myDelegate //- (void)modaViewController:(ModaViewController *)vc sendValue:(NSString *)value { // NSLog(@"打印代理傳過來的值 == %@", value); //} @end
屏幕快照 2017-10-17 下午3.12.06.png
屏幕快照 2017-10-17 下午3.12.31.png
屏幕快照 2017-10-17 下午3.13.13.png
-
4.block作為參數(shù)進(jìn)行延時回調(diào)
定義網(wǎng)絡(luò)請求的類@interface HttpTool : NSObject -(void)loadRequest:(void (^)())callBackBlock; @end @implementation HttpTool -(void)loadRequest:(void (^)())callBackBlock { dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"異步延時請求操作在這里,加載網(wǎng)絡(luò)數(shù)據(jù):%@", [NSThread currentThread]); dispatch_async(dispatch_get_main_queue(), ^{ callBackBlock(); }); }); } @end
進(jìn)行網(wǎng)絡(luò)請求,請求到數(shù)據(jù)后利用block進(jìn)行回調(diào)
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { [self.httpTool loadRequest:^{ NSLog(@"主線程中,將數(shù)據(jù)回調(diào).%@", [NSThread currentThread]); }]; }
Swift中的閉包
<1>. 閉包寫法總結(jié):
類型:(形參列表)->(返回值)
技巧:初學(xué)者定義閉包類型,直接寫()->().再填充參數(shù)和返回值
值:
{
(形參) -> 返回值類型 in
// 執(zhí)行代碼
}
let b = { (parm : Int) -> (Int) in
print(parm)
}
//調(diào)用
b(100)
<2>.閉包的簡寫
如果閉包沒有參數(shù),沒有返回值,in和in之前的內(nèi)容可以省略
httpTool.loadRequest({
print("回到主線程", NSThread.currentThread());
})
尾隨閉包寫法:
如果閉包是函數(shù)的最后一個參數(shù),則可以將閉包寫在()后面
如果函數(shù)只有一個參數(shù),并且這個參數(shù)是閉包,那么()可以不寫
httpTool.loadRequest() {
print("回到主線程", NSThread.currentThread());
}
// 開發(fā)中建議該寫法
httpTool.loadRequest {
print("回到主線程", NSThread.currentThread());
}
<3>.使用閉包代替block,閉包作為參數(shù)進(jìn)行延時回調(diào)
定義網(wǎng)絡(luò)請求的類
class HttpTool: NSObject {
func loadRequest(callBack : ()->()){
dispatch_async(dispatch_get_global_queue(0, 0)) { () -> Void in
print("加載數(shù)據(jù)", [NSThread.currentThread()])
dispatch_async(dispatch_get_main_queue(), { () -> Void in
callBack()
})
}
}
}
進(jìn)行網(wǎng)絡(luò)請求,請求到數(shù)據(jù)后利用閉包進(jìn)行回調(diào)
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
// 網(wǎng)絡(luò)請求
httpTool.loadRequest ({ () -> () in
print("回到主線程", NSThread.currentThread());
})
}
<3>.實例二,閉包的回調(diào)傳值
//[weak self]:解決循環(huán)引用導(dǎo)致的內(nèi)存泄露
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
delayMethod {[weak self] (re) ->() in
print("$$$$$$$$$$$$$$$$$:\(re)%%%%%%%%%%%\(String(describing: self))")
}
delayMethod(comletion: {[weak self] (re)->() in
print("********:\(re)*************\(String(describing: self))")
})
}
//@escaping:逃逸閉包。它的定義非常簡單而且易于理解。如果一個閉包被作為一個參數(shù)傳遞給一個函數(shù),并且在函數(shù)return之后才被喚起執(zhí)行,那么這個閉包是逃逸閉包。
func delayMethod(comletion: @escaping (_ results: String,_ resultss:String) -> ()) ->(){
//開啟一個全局異步子線程
DispatchQueue.global().async {
Thread.sleep(forTimeInterval: 2.0)
//回調(diào)到主線程
DispatchQueue.main.async(execute: {
print("主線程更新 UI \(Thread.current)")
comletion("qwertyui","asdf")
})
}
}
<4>.閉包進(jìn)行兩個界面的傳值
我們要實現(xiàn)點擊第二個界面后,關(guān)掉第二個界面,并且傳值給第一個界面
<1>.首先在第二個界面聲明閉包進(jìn)行操作
class NewViewController: UIViewController {
//聲明閉包
typealias lewisCloser = (_ paramOne : String? ) -> ()
//定義個變量 類型就是上面聲明的閉包
var customeCloser: lewisCloser?
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if(customeCloser != nil) {
customeCloser!("要發(fā)給第一個界面的值")
}
self.dismiss(animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
<2>.在第一個界面實現(xiàn)閉包,取得要穿的值
let vc = NewViewController()
//實現(xiàn)閉包
vc.customeCloser = {(cusValue) -> () in
//cusValue就是傳過來的值
print("^^^^^^^^^^^^^^^^^^^^^:\(cusValue!)")
}
self.present(vc, animated: true, completion: nil)
關(guān)于Swift許多部分我自己也是一知半解,這篇文章完全是為自學(xué)加深記憶。如有發(fā)現(xiàn)錯誤煩請指正~~~
閉包部分完全抄襲:LewisZhu,抱歉?。。?大家也可移步至下方原著鏈接??
作者:LewisZhu
鏈接:http://www.lxweimin.com/p/1457a4894ec7