iOS

FaceBook Pop 源码分析 (1)

2018-09-13  本文已影响95人  充满活力的早晨

今天开始看看FaceBook Pop 源码。

基本使用

    CALayer *layer = self.label.layer;
    [layer pop_removeAllAnimations];
    
    POPBasicAnimation *anim = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerPositionY];
    anim.fromValue = @(100);
    anim.toValue = @(300);
    anim.completionBlock = ^(POPAnimation *anim, BOOL finished) {
        NSLog(@"Animation has completed.");
        self.tapGesture.enabled = YES;
    };
    [layer pop_addAnimation:anim forKey:@"size"];

这就是实现label 沿着y轴的移动
这里涉及类POPSpringAnimation,我们就从这个类开始。

POPBasicAnimation

先看类的属性和方法


POPBasicAnimation.png

从上图我们能看出来

1 POPBasicAnimation 继承POPPropertyAnimationPOPPropertyAnimation继承POPAnimation
2 POPBasicAnimationPOPPropertyAnimationPOPAnimation都有结构体_state 。类型分别是struct _ POPBasicAnimation Statestruct _POPPropertyAnimationStatestruct _POPAnimationState
3 struct _POPPropertyAnimationStatestruct _POPPropertyAnimationStatestruct _POPAnimationState的继承关系和类继承关系一致
4 ** POPBasicAnimation, POPPropertyAnimationPOPAnimation都有结构体变量 _state

POPBasicAnimation.png
都含- (void)_initState函数,这个函数就是用来初始化结构体_state的。

看初始化

+ (instancetype)animation
{
  return [[self alloc] init];
}

+ (instancetype)animationWithPropertyNamed:(NSString *)aName
{
  POPBasicAnimation *anim = [self animation];
  anim.property = [POPAnimatableProperty propertyWithName:aName];
  return anim;
}

这里就是让类的_state 变量进行初始化。初始化的具体过程后面再说。

这里多了个类 POPAnimatableProperty 接下来我们看看这个类的结构

POPAnimatableProperty

POPAnimatableProperty.png

这个类相关的继承关系相对简单,函数也简单,我们看看初始化

+ (id)propertyWithName:(NSString *)aName
{
  return [self propertyWithName:aName initializer:NULL];
}

+ (id)propertyWithName:(NSString *)aName initializer:(void (^)(POPMutableAnimatableProperty *prop))aBlock
{
  POPAnimatableProperty *prop = nil;

  static NSMutableDictionary *_propertyDict = nil;
  if (nil == _propertyDict) {
    _propertyDict = [[NSMutableDictionary alloc] initWithCapacity:10];
  }

  prop = _propertyDict[aName];
  if (nil != prop) {
    return prop;
  }

  NSUInteger staticIdx = staticIndexWithName(aName);

  if (NSNotFound != staticIdx) {
    POPStaticAnimatableProperty *staticProp = [[POPStaticAnimatableProperty alloc] init];
    staticProp->_state = &_staticStates[staticIdx];
    _propertyDict[aName] = staticProp;
    prop = staticProp;
  } else if (NULL != aBlock) {
    POPMutableAnimatableProperty *mutableProp = [[POPMutableAnimatableProperty alloc] init];
    mutableProp.name = aName;
    mutableProp.threshold = 1.0;
    aBlock(mutableProp);
    prop = [mutableProp copy];
  }

  return prop;
}

1 判断全局字典是否为nil,是nil就生成字典.从字典中查找是否有name对应的动画,有就返回,没有执行2.
2 根据name 查找name所对应的静态属性。找到执行3,没有执行4
3 生成POPStaticAnimatableProperty对象,记录住name动画相关属性,并且将静态动画保存在全局字典中
4 要是没有静态动画,那么我们就生成一个POPMutableAnimatableProperty 对象,并且返回
5 返回 对象

静态动画的结构如下


AnimationProperty

这里第二步查找

static NSUInteger staticIndexWithName(NSString *aName)
{
  NSUInteger idx = 0;
  while (idx < POP_ARRAY_COUNT(_staticStates)) {
    if ([_staticStates[idx].name isEqualToString:aName])
      return idx;
    idx++;
  }

  return NSNotFound;
}

这里有个 _staticStates 数组 。定义如下

typedef struct
{
  NSString *name;
  pop_animatable_read_block readBlock;
  pop_animatable_write_block writeBlock;
  CGFloat threshold;
} _POPStaticAnimatablePropertyState;
typedef _POPStaticAnimatablePropertyState POPStaticAnimatablePropertyState;

static POPStaticAnimatablePropertyState _staticStates[]

