AnsycDisplayKit源代碼分析

AsyncDisplayKit

AnsycDisplayKit是關(guān)注的人比較少的庫之一,這是因為這是個很重量級的庫,它基本重寫了UIKit,使用它基本上就等同于放棄原來的UIView和UILayer的方案,還有個原因是很少有界面復(fù)雜到像Facebook那樣對體驗要求那么高。但這些問題都不影響我們探究它內(nèi)部的機制,畢竟這是個Facebook內(nèi)部使用的庫。
AnsycDisplayKit 的下載地址 https://github.com/facebookarchive/AsyncDisplayKit
正如github上所說,AsyncDisplayKit已經(jīng)重新命名為Texture ,究其原因筆者猜測是因為作者(Scott Goodson)的離職。他曾經(jīng)就職于Facebook以及Instagram等公司,并在這里大致介紹了AsyncDisplayKit 的概況 :
Scott Goodson - Behind AsyncDisplayKit
這個庫太龐大了,以至于我們不可能在一篇文章中描述完全,因此,筆者會做個系列博客和大家討論這個庫。本文的目錄如下:

git clone AnsycDisplayKit的代碼后我們進入example目錄,可以看到如下這么多目錄


AnsycDisplayKit的Example目錄

我們選中ASViewController并打開,然后在該目錄下pod update完成后即可運行運行程序,截圖如下:


截圖1

我們選中其中的任何一個(這里選中第一個)可以發(fā)現(xiàn):
截圖2

下面我們針對上面的兩張圖一一分析。
第一張圖是一個tableview列表頁(對應(yīng)的Controller是ViewController),第二章是collectionview列表頁(對應(yīng)的Controller是DetailViewController)。

由代碼

@interface ViewController : ASViewController<ASTableNode *>
@end

可知,ViewController繼承自ASViewController。
當(dāng)然,從代碼

@interface ASViewController<__covariant DisplayNodeType : ASDisplayNode *> : UIViewController <ASVisibilityDepth>
@end

顯而易見,ASViewController是UIViewController的一個子類。

在ViewController的初始化中,我們看到

- (instancetype)init
{
    self = [super initWithNode:[ASTableNode new]];
    if (self == nil) { return self; }
        
    return self;
}

因此,這里在ViewController的創(chuàng)建中,新建了一個ASTableNode。我們繼續(xù)看ASTableNode的代碼

@interface ASTableNode : ASDisplayNode <ASRangeControllerUpdateRangeProtocol>
@property (strong, nonatomic, readonly) ASTableView *view;
@end

顯而易見,Node與View的關(guān)系:


其中,view是作為node的一個屬性存在,后面我們會發(fā)現(xiàn),所有的針對UIKit層的操作,后面都是只針對ASNode的操作。那從view如何獲取node呢,這里先不做說明,后面的文章會有更加細(xì)致的說明。
總所周知,View和Layer是有很大聯(lián)系的,layer層負(fù)責(zé)UI的繪制,View負(fù)責(zé)事件的處理。所以我們不難得出如下的圖:



到這里,AsyncDisplayKit 的中心思想已經(jīng)介紹完了。我們不難得出,在ViewController中如下代碼的大概意思

- (NSInteger)tableNode:(ASTableNode *)tableNode numberOfRowsInSection:(NSInteger)section
{
}
- (ASCellNodeBlock)tableNode:(ASTableNode *)tableNode nodeBlockForRowAtIndexPath:(NSIndexPath *)indexPath
{
}
- (void)tableNode:(ASTableNode *)tableNode didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
}

第一個的意思應(yīng)該是cell的個數(shù)
第二個是每個cell的樣式
第三個是點擊cell的處理
那以前的UITableView的代理方法

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(nonnull NSIndexPath *)indexPath
{
}

<h1 id="2">2.ASDK的消息轉(zhuǎn)發(fā)機制</h1>
眾所周知,iOS的方法調(diào)用會經(jīng)歷三個階段:
消息轉(zhuǎn)發(fā)分為三大階段

  • 第一階段先征詢消息接收者所屬的類,看其是否能動態(tài)添加方法,以處理當(dāng)前這個無法響應(yīng)的 selector,這叫做 動態(tài)方法解析(dynamic method resolution)。如果運行期系統(tǒng)(runtime system) 第一階段執(zhí)行結(jié)束,接收者就無法再以動態(tài)新增方法的手段來響應(yīng)消息,進入第二階段。

  • 第二階段看看有沒有其他對象(備援接收者,replacement receiver)能處理此消息。如果有,運行期系統(tǒng)會把消息轉(zhuǎn)發(fā)給那個對象,轉(zhuǎn)發(fā)過程結(jié)束;如果沒有,則啟動完整的消息轉(zhuǎn)發(fā)機制。

  • 第三階段 完整的消息轉(zhuǎn)發(fā)機制。運行期系統(tǒng)會把與消息有關(guān)的全部細(xì)節(jié)都封裝到 NSInvocation 對象中,再給接收者最后一次機會,令其設(shè)法解決當(dāng)前還未處理的消息。

我們就在第二階段進行插入代理者的操作,代碼如下所示(其中Animal是繼承自NSObject的類)
Cat.h

@interface Cat : Animal
-(void)say;
@end

Cat.m

