3.0到4.0闭包 swift

2017-02-03 16:21:52 MinggeQingchun 阅读数 2067

Swift3.0-反向传值

1、使用代理协议

在ViewController.swift中

class ViewController: UIViewController,SubDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
//        setNavigation()
        myButton()
    }
    
    
    func myButton() {
        let btn = UIButton(type:UIButtonType.system) as UIButton
        btn.frame = CGRect(x:60,y:100,width:100,height:30)
        btn.backgroundColor = UIColor.lightGray
        btn.setTitle("进入下一界面", for: UIControlState.normal)
        self.view.addSubview(btn)
        
        btn.addTarget(self, action: #selector(click), for: UIControlEvents.touchUpInside)
    }
    func click(){
        let vc = SubViewController()
        vc.delegate = self
//        let vc = SubClosureViewController()
//        vc.changeTitleAndClosure = {
//            (title:String,color:UIColor) in
//            self.title = title
//            self.view.backgroundColor = color
//        }
        self.navigationController?.pushViewController(vc, animated: true)
    }
    
    func changeTitle(title: String) {
        self.title = title
    }
    
    func changeColor(color: UIColor) {
        self.view.backgroundColor = color
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}
SubViewController.swift中
//在委托中定义协议
protocol SubDelegate:NSObjectProtocol {
    //实现一些方法
    func changeTitle(title:String)
    func changeColor(color:UIColor)
}

class SubViewController: UIViewController {

    //定义遵守协议
    var delegate:SubDelegate?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        myView()
    }
    func myView() {
        self.view.backgroundColor = UIColor.red
        
        let tf = UITextField(frame:CGRect(x:60,y:180,width:100,height:30))
        tf.borderStyle = UITextBorderStyle.roundedRect
        tf.tag = 100
        self.view.addSubview(tf)
        
        let btn = UIButton(type:UIButtonType.system) as UIButton
        btn.frame = CGRect(x:60,y:100,width:100,height:30)
        btn.backgroundColor = UIColor.lightGray
        btn.setTitle("返回上一界面", for: UIControlState.normal)
        self.view.addSubview(btn)
        
        btn.addTarget(self, action: #selector(click), for: UIControlEvents.touchUpInside)
    }
    func click(){
        let tf = self.view.viewWithTag(100) as? UITextField
        delegate?.changeTitle(title: tf!.text!)
        delegate?.changeColor(color: UIColor.blue)
        self.navigationController?.popViewController(animated: true)
    }
}


2、使用闭包

在ViewController.swift中

class ViewController: UIViewController,SubDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
//        setNavigation()
        myButton()
    }
    
    
    func myButton() {
        let btn = UIButton(type:UIButtonType.system) as UIButton
        btn.frame = CGRect(x:60,y:100,width:100,height:30)
        btn.backgroundColor = UIColor.lightGray
        btn.setTitle("进入下一界面", for: UIControlState.normal)
        self.view.addSubview(btn)
        
        btn.addTarget(self, action: #selector(click), for: UIControlEvents.touchUpInside)
    }
    func click(){
//        let vc = SubViewController()
//        vc.delegate = self
        let vc = SubClosureViewController()
        vc.changeTitleAndClosure = {
            (title:String,color:UIColor) in
            self.title = title
            self.view.backgroundColor = color
        }
        self.navigationController?.pushViewController(vc, animated: true)
    }
}
SubClosureViewController.swift中
class SubClosureViewController: UIViewController {