我们发现 _staticStates 是个数组,里面装有的POPStaticAnimatablePropertyState类型的结构体。
POPStaticAnimatablePropertyState 正好对应的是POPAnimatableProperty 的四个属性。

这里我们拿出一个POPStaticAnimatablePropertyState 属性看看

 {kPOPLayerBackgroundColor,
    ^(CALayer *obj, CGFloat values[]) {
      POPCGColorGetRGBAComponents(obj.backgroundColor, values);
    },
    ^(CALayer *obj, const CGFloat values[]) {
      CGColorRef color = POPCGColorRGBACreate(values);
      [obj setBackgroundColor:color];
      CGColorRelease(color);
    },
    kPOPThresholdColor
  }

这里我们看看readBlock 和writeBlock

void POPCGColorGetRGBAComponents(CGColorRef color, CGFloat components[])
{
  if (!color) {
#if TARGET_OS_IPHONE
    color = [UIColor clearColor].CGColor;
#else
    color = [NSColor clearColor].CGColor;
#endif
  }
  
  const CGFloat *colors = CGColorGetComponents(color);
  size_t count = CGColorGetNumberOfComponents(color);
  
  if (4 == count) {
    // RGB colorspace
    components[0] = colors[0];
    components[1] = colors[1];
    components[2] = colors[2];
    components[3] = colors[3];
  } else if (2 == count) {
    // Grey colorspace
    components[0] = components[1] = components[2] = colors[0];
    components[3] = colors[1];
  } else {
    // Use CI to convert
    CIColor *ciColor = [CIColor colorWithCGColor:color];
    components[0] = ciColor.red;
    components[1] = ciColor.green;
    components[2] = ciColor.blue;
    components[3] = ciColor.alpha;
  }
}

CGColorRef POPCGColorRGBACreate(const CGFloat components[])
{
#if TARGET_OS_IPHONE
  CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
  CGColorRef color = CGColorCreate(space, components);
  CGColorSpaceRelease(space);
  return color;
#else
  return CGColorCreateGenericRGB(components[0], components[1], components[2], components[3]);
#endif
}

其实就是对layer 颜色读写。很简单不做介绍了

NSObject (POP)

我们看看动画如何加上去的

[layer pop_addAnimation:anim forKey:@"size"];

这个是给NSObject 增加了个category
看看都增加了什么方法


NSObject (POP).png

看源码实现也很简单

- (void)pop_addAnimation:(POPAnimation *)anim forKey:(NSString *)key
{
  [[POPAnimator sharedAnimator] addAnimation:anim forObject:self key:key];
}

POPAnimator

这里关键的类是 POPAnimator
接下来我们看看这个类的结构

POPAnimator.png

类结构很简单,没有继承关系什么的。
我们知道这个类是个单例类

- (id)init
{
  self = [super init];
  if (nil == self) return nil;

#if TARGET_OS_IPHONE
  _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(render)];
  _displayLink.paused = YES;
  [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
#else
  CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink);
  CVDisplayLinkSetOutputCallback(_displayLink, displayLinkCallback, (__bridge void *)self);
#endif

  _dict = POPDictionaryCreateMutableWeakPointerToStrongObject(5);
  _lock = OS_SPINLOCK_INIT;

  return self;
}

我们知道这里初始化了变量_displayLink。这里CADisplayLink相当于 vsync信号。什么是vsync信号。可以看我前面的文章垂直同步+双缓存.这里暂时_displayLink
这里并且实例化了_dict 和_lock锁


CFMutableDictionaryRef POPDictionaryCreateMutableWeakPointerToStrongObject(NSUInteger capacity)
{
  CFDictionaryKeyCallBacks kcb = kCFTypeDictionaryKeyCallBacks;

  // weak, pointer keys
  kcb.retain = NULL;
  kcb.release = NULL;
  kcb.equal = pointerEqual;
  kcb.hash = pointerHash;

  // strong, object values
  CFDictionaryValueCallBacks vcb = kCFTypeDictionaryValueCallBacks;

  return CFDictionaryCreateMutable(NULL, capacity, &kcb, &vcb);
}

这里需要注意,这个字典的值是支持hash 和equal的。


static Boolean pointerEqual(const void *ptr1, const void *ptr2) {
  return ptr1 == ptr2;
}

static CFHashCode pointerHash(const void *ptr) {
  return (CFHashCode)(ptr);
}

接下来我们看看动画如何执行的。

