AnsycDisplayKit是關注的人比較少的庫之一刽酱,這是因為這是個很重量級的庫朗恳,它基本重寫了UIKit罢防,使用它基本上就等同于放棄原來的UIView和UILayer的方案暑竟,還有個原因是很少有界面復雜到像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類的繼承關系
-
2.ASDK的消息轉(zhuǎn)發(fā)機制
<h1 id="1">1.由一個Demo探究ASDK類的繼承關系</h1>
git clone AnsycDisplayKit的代碼后我們進入example目錄缰泡,可以看到如下這么多目錄
我們選中ASViewController并打開,然后在該目錄下pod update完成后即可運行運行程序代嗤,截圖如下:
我們選中其中的任何一個(這里選中第一個)可以發(fā)現(xiàn):
下面我們針對上面的兩張圖一一分析棘钞。
第一張圖是一個tableview列表頁(對應的Controller是ViewController),第二章是collectionview列表頁(對應的Controller是DetailViewController)干毅。
由代碼
@interface ViewController : ASViewController<ASTableNode *>
@end
可知宜猜,ViewController繼承自ASViewController。
當然硝逢,從代碼
@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的關系:
其中丽蝎,view是作為node的一個屬性存在猎拨,后面我們會發(fā)現(xiàn)膀藐,所有的針對UIKit層的操作,后面都是只針對ASNode的操作红省。那從view如何獲取node呢额各,這里先不做說明,后面的文章會有更加細致的說明吧恃。
總所周知虾啦,View和Layer是有很大聯(lián)系的,layer層負責UI的繪制痕寓,View負責事件的處理傲醉。所以我們不難得出如下的圖:
到這里,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
{
}
第一個的意思應該是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)添加方法吐咳,以處理當前這個無法響應的 selector,這叫做 動態(tài)方法解析(dynamic method resolution)元践。如果運行期系統(tǒng)(runtime system) 第一階段執(zhí)行結(jié)束韭脊,接收者就無法再以動態(tài)新增方法的手段來響應消息,進入第二階段单旁。
第二階段看看有沒有其他對象(備援接收者乾蓬,replacement receiver)能處理此消息。如果有慎恒,運行期系統(tǒng)會把消息轉(zhuǎn)發(fā)給那個對象任内,轉(zhuǎn)發(fā)過程結(jié)束;如果沒有融柬,則啟動完整的消息轉(zhuǎn)發(fā)機制死嗦。
第三階段 完整的消息轉(zhuǎn)發(fā)機制。運行期系統(tǒng)會把與消息有關的全部細節(jié)都封裝到 NSInvocation 對象中粒氧,再給接收者最后一次機會越除,令其設法解決當前還未處理的消息。
我們就在第二階段進行插入代理者的操作外盯,代碼如下所示(其中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;
}
這其實就是設置了tableview的delegate以及dataSource。并且還加入了自己的一些方法砰琢。這里我們以設置Delegate為例進行講解蘸嘶。
- (void)setDelegate:(id <ASTableDelegate>)delegate
{
if ([self pendingState]) {
_pendingState.delegate = delegate;
} else {
//這里獲取TableView
ASTableView *view = self.view;
//這里設置Delegate
ASPerformBlockOnMainThread(^{
view.asyncDelegate = delegate;
});
}
}
如圖,view.asyncDelegate = delegate;
的詳細實現(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 {
//這里開始設置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];
很關鍵,我們要注意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
{
//設置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
等代碼的原因了