    //定义一个闭包
    var changeTitleAndClosure:((_ title:String,_ color:UIColor) -> Void)?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        myView()
    }
    func myView() {
        self.view.backgroundColor = UIColor.red
        
        let tf = UITextField(frame:CGRect(x:60,y:180,width:100,height:30))
        tf.borderStyle = UITextBorderStyle.roundedRect
        tf.tag = 100
        self.view.addSubview(tf)
        
        let btn = UIButton(type:UIButtonType.system) as UIButton
        btn.frame = CGRect(x:60,y:100,width:100,height:30)
        btn.backgroundColor = UIColor.lightGray
        btn.setTitle("返回上一界面", for: UIControlState.normal)
        self.view.addSubview(btn)
        
        btn.addTarget(self, action: #selector(click), for: UIControlEvents.touchUpInside)
    }
    func click(){
        let tf = self.view.viewWithTag(100) as? UITextField
        changeTitleAndClosure?(tf!.text!,UIColor.green)
        self.navigationController?.popViewController(animated: true)
    }
}



2016-04-24 23:47:00 feng2qing 阅读数 3746

在项目开发中有时候需要把一些循环执行的异步操作加入到group中,让彻底循环完之后再进行下一步操作,直接上代码

创建一个组

swift2.3:
let group = dispatch_group_create()

swift3.0:
let group = DispatchGroup()

循环加载数据

swift2.3:
for _ in array.count {

    //将当前的下载操作添加到组中
    dispatch_group_enter(group)

    //在这里异步加载任务

    //离开当前组
    dispatch_group_leave(group)
}

swift3.0:
for _ in array {

    //将当前的下载操作添加到组中
    group.enter()

    //在这里异步加载任务

    //离开当前组
    group.leave()
}

全部加载完后通过闭包通知调用者

swift2.3:
dispatch_group_notify(group, dispatch_get_main_queue()) { () -> Void in

    //在这里告诉调用者,下完完毕,执行下一步操作
}

swift3.0:
group.notify(queue: DispatchQueue.main) { 
    //在这里告诉调用者,下完完毕,执行下一步操作
}
2017-02-14 13:49:26 viiimaus 阅读数 4167
import UIKit

class ViewController: UIViewController {

    var a: (() -> ())?
    
    
    override func viewDidLoad() {
        super.viewDidLoad()

        //block 中如果出现 self. 要特别小心!
        // "循环"引用,单方向的引用是不会产生循环引用的
        // - 只是闭包对self 进行了copy
        // - 同时需要self对闭包引用
        
        //*******解除循环引用
        // 方法一:用oc的方法
        // 细节1:用var不用let,weak只能修饰var 不能 修饰 let
        // 'weak' must be a mutable variable, because it may change at runtime
        // weak可能会在运行时被修改 -> 指向的对象一旦被释放,会自动设置为nil
//        weak var weakSelf = self;
//        loadData {
            // 细节2
            // 解包有两种方式
            // ? 可选解包  如果self已经被释放,不会向对象发送 view 的消息,更安全
            // ! 强行解包  如果self已经被释放,强行解包会导致崩溃
            // Expression implicitly coerced from 'UIView?' to Any
            
            /*
                weakSelf?.view 只是单纯的发送消息,不参与计算
                强行解包,因为需要计算,可选项不能直接参与计算
            */
//            print(weakSelf?.view);
//        }
        
        
        //方法2 - swift的推荐方法
        //[weak self] 表示 () 中的所有 self 都为弱引用
//        loadData { [weak self] in
//            print(self?.view as Any);
//        }
        
        // 方法3 - swift的另一种方法,知道就好,不安全
        // [unowned self] 表示 () 中的所有 self 都为assign, 不会强引用,如果对象释放,指针地址依然存在
        // 如果对象释放, 会出现野指针的现象
        loadData { [unowned self] in
            print(self.view);
        }
    }
    
    func loadData(bibao: @escaping () -> ()) {
        // 使用属性记录闭包 -> self 对闭包引用
        a = bibao;
        
        //异步
        DispatchQueue.global().async { 
            print("1111");
            
            Thread.sleep(forTimeInterval: 2);
            
            DispatchQueue.main.async(execute: { 
                print("2222");
                bibao();
            })
        }
        
    }
    
