FaceBook POP原始碼解析二

醬了裡個醬發表於2019-03-12

FaceBook POP原始碼解析一

FaceBook POP原始碼解析三

FaceBook POP原始碼解析四

一、前言

本文主要專注於原始碼分析,具體pop的相關使用,可參考下文連結

二、POP原始碼架構

pop原始碼主要分為Animations、Engine、Uility和WebCore四部分,每一部分承擔不同的職責。

pop

  • Animations:包含了pop中所有的動畫型別
  • Engine:負責pop中的動畫處理
  • Uitity:常用到的一些工具類
  • WebCore:包括矩陣處理以及貝塞爾曲線等

三、POP動畫型別

pop中可使用的動畫包括POPBasicAnimation、POPSpringAnimation、POPDecayAnimation和POPCustomAnimation四種,具體類之間的關係如下:

animation

1.POPAnimation

該類類似於CAAnimation,是動畫的抽象基類,提供一些共用的屬性和方法。

@interface POPAnimation : NSObject
//屬性對應的名稱,用於區分動畫物件
@property (copy, nonatomic) NSString *name;
//開啟動畫的時間
@property (assign, nonatomic) CFTimeInterval beginTime;

@property (weak, nonatomic) id delegate;
//用於記錄動畫過程
@property (readonly, nonatomic) POPAnimationTracer *tracer;
//動畫開始時被呼叫
@property (copy, nonatomic) void (^animationDidStartBlock)(POPAnimation *anim);
//當動畫屬性值達到或超過設定的值時被呼叫
@property (copy, nonatomic) void (^animationDidReachToValueBlock)(POPAnimation *anim);
//動畫結束時被呼叫
@property (copy, nonatomic) void (^completionBlock)(POPAnimation *anim, BOOL finished);
//動畫每執行完一幀後被呼叫
@property (copy, nonatomic) void (^animationDidApplyBlock)(POPAnimation *anim);
//動畫執行完後,是否移除動畫,預設為NO,若設定為YES,動畫執行完後,會恢復原狀
@property (assign, nonatomic) BOOL removedOnCompletion;
//獲取動畫執行的狀態
@property (assign, nonatomic, getter = isPaused) BOOL paused;
//是否執行來回動畫
@property (assign, nonatomic) BOOL autoreverses;
//動畫重複次數
@property (assign, nonatomic) NSInteger repeatCount;
//是否一直重複動畫
@property (assign, nonatomic) BOOL repeatForever;
@end

//獲取動畫的執行狀態
@protocol POPAnimationDelegate <NSObject>
@optional
- (void)pop_animationDidStart:(POPAnimation *)anim;
- (void)pop_animationDidReachToValue:(POPAnimation *)anim;
- (void)pop_animationDidStop:(POPAnimation *)anim finished:(BOOL)finished;
- (void)pop_animationDidApply:(POPAnimation *)anim;
@end
複製程式碼

a. POPAnimation為抽象類,不能被例項化

- (id)init
{
  [NSException raise:NSStringFromClass([self class]) format:@"Attempting to instantiate an abstract class. Use a concrete subclass instead."];
  return nil;
}
複製程式碼

b. POPAnimation的初始化:在初始化方法中,建立了一個POPAnimationState物件,並將自己作為引數傳遞過去。

- (void)_initState
{
  _state = new POPAnimationState(self);
}
複製程式碼

POPAnimationState為一個struct宣告,裡面包含與POPAnimation對應的屬性

struct _POPAnimationState
{
  id __unsafe_unretained self;
  POPAnimationType type;
  NSString *name;
  NSUInteger ID;
  CFTimeInterval beginTime;
  CFTimeInterval startTime;
  CFTimeInterval lastTime;
  id __weak delegate;
  POPAnimationDidStartBlock animationDidStartBlock;
  POPAnimationDidReachToValueBlock animationDidReachToValueBlock;
  POPAnimationCompletionBlock completionBlock;
  POPAnimationDidApplyBlock animationDidApplyBlock;
  NSMutableDictionary *dict;
  POPAnimationTracer *tracer;
  CGFloat progress;
  NSInteger repeatCount;
  ...
  }
複製程式碼

c. POPAnimation的setter和getter方法