@implementation Cat
-(void)say
{
    NSLog(@"miao");
}
@end

看以上代碼,我們可以發(fā)現(xiàn),“貓”擁有說話的能力。
Pet.h

@interface Pet : NSObject
@property (nonatomic, strong) NSObject *intercetper;
-(void) play;
@end

Pet.m

@implementation Pet
-(void) play{
    NSLog(@"pay");
}

-(void)forwardInvocation:(NSInvocation *)invocation
{
    [invocation setTarget:self.intercetper];
    [invocation invoke];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
{
    NSMethodSignature *signature = nil;
    if ([self.intercetper methodSignatureForSelector:sel]) {
        signature = [self.intercetper methodSignatureForSelector:sel];
    }else{
        signature = [self methodSignatureForSelector:sel];
    }
    return signature;
}
@end

由Pet的代碼可知Pet只有Play的能力,沒有say的能力。
最后我們在ViewController中調(diào)用如下代碼:

Cat *cat = [[Cat alloc] init];
Pet *pet = [[Pet alloc] init];
pet.intercetper = cat;
[pet performSelector:@selector(say) withObject:nil];

我們會發(fā)現(xiàn)Pet也擁有了說話的能力。
這是因為Pet在調(diào)用say方法的時候發(fā)現(xiàn)找不到,于是它去調(diào)用了methodSignatureForSelector方法。
這就是第二階段的含義。

我們?nèi)匀灰陨弦还?jié)的Demo為例,我們一一分析。

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.title = @"Image Categories";
    self.node.delegate = self;
    self.node.dataSource = self;
}

這其實就是設(shè)置了tableview的delegate以及dataSource。并且還加入了自己的一些方法。這里我們以設(shè)置Delegate為例進行講解。

- (void)setDelegate:(id <ASTableDelegate>)delegate
{
  if ([self pendingState]) {
    _pendingState.delegate = delegate;
  } else {
//這里獲取TableView
    ASTableView *view = self.view;
//這里設(shè)置Delegate
    ASPerformBlockOnMainThread(^{
      view.asyncDelegate = delegate;
    });
  }
}

如圖,view.asyncDelegate = delegate;的詳細(xì)實現(xiàn)如下

- (void)setAsyncDelegate:(id<ASTableDelegate>)asyncDelegate
{
  ASDisplayNodeAssertMainThread();
  NS_VALID_UNTIL_END_OF_SCOPE id oldDelegate = super.delegate;
  //如果是置空,則表明是要釋放delegate
  if (asyncDelegate == nil) {
    _asyncDelegate = nil;
    _proxyDelegate = _isDeallocating ? nil : [[ASTableViewProxy alloc] initWithTarget:nil interceptor:self];
    memset(&_asyncDelegateFlags, 0, sizeof(_asyncDelegateFlags));
  } else {
//這里開始設(shè)置Delegate
    _asyncDelegate = asyncDelegate;
//這里插入了代理
    _proxyDelegate = [[ASTableViewProxy alloc] initWithTarget:_asyncDelegate interceptor:self];
    //這里用于判斷Delegate是否實現(xiàn)了如下方法。
    _asyncDelegateFlags.scrollViewDidScroll = [_asyncDelegate respondsToSelector:@selector(scrollViewDidScroll:)];
    _asyncDelegateFlags.tableViewWillDisplayNodeForRow = [_asyncDelegate respondsToSelector:@selector(tableView:willDisplayNode:forRowAtIndexPath:)];
    _asyncDelegateFlags.tableNodeWillDisplayNodeForRow = [_asyncDelegate respondsToSelector:@selector(tableNode:willDisplayRowWithNode:)];
//這里省略一大段類似的代碼
  }
  super.delegate = (id<UITableViewDelegate>)_proxyDelegate;
}

其中

 _proxyDelegate = [[ASTableViewProxy alloc] initWithTarget:_asyncDelegate interceptor:self];

很關(guān)鍵,我們要注意ASTableViewProxy的實現(xiàn)

- (BOOL)interceptsSelector:(SEL)selector
{
  return (
          // handled by ASTableView node<->cell machinery
          selector == @selector(tableView:cellForRowAtIndexPath:) ||
          selector == @selector(tableView:heightForRowAtIndexPath:) 
//這里省略一大段類似的代碼
            );
}

@end

interceptsSelector指的就是需要攔截的方法,并且攔截的方法要被ASTableview使用。因此,我們能在ASTableview中發(fā)現(xiàn)如下代碼:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath

而他們的實現(xiàn)如下:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//設(shè)置cell
  _ASTableViewCell *cell = [self dequeueReusableCellWithIdentifier:kCellReuseIdentifier forIndexPath:indexPath];
  cell.delegate = self;
//創(chuàng)建自己的Node
  ASCellNode *node = [_dataController.visibleMap elementForItemAtIndexPath:indexPath].node;
  if (node) {
    [_rangeController configureContentView:cell.contentView forCellNode:node];
    cell.node = node;
  }
  return cell;
}

看到這里,想必大家知道為什么在ViewController中能見到

- (ASCellNodeBlock)tableNode:(ASTableNode *)tableNode nodeBlockForRowAtIndexPath:(NSIndexPath *)indexPath

等代碼的原因了

相關(guān)代碼

ProxyDemo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容