    deinit {
        print("qqqqq");
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


}


与OC的对比

#import "DemoViewController.h"

@interface DemoViewController ()
@property (nonatomic, copy) void (^blockCallBack)();
@end

@implementation DemoViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
//    //解除方法1:-- __weak
//    __weak typeof(self) weakSelf = self;
//    [self loadData:^{
//        NSLog(@"%@", weakSelf.view);
//    }];
    
    //解除方法2:-- __unsafe_unretained
    //EXC_BAD_ACCESS ,坏内存访问,野指针访问
    // __unsafe_unretained 同样是assign的引用(MRC中并未有weak)
    // MRC中,弱引用直接用assign,不会增加引用计数,但是对象一旦被释放,会出现野指针
    // ARC中,__weak相当于一个观察者,一旦发现对象被释放,会自动设置为nil,会更加安全。
    
    //weak的效率会差一些
    __unsafe_unretained typeof(self) weakSelf = self;
    [self loadData:^{
        NSLog(@"%@", weakSelf.view);
    }];
}

-(void)loadData:(void (^)())block {
    
    self.blockCallBack = block;
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
       
        NSLog(@"延时操作:%@", [NSThread currentThread]);
        
        [NSThread sleepForTimeInterval:2.0];
        
        dispatch_async(dispatch_get_main_queue(), ^{
            
            block();
            
        });
        
    });
    
}

@end


2017-03-08 15:42:51 jeffasd 阅读数 1893

http://www.jb51.net/article/97240.htm

IOS swift3.0 下闭包语法整理

一、闭包的概念

有oc基础的都知道,闭包其实是oc里面的block,语法格式不一样,但作用是一样的。主要是用于callBack(异步回调)或者两个类之间的通信。它的本质一个函数,一个可执行的代码块,只是这个函数是没有名字的,也就是匿名函数。你也可以把他看作如 int、float一样,是一种数据类型,一种可以作为参数传递的数据类型。

二、基本语法

1、闭包的声明

 //定义一个求和闭包
    //闭包类型:(Int,Int)->(Int)
    let add:(Int,Int)->(Int) = {
      (a,b) in
      return a + b;
    }
   //执行闭包,相当于调用函数 
   let result = add(1100, 200);
    //打印闭包返回值
    print("result=\(result)");

闭包类型是由参数返回值决定,如上述add闭包类型为(Int,Int)->(Int),箭头前面括号是参数类型,多个参数逗号隔开,箭头后面括号返回值类型。

分析下上面代码,“=”左边的“ let add:(Int,Int)->(Int) ”意思是声明一个add常量,add是一个闭包类型,并且这个闭包的类型是:(Int,Int)->(Int)。

“=”右边是一个代码块,即闭包的具体实现,相当于给左边add常量赋值。代码块的语法格式:

{
    (参数1,参数2) in
    //code
 }

参数和需执行的代码(code)用 关键字“in”隔开,如果闭包没有参数, “ () in”可以直接省略:

{
  //code
 }

你也可以用关键字“typealias”先声明一个闭包的数据类型

import UIKit

//声明一个闭包类型 AddBlock
typealias AddBlock = (Int,Int)->(Int);

class ViewController: UIViewController {

  override func viewDidLoad() {
    super.viewDidLoad()
    let add:AddBlock = {
      (a,b) in
      return a + b;
    }

   let result = add(1100, 200);
    print("result=\(result)");
 }
}

3、闭包的用法

1、两个类之间的通信

ios中类之间的通信方式有多种,常用的有:协议代理、通知,以及本章要讲的闭包。因为协议代理用起来比较麻烦,又是声明协议方法、又要设置代理的,代码步骤太多,我一般不用;通知一般用于两个完全没有关联的类通信,可以一对多,但解耦和的太厉害,我一般是特定的场合用。所以针对有关联的两个类之间的通信,我一般是用闭包或block的,这样比较简洁迅速。

示例程序:监听控制器上一个自定义view按钮的点击


