2019-12-10 22:47:22 wj610671226 阅读数 5
  • iOS从初级到精通就业 Objective-C

    C语言基础入门,面向初级,毫无编程经验的学生群体,通过学习能够掌握C语言的基本语法,编程的基本概念,培养自己的编程思维!OC语言详解,掌握OC核心内容(面向对象、内存管理,类的扩展,KVC和KVO,闭包和常见的Foundation类库的使用方法等,为后续学习iOS SDK打好基础。

    10109 人正在学习 去看看 栾斌

RxSwift KVO的实现原理

RxSwift中的KVO使用是不需要自己移除观察者的,那它的底层实现原理是什么呢?

  • 案例使用
person.rx.observe(String.self, "name").subscribe(onNext: { (change) in
    print("observe订阅到了KVO:\(String(describing: change))")
}).disposed(by: disposeBag)

person.rx.observeWeakly(String.self, "name").subscribe(onNext: { (change) in
    print("observeWeakly订阅到了KVO:\(String(describing: change))")
}).disposed(by: disposeBag)
  • 源码流程分析图

在这里插入图片描述

  • 源码分析

1、首先person.rx.observe(String.self, “name”)创建一个KVOObservable序列,这个序列中实现了KVOObservableProtocol协议,保存了target、keyPath等信息

public func observe<Element>(_ type: Element.Type, _ keyPath: String, options: KeyValueObservingOptions = [.new, .initial], retainSelf: Bool = true) -> Observable<Element?> {
    return KVOObservable(object: self.base, keyPath: keyPath, options: options, retainTarget: retainSelf).asObservable()
}
    

2、KVOObservable实现了ObservableType协议,所以自己实现了subscribe方法,并且创建了一个KVOObserver观察者

init(object: AnyObject, keyPath: String, options: KeyValueObservingOptions, retainTarget: Bool) {
    self.target = object
    self.keyPath = keyPath
    self.options = options
    self.retainTarget = retainTarget
    if retainTarget {
        self.strongTarget = object
    }
}

func subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element? {
let observer = KVOObserver(parent: self) { value in
    if value as? NSNull != nil {
        observer.on(.next(nil))
        return
    }
    observer.on(.next(value as? Element))
}

return Disposables.create(with: observer.dispose)
    

3、KVOObserver继承了一个OC类_RXKVOObserver,实现了Disposable协议。在init方法和dispose方法中都是调用了父类的实现
4、_RXKVOObserver实际上就是对OC中KVO实现的封装使用,将_RXKVOObserver变成target的观察者,在dispose方法中移除观察者

-(instancetype)initWithTarget:(id)target
                 retainTarget:(BOOL)retainTarget
                      keyPath:(NSString*)keyPath
                      options:(NSKeyValueObservingOptions)options
                     callback:(void (^)(id))callback {
    // 省略
    self.keyPath = keyPath;
    self.callback = callback;
    [self.target addObserver:self forKeyPath:self.keyPath options:options context:nil];
    return self;
}

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    @synchronized(self) {
        self.callback(change[NSKeyValueChangeNewKey]);
    }
}

-(void)dispose {
    [self.target removeObserver:self forKeyPath:self.keyPath context:nil];
    self.target = nil;
    self.retainedTarget = nil;
}

5、当创建的序列订阅subscribe时候就会来到KVOObservable的subscribe方法
6、当观察的属性值发生变化的时候就回来到_RXKVOObserver类的observeValueForKeyPath方法,然后触发KVOObserver的闭包,观察者发送next事件

observer.on(.next(value as? Element))

7、这个时候就会来到刚开始订阅的onNext事件闭包回调
8、关于销毁,这个序列的销毁者是加入到disposeBag中的,只有当disposeBag对象销毁的时候,就会对disposeBag中保存的销毁者调用dispose()方法,这个时候就会触发_RXKVOObserver中的dispose方法移除销毁者。

2019-08-09 15:50:24 qq_42792413 阅读数 40
  • iOS从初级到精通就业 Objective-C

    C语言基础入门,面向初级,毫无编程经验的学生群体,通过学习能够掌握C语言的基本语法,编程的基本概念,培养自己的编程思维!OC语言详解,掌握OC核心内容(面向对象、内存管理,类的扩展,KVC和KVO,闭包和常见的Foundation类库的使用方法等,为后续学习iOS SDK打好基础。

    10109 人正在学习 去看看 栾斌

收录:原文地址

KVO在我们实际开发之中运用非常之多,很多开发者都知道原理!但是这些原理是如何来的,一般都是浅尝辄止。这个篇章我会从 Swift 入手分析,探索KVO底层源码.希望让读者真正掌握这一块底层,知其然而知其所以然!

KVO简介

首先我们从KVO的三部曲开始

// 1: 添加观察
person.addObserver(self, forKeyPath: "name", options: .new, context: nil)
// 2: 观察响应回调
override func observeValue(forKeyPath keyPath:, of object:, change: , context:){}
// 3: 移除观察
person.removeObserver(self, forKeyPath: "name")

其实我们也知道,就是平时在开发的时候,我们也可以通过计算型属性也可以直接观察

var name: String = ""{
    willSet{
        print(newValue)
    }
    didSet{
        print(oldValue)
    }
}

问题来了:这两者有什么关系?

KVO与计算型属性的关系

下面我们开始分析,首先感谢苹果开源精神,在Github可以直接下载,我们通过 Swift 源码展开分析

public func willChangeValue<Value>(for keyPath: __owned KeyPath<Self, Value>) {
    (self as! NSObject).willChangeValue(forKey: _bridgeKeyPathToString(keyPath))
}

public func willChange<Value>(_ changeKind: NSKeyValueChange, valuesAt indexes: IndexSet, for keyPath: __owned KeyPath<Self, Value>) {
    (self as! NSObject).willChange(changeKind, valuesAt: indexes, forKey: _bridgeKeyPathToString(keyPath))
}

public func willChangeValue<Value>(for keyPath: __owned KeyPath<Self, Value>, withSetMutation mutation: NSKeyValueSetMutationKind, using set: Set<Value>) -> Void {
    (self as! NSObject).willChangeValue(forKey: _bridgeKeyPathToString(keyPath), withSetMutation: mutation, using: set)
}

public func didChangeValue<Value>(for keyPath: __owned KeyPath<Self, Value>) {
    (self as! NSObject).didChangeValue(forKey: _bridgeKeyPathToString(keyPath))
}

public func didChange<Value>(_ changeKind: NSKeyValueChange, valuesAt indexes: IndexSet, for keyPath: __owned KeyPath<Self, Value>) {
    (self as! NSObject).didChange(changeKind, valuesAt: indexes, forKey: _bridgeKeyPathToString(keyPath))
}

public func didChangeValue<Value>(for keyPath: __owned KeyPath<Self, Value>, withSetMutation mutation: NSKeyValueSetMutationKind, using set: Set<Value>) -> Void {
    (self as! NSObject).didChangeValue(forKey: _bridgeKeyPathToString(keyPath), withSetMutation: mutation, using: set)
}

  • willChangeValuedidChangeValue 作为数据改变的两个重要的方法
  • 我们通过这两个方法继续展开分析
class Target : NSObject, NSKeyValueObservingCustomization {
    // This dynamic property is observed by KVO
    @objc dynamic var objcValue: String
    @objc dynamic var objcValue2: String {
        willSet {
            willChangeValue(for: \.objcValue2)
        }
        didSet {
            didChangeValue(for: \.objcValue2)
        }
    }
}

  • 很明显的继承关系来自NSObject
  • 实现了 NSKeyValueObservingCustomization 的协议
public protocol NSKeyValueObservingCustomization : NSObjectProtocol {
    static func keyPathsAffectingValue(for key: AnyKeyPath) -> Set<AnyKeyPath>
    static func automaticallyNotifiesObservers(for key: AnyKeyPath) -> Bool
}

  • 这也是我们两个非常重要的方法,平时在开发也是很有利的方法,keyPathsAffectingValue能够建立 keyPath 的依赖,例如两个属性的变化同时影响一个重要属性的改变:进度 = 下载量 / 总量
  • automaticallyNotifiesObservers 自动开关
  • 很明显我们的计算型属性在willSet里面就调用willChangeValuedidSet调用didChangeValue,的确我们计算型属性是和我们KVO相关方法是有所关联,这里也直接证明!
  • OK,我们探索完这个问题,我们摸着这条线继续探索KVO底层

KVO底层

这里说明一下,本篇章的贴出的源码没有给大家省略,目的是想让大家认真阅读,自己对照学习。当然可能中间我也忽略过一些细节,源码直接贴出来方便自己理解

添加观察

person.addObserver(self, forKeyPath: "name", options: .new, context: nil)

现在我们开始探索底层源码:

- (void) addObserver: (NSObject*)anObserver
      forKeyPath: (NSString*)aPath
         options: (NSKeyValueObservingOptions)options
         context: (void*)aContext
{
  GSKVOInfo             *info;
  GSKVOReplacement      *r;
  NSKeyValueObservationForwarder *forwarder;
  NSRange               dot;

  setup();
  [kvoLock lock];

  // Use the original class
  r = replacementForClass([self class]);

  /*
   * Get the existing observation information, creating it (and changing
   * the receiver to start key-value-observing by switching its class)
   * if necessary.
   */
  info = (GSKVOInfo*)[self observationInfo];
  if (info == nil)
    {
      info = [[GSKVOInfo alloc] initWithInstance: self];
      [self setObservationInfo: info];
      object_setClass(self, [r replacement]);
    }

  /*
   * Now add the observer.
   */
  dot = [aPath rangeOfString:@"."];
  if (dot.location != NSNotFound)
    {
      forwarder = [[NSKeyValueObservationForwarder alloc]
        initWithKeyPath: aPath
           ofObject: self
         withTarget: anObserver
        context: aContext];
      [info addObserver: anObserver
             forKeyPath: aPath
                options: options
                context: forwarder];
    }
  else
    {
      [r overrideSetterFor: aPath];
      [info addObserver: anObserver
             forKeyPath: aPath
                options: options
                context: aContext];
    }

  [kvoLock unlock];
}

  • 中间replacementForClass做了一些处理:

    • 创建了一个动态子类名字:“NSKVONotifing_原类的名字”
    • 添加了class、set、dealloc方法
    • 原类的isa与动态isa切换
  • 由原来的观察者进行迁移到 GSKVOInfo

- (void) addObserver: (NSObject*)anObserver
      forKeyPath: (NSString*)aPath
         options: (NSKeyValueObservingOptions)options
         context: (void*)aContext
{
  GSKVOPathInfo         *pathInfo;
  GSKVOObservation      *observation;
  unsigned              count;

  if ([anObserver respondsToSelector:
    @selector(observeValueForKeyPath:ofObject:change:context:)] == NO)
    {
      return;
    }
  [iLock lock];
  pathInfo = (GSKVOPathInfo*)NSMapGet(paths, (void*)aPath);
  if (pathInfo == nil)
    {
      pathInfo = [GSKVOPathInfo new];
      // use immutable object for map key
      aPath = [aPath copy];
      NSMapInsert(paths, (void*)aPath, (void*)pathInfo);
      [pathInfo release];
      [aPath release];
    }

  observation = nil;
  pathInfo->allOptions = 0;
  count = [pathInfo->observations count];
  while (count-- > 0)
    {
      GSKVOObservation      *o;

      o = [pathInfo->observations objectAtIndex: count];
      if (o->observer == anObserver)
        {
          o->context = aContext;
          o->options = options;
          observation = o;
        }
      pathInfo->allOptions |= o->options;
    }
  if (observation == nil)
    {
      observation = [GSKVOObservation new];
      GSAssignZeroingWeakPointer((void**)&observation->observer,
    (void*)anObserver);
      observation->context = aContext;
      observation->options = options;
      [pathInfo->observations addObject: observation];
      [observation release];
      pathInfo->allOptions |= options;
    }

  if (options & NSKeyValueObservingOptionInitial)
    {
      /* If the NSKeyValueObservingOptionInitial option is set,
       * we must send an immediate notification containing the
       * existing value in the NSKeyValueChangeNewKey
       */
      [pathInfo->change setObject: [NSNumber numberWithInt: 1]
                           forKey:  NSKeyValueChangeKindKey];
      if (options & NSKeyValueObservingOptionNew)
        {
          id    value;

          value = [instance valueForKeyPath: aPath];
          if (value == nil)
            {
              value = null;
            }
          [pathInfo->change setObject: value
                               forKey: NSKeyValueChangeNewKey];
        }
      [anObserver observeValueForKeyPath: aPath
                                ofObject: instance
                                  change: pathInfo->change
                                 context: aContext];
    }
  [iLock unlock];
}

  • 判断我们的观察者是否能够响应:observeValueForKeyPath:ofObject:change:context:方法。常规操作,没有回调,响应就没有什么意义了!
  • 通过获取pathInfo来保存KVO信息
  • 中间对context&options的处理数据
  • NSKeyValueObservingOptionInitial就会主动发起一次KVO响应:observeValueForKeyPath

观察属性变化的时候

- (void) willChangeValueForKey: (NSString*)aKey
{
  GSKVOPathInfo *pathInfo;
  GSKVOInfo     *info;

  info = (GSKVOInfo *)[self observationInfo];
  if (info == nil)
    {
      return;
    }

  pathInfo = [info lockReturningPathInfoForKey: aKey];
  if (pathInfo != nil)
    {
      if (pathInfo->recursion++ == 0)
        {
          id    old = [pathInfo->change objectForKey: NSKeyValueChangeNewKey];

          if (old != nil)
            {
              /* We have set a value for this key already, so the value
               * we set must now be the old value and we don't need to
               * refetch it.
               */
              [pathInfo->change setObject: old
                                   forKey: NSKeyValueChangeOldKey];
              [pathInfo->change removeObjectForKey: NSKeyValueChangeNewKey];
            }
          else if (pathInfo->allOptions & NSKeyValueObservingOptionOld)
            {
              /* We don't have an old value set, so we must fetch the
               * existing value because at least one observation wants it.
               */
              old = [self valueForKey: aKey];
              if (old == nil)
                {
                  old = null;
                }
              [pathInfo->change setObject: old
                                   forKey: NSKeyValueChangeOldKey];
            }
          [pathInfo->change setValue:
            [NSNumber numberWithInt: NSKeyValueChangeSetting]
            forKey: NSKeyValueChangeKindKey];

          [pathInfo notifyForKey: aKey ofInstance: [info instance] prior: YES];
        }
      [info unlock];
    }
  [self willChangeValueForDependentsOfKey: aKey];
}

  • 通过pathInfo获取回之前的旧值
  • pathInfo->change 里面的数据处理
  • 重点:[pathInfo notifyForKey: aKey ofInstance: [info instance] prior: YES];开始发起响应通知
- (void) notifyForKey: (NSString *)aKey ofInstance: (id)instance prior: (BOOL)f
{
  unsigned      count;
  id            oldValue;
  id            newValue;

  if (f == YES)
    {
      if ((allOptions & NSKeyValueObservingOptionPrior) == 0)
        {
          return;   // Nothing to do.
        }
      [change setObject: [NSNumber numberWithBool: YES]
                 forKey: NSKeyValueChangeNotificationIsPriorKey];
    }
  else
    {
      [change removeObjectForKey: NSKeyValueChangeNotificationIsPriorKey];
    }

  oldValue = [[change objectForKey: NSKeyValueChangeOldKey] retain];
  if (oldValue == nil)
    {
      oldValue = null;
    }
  newValue = [[change objectForKey: NSKeyValueChangeNewKey] retain];
  if (newValue == nil)
    {
      newValue = null;
    }

  /* Retain self so that we won't be deallocated during the
   * notification process.
   */
  [self retain];
  count = [observations count];
  while (count-- > 0)
    {
      GSKVOObservation  *o = [observations objectAtIndex: count];

      if (f == YES)
        {
          if ((o->options & NSKeyValueObservingOptionPrior) == 0)
            {
              continue;
            }
        }
      else
        {
          if (o->options & NSKeyValueObservingOptionNew)
            {
              [change setObject: newValue
                         forKey: NSKeyValueChangeNewKey];
            }
        }

      if (o->options & NSKeyValueObservingOptionOld)
        {
          [change setObject: oldValue
                     forKey: NSKeyValueChangeOldKey];
        }

      [o->observer observeValueForKeyPath: aKey
                                 ofObject: instance
                                   change: change
                                  context: o->context];
    }

  [change setObject: oldValue forKey: NSKeyValueChangeOldKey];
  [oldValue release];
  [change setObject: newValue forKey: NSKeyValueChangeNewKey];
  [newValue release];
  [self release];
}

  • change里面值的处理完毕之后
  • 让我们的观察者响应实现的KVO回调方法: [o->observer observeValueForKeyPath: aKey ofObject: instance change: change context: o->context];
  • 完美看到响应回调,舒服

移除观察者

移除观察的流程相对来说,比较简单了,但是优秀的我还是愿意和大家一起探索

- (void) removeObserver: (NSObject*)anObserver forKeyPath: (NSString*)aPath
{
  GSKVOInfo *info;
  id            forwarder;

  /*
   * Get the observation information and remove this observation.
   */
  info = (GSKVOInfo*)[self observationInfo];
  forwarder = [info contextForObserver: anObserver ofKeyPath: aPath];
  [info removeObserver: anObserver forKeyPath: aPath];
  if ([info isUnobserved] == YES)
    {
      /*
       * The instance is no longer being observed ... so we can
       * turn off key-value-observing for it.
       */
      object_setClass(self, [self class]);
      IF_NO_GC(AUTORELEASE(info);)
      [self setObservationInfo: nil];
    }
  if ([aPath rangeOfString:@"."].location != NSNotFound)
    [forwarder finalize];
}

  • 拿回我们observationInfo就是我们信息收集者
  • 利用NSMapRemove(paths, (void*)aPath)移除
  • 动态子类的isa和原类的isa切换回来
  • 把当前设置的info置空

OK 完美解析了KVO底层源码!我们在探索完KVO底层实现才能说是真正的掌握了,而不是通过面试宝典背下结论,那是没有什么意义! 在真正的高手对决间一眼就能看出,中间忽略了一些小细节,比如set的多种情况,setNumber类型,setInt类型, setLong类型…我相信聪明的你一样可以解析读懂!


对于以上技术点,想要更好的探讨,可以进入iOS技术圈,一起探讨学习

2019-04-23 16:04:02 happyshaotang2 阅读数 110
  • iOS从初级到精通就业 Objective-C

    C语言基础入门,面向初级,毫无编程经验的学生群体,通过学习能够掌握C语言的基本语法,编程的基本概念,培养自己的编程思维!OC语言详解,掌握OC核心内容(面向对象、内存管理,类的扩展,KVC和KVO,闭包和常见的Foundation类库的使用方法等,为后续学习iOS SDK打好基础。

    10109 人正在学习 去看看 栾斌

文章目录

  • 一 什么是RxSwift
  • 二 RxSwift做了什么
    • 2-1简单介绍观察者设计模式
    • 2-1RxSwift做了什么
    • 2-3 简单理解Observable&Observer
  • 三 RxSwift初级操作
    • 3-1 监听事件
    • 3-2 监听文本输入框的文字改变
    • 3-3 绑定数据赋值操作
    • 3-4 KVO
  • 四 RxSwift常见操作
    • 4-1 never的obserable
    • 4-2 empty的Obserable
    • 4-3 just
    • 4-4 of
    • 4-5 from
    • 4-6 create 自定义事件 开发中常用
    • 4-7 range
    • 4-8 repeat
  • 五 RxSwift中的Subjects
    • 5-1 Subjects 是什么?
    • 5-2 PublishSubject
    • 5-3 ReplySubject
    • 5-4 BehaviorSubject
    • 5-5 Variable
  • 六 RxSwift中的变换操作
    • 6-1 map
    • 6-2 flatmap
  • 七 资源的释放
    • 7-1 dispose
    • 7-2 Dispose Bags
  • 八 RxSwift下的UITableView的使用

一 什么是RxSwift

  • RxSwift是Swift函数响应式编程的一个开源库,由GitHub的ReactiveX组织开发和维护
  • 其他语言像C#,Java 和JS也有: Rx.Net、RxJava、RxJS
  • RxSwift的目的是让数据/事件流和异步任务能够更方便的序列化处理 能够使用Swift进行响应式编程

二 RxSwift做了什么?

2-1 简单介绍观察者设计模式

  • KVO,通知,甚至代理都是观察者模式,在设计模式中他可是一个重中之重的设计模式
  • 在我们iOS开发中 很多事件的监听都是通过观察者设计模式进行监听的

2-2 RxSwift做了什么?

  • RxSwift把我们程序中每一个操作都看成一个事件
  • 比如一个TextFiled中的文本改变,一个按钮的点击,或者一个网络请求结束等,每一个事件源就可以看成一个管道,也就是Sequence
  • 比如一个TextFiled,当我们改变里边的文本的时候 这个时候TextFiled就会不断的发出事件, 从他的Sequence中不断的流出 我们只需要监听这个Sequence,每流出一个事件 就做相应的处理
  • 同理UIButton 也是一个Sequence 每点击一次就流出一个事件

2-3 简单理解Observable&Observer

三RxSwift初级操作

3-1 监听事件


        // 1 监听按钮点击
        // 要用局部的按钮 方便提示  之所以在这里备用一个局部按钮 是因为RxSwift 有什么提示很不好 局部变量就可以解决这个问题
        // let button = UIButton();
        btn1.rx.tap.subscribe { (event : Event<()>) in
            print("我是按钮我被点击了")
        }.addDisposableTo(bag)

3-2 监听文本输入框的文字改变

  // 2.1 第一种方式
        
        textFlied.rx.text.subscribe { (event : Event<String?>) in
            // element 取出这个元素  取出来的是可选类型中的可选类型 可以解包两次
            print(event.element!!)
        }.disposed(by: bag)
        
        // 2.1 第二种方式 onNext
        textFlied.rx.text.subscribe(onNext: { (str : String?) in
            print(str!)
        }).disposed(by: bag)

3-3 绑定数据赋值操作

// 老套的做法
        textFlied.rx.text.subscribe(onNext: { (str : String?) in
            self.label.text = str!            
        }).disposed(by: bag)

//  RxSwift推荐的做法 .bind(to: <#T##ObserverType#>) observerType 是一个协议  而UIbindObser遵守了这个协议
    textFlied.rx.text.bind(to: label.rx.text).disposed(by: bag)

3-4 KVO

// KVO 传统的做法
  // 1 监听属性
  lbl.addObserver(self, forKeyPath: "text", options: .new, context: nil)
// 2 代理方法
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        print((change?[NSKeyValueChangeKey.newKey])!)
    }


// RxSwift 的做法

        label.rx.observe(String.self, "text").subscribe(onNext: { (str : String?) in            
            print(str!)            
        }).disposed(by: bag)        
        label.rx.observe(CGRect.self, "frame").subscribe(onNext: { (frame : CGRect?) in            
            print(frame!)            
        }).disposed(by: bag)

四 RxSwift常见操作

4-1 never的obserable

// 创建一个naver的obserable 从来不执行
        let neverO = Observable<String>.never()
        neverO.subscribe { (event : Event<String>) in
            print(event)
        }.disposed(by: bag)
        

4-2 empty的Obserable 只发出complete事件

        // 创建一个empty的Obserable 只能发出一个complete事件
        let empty = Observable<String>.empty()
        empty.subscribe { (event : Event<String>) in
            print(event)
        }.disposed(by: bag)

4-3 just


// just是创建一个sequence只能发出一种特定的事件 能正常结束
        let just = Observable.just("10")
        just.subscribe { (event : Event<String>) in
            print(event)
        }.disposed(by: bag)

4-4 of

//创建一个sequence 能发出很多事件信号
        let of = Observable.of("a","b","c")
        of.subscribe { (event : Event<String>) in
            print(event)
        }.disposed(by: bag)

4-5 from

 // from就是从数组中创建sequence
        let from = Observable.from(["1","2","3"])
        from.subscribe { (event : Event<String>) in
            print(event)
        }.disposed(by: bag)

4-6 create 自定义事件 开发中常用

//  create 创建一个自定义的disposable  实际开发中常用的  会在自定义很多事件来监听的

        let create = createObserable()
        create.subscribe { (event : Event<Any>) in
            print(event)
        }.disposed(by: bag)

// 方法
    func createObserable() -> Observable<Any> {        
        return Observable.create({ (observer : AnyObserver<Any>) -> Disposable in            
            observer.onNext("奥卡姆剃须刀")
            observer.onNext("18")
            observer.onNext("65")
            observer.onCompleted()
            
            return Disposables.create()
        })
    }


// 自定义just
let myJust = myJustObservable(element: "奥卡姆剃须刀")        
        myJust.subscribe { (event : Event<String>) in
            print(event)
        }.disposed(by: bag)


// 方法
    func myJustObservable(element:String) -> Observable<String> {        
        return Observable.create({ (observer : AnyObserver<String>) -> Disposable in            
            observer.onNext(element)            
            observer.onCompleted()            
            return Disposables.create()            
        })
    }

4-7 range 这个作用不是很大


        let range = Observable.range(start: 1, count: 10)
        range.subscribe { (event : Event<Int>) in
            print(event)
        }.disposed(by: bag)

4-8 repeat


        let repeatLL = Observable.repeatElement("奥卡姆剃须刀")
        // 重复次数
        repeatLL.take(5).subscribe { (event : Event<String>) in
            print(event)
        }.disposed(by: bag)

五 RxSwift中的Subjects

5-1 Subjects 是什么?

Subjects 是Observable 和 Observer 之间的桥梁, 一个subject 既是一个Obserable 也是一个 Observer 他既可以发出事件,也可以监听事件

5-2 PublishSubject

当你订阅PublishSubject的时候,你只能接受订阅他之后发生的事件,subject.onNext() 发出onNext事件,对应的还有onError() 和onCompleted() 事件

        // 1 publishSubject 订阅者只能接受,订阅之后的事件 也就是必须先订阅 再发送事件
        let publishSub = PublishSubject<String>()        
        publishSub.subscribe { (event : Event<String>) in
            print(event)
        }.disposed(by: bag)        
        publishSub.onNext("奥卡姆剃须刀")

5-3 ReplySubject

ReplySubject 当你订阅ReplySubject 的时候,你可以接收到订阅他之后的事件,但也可以接收订阅他之前发出的事件 ,接收几个事件取决于bufferSize的大小 会接收最后发送的信号

     
// 限制两个信号         
//      let replySub  = ReplaySubject<String>.create(bufferSize: 2)
        // 没有限制的replySubject
        let replySub = ReplaySubject<String>.createUnbounded()
        
        replySub.onNext("1")
        replySub.onNext("2")
        replySub.onNext("3")
        replySub.onNext("4")
        
        replySub.subscribe { (event : Event<String>) in
            print(event)
        }.disposed(by: bag)
        
        replySub.onNext("5")

5-4 BehaviorSubject

当你订阅了BehaviorSubject 你就会接收到订阅之前的最后一个事件 可以初始化的时候就给一个订阅值
这个用的是最多的 一般用法是初始的时候给一个默认数据 然后进行刷新加载更多数据

        let behaviorSub = BehaviorSubject(value: "10")        
        behaviorSub.onNext("a")
        // 只能订阅到 b
        behaviorSub.onNext("b")        
        behaviorSub.subscribe { (event : Event<String>) in
            print(event)
        }.disposed(by: bag)        
        behaviorSub.onNext("11")
        behaviorSub.onNext("12")
        behaviorSub.onNext("13")

5-5 Variable

1 Variable 是BehaviorSubject 一个包装箱 就像是一个箱子一样 使用的时候需要调用asObservable() 拆箱,里面的Value 是一个BehaviorSubject
2 如果 Variable 打算发出事件 直接修改对象的Value即可
3 当事件结束的时候 Variable 会自动发出complete事件

        let variable = Variable("a")        
        // 要是想修改值 直接修改value就好
        variable.value = "b"        
        variable.asObservable().subscribe { (event : Event<String>) in
            print(event)
        }.disposed(by: bag)        
        // 也能发出事件
        variable.value = "c"
        variable.value = "d"

六 RxSwift中的变换操作

6-1 map

通过传入一个函数闭包把原来的sequence转变为一个新的sequence的操作


    //  1 swift中map的使用
        let array = [1,2,3,4]
        
        let arr2 = array.map { (num : Int) -> Int in
            return num * num
        }
        
        print(arr2)
  //    2  swift的函数式编程  用的还是比较少的 还是习惯于面向对象的方式
        let arr3 = array.map({ $0 * $0 })
        
        print(arr3)
        
 //    3 rxswift 中map函数的使用
        Observable.of(10,11,12,13)
            .map { (num : Int) -> Int in
                return num * num
            }.subscribe { (event : Event<Int>) in
                print(event)
            }.disposed(by: bag)

6-2 flatmap

将一个sequence转换成sequences。 当你接受一个sequence事件,你还想接受其他sequence发出的事件的话 可以使用flatmap,他会将每一个sequence事件进行处理后 然后再以一个sequence形式发出事件

// 1  先定义一个结构体

// RxSwift的思想  让任何一个变量都是可以监听的
struct Student {
    var score : Variable<Double>    
}


        // 3 flatmap 的使用  映射 Observable的
        let stu1 = Student(score: Variable(100))
        let stu2 = Student(score: Variable(99))
        let stu3 = Student(score: Variable(98))
        
        
        //   链式编程思维
        let studentVariable = Variable(stu1)
        //  这个方法会把所有的是都监听到 都打印 但常常我们只关心最新的一个订阅
//        studentVariable.asObservable().flatMap { (stu : Student) -> Observable<Double> in
//            return stu.score.asObservable()
//            }.subscribe { (event : Event<Double>) in
//            print(event)
//        }.disposed(by: bag)
        
        // 常用的是这一个方法  会先打印一下初始化的数据  只会关心最新订阅值的改变
        studentVariable.asObservable().flatMapLatest { (stu : Student) -> Observable<Double> in
            return stu.score.asObservable()
            }.subscribe { (event : Event<Double>) in
                print(event)
            }.disposed(by: bag)
        
        studentVariable.value = stu2
        stu2.score.value = 0
        // 这样的话每一个值的改变都会发送信号
        
        //  我已经不关心你的值的改变 我只关心最新的订阅的值的变化
        stu1.score.value = 1000;

七 资源的释放

  • 当监听一个事件序列的时候,有消息事件来了,我们做某些事情,但是这个事件序列不在发出消息了,我们的监听也就没有什么存在价值了,所以我们需要释放我们这些监听资源,其实也就是每种编程语言中的内存资源释放,
  • OC 和Swift 也一样,在你不需要某些变量的时候,你需要吧这些变量所占用的内存空间释放掉,
  • 释放某一个监听的时候,我们可以手动调用释放方法

7-1 dispose

  • 相当于MRC中手动调用release操作

  • 注意:因为观察者已经销毁,所以后面无法接受事件

      // 1  dispose() 让对象立即销毁掉 后边在改变值的时候 此对象已经销毁了
      let variable1 = Variable("奥卡姆剃须刀")
      variable1.asObservable().subscribe { (event : Event<String>) in
          print(event)
      }.dispose()
    

7-2 Dispose Bags

  • 除了上边手动的方法 还有一种自动的方式 推荐使用这种方式 这种方式就好像iOS中的ARC方式 会自动去释放资源
    // 2 DisposeBag
    fileprivate lazy var bag : DisposeBag = DisposeBag()

    let subject = BehaviorSubject(value: "a")
    subject.subscribe { (event : Event<String>) in
        print(event)
    }.disposed(by: bag)

八 RxSwift下的UITableView的使用


// VC 中的代码

        //1.创建UITableView(跟OC几乎一样)
        let tabV = UITableView(frame: view.bounds, style: UITableViewStyle.plain)
        tableView = tabV
        
        
        //2.注册Cell
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellID)
        
        self.view.addSubview(tableView)
        
        //  负责数据绑定  传回来row  model  和 cell  自行复制
        viewModel.modelObserable.asObservable()
            .bind(to: tableView.rx.items(cellIdentifier: cellID,
                 cellType: UITableViewCell.self)){ row, model, cell in
                 cell.textLabel?.text = model.name
        }.disposed(by: bag)                

        // 监听点击cell 获取index
        tabV.rx.itemSelected.subscribe(onNext: { (index : IndexPath) in            
//            点击cell就刷新数据
//            self.reloadData()            
        }).disposed(by: bag)        

        // 监听点击cell 获取Model
        tabV.rx.modelSelected(RxModel.self).subscribe(onNext: { (model : RxModel) in            
            print(model.name)            
        }).disposed(by: bag)

VIewModel中的代码
var modelObserable : Variable<[RxModel]> = {        
        var models : [RxModel] = [RxModel]()        
        for i in 0..<20 {            
            let model = RxModel()
            model.name = "我是小\(i)"
            models.append(model)
        }        
        return Variable(models)        
    }()

 

 

2018-01-09 05:28:04 ZCMUCZX 阅读数 2301
  • iOS从初级到精通就业 Objective-C

    C语言基础入门,面向初级,毫无编程经验的学生群体,通过学习能够掌握C语言的基本语法,编程的基本概念,培养自己的编程思维!OC语言详解,掌握OC核心内容(面向对象、内存管理,类的扩展,KVC和KVO,闭包和常见的Foundation类库的使用方法等,为后续学习iOS SDK打好基础。

    10109 人正在学习 去看看 栾斌

之前学了下RAC,在RAC当中的话其实基本都是block,它的中心思想其实包含了KVO和迭代器模式的思想下一步需要做什么还有就是函数式编程。

在RxSwift中有几个概念比较重要的就是Observer也就是观察者,而Observerable是可被观察者,这个是个事件源,我们的观察者需要去订阅这个事件源

才会去收到消息的通知。在RxSwift中会把每个事件源都去看作是一个序列(sequence)。


下面我们会去进行调用最多的就是Observable.方法其实就是去创建一个事件序列,然后我们去订阅它。

下面介绍下just方法其实就是去创建一个sequence,然后这个sequence中只会有一个事件,这个事件中包裹的内容也就是我们传进入的hello world,下面就会打印出hello world,而且会自动的去做完成操作

let disposeBag = DisposeBag()
        
        let justSequence = Observable.just("hello world")
        justSequence.subscribe(onNext: {
            print($0)
        }).disposed(by: DisposeBag)

因为我们在去使用这个订阅方法的时候,也就是说会先去发送next,再去发送completed

let disposeBag = DisposeBag()
let justSequence = Observable.just("hello world")
justSequence.subscribe { (event) in
print(event)
}
self.justSequence = justSequence

会输出的是

紧接着我们来看个东西,一个枚举值,这里其实就是说next就是序列中的新元素产生的,会去处理,error就是序列中的元素发生错误,处理错误,结束整个序列,对所有的观察者取消订阅,其实这里的元素可以理解为信号,complete标记整个序列已经完成,取消所有观察者的订阅,一个序列发送 completed 或者 error 事件时,不会再去产生新的信号了,

public enum Event<Element> {
    /// Next element is produced.
    case next(Element)

    /// Sequence terminated with an error.
    case error(Swift.Error)

    /// Sequence completed successfully.
    case completed
}
下面还需要说一下在Swift中我们不需要去写出enum的实际名称也就是case Event.next:print(value)。因为类型检查器能够自动为此进行类型推算,在下面其实我们就是通过一个集合去创建一个序列.
let fromSequence = Observable.from(["1","2","3","4"])
        fromSequence.subscribe { (event) in
            switch event{
            case .next(let value):
                
                print(value)
            case .error(let error):
                print(error)
            case .completed:
                 print("完成")
            }
        }

打印如下


其中上面调用的是on:Event订阅所有的内容了就是说不管发生了错误也好还是完成了也好我都会去监听,去做处理


接下来我们还可以自己去创建一个自定义序列,这里把可被观察者给创建出来了,然后这个函数接收观察者作为其的参数

_ = Observable<String>.create({ observer in
            
            observer.on(.next("哈哈"))
            observer.on(.completed)
            
            return Disposables.create()
        }).subscribe{ x in
            print(x)
        }
我们可以看看create的,我们传入的observer应该就是在RxSwift.AnyObserver<Self.E>这里面做了实现,可以看到最后返回的是一个可被观察者Observable

 public static func create(_ subscribe: @escaping (RxSwift.AnyObserver<Self.E>) -> Disposable) -> RxSwift.Observable<Self.E>

再简单的介绍下Subject,Subject即可以是Observable也可以是一个Observer,也就是说它是一个既可以去发送事件也可以去监听事件。

PublishSubject:订阅PublishSubject的时候,只能接收到订阅它之后发生的事件,前面发送的事件是不会接受的

let publishSubject = PublishSubject<Any>()
        
        
        publishSubject.subscribe { (value) in
            
            print(value)
        }
        //创建序列中一个事件,这个事件的内容是Hello
        publishSubject.onNext("Hello")
        publishSubject.onNext("world")
        publishSubject.onNext("world")


ReplaySubject:订阅ReplaySubject的时候,可以接收到订阅它之后的事件,也可以接受订阅它之前发出的事件,接受几个事件取决与bufferSize的大小,下面就是只去接收前面两个事件

        let replySubject = ReplaySubject<String>.create(bufferSize: 2)
        
        replySubject.onNext("1")
        replySubject.onNext("2")
        replySubject.onNext("3")
        replySubject.subscribe { (value) in
            print("打印的值为\(value)")
        }
        replySubject.onNext("4")
        replySubject.onNext("5")


BehaviorSubject:订阅了BehaviorSubject,默认情况下订阅者只能接受前面的最新的一条消息,而后面的都可以收到,这里的BehaviorSubject其实也就是为了防止第一个订阅者没有一个前一个信号可以接收而设立的

 let behaviorSubject = BehaviorSubject(value: "A")
        
        behaviorSubject.onNext("E")
        behaviorSubject.onNext("F")
        
        behaviorSubject.subscribe({
            print($0)
        })
        behaviorSubject.onNext("B")
        behaviorSubject.onNext("C")
        
        behaviorSubject.subscribe({
            print("2----\($0)")
        })
        
        behaviorSubject.onNext("D")
        behaviorSubject.onNext("H")

再记录一个Variable

Variable可以解释成是一个可监听的数据结构。使用Variable,可以监听数据的变化,当Variable被释放的时候,它会完成通过asObservable()所创建的序列,其实Variable里面是封装了BehaviorSubject的


Variable的使用,其实可以看出和BehaviorSubject一样都是只接收了上面的最新的一个信号,而后面的都是全部接收的,以及在通过asObservable()创建的序列释放的时候会去发送completed信号,因为当我们把variable设为全局属性的时候是没有去打印complete的

   let variable = Variable(0)
        
        variable.value=1;
        variable.value=2;
     let observable =  variable.asObservable()
        
           observable.subscribe(onNext: { (value) in
            print("1、打印的值为\(value)")
        }) {
            print("complete")
        }
        variable.value=3;
        variable.value=4;
        variable.value=5;
        observable.subscribe(onNext: { (value) in
            print("2、打印的值为\(value)")
        }) {
            print("complete")
        }
        variable.value=6;
        variable.value=7;


一般我们在开发的时候都会去创建一个全局的DisposeBag,就比如说我们可以把它引用为我们的属性,Disposebags 的作用跟ARC类似。当一个 Disposebags 被释放时,它会对内部每一个可以被清理的元素调用 dispose。DisposeBag 没有 dispose 方法,所以我们肯定不能去手动的调用。如果我们需要立即的清理资源,可以创建一个新的DisposeBag去替换掉我们之前强引用的那个Disposebags。用到这个主要想在事件完成之后去取消订阅,减少内存的使用和性能上面的消耗

一行代码可以绑定textField的text和label的text,这里的map其实就和RAC中的map是相似的,我不会立刻给你数据,我可以在里面先做完操作再给你。

 textField.rx.text.map{
               return "? \($0!)"
            }
            .bind(to:helloLabel.rx.text).disposed(by: disposeBag)

关于throttle和debounce这两个其实都是去做了限制事件的接受为什么这么说?就拿上面的举例子,如果我们不想让文本框一改变上面的label就紧跟着改变的话,其实我们就可以用到这两个中的其中一个,这两个都可以去限制在某个时间间隔内我不去接受你文本框改变所产生的那个事件。

再结合一个小案例,就是通过在文本框输入点击提交按钮之后可以把文本框里面的每次提交内容都插入label中。

//创建一个可监听的数组,其实这里用普通的可变数组也是可以的
let nameArray: Variable<[String]> = Variable([])
submitBtn.rx.tap.subscribe(onNext:{
            
            if self.textField.text != ""
            {
                //把textField中的数据放入这个nameArray中
                self.nameArray.value.append(self.textField.text!)
                //将数组中的元素以,来分隔开来,调用下面的joined方法的元素必须是String
                self.namesLabel.rx.text.onNext(self.nameArray.value.joined(separator: ","))
                print(self.nameArray.value.joined(separator: ","))
                //将文本框里面的内容清空
                self.textField.rx.text.onNext("")
            }
        }).disposed(by: disposeBag)
首先看控制台打印也就是我们上面输出的数组经过插入 , 之后的内容


其中这里有一个注意点就是joined(separator: ",")这个方法,只能是数组里面的元素是String的时候才能使用,因为我们点进去看会看到下面的内容,这里面的Element==String就表示的是数组里面的元素的类型必须要是String

extension Array where Element == String {

    /// Returns a new string by concatenating the elements of the sequence,
    /// adding the given separator between each element.
    ///
    /// The following example shows how an array of strings can be joined to a
    /// single, comma-separated string:
    ///
    ///     let cast = ["Vivien", "Marlon", "Kim", "Karl"]
    ///     let list = cast.joined(separator: ", ")
    ///     print(list)
    ///     // Prints "Vivien, Marlon, Kim, Karl"
    ///
    /// - Parameter separator: A string to insert between each of the elements
    ///   in this sequence. The default separator is an empty string.
    /// - Returns: A single, concatenated string.
    public func joined(separator: String = default) -> String
}
当然我们绑定textField的文本到label上面的文本,还有另外一种方式就是不经过map的这里为什么要
extension ControlPropertyType where Self.E == String? {

    /// Transforms control property of type `String?` into control property of type `String`.
    public var orEmpty: RxCocoa.ControlProperty<String> { get }
}

通过orEmpty?是因为其实就这里面将String?转为String处理,这样的话我们就不需要考虑String为nil的情况。在Rxswift中,对于所有字符串的监听都是转为orEmpty处理的

self.textField1.rx.text.orEmpty.bind(to:self.inputLabel.rx.text)

关于orEmpty其实框架里面也略微有解释

extension ControlPropertyType where Self.E == String? {

    /// Transforms control property of type `String?` into control property of type `String`.
    public var orEmpty: RxCocoa.ControlProperty<String> { get }
}




这里为什么可以绑定,其实我们用到的方法是bind(to: ObserverType)这个方法,其中ObserverType是一个协议,而我们的self.inputLabel.rx.text实际上是这个类型Binder<String?>


发在我们再点进去看,我们会发现它实现了ObserverType的协议,而实现ObserverType协议的是观察者对象,用于观察Observable发出的信号。所以我们这个label的就可以监听到textField信号的发出,然后去进行改变自己的值


还有一个那就是为什么这个textField是可被UILabel的rx.text观察的?而且它还可以被订阅。例如下面可以对它进行订阅

 self.textField1.rx.text.subscribe(onNext: { (value) in
            print("1、打印的值为\(value!)")
        }) {
            print("complete")
        }

之所以可以去订阅它的原因其实是因为当我们去看UITextField+Rx的文件的时候会发现这个rx.text其实是一个ControlProperty<String?>类型的

之后我们再往ControlProperty<String?>点进去看会发现是个结构体,并且遵守了ControlPropertyType协议

再点进ControlPropertyType协议去看,发现其也继承了ObservableType协议和ObserverType协议,而上文提到的ControlProperty其实是一个实现ControlPropertyType的结构体,因此我们可以将ControlProperty的对象当作是一个Observable来使用,也就是一个可被观察者


介绍下never,这个never就是创建出的Sequence是不会发出任何信号的,我们有的时候可以把它和其他信号进行concat,这样也能起到屏蔽信号的作用

 let neverSequence = Observable<Any>.never()
        
          neverSequence.subscribe { (_) in
            print("什么都不会发生")
        }.disposed(by: disposeBag)
empty,创建一个空的sequence,只会去发送一个completed的事件,在下面我们可以看到打印的value都是completed,这里应用场景的话可能会用于用户做了某件事情你只需要告诉它结果,就比如说下单了,订单完成还是没完成。
let emptySequence = Observable<Any>.empty()
        emptySequence.subscribe { (value) in
            print(value)
        }.disposed(by: disposeBag)
        emptySequence.subscribe { (value) in
            print(value)
        }.disposed(by: disposeBag)

of就是创建出一个sequence,里面有很多事件信号,顺序发送出去,发送完成之后,然后再发送completed。这里of和from的区别其实就在于of是把它括号里面的1 2 3分别当做单独的事件,一个一个的拿出来发送,而from我们括号里面传入的是集合类,其实就是解析事件里面的元素,再把元素给一个个的去拿出来。

let ofSequence = Observable.of(1,2,3)
       
        ofSequence.subscribe { (event) in
            print(event)
        }


error,创建了一个可观察的序列,只发出error事件并结束,其他事件不会发出

enum hhError:Error{
            case test
        }
        
        Observable<Error>.error(hhError.test).subscribe({ (event) in
            print(event)
        })
输出为


这里再简单的介绍下Driver,Driver是驱动者,司机的意思,我们其实可以去理解为一个驱动者,在功能上它和被观察者Observable是类似的,它是可以和被观察者进行相互转换的,它会驱动着一个观察者,当它的序列中有事件的时候,被它驱动着的观察者就能进行相应的操作。一般我们会将一个Observable被观察者转换成Driver后再进行driver其实也就是开车操作做具体的事情,比如说绑定将某个值绑定到另一个值上面去。

    它不会去直接的发送Error事件
    它的观察订阅是发生在主线程(UI线程)的,比如说我们将一个UI控件的值绑定到另外一个UI控件,用drive是默认在主线程上执行的,不然的话我们调用绑定方法的时候如果再子线程上执行,那么在更新UI的时候是会出现问题的
    本身就有使用shareReplayLatestWhileConnected,保证只被订阅一次,

Observable有个隐式的规定,就是在一个信号处理完成之前,不会发送下一个信号

2017-08-01 00:00:00 FyfFomnVXb2f97Y0dp7 阅读数 272
  • iOS从初级到精通就业 Objective-C

    C语言基础入门,面向初级,毫无编程经验的学生群体,通过学习能够掌握C语言的基本语法,编程的基本概念,培养自己的编程思维!OC语言详解,掌握OC核心内容(面向对象、内存管理,类的扩展,KVC和KVO,闭包和常见的Foundation类库的使用方法等,为后续学习iOS SDK打好基础。

    10109 人正在学习 去看看 栾斌

什么是RxSwift?

  • RxSwift是Swift函数响应式编程的一个开源库,由Github的ReactiveX组织开发、维护

  • 其他语言像C#, Java 和 js 也有,Rx.NET、RxJava、RxJS

  • RxSwift的目的是让让数据/事件流和异步任务能够更方便的序列化处理,能够使用swift进行响应式编程

  • 函数式响应编程?

  • RxSwift做了什么?

观察者模式

  • 什么KVO,通知, 甚至代理都是观察者模式,在设计模式中他可是一个重中之重的设计模式

  • 比如一个宝宝在睡觉,爸爸妈妈,爷爷奶奶总不能在那边一只看着吧?那样子太累了。他们该做啥事就做啥事呗,只要听到宝宝的哭声,他们就给宝宝喂奶就行了。这就是一个典型的观察者模式。

  • 宝宝是被观察者,爸爸妈妈等是观察者也称作订阅者,只要被观察者发出了某些事件比如宝宝哭声、叫声都是一个事件,通知到订阅者,订阅者们就可以做相应的处理工作

  • 在我们iOS开发中, 很多的事件监听都是通过观察者设计模式的

RxSwift做了什么

  • RxSwift把我们程序中每一个操作都看成一个事件

  • 比如一个TextField中的文本改变,一个按钮被点击,或者一个网络请求结束等,每一个事件源就可以看成一个管道,也就是sequence

  • 比如TextField,当我们改变里面的文本的时候,这个TextField就会不断的发出事件,从他的这个sequence中不断的流出,我们只需要监听这个sequence,每流出一个事件就做相应的处理。

  • 同理,Button也是一个sequence,每点击一次就流出一个事件。

理解Observable&Observer

RxSwift的优点

  • Composable 可组合,在设计模式中有一种模式叫做组合模式,你可以方便的用不同的组合实现不同的类

  • Reusable 代码可重用,原因很简单,对应RxSwift,就是一堆Obserable

  • Declarative 响应式的,因为状态不可变,只有数据变化

  • Understandable and concise 简洁,容易理解。

  • Stable 稳定,因为RxSwift写出的代码,单元测试时分方便

  • Less stateful “无”状态性,因为对于响应式编程,你的应用程序就是一堆数据流

  • Without leaks 没有泄漏,因为资源管理非常简单

使用心得

  • 时刻牢记,使用RxSwift,尽量把所有的任务(可以理解为方法)抽象成Obserable(序列)和Obserable创建者,监听者

  • 能用数据绑定的(bindTo和Driver)的就不要手动绑定

  • 一定要熟练RxSwift提供的操作符,要会自定义操作符

iOS开发者交流群:①446310206 ②446310206


ReactiveX/RxSwift 初见

阅读数 2819

没有更多推荐了,返回首页