
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
這個庫太龐大了,以至于我們不可能在一篇文章中描述完全,因此,筆者會做個系列博客和大家討論這個庫。本文的目錄如下:
- 1.由一個Demo探究ASDK類的繼承關(guān)系
-
2.ASDK的消息轉(zhuǎn)發(fā)機制
<h1 id="1">1.由一個Demo探究ASDK類的繼承關(guān)系</h1>
git clone AnsycDisplayKit的代碼后我們進入example目錄,可以看到如下這么多目錄

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

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

下面我們針對上面的兩張圖一一分析。
第一張圖是一個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
等代碼的原因了