界面效果

CustomView类中代码

class CustomView: UIView {

  //声明一个属性btnClickBlock,type为闭包可选类型
  //闭包类型:()->() ,无参数,无返回值
  var btnClickBlock:(()->())?;

  //重写 init(frame: CGRect)构造函数
  override init(frame: CGRect) {
    super.init(frame:frame);
    //创建按钮
    let btn = UIButton(frame: CGRect(x: 15, y: 15, width: 80, height: 32));
    btn.setTitle("按钮", for: .normal);
    btn.backgroundColor = UIColor.blue;
    //绑定事件
    btn.addTarget(self, action: #selector(CustomView.btnClick), for: .touchDown);
    //添加
    addSubview(btn);

  }
  //按钮点击事件函数
  func btnClick(){

    if self.btnClickBlock != nil {
      //点击按钮执行闭包
      //注意:属性btnClickBlock是可选类型,需要先解包
      self.btnClickBlock!();
    }
  }

  required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }

}

Controller类中代码:

class ViewController: UIViewController {

  override func viewDidLoad() {
    super.viewDidLoad()

    //创建CustomView对象
    let cutomeView = CustomView(frame: CGRect(x: 50, y: 50, width: 200, height: 200));
    //给cutomeView的btnClickBlock闭包属性赋值
    cutomeView.btnClickBlock = {
      // () in 无参数可以省略
      //当按钮被点击时会执行此代码块
      print("按钮被点击");
    }
    cutomeView.backgroundColor = UIColor.yellow;
    //添加到控制器view上
    self.view.addSubview(cutomeView);

  }
}

2、异步回调(callBack)

以发送一个简单的网络请求为例:

/// 定义一个网络请求函数
  ///
  /// - parameter urlString: 请求接口  String
  /// - parameter succeed:  成功的回调 可选闭包
  /// - parameter failure:  失败的回调 可选闭包
  func requestData(urlString:String,succeed: ((Any?)->(Void))?,failure:((Any?)->(Void))?){

    let request = URLRequest(url: URL(string: urlString)!);

    //发送网络请求
    NSURLConnection.sendAsynchronousRequest(request, queue: OperationQueue()) { (_, data, error) in
      if error == nil {
        //请求成功,执行成功的回调,并把数据传递出去
        succeed?(data);
      }else{
         //请求失败,执行失败的回调,并把错误传递出去
        failure?(error);
      }
    }
  }

// 调用函数requestData函数
    requestData(urlString: "http://www.baidu.com", succeed: { (data) -> (Void) in

      //成功的回调
      guard let result = data as? Data else{
        return;
      }

      let srt = NSString(data: result, encoding: String.Encoding.utf8.rawValue);

      print(srt!)


      }) { (error) -> (Void) in
        //失败的的回调
        print(error);
    }

四、闭包的一些特殊语法

1、尾随闭包

当闭包作为函数的最后一个参数时,可以省略前面的括号。尾随闭包没什么特殊的作用,纯粹是一种语法上的简洁,增加易读性。

例:定义一个函数:

//第二个参数:闭包 (String)->(Void)
func post(url:String,succesce:(String)->Void) {

    print("发送请求");

    succesce("请求完成");
  }

执行函数,正常写法:

 //正常写法,第二个参数,传递一个闭包
   post("http", succesce: {
      //闭包传递的参数
      (json) in
      //执行的代码
       print(json);

    });

执行函数,尾随闭包写法:

//尾随闭包,当闭包作为函数的最后一个参数时,可以省略前面的括号
 HttpTool.post("http") { (json) in
      print(json);
    };

2、逃逸闭包

看起来很“吊炸天”的一个名字,其实很简单。当闭包作为一个参数传递到函数时,我们知道它一般是用于函数内部的异步回调,闭包是等异步任务完成以后才调用,而函数是会很快执行完毕并返回的,所以闭包它需要逃逸,以便稍后的回调。