- (void)addAnimation:(POPAnimation *)anim forObject:(id)obj key:(NSString *)key
{
  if (!anim || !obj) {
    return;
  }

  // support arbitrarily many nil keys
  if (!key) {
    key = [[NSUUID UUID] UUIDString];
  }

  // lock
  OSSpinLockLock(&_lock);

  // get key, animation dict associated with object
  NSMutableDictionary *keyAnimationDict = (__bridge id)CFDictionaryGetValue(_dict, (__bridge void *)obj);

  // update associated animation state
  if (nil == keyAnimationDict) {
    keyAnimationDict = [NSMutableDictionary dictionary];
    CFDictionarySetValue(_dict, (__bridge void *)obj, (__bridge void *)keyAnimationDict);
  } else {
    // if the animation instance already exists, avoid cancelling only to restart
    POPAnimation *existingAnim = keyAnimationDict[key];
    if (existingAnim) {
      // unlock
      OSSpinLockUnlock(&_lock);

      if (existingAnim == anim) {
        return;
      }
      [self removeAnimationForObject:obj key:key cleanupDict:NO];
        
      // lock
      OSSpinLockLock(&_lock);
    }
  }
  keyAnimationDict[key] = anim;

  // create entry after potential removal
  POPAnimatorItemRef item(new POPAnimatorItem(obj, key, anim));

  // add to list and pending list
  _list.push_back(item);
  _pendingList.push_back(item);

  // support animation re-use, reset all animation state
  POPAnimationGetState(anim)->reset(true);

  // update display link
  updateDisplayLink(self);

  // unlock
  OSSpinLockUnlock(&_lock);

  // schedule runloop processing of pending animations
  [self _scheduleProcessPendingList];
}

分步分析

1 检查参数是否合法,不合法返回
2 检查是否有key ,没有key自己生成uuid作为key
3 加锁
4从_dict 字典中获取obj 对应的值keyAnimationDict。keyAnimationDict发现是个字典
5 检查obj 对应的字典是否是nil 。是nil,执行6 ,不是nil,执行7
6 生成字典,_dict 保存该字典,key 是obj
7 从keyAnimationDict 字典中查找key 对应的对象POPAnimation,如果存在对象,执行8,不存在执行9
8解锁,判断key对应的POPAnimation 和传入的参数anim是否相等,相等返回,不相等,移除对象(这里移除对象后期再分析,看源码设计好多地方)
9 让keyAnimationDict 保存anim ,key 作为在字典的key
10 创建POPAnimatorItemRef 对象(C++ 方式创建)
11 _list 添加POPAnimatorItemRef 对象(这里需要说明下_list 是c++list,可以不用分配空间,直接使用就可以了)
12_pendingList 也添加POPAnimatorItemRef 对象
13 开启动画标志位
14 检查是否需要开启刷新 这里就会执行-render 函数了
15 增加runloop 检查runloop的kCFRunLoopBeforeWaiting 和kCFRunLoopExit 状态

这里我们应该看看如何存储POPAnimation 动画的


POPAnimator数据结构

这里还需要注意的是POPAnimatorItem ,因为这里还将obj 和key POPAnimation 封装在了 POPAnimatorItem 中,保存在_list 和_pending 中

POPAnimatorItem

我们看看增加这个runloop observer干嘛了

- (void)_scheduleProcessPendingList
{
  // see WebKit for magic numbers, eg http://trac.webkit.org/changeset/166540
  static const CFIndex CATransactionCommitRunLoopOrder = 2000000;
  static const CFIndex POPAnimationApplyRunLoopOrder = CATransactionCommitRunLoopOrder - 1;

  // lock
  OSSpinLockLock(&_lock);

  if (!_pendingListObserver) {
    __weak POPAnimator *weakSelf = self;

    _pendingListObserver = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopBeforeWaiting | kCFRunLoopExit, false, POPAnimationApplyRunLoopOrder, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
      [weakSelf _processPendingList];
    });

    if (_pendingListObserver) {
      CFRunLoopAddObserver(CFRunLoopGetMain(), _pendingListObserver,  kCFRunLoopCommonModes);
    }
  }

  // unlock
  OSSpinLockUnlock(&_lock);
}

这个方法很简单,就是增加一个runloop observer,不过这个observer只观察一次就退出了。会调用_processPendingList方法

- (void)_processPendingList
{
  // rendering pending animations
  CFTimeInterval time = [self _currentRenderTime];
  [self _renderTime:(0 != _beginTime) ? _beginTime : time items:_pendingList];

  // lock
  OSSpinLockLock(&_lock);

  // clear list and observer
  _pendingList.clear();
  [self _clearPendingListObserver];

  // unlock
  OSSpinLockUnlock(&_lock);
}

1 获取下当前时间
2 执行动画
3 清空_pendingLIst
4 移除通知

