ReactiveCocoa详解

图片 2

本节对事件进行总结。

为什么使用ReactiveCocoa

原文发布时间为:2008-11-01 —— 来源于本人的百度文章 [由搬家工具导入]

二、事件:

1、开发过程中,状态及状态之间依赖过多,状态变化很难跟踪,令人头痛,RAC能更加有效处理事件流,而无需去管理状态。

1、委托的声明:

1、概念:Event:A member that enables an object or class to provide notifications;官方的解释是这样,就是说在C#中,事件是使

2、减少方法的调用,由于它跟踪状态和值的变化,因此不需要状态更新时手动调用,减少出错的可能。

<access modifier> delegate <returnType> HandlerName
([parameters])

对象或者类具备通知能力的成员。比如说手机收到短信提醒我去开会,那么手机就充当了一个具备通知能力的成员。说白了,事件

3、提供统一的消息传递方法,将通知、代理、kvo以及其他所有UIControl事件的变化都进行监控,当发生变化时,就会传递事件和值。

例如:

的作用就是对象和类之间的信息传递的桥梁。

4、当值随着事件变化时,可以使用combineLatest、map、filter等函数便利地对值进行变化操作。

public delegate void PrintHandler(string str);

2、原理:源于发生-响应模型:

5、事件的处理及监听可以放在一起,符合高内聚、低耦合的思想

     
委托声明定义了一种类型,它用一组特定的参数以及返回类型来封装方法。对于静态方法,委托对象封装要调用的方法。对于实例方法,委托对象同时封装一个实例和该实例上的一个方法。如果您有一个委托对象和一组适当的参数,则可以用这些参数调用该委托。

事件源(event source) + 事件本身(event) => 事件的订阅者(event subscriber) + 事件处理器(event handler)           

RAC的编程思想

在C#中使用委托方法:

(另外还有事件的订阅者和事件源之间的订阅关系subscribe relationship)

面向过程:以处理事件的过程为核心,一步一步实现。

·         
创建委托所使用的方法必须和委托声明相一致(参数列表、返回值都一致)

还是以手机收到短信提醒我去开会为例,事件源:手机吗,事件:收到短信,事件的订阅者:我,事件处理器:去开会,订阅关系:我订阅手机

面向对象:万物皆对象

·          利用
+=、-=来进行委托的链接、取消链接或直接使用Delegate.Combine和Delegate.Remove方法来实现

3、事件的声明:分为详细声明和简略声明:

链式编程:将多个操作通过点号链接在一起成为一句代码,是代码的可读性更好,代表masonry框架

·         
可以使用MulticastDelegate的实例方法GetInvocationList()来获取委托链中所有的委托

(1)详细声明:

public delegate void MyDelegateEventHandler();
    public class Event
    {
        private MyDelegateEventHandler myDelegateEventHandler;
        public event MyDelegateEventHandler MyDelegate
        {
            add
            {
                this.myDelegateEventHandler += value;
            }
            remove
            {
                this.myDelegateEventHandler -= value;
            }
        }
    }

链式编程的特点:方法的返回值是block,block必须有返回值(本身对象),block参数(需要操作的值)

·          不能撰写包含 out 参数的委托

(2)简略说明:

public delegate void MyDelegateEventHandler();
    public class Event
    {
         public event MyDelegateEventHandler myDelegate;
    }

响应式编程:不需要考虑调用的顺序,只需要考虑结果,类似于蝴蝶效应,产生一个事件,会影响很多东西,这些事件就像流一样的传播出去,借用面向对象的一句话就是万物皆流。

二、事件的简介

可以看到,在完整声明中首先添加了一个委托类型的字段,然后暴漏了添加和移除事件处理器的功能,但是我们经常用的是简略声明,因为代码更加简洁,

代表:KVO

C# 中的“事件”是当对象发生某些事情时,类向该类的客户提供通知的一种方法。

可以看出事件对外界隐藏了大部分功能,它的本质就是对其中委托字段的一个封装(encapsulation),防止外界偷用滥用委托字段。

函数式编程:把操作尽量写成一系列嵌套的函数或者方法调用

1、事件的声明:

那么问题来了:第一个问题:有了委托为什么还会有事件呢,事件内部不就是委托吗,原因是为了防止public型的委托字段在外面被滥用,比如委托可以用invoke调用,

函数式编程的特点:每一个方法必须有返回值(本身对象),把函数或者block当做参数,block参数(需要操作的值)block返回值(操作结果)

