读 Facebook App 头文件的一些收获.docx
- 文档编号:23698512
- 上传时间:2023-05-20
- 格式:DOCX
- 页数:7
- 大小:21.59KB
读 Facebook App 头文件的一些收获.docx
《读 Facebook App 头文件的一些收获.docx》由会员分享,可在线阅读,更多相关《读 Facebook App 头文件的一些收获.docx(7页珍藏版)》请在冰豆网上搜索。
读FacebookApp头文件的一些收获
最近在看一些App架构相关的文章,也看了Facebook分享的两个不同时期的架构(2013和2014),于是就想一窥FacebookApp的头文件,看看会不会有更多的收获,确实有,还不少。
由于在选择ipa上的失误,下了个7.0版的Facebook(最新的是18.1),会稍有过时,不过后来又下了个18.1的看了下,发现变动其实不大。
以下是我从头文件中获取到的一些信息(20多万行,浏览起来还是挺累的)让视图组件可以方便地配置这个在Facebook的演讲中也提到过,自定义的UI组件在初始化时可以传一些数值来表示想要呈现的效果,就像HTML和CSS一样,Dom结构表示这是什么,CSS对该结构进行个性化定制。
Facebook是通过Struct来做这件事的,比如:
structFBActionSheetButtonMetrics{CDUnknownFunctionPointerType*_vptr$FBMetrics;_Bool_initialized;floatleftMargin;floattextLeftMargin;floatbottomSeperatorSideMargin;floatbottomSeperatorHeight;intdetailMaxNumLines;UIColor*titleColor;//...};好处是减少了代码量,而且直观,方便复用。
尽量使用组合,适度使用继承如果过度使用继承,尤其是继承层次过深,往往会带来更大的维护成本。
有新需求或需求变更时,会花很多时间在「是否需要在基类/子类增加一个方法」,「是否需要新建一个子类」等设计相关的问题上。
而组合则没有这个问题,大不了换一个组件。
不过Objective-C对于组合并没有特别的支持,所以实现起来会略麻烦@interfacePeople{}@propertyidveachle;-(void)move;@end@implementationPeople-(id)initWithVeachle:
(id)veachle{if(self=[superinit]){self.veachle=veachle;}returnself;}-(void)move{[self.veachlemove];}@end如果有很多类似move这样需要交给外部的object来做的方法,就会显得冗余,尽管如此,比起继承来还是更方便维护的。
使用组合的话,一般会使用「依赖注入」,比如这里的Veachle,并不需要特别指出是Bike还是Car,只要有move方法就可以,这样就可以很方便地替换,对于People来说不需要做任何改动。
在Objective-C里是通过protocol来实现的。
所以Facebook定义了一大堆的接口,包括Delegate,DataSource和Protocol,ViewController有Protocol,也有Delegate(如FBMediaGalleryViewControllerDelegate),View/Cell也有Delegate(如FBMediaGalleryViewDelegate),还有各种零零碎碎的Protocol,如FBDiscoveryCardProtocol,FBEventProtocol等。
定义接口的过程也是梳理架构的过程,如果对架构理解不够深刻,是很难将接口恰当地抽象出来的。
很多人放弃使用组合,有一部分原因也是架构上的不合理。
组件的粒度也是个问题,过细会导致组件过多,组合的过程就会花去很多时间;过粗又导致组件臃肿,难以复用。
当组件的接口定义完之后,使用起来大概会是这样:
@interfaceFBResponseHandler:
NSObject@interfaceFBPhotoViewController:
UIViewController这样一眼就大概能看出来这个Class大概会有哪些功能,如果某个组件要作调整,只需修改一处,就可以全局通用。
适度使用继承,可以在易维护和便利上达到平衡,比如FBTableViewController,FBDialog等,自定义的组件可以在它们的基础上进行开发。
继承的层次一般不超过2层,比如UITableViewController依赖注入前面讲过,组合往往和依赖注入搭配使用,Facebook主要是通过FBProvider,FBProviderMapData,FBProviderMap来实现依赖注入的。
Provider会产生一个Object,比如CameraControllerProvider调用get方法后,会生成一个MNCameraController的实例。
同时Provider还有两个子类SingletonProvider和BlockProvider,前者用来生成一个单例,后者用在需要初始化参数的情景。
ProviderMap跟ProviderMapData有些重复,它们之间的关系我也没有捋清,感觉ProviderMap像是一个Manager,注册了一堆Provider,然后可以通过Provider的ID来找到之前注册的Provider。
模块化不光是在Cocoa开发领域,其他的编程领域也一样,模块化是一个理想的状态,高内聚,低耦合。
像shell命令一样,接受参数或标准输入,生成格式化的标准输出,通过管道传递给其他支持标准输入的命令行工具。
但现实场景要复杂的多,模块化的实现也更加困难。
Facebook有一个FBAppModule协议@protocolFBAppModule+(id)instanceForSession:
(FBSession*)arg1providerMap:
(FBProviderMap*)arg2;@property(readonly,nonatomic)NSArray*supportedURLSchemes;@property(readonly,nonatomic)NSArray*supportedKeys;@property(retain,nonatomic)idactiveMenuItem;@property(readonly,nonatomic)NSString*defaultIcon;@property(readonly,nonatomic)NSString*ID;-(UIViewController*)viewControllerForMenuItem:
(id)arg1;初始化时传入一个FBSession(后面会讲到)和ProviderMap,然后设置支持的urlschemes,keys(具体作用未知),对应的menuItem,icon(用于在menuItem显示)和ID。
有了Module,自然还有ModuleManager,它的作用是注册Module,当一个url过来时,可以遍历Module,看看是不是有模块可以处理这个url,有的话,就调用该Module的openURL:
方法。
当然也可以根据ModuleID来获取Module。
FBAppModule是一个Protocol,FBNativeAppModule是对该协议的实现,所以具体的模块都继承该类。
导航管理一般来说系统的UINavigationController已经够使用了,如果需要更大的自由度和更高的可定制性,可以自定义一个导航管理器,Facebook使用了FBUINavigationController(Protocol)来实现自定义导航的管理,属性和方法跟系统的差不多。
它有多个实现:
FBTariffedNavigationController,FBSwipeNavigationController,FBCustomNavigationController,FBNavigationController。
前面讲过继承一般不超过2层,这里是一般之外的情况,有3层。
MVVMMVVM是解决MassiveViewController的一个有效方法,独立出一个ViewModel作为View的数据源,以及处理View的一些交互操作,而VC只需要将ViewModel和View关联起来即可。
一般会搭配某种绑定的实现,KVO或ReactiveCocoa都可以,这样ViewModel的数据有变化就可以自动映射到View上。
Facebook也采用了这种方式,有一个FBViewModel基类@interfaceFBViewModel:
NSObject//省略了一些相关性不大的属性和方法@property__weakFBViewModelManager*viewModelManager;//@synthesizeviewModelManager=_viewModelManager;@property(nonatomic)unsignedintviewModelSource;//@synthesizeviewModelSource=_viewModelSource;@property(retain,nonatomic)FBViewModelConfiguration*viewModelConfiguration;//@synthesizeviewModelConfiguration=_viewModelConfiguration;@property(readonly,nonatomic)unsignedintviewModelVersion;//@synthesizeviewModelVersion=_viewModelVersion;@property(readonly,nonatomic)NSString*viewModelUUID;//@synthesizeviewModelUUID=_viewModelUUID;@property(retain)FBMemModelObject*memModel;//@synthesizememModel=_memModel;-(void)setNilValueForKey:
(id)arg1;-(id)initWithViewModelUUID:
(id)arg1viewModelVersion:
(unsignedint)arg2;-(void)setViewModelVersion:
(unsignedint)arg1;-(id)humanDescription;-(void)loadPermanentDataModelObjectIDFromDataModelObjectID:
(id)arg1block:
(CDUnknownBlockType)arg2;-(void)didUpdateWithChangedProperties:
(id)arg1;@property__weakFBViewModelController*modelController;@property(nonatomic)intloadState;@endFacebook自己实现了一套ViewModel的更新通知机制,因为ViewModel都是Immutable的,所以无法改变,那么就需要有一个地方去集中管理这些ViewModel,有更新时可以及时通知到,FBViewModelController应该就是干这事的,里面有一个方法-(void)_notifyViewModel:
(id)arg1didUpdateWithChanges:
(id)arg2;。
但FBViewModelManager看起来更合适,二者的功能没有太理清楚。
FBViewModelController还有一个Delegate,主要有3个方法didUpdate[Delegate][Insert]ViewModel:
,可以做一些事后的操作。
BuilderPattern在定义一个ViewController时,往往需要接收很多个参数,以initWith:
这种形式出现不太合适,除非你能容忍一个10行的方法声明。
通常的做法是把这些参数声明为property,然后在初始化VC后,对这些property赋值,然后在ViewDidLoad里使用这些property。
这样做有几个问题:
1)不知道哪些是需要在ViewDidLoad前设置的,会出现忘了设置的现象。
2)这些属性可以在外部被改动。
3)代码不够优雅。
BuilderPattern就是用来解决这个问题的,它跟工厂模式有点像。
Facebook也用到了这个模式,比如有一个FBMUserFetchStatus类,该类初始化时需要一些参数,于是就有了FBMUserFetchStatusBuilder类@interfaceFBMUserFetchStatusBuilder:
NSObject+(id)aMUserFetchStatusFromExistingMUserFetchStatus:
(id)arg1;+(id)aMUserFetchStatus;-(id)withIdentifiers:
(BOOL)arg1;-(id)withImageUrls:
(BOOL)arg1;-(id)withHasVerifiedPhone:
(BOOL)arg1;-(id)withCanInstallMessenger:
(BOOL)arg1;-(id)withHasMessenger:
(BOOL)arg1;-(id)withIsFriend:
(BOOL)arg1;-(id)withNickname:
(BOOL)arg1;-(id)withPhoneticName:
(BOOL)arg1;-(id)withName:
(BOOL)arg1;-(id)withUserId:
(BOOL)arg1;-(id)build;@end最后的build方法会生成一个FBMUserFetchStatus实例,有了这个Builder就知道有哪些参数是可以在初始化时进行设置的。
DataManager这是重头戏,所以看起来略累,东西很多,很可能推断错误。
先来看看实体类,首先是FBEntityRequest@protocolFBEntityRequestParse@optional+(BOOL)canParse:
(id)arg1error:
(id*)arg2;@property(retain,nonatomic)NSError*syncError;@property(nonatomic,getter=isSyncing)BOOLsyncing;-(unsignedint)parse:
(id)arg1request:
(id)arg2error:
(id*)arg3;-(id)request;@end所以实体都是可以被解析和同步的,还自带了一个Request。
再来看看FBEntity@protocolFBEntity+(NSURL*)entityURLForFBID:
(NSString*)arg1;@property(readonly,nonatomic)NSURL*entityURL;@property(readonly,nonatomic,getter=isDataStale)BOOLdataStale;@property(retain,nonatomic)NSDate*lastSyncTime;@property(retain,nonatomic)NSString*fbid;@optional+(unsignedint)collection:
(FBEntityCollection*)arg1parse:
(id)arg2request:
(id)arg3error:
(id*)arg4;+(id)collectionRequest:
(FBEntityCollection*)arg1;@property(readonly,nonatomic)FBEntityDownloader*entityDownloader;-(NSSet*)parentEdges;-(NSSet*)parentCollections;-(void)entityInitializeWithFBID:
(NSString*)arg1;@end每个Entity都有一个entityURL,或许可以用来同步?
dataStale应该是用来表示数据是否dirty,如果是的话,可能需要同步。
还可以请求Collection。
FBEntityCollection跟FBEntity类似,不过多了syncAll/memberClass/allObjects这些属性/方法。
再来看看数据请求,首先是FBRequest,不太明白这个Class的具体功能,因为没有URL,一个没有URL的Request能做什么?
然后看到了FBRequester,这个看起来是一个数据请求类,有URL,responseHandler,connection状态,delegate等。
但这只是单个的请求,如何对多个请求进行管理呢,这时看到了FBNetworker,它有+sharedNetworker,requestQueue,cancelRequests:
addRequest:
所以就是它了。
等等,为什么下面还有一个FBNetworkerRequest?
看起来像是FBNetworker的Delegate,但不确定。
为了避免URI散落在各处,Facebook还专门为NSURL写了个Category来统一管理URI。
@interfaceNSURL(FBFoundation)+(id)friendsNearbyURL;+(id)codeGeneratorURL;+(id)tagApprovalURLWithTagId:
(id)arg1;+(id)tagApprovalURL;+(id)pokesURL;+(id)personExpandedAboutURLWithFBID:
(id)arg1;//...还有一个URL生成类,FBURLRequestGenerator,该类保存了appSecret和appVersion,生成的URL会自动带上这些属性。
其实还有很多,实在看不下来了···SmarterViews我们都知道ViewController自带了一个view,可以直接在这个view上addSubview,正是由于这个便利性,很多创建View的代码也挤在了VC里,实在是不雅观。
更好的方法是替换VC的view为自定义的View,然后把这个自定义View独立出去。
比如在-loadView时覆盖view@implementationMyProfileViewController-(void)loadView{self.view=[MyProfileViewnew];}可以同时重定义view的类型,如@property(nonatomic)MyProfileView*view,让编译器明白view的类型已经变了。
因为看到了不少VC中都有-loadView方法,所以推断可能使用了这项技术。
FBSession在Web开发领域,Session是用来保存用户相关的信息的,FBSession自然也不例外,不过它保存的内容还真是多呢。
@interfaceFBSession:
NSObject+(void)setCurrentSession:
(id)arg1;+(id)_globalSessionForDebugging;+(id)DO_NOT_USE_OR_YOU_WILL_BE_FIREDcurrentSession;@property(readonly)FBAPISessionStore*apiSessionStore;//@synthesizeapiSessionStore=_apiSessionStore;@property(readonly)FBSessionDiskStore*sessionDiskStore;//@synthesizesessionDiskStore=_sessionDiskStore;@property(readonly)FBStore*store;//@synthesizestore=_store;@property(readonly)NSString*appSecret;//@synthesizeappSecret=_appSecret;@property(readonly,nonatomic,getter=isValid)BOOLvalid;@property(readonly)BOOLhasUser;@property(readonly)NSString*userFBID;@property(retain)FBViewerContext*viewerContext;@property(retain)FBUserPreferences*userPreferences;@property(retain)FBPreferences*sessionPreferences;-(void)updateAccessToken:
(id)arg1;-(id)updateActingViewer:
(id)arg1;-(void)clearPreferences;-(void)invalidate;-(id)DO_NOT_USE_OR_YOU_WILL_BE_FIREDvalueForKeyRequiresUser:
(id)arg1withInitializer:
(CDUnknownBlockType)arg2;-(id)valueForKey:
(id)arg1withInitializer:
(CDUnknownBlockType)arg2;-(id)valueForKey:
(id)arg1;-(id)initWithAppSecret:
(id)arg1store:
(id)arg2apiSessionStore:
(id)arg3;@property(readonly,nonatomic)FBReactionController*reactionController;@property(readonly,nonatomic)FBLocationPingback*locationPingback;@property(readonly,nonatomic)FBAppSectionManager*appSectionManager;@property(readonly,nonatomic)FBBookmarkManager*bookmarkManager;//andmanymore...Session是可以保存到本地的,有一个状态变量用来标识是否有效(valid),是否已登录(hasUser),用户的一些设置(这些设置会保存到本地),可以更新AccessToken,还带了各种Controller和Manager,所以东西还是挺多的。
这里有两个特殊方法,使用后会被Fire···ServicesService顾名思义,提供某种服务,往往跟界面无关。
从目录层级上看,Service并不在Module里面,也就是说这二者是独立的,比如FBTimelineModule并不包含FBTimelineService。
Service之间可以有依赖,这里是通过startAppServiceWithDependencies:
来实现的,不过不清楚Service自身如何声明依赖哪些其他的Services。
Styl
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Facebook App 头文件的一些收获 文件 一些 收获