今天开始看看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
先看类的属性和方法
从上图我们能看出来
1 POPBasicAnimation 继承POPPropertyAnimation ,POPPropertyAnimation继承POPAnimation
2 POPBasicAnimation, POPPropertyAnimation 和POPAnimation都有结构体_state 。类型分别是struct _ POPBasicAnimation State,struct _POPPropertyAnimationState和struct _POPAnimationState
3 struct _POPPropertyAnimationState,struct _POPPropertyAnimationState和struct _POPAnimationState的继承关系和类继承关系一致
4 ** POPBasicAnimation, POPPropertyAnimation 和POPAnimation都有结构体变量 _state
都含- (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
这个类相关的继承关系相对简单,函数也简单,我们看看初始化
+ (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 返回 对象
静态动画的结构如下
这里第二步查找
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
看看都增加了什么方法
看源码实现也很简单
- (void)pop_addAnimation:(POPAnimation *)anim forKey:(NSString *)key
{
[[POPAnimator sharedAnimator] addAnimation:anim forObject:self key:key];
}
POPAnimator
这里关键的类是 POPAnimator
接下来我们看看这个类的结构
类结构很简单,没有继承关系什么的。
我们知道这个类是个单例类
- (id)sharedAnimator
{
static POPAnimator* _animator = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_animator = [[POPAnimator alloc] init];
});
return _animator;
}
看看都实例化什么了
- (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 动画的
这里还需要注意的是POPAnimatorItem ,因为这里还将obj 和key POPAnimation 封装在了 POPAnimatorItem 中,保存在_list 和_pending 中
我们看看增加这个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的组织结构图