- (BOOL)isPaused
{
  return _state->paused;
}
- (void)setPaused:(BOOL)paused
{
  _state->setPaused(paused ? true : false);
}
- (NSInteger)repeatCount
{
  if (_state->autoreverses) {
    return _state->repeatCount / 2;
  } else {
    return _state->repeatCount;
  }
}
- (void)setRepeatCount:(NSInteger)repeatCount
{
  if (repeatCount > 0) {
    if (repeatCount > NSIntegerMax / 2) {
      repeatCount = NSIntegerMax / 2;
    }

    if (_state->autoreverses) {
      _state->repeatCount = (repeatCount * 2);
    } else {
      _state->repeatCount = repeatCount;
    }
  }
}
複製程式碼

我們可以看到setter其實就是將對應的屬性賦值給_state,getter也是從_state中獲取再返回。也就是說其實POPAnimation可以看作是一箇中介者,是OC暴露的介面,實際上操作這些屬性還是通過POPAnimationState結構體。

d.使用巨集來簡化OC物件與結構體之間屬性的轉換

#define FB_PROPERTY_GET(stype, property, ctype) \
- (ctype)property { \
  return ((stype *)_state)->property; \
}

#define FB_PROPERTY_SET(stype, property, mutator, ctype, ...) \
- (void)mutator (ctype)value { \
  if (value == ((stype *)_state)->property) \
    return; \
  ((stype *)_state)->property = value; \
  __VA_ARGS__ \
}
#define DEFINE_RW_PROPERTY_OBJ(stype, flag, mutator, ctype, ...) \
  FB_PROPERTY_GET (stype, flag, ctype) \
  FB_PROPERTY_SET (stype, flag, mutator, ctype, __VA_ARGS__)
  
DEFINE_RW_PROPERTY_OBJ_COPY(POPAnimationState, animationDidStartBlock, setAnimationDidStartBlock:, POPAnimationDidStartBlock);
DEFINE_RW_PROPERTY_OBJ_COPY(POPAnimationState, animationDidReachToValueBlock, setAnimationDidReachToValueBlock:, POPAnimationDidReachToValueBlock);
複製程式碼

對於需要特殊處理的setter和getter方法,則需要重寫,而常規的setter和getter方法,則通過巨集來簡化這一過程。

e.重寫valueForUndefinedKey方法

- (id)valueForUndefinedKey:(NSString *)key
{
  return _state->dict[key];
}

- (void)setValue:(id)value forUndefinedKey:(NSString *)key
{
  if (!value) {
    [_state->dict removeObjectForKey:key];
  } else {
    if (!_state->dict)
      _state->dict = [[NSMutableDictionary alloc] init];
    _state->dict[key] = value;
  }
}
複製程式碼

在KVC呼叫valueForKey方法時,若key不存在,則會丟擲NSUndefinedkeyException異常,為了避免該異常,重寫了valueForUndefinedKey方法。

2.POPAnimation分類

該分類主要提供了外部對動畫增刪查的介面,但實際上真正執行這些操作的是POPAnimator物件。

@interface NSObject (POP)
- (void)pop_addAnimation:(POPAnimation *)anim forKey:(NSString *)key;

- (void)pop_removeAllAnimations;

- (void)pop_removeAnimationForKey:(NSString *)key;

- (NSArray *)pop_animationKeys;

- (id)pop_animationForKey:(NSString *)key;
@end

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

- (void)pop_removeAllAnimations
{
  [[POPAnimator sharedAnimator] removeAllAnimationsForObject:self];
}

- (void)pop_removeAnimationForKey:(NSString *)key
{
  [[POPAnimator sharedAnimator] removeAnimationForObject:self key:key];
}

- (NSArray *)pop_animationKeys
{
  return [[POPAnimator sharedAnimator] animationKeysForObject:self];
}

- (id)pop_animationForKey:(NSString *)key
{
  return [[POPAnimator sharedAnimator] animationForObject:self key:key];
}
@end
複製程式碼

四、總結

本小節主要介紹了pop的大致架構以及動畫的基類POPAnimation的相關內容。該小節比較值得關注的一點就是pop將OC物件和struct物件之間的相互轉化。

相關文章