逃逸闭包一般用于异步函数的回调,比如网络请求成功的回调和失败的回调。语法:在函数的闭包行参前加关键字“@escaping”。

或许细心的人已经发现我上面的示例网络请求为什么没有出现关键字“@escaping”,你可以拉回去看下成功回调或失败的回调,类型是“((Any?)->(Void))?”,后面带了个“?”,这是闭包可选类型,并不是闭包类型,所以无需关键字“@escaping”。

假设成功和失败的回调要弄成闭包类型,而你又要异步使用的话,那就要在形参前面加关键字,如下:

 /// 定义一个网络请求函数
  ///
  /// - parameter urlString: 请求接口  String
  /// - parameter succeed: 成功的回调 闭包 因需要异步使用,前面加关键字@escaping修饰,指明其为逃逸闭包
  /// - parameter failure: 失败的回调 闭包 因需要异步使用,前面加关键字@escaping修饰,指明其为逃逸闭包
  func requestData(urlString:String,succeed: @escaping (Any?)->(Void),failure:@escaping (Any?)->(Void)){

    let request = URLRequest(url: URL(string: urlString)!);

    //发送网络请求
    NSURLConnection.sendAsynchronousRequest(request, queue: OperationQueue()) { (_, data, error) in
      if error == nil {
        //请求成功,执行成功的回调,并把数据传递出去
        succeed(data);
      }else{
         //请求失败,执行失败的回调,并把错误传递出去
        failure(error);
      }
    }
  }

假设成功和失败的回调要弄成闭包类型,而你又要异步使用的话,但你又不想在形参前面加关键字,那对不起,我也没有办法,编译直接报错!


2019-01-15 10:28:02 MChuajian 阅读数 118

闭包英文名叫closure , 在swift开发项目中随处可见, 它的本质和函数差不多,但是却比函数使用方便,因为swift中可以通过上下文识别传入的值,我们就不用像使用函数一样把全部的值都定义一边!当一个函数返回值或着参数需要传入一段逻辑运算或操作,就可以考虑去使用闭包,在OC语言中我们称之为block ,在Java语言中我们称之为Lamdba,这种调用函数的方法,也叫做函数式编程

下面看一个闭包使用的例子 创建一个数组 并用.sorted函数按照用户传入的排序逻辑排序

var num : [Int] = [1,34,5,7,88,8,5]

//可以看到系统要我们传入的是 两个Int参数 并返回一个Bool值 