声明的格式为:<access modifier> event <delegate type>
EventName

但是事件只能在+=或-=的左侧,这样就增加了整个程序的安全性。

代表:ReactiveCocoa

       
因为使用委托来声明事件,所以在类里声明事件时,首先必须先声明该事件的委托类型<delegate
type>(如果尚未声明的话)。在上面我们已经提到过了委托类型的声明,但是在.net
framework下为事件使用的委托类型进行声明时有更严格的规定:

第二个问题:那委托和事件的关系什么样的呢?我们说事件是基于委托的。一方面,事件需要委托来做一个约束,这个约束规定了事件源发送什么要求给事件的订阅者,

ReactiveCocoa常见类

(1)、 事件的委托类型应采用两个参数;

事件订阅者的事件处理器必须和这个约束相对应才可以订阅这个事件,另一方面,事件订阅者收到事件以后做出事件处理器,而这个事件处理器必须通过委托才可以做到。

在RAC中最核心的类RACSiganl,搞定这个类就能用ReactiveCocoa开发了。

(2)、两个参数分别是:指示事件源的“对象源”参数和封装事件的其他任何相关信息的“e”参数;

4、简单实例:

RACSignal:信号类,一般表示将来有数据传递,只要有数据变化,信号内部接收到数据,就会马上发出数据。

(3)、“e”参数的类型应为EventArgs 类或派生自 EventArgs 类。

Example:做一个窗口,有文本框和按钮,点击按钮文本框显示时间,不用WindowsForms

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ConsoleApp14
{
    class Program
    {
        public static TextBox textBox;
        public static Button button;
        static void Main(string[] args)
        {
            Form form = new Form();
            TextBox textBox = new TextBox();
            Button button = new Button();
            form.Controls.Add(textBox);
            form.Controls.Add(button);
            textBox.Width = 400;
            button.Top = 100;
            button.Click += Button_Click;
            form.ShowDialog();
        }

        private static void Button_Click(object sender, EventArgs e)
        {
            textBox.Text = DateTime.Now.ToString();
        }
    }
}

图片 1

 

注意:

如下的定义:

这里举的事例就是windowsforms内部的代码,我们说事件本身是不会发生的是由事件源内部的逻辑所触发,在本例中,并不是人按了按钮然后按钮触发了事件,

信号类(RACSignal),只是表示当数据改变时,信号内部会发出数据,它本身不具备发信号的能力,而是交给内部一个订阅者去发出。

public delegate void PrintHandler(object sender,System.EventArgs e);

这其中还有一个小过程,就是当按钮被key down再key up时,向程序内部发送了一系列电讯号,通知电脑,然后再发生事件,

默认一个信号都是冷信号,也就是值改变了也不会触发,只有订阅了这个信号,这个信号才变成热信号,值改变了才会触发。

然后我们才能声明该委托类型的事件

5、声明事件的相关约定:

RACSignal的简单使用:

例如:

用于声明事件的委托一般用:事件+EvnetHandler,参数一般有2个,第一个事件源,第二个EventArgs的派生类,用于触发事件的方法名一般为On+方法名,

   //RACSignal底层实现:

public event PrintHandler Print;

访问级别Protected。可能有点蒙,举个实例就懂了。

    //1创建信号,首先把didSubscribe保存到信号中,还不会触发

当事件发生时,将调用其客户提供给它的委托。

Example:举一个顾客在KFC点餐的例子

namespace ConsoleApp15
{
    class Program
    {
        static void Main(string[] args)
        {
            Customer customer = new Customer();
            Waitor waitor = new Waitor();
            customer.Order += waitor.Serve;
            customer.Eat();
            customer.Pay();
        }
    }
    public delegate void OrderEventHandler(Customer customer, OrderEventArgs e);
    public class Customer
    {
        public int Money { get; set; }
        public event OrderEventHandler Order;
        public void Pay()
        {
            Console.WriteLine($"OK,{Money} dollars");
        }
        public void Eat()
        {
            Console.WriteLine("Let's go to the KFC...");
            Console.WriteLine("Stand in front of the waitor...");
            Console.WriteLine("A hamburger,Please...");
            OnOrder();
        }
        protected void OnOrder()
        {
            OrderEventArgs orderEventArgs = new OrderEventArgs();
            orderEventArgs.Snack = "Hamburger";
            orderEventArgs.Size = "large";
            this.Order.Invoke(this, orderEventArgs);

        }
    }
    public class OrderEventArgs : EventArgs
    {
        public string Snack { get; set; }
        public string Size { get; set; }
    }
    class Waitor
    {
        public void Serve(Customer customer, OrderEventArgs e)
        {
            Console.WriteLine($"Here is your snack {e.Snack}");
            int price = 20;
            switch (e.Size)
            {
                case "large":
                    price *= 2;
                    break;
                case "small":
                    price *= 1;
                    break;
                default:
                    break;
            }
            customer.Money += price;
        }
    }
}

    //2当信号被订阅,也就是调用signal的subscribeNext:nextBlock

