ReactiveCocoa核心方法bind(绑定)

本文详细介绍了ReactiveCocoa框架中bind方法的工作原理及其使用步骤。bind方法允许开发者在信号发送数据前对其进行处理,从而改变信号的内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转自:http://blog.youkuaiyun.com/u013232867/article/details/51437998


ReactiveCocoa操作

     所有的信号(RACSignal)都可以进行操作处理,因为所有的操作方法都定义在RACStream.h中,而RACSignal继承RACStream。
ReactiveCocoa操作思想
     运用的是(钩子)HooK思想,Hook是一种用于改变API执行结果的技术。
     Hook用处:截获API调用的技术。
     Hook原理:在每次调用一个API返回结果之前,先执行自己的方法,改变方法的输出。
     RAC开发方式RAC核心开发方式也就是绑定,之前的开发方式是赋值,而RAC开发应该把重心放在绑定,也就是在创建一个对象的时候就绑定好以后想要做的事情,而不是等赋值之后在去做事情。
    例如:把数据展示在控件上,之前都是重写控件的setModel方法,用RAC就可以在一开始创建控件的时候就绑定好数据

ReactiveCocoa核心方法bind
     ReactiveCocoa操作的核心方法是bind(绑定),给RAC中的信号进行绑定,只要信号一发送数据就能监听到,从而把发送数据改成自己想要的数据。
    在开发中很少使用bind方法,bind属于RAC中的底层方法,RAC已经封装了很多好用的其他方法,底层都是调用bind,用法比bind简单。
    bind方法简单介绍和使用:
     // 创建信号

1.    RACSubject *subject = [RACSubject subject];

    // 绑定信号,只有绑定的信号被订阅就会被调用

    // bind 返回一个绑定信号

2.    RACSignal  *bindsignal =  [subject bind:^RACStreamBindBlock{

        

        

        return ^RACSignal *(id value, BOOL *stop){

            // block调用:只要原信号发送数据,就会调用block

            // block作用:处理原信号内容

            // value:原信号发送的内容

            

            NSLog(@"接收到原信号的内容%@",value);

            // 返回信号,不能传nil,返回空信号[RACSignal empty]

            value = @"程倩";

            return [RACReturnSignal return:value];

        };

        

    }];

    

 3.  [bindsignal subscribeNext:^(id x) {

         NSLog(@"接收到绑定信号处理完的内容%@",x);

    }];

  // 原型号发送数据

 4.  [subject sendNext:@"123"];

输出:

2016-05-17 11:41:00.549 ReactiveCocoaTest1[1102:67222] 接收到原信号的内容123

2016-05-17 11:41:00.549 ReactiveCocoaTest1[1102:67222] 接收到绑定信号处理完的内容程倩


分析

1.     RACSubject *subject = [RACSubject subject];

   创建一个RACSubject原信号 初始化一个数组_subscribers

   源码:

   @implementation RACSubject


         #pragma mark Lifecycle


