方法柯里化,就是把接受多个参数的方法变换成接受第一个参数的方法,并且返回接受余下的参数并且返回结果的新方法。

可以通过柯里化一个方法模板来避免写出很多的重复代码,方便维护。

举一个实际应用时候的例子:

button.addTarget(self,action: "onClickBtn:",forControlEvents: .TouchDown)
func onClickBtn(sender: UIButton){
    println("点击了Button");
}
在Swift 中Selector 只能使用字符串在生成。这面临一个很严重的问题,就是难以重构,并且无法在编译期间进行检查,其实这是十分危险的行为。

但是target-action 又是Cocoa 中如此重要的一种设计模式,无论如何我们都想安全地使用的话,应该怎么办呢?一种可能的解决方式就是利用方法的柯里化

Ole Begemann 在这篇帖子里提到了一种很好封装,这为我们如何借助柯里化,安全地改造和利用target-action 提供了不少思路。

示例一

假设现在我们有一个简单的银行账户类BankAccount

class BankAccount {
    var balance: Double = 0.0

    func deposit(amount: Double) {
        balance += amount
    }
}
我们可以明显的创建这个类的一个实例,然后通过这个实例调用deposit()方法:
let account = BankAccount()
account.deposit(100) // balance is Now 100

没错,上面非常简单。但我们也可以这样做:
let depositor = BankAccount.deposit
depositor(account)(100) // balance is Now 200

这样做和上面的写法是等价的。 等等,出现什么了,为什么会这样?我们首先为方法分配了一个变量。注意:BankAccount.deposit缺少一个括号,这里我们并不是调用这个方法(这将产生一个错误,因为你不能调用该类型的实例方法),而仅仅是引用它,这更像是C语言中的函数指针。第二步就是调用存储在depositor变量中的函数。这个变量的类型如下所示:

let depositor: BankAccount -> (Double) -> ()

换句话说,第一部分: 这个函数一个参数一个BankAccount的实例并返回另一个函数。第二部分:后面这个函数接收一个Double类型的参数,并返回空。在第二部分中,你应该理解实例方法deposit()的签名。


我希望你能看到在Swift中一个实例方法是一种简单的方法,这个方法其实就是将实例作为参数,然后返回一个函数,将其应用到实例中。

当然,我们也可以用一行代码做到这点,这使得类型方法和实例方法之间的关系更加清晰:

BankAccount.deposit(account)(100) // balance is Now 300

通过将实例account作为参数传给 BankAccount.deposit(),该实例被绑定到方法上,然后我们传入参数,调用这个方法。非常棒,对不?

示例二

Swift中实现Target-Action

Christoffer Lernö 在开发者论坛上展示了如何使用纯swift语言将Swift的类型系统这一特点应用到target-action模式中。不同于Cocoa种的常见实现,Christoffer的解决方案并没用依赖OC中的动态消息调度机制。它是完全类型安全的,因为其并不依赖selectors。

这种模式往往比用闭包实现回调更好,特别是当在一个不确定的时间接收对象必须使用闭包。使用闭包经常使API的调用者做额外的工作来防止引用循环。使用target-action模式,提供API的对象可以做内部类型转换,使得在调用方面让代码更加简洁。

例如,一个使用Target-Action控制可能是这样(采纳自a dev forums post by Jens Jakob Jensen)

Update July 29,2014: Made the action property in TargetActionWrapper non-optional. target must be optional because it is weak.

protocol TargetAction {
    func performAction()
}

struct TargetActionWrapper<T: AnyObject> : TargetAction {
    weak var target: T?
    let action: (T) -> () -> ()

    func performAction() -> () {
        if let t = target {
            action(t)()
        }
    }
}

enum ControlEvent {
    case TouchUpInside
    case ValueChanged
    // ...
}

class Control {
    var actions = [ControlEvent: TargetAction]()

    func setTarget<T: AnyObject>(target: T,action: (T) -> () -> (),controlEvent: ControlEvent) {
        actions[controlEvent] = TargetActionWrapper(target: target,action: action)
    }

    func removeTargetForControlEvent(controlEvent: ControlEvent) {
        actions[controlEvent] = nil
    }

    func performActionForControlEvent(controlEvent: ControlEvent) {
        actions[controlEvent]?.performAction()
    }
}

用法如下:

class MyViewController {
    let button = Control()

    func viewDidLoad() {
        button.setTarget(self,action: MyViewController.onButtonTap,controlEvent: .TouchUpInside)
    }

    func onButtonTap() {
        println("Button was tapped")
    }
}

Target-action:目标-动作模式,贯穿于iOS开发始终。我们自己在 Swift中实现了这一模式,是不是略叼~~! :-)