2、调用事件:

图片 2

 

   
//2.1subscribeNext内部创建订阅者subscriber,并且把nextBlock保存到subcriber中

       
类声明了事件以后,可以就像处理所指示的委托类型的字段那样处理该事件。如果没有任何客户将委托与该事件绑定,则该字段将为空;否则该字段引用应在调用该事件时调用的委托。因此,调用事件时通常先检查是否为空,然后再调用事件。(调用事件,即触发事件,只能从声明该事件的类内进行)

按照事件的五个要素,首先需要事件源,做一个Customer类,还需要一个事件订阅者,做一个Waitor类,然后根据订阅关系去写具体的方法,订阅关系customer.Order += waitor.Serve; Customer点餐Waitor服务,waitor类中上餐并算好价格,这个时候需要一个事件处理器OrderEventHandler,这个委托的参数需要一个OrderEventArgs,创建这个类写好属性,在写好委托和事件,然后在Customer类中写点餐事件,点餐事件为Protected的,和public型的委托字段一样防止被外界滥用,提高安全性。

    //2.2subscribeNext内部调用signal的didSubscribe

if(Print != null)

想融会贯通其实也不难,只需要将事件的5个要素每一个列举出来,那么最后事件也就出来了。

    //3.Signal的didsubscribe中调用[subscriber sendNext:@1];

{

 

    //3.2sendNext底层其实就是执行subscriber的nextBlock

                Print (this,e);

至此事件总结完毕,有不明之处还请指教。                2018-08-17   16:43:19

 

    //1创建信号

}

    RACSignal *signal = [RACSignal createSignal:^RACDisposable *
_Nullable(id<RACSubscriber>  _Nonnull subscriber) {

3、事件绑定:

        //每当有订阅者订阅信号,就会调用该block

        从类的外面来看,事件就象类的一个公共成员,通过 类名.事件名
的形式来访问,但是只能对它做绑定和解除绑定的操作,而不能有其他操作。

        //3发送信号

类名. Print += new PrintHandler(绑定的方法名) //
将某个方法绑定到Print事件上

        [subscriber sendNext:@”1″];

类名. Print -= new PrintHandler(绑定的方法名) //
将某个已绑定到Print事件上的方法从Print事件上解除

        [subscriber sendNext:@”2″];

三、委托和事件的使用

        [subscriber sendNext:@”3″];

委托和事件在用户界面程序里用的比较的多,比如象在winform或webform的用户UI上的button和它的click事件:

        //如果不再发送数据,内部会自动调用[RACDisposable
disposable]取消订阅信号

// 将Button1_Click()方法绑定到按钮控件Button1的Click事件上

        [subscriber sendCompleted];

this.Button1.Click += new System.EventHandler(this. Button1_Click);

        [subscriber sendNext:@”5″];

private void Button1_Click(object sender, System.EventArgs e)    //
Button1_Click()方法

        return [RACDisposable disposableWithBlock:^{

{

           
//block调用的时刻:当信号发送完成或者发送错误,就会执行这个blcok,取消订阅信号

                ……

            //执行完block后,当前信号就不存在被订阅了

}

            NSLog(@”信号订阅者被销毁”);

然而除了用户界面程序外,在很多其他地方也用到了事件驱动模式,比如观察者模式(Observer)或发布/订阅(Publish/Subscribe)里:在一个类里发布(Publish)某个可以被触发的事件,而其他的类就可以来订阅(Subscribe)该事件。一旦这个发布者类触发了该事件,那么运行时环境会立刻告知所有订阅了该事件的订阅者类:这个事件发生了!从而各个订阅者类可以作出它们自己的反应(调用相应方法)。

You can leave a response, or trackback from your own site.

Leave a Reply

网站地图xml地图