编程

当前位置:澳门新莆京娱乐官网 > 编程 > 阅读源码的乐趣

阅读源码的乐趣

来源:http://www.drgigabytes.com 作者:澳门新莆京娱乐官网 时间:2020-05-06 05:17

翻阅源码越发是上好的源码是一件很有意趣的作业,能够推广视线,提升程度,锻练思维,就好像直接地在跟作者联系近似。Quora 上有一个标题是:TJ-Holowaychunk是哪些学习编制程序的,他的答问是

I don't read books, never went to school, I just read other people's code and always wonder how things work

借使有丰富的好奇心,况兼总想知道「How Things Work」,那么阅读源码正是个不利的路子。

澳门新莆京娱乐官网,源码的复杂度不相同,须要投入的时光、使用的秘诀也不及,以一个西路复杂度的类型为例,轻便分享下自家阅读源码的有的经历。

WWDC 2016,有三个 Session 是讲「Advanced User Interfaces with Collection Views」,之所以接纳那几个,是因为它是大家还算理解的对象(Collection View),但苹果用了有的「特殊」的结构来成功代码复用,并且裁减 VC 的容量,並且使用了一些 iTunes Connect 的源码,并非简约的现身说法代码。所以决定一窥毕竟。

为了有一个大致的感触,先看二次录像,无需掌握各样主旨,先记下一些珍视新闻,方便届期翻源码。

  • 这套构造能够拍卖百端待举的 DataSource
  • 能够同期适配 BlackBerry / surface
  • 有二个统一的 loading indicator
  • 能够安装有些 Header 是还是不是置顶
  • 可以有一个大局的 Header
  • 经过聚合 DataSource 的办法来达到代码复用,何况独有一个 VC
  • 能够设置聚合情势为 Segmented / Composed
  • layout音讯方可配备,且能够覆盖
  • 利用了点儿状态机
  • 子 DataSource 在数码载入实现后会有一个 block,所需的 DataSource 都载入达成时,这几个 block 会被归拢进行
  • Section Metrics 能够安装 Section 的具体表现
  • layout 的新闻会在中间被保存,制止重新总结 (Snapshot Metrics卡塔尔国
  • Optional Layout Methods 会有意外的好功效

发出了部分疑团,比如

  • 七个子 DataSource 被组合成叁个 Composed DataSource 时,怎么着通过 IndexPath 找到相应的 DataSource?
  • 找到之后如何管理?
  • 是不是置顶是怎样促成的?
  • 哪些通过简单状态机来管理 Loading 状态?
  • 如果有开关,那么按键的点击事件如哪个地点理?
  • Collection View 没有 headerView,那又是怎么贯彻的?
  • 数量是怎么载入的?

大约有了些概念和疑问之后,就能够展开源码痛快看了,先来拜会目录构造(能够在那地在线浏览卡塔尔国

|-Framework|-Categories|-DataSources|-Layouts|-ViewControllers|-Views|-Application

因此看来关键的音讯都在 Framework 里了,那怎么切入呢?反其道而行之,先来拜访这个 Framework 是怎么用的,最直白的就从 ViewController 出手。那就先来探视 AAPLCatListViewController 那么些类吧,如若没猜错的话,应该是显得猫咪列表。

果然超小,居然只有 140 行,倘诺不分开的话,1400 行也是足以轻便实现的。见到定义了二个AAPLSegmentedDataSource,脑公里差不离能够杜撰出是一个得以切换 Tag 的页面,接着又看见了五个DataSource,那那七个页面包车型客车数据源应该正是它们了。

@interfaceAPPLCatListViewController()@property(nonatomic,strong)AAPLSegmentedDataSource*segmentedDataSource;@property(nonatomic,strong)AAPLCatListDataSource*catsDataSource;@property(nonatomic,strong)AAPLCatListDataSource*favoriteCatsDataSource;@property(nonatomic,strong)NSIndexPath*selectedIndexPath;@property(nonatomic,strong)idselectedDataSourceObserver;@end

接下来又来看这般一行

-dealloc{[self.segmentedDataSourceaapl_removeObserver:self.selectedDataSourceObserver];}

看起来是苹果本身完结了一个 KVO Wrapper,果然他们协和也无法忍受原生的KVO,哈哈。接着到了 ViewDidLoad,新建了四个 DataSource,那新建的时候都干了些什么?

-(AAPLCatListDataSource*)newAllCatsDataSource{AAPLCatListDataSource*dataSource=[[AAPLCatListDataSourcealloc]init];dataSource.showingFavorites=NO;dataSource.title=NSLocalizedString(@"All",@"Title for available cats list");dataSource.noContentMessage=NSLocalizedString(@"All the big ...",@"The message to show when no cats are available");dataSource.noContentTitle=NSLocalizedString(@"No Cats",@"The title to show when no cats are available");dataSource.errorMessage=NSLocalizedString(@"A problem with the network ....",@"Message to show when unable to load cats");dataSource.errorTitle=NSLocalizedString(@"Unable To Load Cats",@"Title of message to show when unable to load cats");returndataSource;}

故此只是初阶化,然后设置有个别新闻,Nothing Special。然后看见了 AAPLLayoutSectionMetrics ,看起来是设置 Layout 的一部分展现消息,如 height / backgroundColor 之类的。

末尾创立了三个 KVO 来监测 selectedDataSource 的变迁,分界面上做相应的调动。

接下去看看 AAPLCatListDataSource 的兑现,一进去开掘

@interfaceAAPLCatListDataSource:AAPLBasicDataSource/// Is this list showing the favorites or all available cats?@property(nonatomic)BOOLshowingFavorites;@end

因此看来 AAPLBasicDataSource 一定做了广大事,步入到 AAPLBasicDataSource.m 文件,见到那几个办法

-setShowingFavorites:showingFavorites{if(showingFavorites==_showingFavorites)return;_showingFavorites=showingFavorites;[selfresetContent];[selfsetNeedsLoadContent];if(showingFavorites)[[NSNotificationCenterdefaultCenter]addObserver:selfselector:@selector(observeFavoriteToggledNotification:)name:AAPLCatFavoriteToggledNotificationNameobject:nil];}

稳重到有八个setNeedsLoadContent措施,看起来数据的载入应该是通过那几个法子来触发的,进去看看

-setNeedsLoadContent{[NSObjectcancelPreviousPerformRequestsWithTarget:selfselector:@selector(loadContent)object:nil];[selfperformSelector:@selector(loadContent)withObject:nilafterDelay:0];}

首先个章程没怎么接触过,查一下文书档案先,原本是足以撤除在此之前经过performSelector:withObject:afterDelay:接触的法子,为了深化影像,顺便 Google一下那么些办法,原本performSelector:withObject:afterDelay在格局被施行前,会持有 Object,方法实践后在清除对 Object 的有所,即使不小心多次调用那几个法子就有十分的大也许招致内部存款和储蓄器走漏,所以在调用此措施前先 cancel 一下是个好习贯。

再来看看那几个loadContent都做了怎么着

-loadContent{// To be implemented by subclasses…}

一言以蔽之须求在子类实现那么些点子,那就到 AAPLCatListDataSource 里看看那些主意都做了怎么

-loadContent{[selfloadContentWithBlock:^(AAPLLoading*loading){void(NSArray*cats,NSError*error)=^(NSArray*cats,NSError*error){// Check to make certain a more recent call to load content hasn't superceded this one…if(!loading.current){[loadingignore];return;}if{[loadingdoneWithError:error];return;}if(cats.count)[loadingupdateWithContent:^(AAPLCatListDataSource*me){me.items=cats;}];else[loadingupdateWithNoContent:^(AAPLCatListDataSource*me){me.items=@[];}];};if(self.showingFavorites)[[AAPLDataAccessManagermanager]fetchFavoriteCatListWithCompletionHandler:handler];else[[AAPLDataAccessManagermanager]fetchCatListWithCompletionHandler:handler];}];}

使用了loadContentWithBlock:方式,进去看看,这么些方式做了哪些

-loadContentWithBlock:(AAPLLoadingBlock)block{[selfbeginLoading];__weaktypeof(&*self)weakself=self;AAPLLoading*loading=[AAPLLoadingloadingWithCompletionHandler:^(NSString*newState,NSError*error,AAPLLoadingUpdateBlockupdate){if(!newState)return;[selfendLoadingWithState:newStateerror:errorupdate:^{AAPLDataSource*me=weakself;if(update&&me)update;}];}];// Tell previous loading instance it's no longer current and remember this loading instanceself.loadingInstance.current=NO;self.loadingInstance=loading;// Call the provided block to actually do the loadblock;}

总结说来正是生成了多少个 loading,然后把 loading 传给 block,那loadingWithCompletionHandler:以此艺术又做了如何

+(instancetype)loadingWithCompletionHandler:(NSString*state,NSError*error,AAPLLoadingUpdateBlockupdate))handler{NSParameterAssert(handler!=nil);AAPLLoading*loading=[[selfalloc]init];loading.block=handler;loading.current=YES;returnloading;}

因此就是生成三个 loading 实例,然后把 handler 存到 block 属性里。既然存了,那前些天有个别时候自然会用到,从名字上来看,应该是 loading 完结时会被调用,搜索 block 关键字,开掘独有在底下这一个格局中 block 才会被调用

-_doneWithNewState:(NSString*)newStateerror:errorupdate:(AAPLLoadingUpdateBlock)update{#if DEBUGif(!OSAtomicCompareAndSwap32(0,1,&_complete))NSAssert(false,@"completion method called more than once");#endifvoid(NSString*state,NSError*error,AAPLLoadingUpdateBlockupdate)=_block;dispatch_async(dispatch_get_main_queue(),^{block(newState,error,update);});_block=nil;}

既然是 _ 开头,这应该是此中方法,对外封装了二种状态,如ignore,done,updateWithContent:等。

哎呀,这里怎么要先把*block 赋给三个有的时候变量 block,然后再把 _block 设为 nil呢?看起来疑似为了撤废某种内部存储器难题。借使直接 *block(newState, error, update) 会如何?哦,即便这里未有现身 self,但 _block 是二个 instance 变量,所以在 ^{} 里会对 self 举办强引用。而假设赋给八个不经常变量,那么只会对那么些不常变量强引用,就不会产出循环援引的景色。

AAPLLoading 看的大约了,再出去看loadContentWithBlock:,注意到在 CompletionHandler 里,有像这种类型一段

[selfendLoadingWithState:newStateerror:errorupdate:^{AAPLDataSource*me=weakself;if(update&&me)update;}];

此间的 self 是 AAPLDataSource (Block嵌套多了,还真是轻易晕啊),来看看endLoadingWithState:error:update以此艺术都做了哪些

-endLoadingWithState:(NSString*)stateerror:errorupdate:(dispatch_block_t)update{self.loadingError=error;self.loadingState=state;if(self.shouldDisplayPlaceholder){if[selfenqueuePendingUpdateBlock:update];}else{[selfnotifyBatchUpdate:^{// Run pending updates[selfexecutePendingUpdates];ifupdate();}];}self.loadingComplete=YES;[selfnotifyContentLoadedWithError:error];}

设置有个别情形,然后在适当的机缘调用 update block,咦,这里有个 dispatchblockt 没怎么见过,查了弹指间原来是三个放松权利的空传值和空再次回到的block。

看了下enqueuePendingUpdateBlock,会把前不久的这一个 update 结合从前的 updateBlock,形成一个新的 updateBlock,应该就是摄像里关系的当全部的 DataSource 都载入完时,统一实践在此以前的 update block

notifyBatchUpdate:所做的是看一下 Delegate 是还是不是响应dataSource:performBatchUpdate:complete:设若响应则走你,不然挨个试行update / complete。

看完了loadContentWithBlock阅读源码的乐趣。再来看看这一个 Block 里面都做了怎么,大体是基于 self.showingFavorites 来切换差别的数据源,这里见到了叁个新的类 AAPLDataAccessManager,看起来疑似统一的数据层,瞄一眼

@classAAPLCat;@interfaceAAPLDataAccessManager:NSObject+(AAPLDataAccessManager*)manager;-fetchCatListWithCompletionHandler:(NSArray*cats,NSError*error))handler;-fetchFavoriteCatListWithCompletionHandler:(NSArray*cats,NSError*error))handler;-fetchDetailForCat:catcompletionHandler:(AAPLCat*cat,NSError*error))handler;-fetchSightingsForCat:catcompletionHandler:(NSArray*sightings,NSError*error))handler;@end

果如其言如此,将来数量的载入方式有调换,或索要做缓存啥的,都得以在此一层管理,其余部分不会深以为变化。

这一轮看下去已经有好些个音讯量了,来回顾捋一下:

[SegmentedDataSource setNeedsLoadContent]↓[CatListDataSource loadContent]↓[DataSource loadContentWithBlock:]↓创建 loading,设置 loading 完成后要做的事 → 拿到数据后放到 updateQueue 里,等全部拿到再执行 batchUpdate ↓执行 loadContentBlock → 使用 DataAccessManager 去获取数据,拿到后交给 loading

到此处,我们还没运维 Project 看功能,因为本身认为代码包蕴的音讯会更拉长,何况那样看下去后,对于分界面社长啥样也会有个大约的摸底。

那只是开始,继续开采下去还或然有好些个好东西,举例 Favorite 按钮的拍卖,它是由此 Responder Chain 并非 Delegate 来落到实处的,也是叁个思路。通过轻便状态机来管理 loading 状态也是很有意思的落到实处。

万一有意思味,能够看下 ComposedDataSource,先不看贯彻,纵然要和煦写大致会是怎样思路,比如当调用[UICollectionView cellForItemAtIndexPath:]时,怎么着找到呼应的 DataSource,找到之后如何渲染对应的 Cell 等。

之所以看源码真的是一件很风趣的作业,像一场冒险,总是会有不测得到,大概在潜意识中,技能就赢得了提拔。

版权归原著者全体原作地址:

本文由澳门新莆京娱乐官网发布于编程,转载请注明出处:阅读源码的乐趣

关键词:

上一篇:没有了

下一篇:没有了