- (void)_renderTime:(CFTimeInterval)time items:(std::list<POPAnimatorItemRef>)items
{
  // begin transaction with actions disabled
  [CATransaction begin];
  [CATransaction setDisableActions:YES];

  // notify delegate
  __strong __typeof__(_delegate) delegate = _delegate;
  [delegate animatorWillAnimate:self];

  // lock
  OSSpinLockLock(&_lock);

  // count active animations
  const NSUInteger count = items.size();
  if (0 == count) {
    // unlock
    OSSpinLockUnlock(&_lock);
  } else {
    // copy list into vectory
    std::vector<POPAnimatorItemRef> vector{ std::begin(items), std::end(items) };

    // unlock
    OSSpinLockUnlock(&_lock);

    for (auto item : vector) {
      [self _renderTime:time item:item];
    }
  }

  // notify observers
  for (id observer in self.observers) {
    [observer animatorDidAnimate:(id)self];
  }

  // lock
  OSSpinLockLock(&_lock);

  // update display link
  updateDisplayLink(self);

  // unlock
  OSSpinLockUnlock(&_lock);

  // notify delegate and commit
  [delegate animatorDidAnimate:self];
  [CATransaction commit];
}

1 关闭layer的隐式动画
2 通知delegate 动画将开启
3 获取_pendingList 中的数据,循环调用- (void)_renderTime:(CFTimeInterval)time item:(POPAnimatorItemRef)item,传入_pendingList 中的数据
4 要是有观察者,那么通知观察者
5 检查是否开启刷新屏幕
提交动画

下面就是核心代码了

- (void)_renderTime:(CFTimeInterval)time item:(POPAnimatorItemRef)item
{
  id obj = item->object;
  POPAnimation *anim = item->animation;
  POPAnimationState *state = POPAnimationGetState(anim);

  if (nil == obj) {
    // object exists not; stop animating
    NSAssert(item->unretainedObject, @"object should exist");
    stopAndCleanup(self, item, true, false);
  } else {

    // start if needed
    state->startIfNeeded(obj, time, _slowMotionAccumulator);

    // only run active, not paused animations
    if (state->active && !state->paused) {
      // object exists; animate
      applyAnimationTime(obj, state, time);

      FBLogAnimDebug(@"time:%f running:%@", time, item->animation);
      if (state->isDone()) {
        // set end value
        applyAnimationToValue(obj, state);

        state->repeatCount--;
        if (state->repeatForever || state->repeatCount > 0) {
          if ([anim isKindOfClass:[POPPropertyAnimation class]]) {
            POPPropertyAnimation *propAnim = (POPPropertyAnimation *)anim;
            id oldFromValue = propAnim.fromValue;
            propAnim.fromValue = propAnim.toValue;

            if (state->autoreverses) {
              if (state->tracing) {
                [state->tracer autoreversed];
              }

              if (state->type == kPOPAnimationDecay) {
                POPDecayAnimation *decayAnimation = (POPDecayAnimation *)propAnim;
                decayAnimation.velocity = [decayAnimation reversedVelocity];
              } else {
                propAnim.toValue = oldFromValue;
              }
            } else {
              if (state->type == kPOPAnimationDecay) {
                POPDecayAnimation *decayAnimation = (POPDecayAnimation *)propAnim;
                id originalVelocity = decayAnimation.originalVelocity;
                decayAnimation.velocity = originalVelocity;
              } else {
                propAnim.fromValue = oldFromValue;
              }
            }
          }

          state->stop(NO, NO);
          state->reset(true);

          state->startIfNeeded(obj, time, _slowMotionAccumulator);
        } else {
          stopAndCleanup(self, item, state->removedOnCompletion, YES);
        }
      }
    }
  }
}

这里就是动画执行的逻辑了。

1 获取需要执行动画的obj
2 获取obj的需要执行的动画
3 获取动画的_state 。这里其实应该要仔细看看则个结构体了。
4.要是obj 是nil。执行5 ,要么执行6
5 没有对象,就将对象的动画删除掉。(先清除_dict的中的obj,并且停止动画)
6 执行动画
7 检查动画结束,执行8
8 将obj设置到相应状态
9重复次数减少
10 如果需要重复,那么就执行11,不需要执行14
11 检查动画属于POPPropertyAnimation动画,执行12,否则执行1
12.给POPPropertyAnimation 对象执行相应的回复操作
13 将state 状态回复
14 清除动画

基本逻辑完毕,总结下看到目前为止的pop的组织结构图

image.png
上一篇 下一篇

猜你喜欢

热点阅读