Swift中实例方法柯里化及自己实现Target-action模式的更多相关文章

  1. Html5 滚动穿透的方法

    这篇文章主要介绍了Html5 滚动穿透的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  2. HTML5 拖放(Drag 和 Drop)详解与实例代码

    本篇文章主要介绍了HTML5 拖放(Drag 和 Drop)详解与实例代码,具有一定的参考价值,有兴趣的可以了解一下

  3. 跨域修改iframe页面内容详解

    这篇文章主要介绍了跨域修改iframe页面内容详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  4. ios – Xcode找不到Alamofire,错误:没有这样的模块’Alamofire’

    我正在尝试按照github(https://github.com/Alamofire/Alamofire#cocoapods)指令将Alamofire包含在我的Swift项目中.我创建了一个新项目,导航到项目目录并运行此命令sudogeminstallcocoapods.然后我面临以下错误:搜索后我设法通过运行此命令安装cocoapodssudogeminstall-n/usr/local/bin

  5. ios – 无法识别的选择器发送到实例NSTimer Swift

    解决方法让updateTime成为一个类方法.如果它是在一个纯粹的Swift类中,你需要在@objc前面说明该方法的声明,如:

  6. ios – 暂停调度队列是否会暂停其目标队列?

    我想创建两个串行队列A&B.队列B是队列A的目标.我想在B上排队一些块,并暂停它直到我准备执行它们,但是我想继续在队列A上执行块.如果我暂停B,这还会暂停它的目标队列(队列A)吗?我的想法是,我想安排这些特定的块在稍后日期执行但是我不希望它们同时运行而我不这样做想要处理信号量.但我希望队列A继续处理它的块,而B则被暂停如果不清楚这里是一些示例代码解决方法queueB被挂起,但queueA未被挂起.queueA和queueB被挂起.

  7. ios – 使用CocoaPods post install hook将自定义路径添加到HEADER_SEARCH_PATHS

    解决方法在Podfile中定义一个方法:然后在post_install中调用该方法:

  8. ios – 类型推断(自动类型检测)如何在swift中工作?

    LLVM如何检测变量是一个字符串?

  9. ios – Swift可选项:语言问题,还是做错了什么?

    应该有可选的类型;type是但是,如果我这样做,它的工作原理:它似乎是基本的替代,但我可能会遗漏一些语言的细微差别.谁能对此有所了解?之后就像暧昧一样,更多,这是我的解决方案:这适用于所有非对象Swift对象,包括Swift字符串,数字等.感谢Viktor提醒我String不是Swift中的对象.如果您知道值的类型,您可以替换任何?使用适当的可选类型,如String?

  10. ios – 覆盖Swift中的超类委托

    我正在开发一个包含两个UIViews的Swift(v1.2)项目.MyView和MyViewSubclass.MyView有一个委托,我想在MyViewSubclass中覆盖它作为一个子协议,类似于UITableViews有一个UITableViewDelegate,它也符合超级uiscrollviewdelegate.我的第一个想法是覆盖超类属性,但这会导致编译器错误,因为子类不能覆盖具有不同类

随机推荐

  1. Swift UITextField,UITextView,UISegmentedControl,UISwitch

    下面我们通过一个demo来简单的实现下这些控件的功能.首先,我们拖将这几个控件拖到storyboard,并关联上相应的属性和动作.如图:关联上属性和动作后,看看实现的代码:

  2. swift UISlider,UIStepper

    我们用两个label来显示slider和stepper的值.再用张图片来显示改变stepper值的效果.首先,这三个控件需要全局变量声明如下然后,我们对所有的控件做个简单的布局:最后,当slider的值改变时,我们用一个label来显示值的变化,同样,用另一个label来显示stepper值的变化,并改变图片的大小:实现效果如下:

  3. preferredFontForTextStyle字体设置之更改

    即:

  4. Swift没有异常处理,遇到功能性错误怎么办?

    本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

  5. 字典实战和UIKit初探

    ios中数组和字典的应用Applicationschedule类别子项类别名称优先级数据包contactsentertainment接触UIKit学习用Swift调用CocoaTouchimportUIKitletcolors=[]varbackView=UIView(frame:CGRectMake(0.0,0.0,320.0,CGFloat(colors.count*50)))backView

  6. swift语言IOS8开发战记21 Core Data2

    上一话中我们简单地介绍了一些coredata的基本知识,这一话我们通过编程来实现coredata的使用。还记得我们在coredata中定义的那个Model么,上面这段代码会加载这个Model。定义完方法之后,我们对coredata的准备都已经完成了。最后强调一点,coredata并不是数据库,它只是一个框架,协助我们进行数据库操作,它并不关心我们把数据存到哪里。

  7. swift语言IOS8开发战记22 Core Data3

    上一话我们定义了与coredata有关的变量和方法,做足了准备工作,这一话我们来试试能不能成功。首先打开上一话中生成的Info类,在其中引用头文件的地方添加一个@objc,不然后面会报错,我也不知道为什么。

  8. swift实战小程序1天气预报

    在有一定swift基础的情况下,让我们来做一些小程序练练手,今天来试试做一个简单地天气预报。然后在btnpressed方法中依旧增加loadWeather方法.在loadWeather方法中加上信息的显示语句:运行一下看看效果,如图:虽然显示出来了,但是我们的text是可编辑状态的,在storyboard中勾选Editable,再次运行:大功告成,而且现在每次单击按钮,就会重新请求天气情况,大家也来试试吧。

  9. 【iOS学习01】swift ? and !  的学习

    如果不初始化就会报错。

  10. swift语言IOS8开发战记23 Core Data4

    接着我们需要把我们的Rest类变成一个被coredata管理的类,点开Rest类,作如下修改:关键字@NSManaged的作用是与实体中对应的属性通信,BinaryData对应的类型是NSData,CoreData没有布尔属性,只能用0和1来区分。进行如下操作,输入类名:建立好之后因为我们之前写的代码有些地方并不适用于coredata,所以编译器会报错,现在来一一解决。

返回
顶部