num.sorted(by: <#T##(Int, Int) throws -> Bool#>)

//我们把这段排序逻辑写成函数形式  a>b返回true 就是说a大的话就排前面

num.sorted(by: { (a : Int,b : Int) -> Bool in a>b })

排序后结果为[88, 34, 8, 7, 5, 5, 1] ,sort函数系统默认是从小到大排序的

当然既然传入的是一个函数 系统自带的+ - * /这些都是操作函数 

num.sorted(by: >)

这样得到的结果也一样 从大到小排序

 

下面我们看一下三个著名的函数式编程式 他们都是系统对象自带的函数 

//函数式编程的著名公式
//map函数  Int -> Any 
//假定数组里面的是一个班的成绩 我们把低于60分的数据转成字符串"不及格" 反则是"及格"
//把原来装着Int的数组改变成一个装着字符串的数组 
//当然你也可以转成Bool 或者把Int都转成二进制 
func TransformMethod1(score:Int) ->String{
    return score<60 ? "不及格": "及格"
}
//传入函数
num.map(TransformMethod1)
//传入闭包
num.map({ (score)-> String in
    score>60 ? "及格":"不及格"
})

//filter函数
//过滤去不要的信息 比如拿到分数里面高于80分的学生数组
func getScoreOverEighty(score:Int) ->Bool{
    return score>80
}
//传入函数
num.filter(getScoreOverEighty)
//传入闭包
num.filter({a in return a>80})

//reduce函数
// 一般用于求数组中所有数的和
func reduceMethod(a : Int ,b : Int)->Int{
   return a+b
}
//传入函数
num.reduce(0, reduceMethod)
//传入闭包
num.reduce(0, {a,b in a+b})

 

闭包的结尾式写法 当闭包作为函数最后一个参数的时候 可以把闭包放在括号外面 可以让人更加清晰的读出这个闭包的逻辑


let num : [Int] = [1,2,3,4,5,6,7]
//设置一个闭包 把所有数转化成二进制
//这里不知道要返回哪个值 所以需要声明返回值
num.map(){(number)->String in
    var number = number
    var result = ""
    repeat{
        result = String(number%2) + result
        number /= 2
    }while number != 0
    return result
}

//返回后的数组值 ["1", "10", "11", "100", "101", "110", "111"]
//当然map函数没有改变原数组

开发IOS人员经常接触的结尾式写法 就是UIView里面的动画执行了 括号放在外面 我们就可以更专注于逻辑的实现而不是该方法的参数是什么

//执行时间3秒钟
UIView.animate(withDuration: 3.0) {
    sonV.backgroundColor = UIColor.white
    sonV.frame = fatherV.frame
}

闭包的更多用法-闭包作为返回值  当然都是涉及函数式编程思想

下面是函数做为返回值的写法 

import UIKit
//设置两个函数 计算自助餐中剩余食物的收费计算方法
//不用额外收费
func chargeMethod1(left:Int)->Int{
    return 0
}
//超出10单位的剩余食物每单位加收15的费用
func chargeMethod2(left:Int)->Int{
    return (left-10)*15
}
func chargeForResidualFoodInBuffet(_ left:Int,_ orignalPrice:Int) ->Int{
    //把该接口隐藏
    //如果剩余食物少于10个单位 就采用第一种收费方式 否则就采用第二种
    func chooseChargeMethodByResidualFood(_ left :Int) -> (Int)->Int{
        return left<10 ? chargeMethod1:chargeMethod2
    }
   
    //得到计算的方法
    let method = chooseChargeMethodByResidualFood(left)
    //返回 剩余食物价钱+原价
    return method(left)+orignalPrice
}

下面是闭包代替函数的写法 代码量更少了

import UIKit

func chargeForResidualFoodInBuffet(_ left:Int,_ orignalPrice:Int) ->Int{
    //把该接口隐藏
    func chooseChargeMethodByResidualFood(_ left :Int) -> (Int)->Int{
        //闭包式写法
        //由于少于10的时候不收取费用 所以我们不需要拿到参数 _代替 
        return left<10 ? { _ in return 0} : { a in return (a-10)*15 }
    }
    //得到计算的方法
    let method = chooseChargeMethodByResidualFood(left)
    
    return method(left)+orignalPrice
}

 

注意⚠️ 闭包和函数都是引用类型的 不是值类型

import UIKit

//使用函数返回值
func eat( food:Int) -> ()->Int
{
    var totalCarolias = 0
    func PersonalEnergy() ->Int {
        totalCarolias = food + totalCarolias
        return totalCarolias
    }
    return PersonalEnergy
}
//使用闭包返回值
func eat1( food:Int) -> ()->Int
{
    var totalCarolias = 0

    return {
        totalCarolias = food + totalCarolias
       return totalCarolias
    }
}
//闭包其为引用类型 所以totalCarolias开辟了一片存储空间 随闭包的调用而改变
//peopleA 加上()表明调用函数
let peopleA = eat(food: 500)
peopleA() //500
peopleA() //1000
peopleA() //1500

let peopleB = eat1(food: 600)
peopleB() //600
peopleB() //1200
peopleB() //1800

这里eat(food : )每调用一次也会会开辟一个新的空间 这时候才会创建一个新的totalCarolias

Swift 3.0 笔记

阅读数 2788