+ (instancetype)subject {

return [[self allocinit];

}


- (id)init {

self = [super init];

if (self == nilreturn nil;

      //初始化一个订阅者和_subscribers数组

_disposable = [RACCompoundDisposablecompoundDisposable];

_subscribers = [[NSMutableArray allocinitWithCapacity:1];

return self;

}

 2. RACSignal  *bindsignal =  [subject bind:^RACStreamBindBlock{

        return ^RACSignal *(id value, BOOL *stop){

            NSLog(@"接收到原信号的内容%@",value);

            // 返回信号,不能传nil,返回空信号[RACSignal empty]

            value = @"程倩";

            return [RACReturnSignal return:value];

        }; 

    }];

   bind:这个函数存在于RACSubject的父类RACSignal中

   源码:

/**

 *  @param block 传入的block

 *  @return 返回绑定后的信号

 */

- (RACSignal *)bind:(RACStreamBindBlock (^)(void))block {

NSCParameterAssert(block != NULL);// 一个异常处理,不太懂




     // 直接返回了一个绑定后的信号 创建一个新的信号返回,创建的是(RACDynamicSignal类型信号)

    // RACDynamicSignalRACSignal的子类 将这个didSubscribe block块保存在了RACDynamicSignal中的_didSubscribe block

{

      可以看到 [RACSignal createSignal:]中返回的结果是[RACDynamicSignal    createSignal:didSubscribe];

   看一下[RACDynamicSignal createSignal:didSubscribe]中的源码:
  @implementation RACDynamicSignal

+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe {

RACDynamicSignal *signal = [[self allocinit];

signal->_didSubscribe = [didSubscribe copy];

return [signal setNameWithFormat:@"+createSignal:"];

}

  返回的是RACDynamicSignal对象,并且将传入的block块保存在了对象的_didSubscribe中

}

       


// 所以说这个block现在并没有被执行,只是将这个block存在了绑定信号中了

return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {

                //当绑定的信号被订阅的时候开始执行这个block

                //调用block后得到创建绑定信号时传入block返回的block 这个block返回一个新的信号

RACStreamBindBlock bindingBlock = block();


NSMutableArray *signals = [NSMutableArrayarrayWithObject:self];


RACCompoundDisposable *compoundDisposable = [RACCompoundDisposable compoundDisposable];


               // 这个block暂时不会被执行

void (^completeSignal)(RACSignal *, RACDisposable *) = ^(RACSignal *signal, RACDisposable *finishedDisposable) {

BOOL removeDisposable = NO;


@synchronized (signals) {

[signals removeObject:signal];


if (signals.count == 0) {

[subscriber sendCompleted];

[compoundDisposable dispose];

else {

removeDisposable = YES;

}

}


if (removeDisposable) [compoundDisposable removeDisposable:finishedDisposable];

};


 // 这个block暂时不会被执行

void (^addSignal)(RACSignal *) = ^(RACSignal *signal) {

@synchronized (signals) {

[signals addObject:signal];

}


RACSerialDisposable *selfDisposable = [[RACSerialDisposable allocinit];

[compoundDisposable addDisposable:selfDisposable];

           // 在这里对返回的RACReturnSignal内容处理完成的信号进行订阅

RACDisposable *disposable = [signal subscribeNext:^(id x) {

[subscriber sendNext:x];

error:^(NSError *error) {

[compoundDisposable dispose];

[subscriber sendError:error];

completed:^{

@autoreleasepool {

completeSignal(signal, selfDisposable);

}

}];


selfDisposable.disposable = disposable;

};


@autoreleasepool {

RACSerialDisposable *selfDisposable = [[RACSerialDisposable allocinit];

[compoundDisposable addDisposable:selfDisposable];


                   //当代码执行到这里的时候

     // 在这里对原信号进行订阅,当原信号发送数据的时候会调用这个Nextblock

RACDisposable *bindingDisposable = [selfsubscribeNext:^(id x) {

if (compoundDisposable.disposedreturn;


BOOL stop = NO;

// 当原信号发送数据的时候在这里调用了之前绑定block返回的block内容,得到一个信号

id signal = bindingBlock(x, &stop);


@autoreleasepool {

// 这这里执行了addSignal block,传入绑定block执行完毕返回的信号

if (signal != nil) addSignal(signal);

if (signal == nil || stop) {

[selfDisposable dispose];

completeSignal(self, selfDisposable);

}

}

error:^(NSError *error) {

[compoundDisposable dispose];

[subscriber sendError:error];

completed:^{

@autoreleasepool {

completeSignal(self, selfDisposable);

}

}];


selfDisposable.disposable = bindingDisposable;

}


return compoundDisposable;

}] setNameWithFormat:@"[%@] -bind:"self.name];

}


3.订阅绑定信号:


  [bindsignal subscribeNext:^(id x) {

         NSLog(@"接收到绑定信号处理完的内容%@",x);

    }];

 分析:

  subscribeNext:函数源码

  bindsignal的类型是RACDynamicSignal类型

 - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {

NSCParameterAssert(nextBlock != NULL);

RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];

return [self subscribe:o];

}

[self subscribe:o]的源码是:

- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {

NSCParameterAssert(subscriber != nil);

RACCompoundDisposable *disposable = [RACCompoundDisposablecompoundDisposable];

      // 这个函数里面记录了绑定信号的订阅者

      源码{

           - (instancetype)initWithSubscriber:(id<RACSubscriber>)subscriber signal:(RACSignal *)signal disposable:(RACCompoundDisposable *)disposable {

NSCParameterAssert(subscriber != nil);


self = [super init];

if (self == nilreturn nil;


    // 记录下了绑定信号的订阅者

_innerSubscriber = subscriber;

_signal = signal;

_disposable = disposable;


[self.innerSubscriberdidSubscribeWithDisposable:self.disposable];

return self;

}


#pragma mark RACSubscriber


- (void)sendNext:(id)value {

if (self.disposable.disposedreturn;


if (RACSIGNAL_NEXT_ENABLED()) {

RACSIGNAL_NEXT(cleanedSignalDescription(self.signal), cleanedDTraceString(self.innerSubscriber.description), cleanedDTraceString([value description]));

}

    // 在这里拿到绑定后的信号订阅者发送数据

[self.innerSubscriber sendNext:value];

}

     

     }

subscriber = [[RACPassthroughSubscriber allocinitWithSubscriber:subscriber signal:self disposable:disposable];

if (self.didSubscribe != NULL) {

RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{

// 在这里可以看到订阅绑定信号的时候调用了创建绑定信号时候传入的block 跳转到2步骤中的 block

RACDisposable *innerDisposable = self.didSubscribe(subscriber);

[disposable addDisposable:innerDisposable];

}];

[disposable addDisposable:schedulingDisposable];

}

return disposable;

}


4.  // 原型号发送数据

    [subject sendNext:@"123"];

 分析:

  拿到数组中的所有的block调用,(原信号被订阅时候存下的block)


 例子:

   假设想监听文本框的内容,并且在每次输出结果的时候,都在文本框的内容拼接一段文字输出:

  
      // 方式一:在返回结果后,拼接。

    [_textfield.rac_textSignal subscribeNext:^(id x) {

        NSLog(@"输出:%@",x);

    }];

    

    // 方式二:在返回结果前,拼接,使用RACbind方法做处理。

    // bind方法参数:需要传入一个返回值是RACStreamBindBlockblock参数

    // RACStreamBindBlock是一个block的类型,返回值是信号,参数(value,stop),因此参数的block返回值也是一个block

    

    // RACStreamBindBlock:

    // 参数一(value):表示接收到信号的原始值,还没做处理

    // 参数二(*stop):用来控制绑定Block,如果*stop = yes,那么就会结束绑定。

    // 返回值:信号,做好处理,在通过这个信号返回出去,一般使用RACReturnSignal,需要手动导入头文件RACReturnSignal.h

    

    // bind方法使用步骤:

    // 1.传入一个返回值RACStreamBindBlockblock

    // 2.描述一个RACStreamBindBlock类型的bindBlock作为block的返回值。

    // 3.描述一个返回结果的信号,作为bindBlock的返回值。

    // 注意:在bindBlock中做信号结果的处理。

    

    // 底层实现:

    // 1.源信号调用bind,会重新创建一个绑定信号。

    // 2.当绑定信号被订阅,就会调用绑定信号中的didSubscribe,生成一个bindingBlock

    // 3.当源信号有内容发出,就会把内容传递到bindingBlock处理,调用bindingBlock(value,stop)

    // 4.调用bindingBlock(value,stop),会返回一个内容处理完成的信号(RACReturnSignal)。

    // 5.订阅RACReturnSignal,就会拿到绑定信号的订阅者,把处理完成的信号内容发送出来。

    

    // 注意:不同订阅者,保存不同的nextBlock,看源码的时候,一定要看清楚订阅者是哪个。

    // 这里需要手动导入#import <ReactiveCocoa/RACReturnSignal.h>,才能使用RACReturnSignal

    

    [[_textfield.rac_textSignal bind:^RACStreamBindBlock{

        // 什么时候调用:

        // block作用:表示绑定了一个信号.

        return ^RACStream *(id value, BOOL *stop){

            // 什么时候调用block:当信号有新的值发出,就会来到这个block

            // block作用:做返回值的处理

            // 做好处理,通过信号返回出去.

            return [RACReturnSignal return:[NSString stringWithFormat:@"输出:%@",value]];

        };

    }] subscribeNext:^(id x) {

        NSLog(@"%@",x);


    }];

不正确之处欢迎补充

代码地址:https://github.com/CharType/ReactiveCocoaTest


<think>好的,用户想了解如何在JavaScript中使用bind方法将类的方法绑定到实例对象。首先,我需要回忆一下bind的基本用法。bind的作用是创建一个新函数,并将this值绑定到指定的对象。这对于类方法特别有用,尤其是在事件处理或回调中,确保this指向正确的实例。 接下来,用户可能需要一个具体的示例。我应该先定义一个类,比如MyClass,里面有一个方法比如handleClick。如果不使用bind,当这个方法被用作事件监听器时,this可能会指向DOM元素而不是实例,导致错误。这时候,在构造函数中使用bind方法绑定到实例,就能解决这个问题。 然后,需要考虑现代JavaScript的特性,比如类字段语法,这样可以在定义方法时直接使用箭头函数,自动绑定this。不过箭头函数不能被重新绑定,这可能是个限制,但通常在这种情况下是可行的替代方案。 还要提到为什么需要绑定,比如类方法的this默认指向实例,但在回调中可能丢失上下文。因此,使用bind可以确保上下文正确。同时,要比较bind和箭头函数的优缺点,让用户知道不同的方法。 可能用户会遇到的问题包括忘记绑定方法,导致this为undefined或者在事件中指向错误的对象。需要指出这些常见错误,并提供解决方案。例如,在构造函数中绑定所有需要的方法,或者在类定义中使用箭头函数自动绑定。 另外,要确保示例代码正确,语法无误,并且注释清楚。比如,在构造函数中绑定handleClick,然后在事件监听中使用绑定的版本。同时,展示使用类字段语法的例子,让用户看到另一种方法。 最后,生成相关问题时要涵盖性能、替代方法、箭头函数比较和应用场景,这样用户如果有进一步的问题,可以继续探索。</think>### JavaScript 类方法绑定实例教程 在 JavaScript 中,当类方法需要作为回调函数(如事件监听器)时,`this` 可能丢失指向实例对象的上下文。使用 `bind()` 可以永久绑定方法与实例对象,确保 `this` 的正确指向[^1]。 #### 基础示例 ```javascript class MyClass { constructor(value) { this.value = value; // 绑定方法到实例 this.handleClick = this.handleClick.bind(this); } handleClick() { console.log("Value:", this.value); } } const obj = new MyClass(42); document.addEventListener('click', obj.handleClick); // 点击页面输出 "Value: 42" ``` **代码解析**: 1. 在构造函数中通过 `bind(this)` 将 `handleClick` 方法的 `this` 永久绑定到当前实例 2. 绑定后的方法作为事件监听器时,仍能正确访问实例属性 #### 现代语法优化(类字段 + 箭头函数) ```javascript class MyClass { constructor(value) { this.value = value; } // 使用箭头函数自动绑定 this handleClick = () => { console.log("Value:", this.value); } } ``` **特性对比**: | 方法 | 绑定方式 | 可重绑定 | 语法简洁度 | |---------------|--------------|---------|-----------| | `bind()` | 显式绑定 | 支持 | 中等 | | 箭头函数 | 自动绑定 | 不支持 | 高 | #### 典型应用场景 1. 事件处理器绑定(如点击、输入事件) 2. 异步回调(如 `setTimeout`、Promise) 3. 高阶函数参数传递
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值