怎样用HTTP/2sql优化常用面试题iOS APP网络层次架构

IOS网络层次 - 移动开发专栏 - CSDN博客
IOS网络层次
UI高级界面
一、iOS网络层次结构
基于iOS提供API实现上传文件和断点续传的思路
常用iOS第三方网路框架简介
AFNetworking(AFN)
ASIHTTPRequest(ASI)
另外一个常用框架
SSZipArchive
二、iOS网络编程层次结构
Cocoa层(NSURL,Bonjour,Game&Kit,WebKit)
Core&Foundation层(基于&C&的&CFNetwork&和&CFNetServices)
OS层(基于&C&的&BSD&socket)
三、iOS网络编程层次结构概述
Cocoa层:是最上层的基于OC的API,比如URL访问,NSStream,Bonjour,GameKit等,这是大多数情况下我们常用的&API。Cocoa层是基于Core&Foundation实现的
Core&Foundation层:基于C语言的框架,因为直接使用socket需要更多的编程工作,所以苹果对OS层的socket进行简单的封装以简化编程任务。该层提供了CFNetwork和CFNetServices,其中CFNetwork又是基于CFStream和CFSocket
OS层:最底层的BSD&socket提供了对网络编程最大程度的控制,但是编程工作也是最多的。苹果建议我们使用Core&Foundation及以上层的API进行编程。BSD是UNIX系统中通用的网络接口,它不仅支持各种不同的网络类型,而且也是一种内部进程之间的通信机制
四、AFN&vs&ASI
官方推荐的使用方法:为一系列相关的请求定义一个HTTPClient,共用一个BaseURL。每次请求把URL中除BaseURL的Path部分做为参数传给HTTPClient的静态方法,并注册一个Block用于回调
基于NSURL,性能和稳定性略差
AFN只封装了一些常用功能,满足基本需求,而直接忽略了很多扩展功能
针对JSON、XML、PList和Image四种数据结构封装了各自处理器,开发者可以把处理器注册到操作队列中,直接在回调方法中获得格式化以后的数据
推荐使用方法:每一个请求都由构造方法初始化一个(共享)实例,通过这个实例配置参数并发起请求。ASI最初使用delegate模式回调,在iOS&SDK支持Block之后也提供了注册Block的实例方法(注:ASI的Block不易使用)
基于CFNetwork,性能和稳定性略高
ASI的扩展功能非常丰富
ASI没有针对任何数据类型做特别封装,只是预留了各种接口和工具供开发者自行扩展
五、AFN和ASI的选择
AFN适合逻辑简单的应用,或者更适合开发资源尚不丰富的团队,因为AFN的易用性要比ASI好很多,而这样的应用(或团队)对底层网络控件的定制化要求也非常低。
ASI更适合已经发展了一段时间的应用,或者开发资源相对丰富的团队,因为往往这些团队(或他们的应用)已经积累了一定的经验,无论是产品上还是技术上的。需求复杂度就是在这种时候高起来,而且底层订制的需求也越来越多,此时AFN就很难满足需求,需要牺牲一定的易用性,使用ASI作为网络底层控件。
六、AFNetworking(AFN)
/AFNetworking/AFNetworking
AFNetworking官网地址:
七、导入AFN框架的步骤
1.&将框架程序拖拽进项目
2.&&添加iOS框架引用
SystemConfiguration.framework
MobileCoreServices.framework
3.&修改xxx-Prefix.pch文件
#import&&MobileCoreServices/MobileCoreServices.h&
#import&&SystemConfiguration/SystemConfiguration.h&
八、AFHTTPClient
1.&建立NSURLRequest
创建GET、HEAD、PUT、DELETE方法请求
requestWithMethod:path:parameters:
创建POST方法请求
multipartFormRequestWithMethod:path:parameters: constructingBodyWithBlock:
2.&检测网路连接状态
setReachabilityStatusChangeBlock
九、AFHttpRequestOperation对NSURLConnection的封装
AFHttpRequestOperation HTTP请求操作
AFJSONRequestOperation 对JSON请求的封装
AFXMLRequestOperation 对XML请求的封装
AFPropertyListRequestOperation 对Plist请求的封装
AFImageRequestOperation 对图像请求的封装
块代码操作
setCompletionBlockWithSuccess 设置请求完成块代码
setUploadProgressBlock 设置上传进度块代码
setDownloadProgressBlock 设置下载进度块代码
下载操作需要设置outputStream
针对请求的操作pause(暂停)resume(继续)
十、检测网络连接状态
AFHTTPClient&*client&=&[AFHTTPClient&clientWithBaseURL:[NSURL&URLWithString:@&&]];
[client&setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus&status)&{
十一、加载JSON&&&XML
AFJSONRequestOperation&*op&=&[AFJSONRequestOperation&JSONRequestOperationWithRequest:request&success:^(NSURLRequest&*request,&NSHTTPURLResponse&*response,&id&JSON)&{
&&&&NSLog(@&%@&,&JSON);
}&failure:^(NSURLRequest&*request,&NSHTTPURLResponse&*response,&NSError&*error,&id&JSON)&{
&&&&NSLog(@&%@&,&JSON);
十二、上传图像
NSURLRequest&*request&=&[client&multipartFormRequestWithMethod:@&POST&&path:@&/~liufan9/itcast/upload.php&&parameters:nil&constructingBodyWithBlock:^(id&AFMultipartFormData&&formData)&{
&&&&[formData&appendPartWithFileData:imageData&name:@&file&&fileName:@&update.png&&mimeType:@&image/png&];
//&定义操作
AFHTTPRequestOperation&*operation&=&[[AFHTTPRequestOperation&alloc]initWithRequest:request];
//&设置上传
//&设置下载进度
十三、解压缩——另一个第三方框架SSZipArchive
下载地址:/samsoffes/ssziparchive
注意:需要引入libz.dylib框架&
//&Unzipping
NSString&*zipPath&=&@&path_to_your_zip_file&;
NSString&*destinationPath&=&@&path_to_the_folder_where_you_want_it_unzipped&;
[SSZipArchive&unzipFileAtPath:zipPath&toDestination:destinationPath];
//&Zipping
NSString&*zippedPath&=&@&path_where_you_want_the_file_created&;
NSArray&*inputPaths&=&[NSArray&arrayWithObjects:
&&&&&&&&&&&&&&&&&&&&&&&[[NSBundle&mainBundle]&pathForResource:@&photo1&&ofType:@&jpg&],
&&&&&&&&&&&&&&&&&&&&&&&[[NSBundle&mainBundle]&pathForResource:@&photo2&&ofType:@&jpg&]
&&&&&&&&&&&&&&&&&&&&&&&nil];
[SSZipArchive&createZipFileAtPath:zippedPath&withFilesAtPaths:inputPaths];
十四、ASIHTTPRequest(ASI)
使用ASI的两点注意事项
ASI框架是不支持ARC的
ASI框架是基于iOS5.0的,如果选择iOS6.0会有一些苹果官方不再维护的方法
十五、ASI下载文件的准备工作
//&1.&指定下载文件地址
NSString&*string&=&@&http://localhost/~liufan9/itcast/download/iTunesConnect_DeveloperGuide_CN.zip&;
NSURL&*url&=&[NSURL&URLWithString:string];
//&2.&设定文件保存路径及缓存路径
NSArray&*documents&=&NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,&NSUserDomainMask,&YES);
NSString&*downloadPath&=&[documents[0]stringByAppendingPathComponent:@&book.zip&];
NSString&*tempPath&=&[documents[0]stringByAppendingPathComponent:@&book.tmp&];
十六、ASI&下载文件请求定义部分代码
//&3.&创建ASIHTTPRequest
ASIHTTPRequest&*request&=&[ASIHTTPRequest&requestWithURL:url];
//&4.&设置代理——ASI是通过代理回调的方式处理网络请求的
[request&setDelegate:self];
//&5.&设置下载路径
[request&setDownloadDestinationPath:downloadPath];
//&6.&设置缓存路径
[request&setTemporaryFileDownloadPath:tempPath];
//&7.&设置断点续传
[request&setAllowResumeForFileDownloads:YES];
//&8.&设置下载进程代理
[request&setDownloadProgressDelegate:self];
//&9.&启动异步请求——用户想知道下载的实际进展情况
[request&start];
十七、NSURLConnectionDataDelegate的代理方法
//&服务器开始返回数据
(void)connection:didReceiveResponse:
//&收到服务器返回的数据,本方法会被调用多次
-&(void)connection:didReceiveData:
//&数据接收完毕
(void)connectionDidFinishLoading:
//&网络连接错误
-&(void)connection:didFailWithError:
//&发送数据给服务器,POST&请求使用此方法
-&(void)connection:didSendBodyData:totalBytesWritten:&totalBytesExpectedToWrite:
十八、ASIRequest&代理方法
//&请求开始
-&(void)requestStarted:(ASIHTTPRequest&*)request
//&请求接收到响应的头部,包括文件大小信息
-&(void)request:(ASIHTTPRequest&*)request&didReceiveResponseHeaders:(NSDictionary&*)responseHeaders
//&请求完成
-&(void)requestFinished:(ASIHTTPRequest&*)request
//&请求失败
-&(void)requestFailed:(ASIHTTPRequest&*)request
对比结果:
ASIRequest不需要处理中间数据
但是请求开始拆分成了两部分
十九、实现代理方法之前需要先遵从代理协议
ASIHTTPRequestDelegate
ASIProgressDelegate
二十、第三方框架SSZipArchive解压缩
下载地址:/samsoffes/ssziparchive
注意:需要引入libz.dylib框架&
//&Unzipping
NSString&*zipPath&=&@&path_to_your_zip_file&;
NSString&*destinationPath&=&@&path_to_the_folder_where_you_want_it_unzipped&;
[SSZipArchive&unzipFileAtPath:zipPath&toDestination:destinationPath];
//&Zipping
NSString&*zippedPath&=&@&path_where_you_want_the_file_created&;
NSArray&*inputPaths&=&[NSArray&arrayWithObjects:
&&&&&&&&&&&&&&&&&&&&&&&[[NSBundle&mainBundle]&pathForResource:@&photo1&&ofType:@&jpg&],
&&&&&&&&&&&&&&&&&&&&&&&[[NSBundle&mainBundle]&pathForResource:@&photo2&&ofType:@&jpg&]
&&&&&&&&&&&&&&&&&&&&&&&nil];
[SSZipArchive&createZipFileAtPath:zippedPath&withFilesAtPaths:inputPaths];
NSLog(@&请求完成&);
//&1.&知道文件保存路径(运行,发现文件已经下载完成了)
//&2.&解压缩文件(导入SSZipArchive框架)
//&根据SSZipArchive框架用法写思路
//&2.1&设置压缩文件路径
NSArray&*documents&=&NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,&NSUserDomainMask,&YES);
NSString&*downloadPath&=&[documents[0]stringByAppendingPathComponent:@&book.zip&];
//&2.2&设置解压缩文件路径,保存在当前路径
NSString&*unzipPath&=&documents[0];
//&2.3&解压缩
[SSZipArchive&unzipFileAtPath:downloadPath&toDestination:unzipPath];
//&3.&删除压缩文件
[[NSFileManager&defaultManager]removeItemAtPath:downloadPath&error:nil];
二十一、ASIProgressDelegate下载进度跟踪
ASIProgressDelegate——setProgress
#pragma&mark&-&下载进度代理方法
-&(void)setProgress:(float)newProgress
&&&&//&通过Log发现传入的是一个百分比的数组
&&&&&&&&&&//&现在需要一个文件大小,并提示用户文件的大小
&&&&&&NSLog(@&%f&,&newProgress);
二十二、ASIRequest响应头部的代码实现
1.&ASIRequest响应头部的代码实现
//&1.&NSLOG看看头部是什么内容
NSLog(@&%@&,&responseHeaders);
//&2.&发现其中有一个&Content-Length&&=&6105204;
//&貌似是和文件下载进度有关的工作可以在这里进行
//&将文件大小转换成M为单位&字节-K-M
_fileLength&=&request.contentLength&/&1024.0&/&1024.0;
NSLog(@&%.2fM&,&_fileLength);
2.&setProgress方法
NSLog(@&%.2f&,&newProgress&*&_fileLength);
我的热门文章君,已阅读到文档的结尾了呢~~
iOS应用架构谈-开篇
扫扫二维码,随身浏览文档
手机或平板扫扫即可继续访问
iOS应用架构谈-开篇
举报该文档为侵权文档。
举报该文档含有违规或不良信息。
反馈该文档无法正常浏览。
举报该文档为重复文档。
推荐理由:
将文档分享至:
分享完整地址
文档地址:
粘贴到BBS或博客
flash地址:
支持嵌入FLASH地址的网站使用
html代码:
&embed src='/DocinViewer--144.swf' width='100%' height='600' type=application/x-shockwave-flash ALLOWFULLSCREEN='true' ALLOWSCRIPTACCESS='always'&&/embed&
450px*300px480px*400px650px*490px
支持嵌入HTML代码的网站使用
您的内容已经提交成功
您所提交的内容需要审核后才能发布,请您等待!
3秒自动关闭窗口iOS APP 架构漫谈(一)
招聘信息:
最近看了一些有关server的东西,一些很简单的东西,不外乎是一些文档规范,另外结合最近看的wwdc的一些video,觉得对软件架构(software architecture)认识又清楚了一些,这里记录下来。software architecture 听上去是一个很大的概念,实际上也包括很多东西,里面的争议也很多。在我看来软件架构最好放在小的场景中理解。问题1我们有2个页面。· 页面A:主页面· 页面B:详情页面2个页面分别显示一个数字,这个数字应该相同。详情会修改这个数字,这里我们发现,详情页面和主页面数字不一样。数据不一致问题1 解决方法A这里首先的感觉就是,详情页面返回,主页面数据没有刷新,导致数据不一致。 那么Fix这个Bug的方法,就是在主页面出现的时候刷新界面-&(void)viewWillAppear:(BOOL)animated&{
&&&&[super&viewWillAppear:animated];
&&&&self.displayLabel.text&=&[[CUDataDAO&selectData].data&stringValue];
}现在来看,还不错。但是,我们调用selectData的次数则变得非常非常多。数据不是经常变化的。问题1 解决方法B我们发现既然数据的改变是在页面B进行的,那么页面B修改这个数据的时候,应该把数据变化”通知”给页面A,那么我们写了一个Delegate@protocol&CUDetailViewControllerDelegate&&NSObject&
-&(void)detailVC:(CUDetailViewController&*)vc&dataChanged:(NSNumber&*)
@end在页面B修改数据之后,通过delegate 通知给页面A。-&(IBAction)changeButtonClicked:(id)sender&{
&&&&int&value&=&arc4random()&%&100;
&&&&[CUDataDAO&setData:value];
&&&&self.displayLabel.text&=&[@(value)&stringValue];
&&&&if&([self.delegate&respondsToSelector:@selector(detailVC:dataChanged:)])&{
&&&&&&&&[self.delegate&detailVC:self&dataChanged:@(value)];
}到此场景1得到了不错的解决。问题2这时我们增加了另一个页面C。这个场景会稍微抽象一点,我们定义了3个数据· 页面A的数据dataA· 页面B的数据dataB· 页面C的数据dataC问题1中 dataA = dataB。在问题2中dataA = dataB + dataC;问题2 解决方法C也就是说页面C的修改,也会影响页面A的数据,那么我们是不是也要写一个XXXXDelegate呢?这时我们的大脑嗅出了一些不好的味道,如果再来个什么dataD,dataE,我们要写这么多的Delegate么?对于多对一”通知”这种味道,很自然的想到了不用Delegate,而是用NSNotification来做。让我们未雨绸缪一下,定义一个NotificaitonNSString&*const&kCUDataChangedNotification&=&@&CUDataChangedNotification&;
[[NSNotificationCenter&defaultCenter]&postNotificationName:kCUDataChangedNotification
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&object:nil
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&userInfo:nil];那这个变化broadcast到listener,看上去是一个很赞的idea。问题3过了一段时间,我们发现问题2的方法有一个Bug,当界面停在页面B的时候,切换到页面C,修改数据,B中再返回时,数据和页面A的数据不一致。数据不一致那也可以类比解决方法B,得到了下面的方法解决方法D既然A和B的数据不一致,而A的数据比B的新,那么保留一个B的指针,然后A变化的时候,更新B就好了。-&(void)handleDataChangedNotification&{
&&&&[self&updateLabel];
&&&&[self.vc&updateLabel];
//&In&a&storyboard-based&application,&you&will&often&want&to&do&a&little&preparation&before&navigation
-&(void)prepareForSegue:(UIStoryboardSegue&*)segue&sender:(id)sender
&&&&if&([segue.identifier&isEqualToString:@&push&])&{
&&&&&&&&CUDetailViewController&*vc&=&[segue&destinationViewController];
&&&&&&&&if&([vc&isKindOfClass:[CUDetailViewController&class]])&{
&&&&&&&&&&&&self.vc&=&
}问题4页面C实在是太简单了,这次我们希望在页面C中显示页面A的数据。因为上次我们就产生了一个数据不一致的问题,这次我们注意到了,那么怎么修改呢?解决方法E在看了看整个APP各种通知之后,觉得挺麻烦,准备用一个取巧的方法。可以类比解决方法A。在页面C出现的时候,刷新数据,至于什么性能问题,不管了,先fix bug。-&(void)viewWillAppear:(BOOL)animated&{
&&&&[self&updateLabel];
-&(void)updateLabel&{
&&&&int&dataB&=&[[CUDataDAO&selectData].data&intValue];
&&&&int&dataC&=&[[CUDataDAO&selectOtherData].data&intValue];
&&&&self.dataLabel.text&=&[@(dataB&+&dataC)&stringValue];
}问题5这时的数据需要不断的变化,我们在CUDataDAO加了一个timer 模拟数据变化,数据变化的原因可能是server push 一些数据。client 本地数据库更新了数据,需要在页面A、B、C中显示。页面C的数据又不一致了。。。。问题到底在哪里呢走到这里,我们需要重新思考为什么这个问题会不断的重复出现呢?software architecture就是来解决这个问题的。但是在提出一个合理的方案之前,先思考一个概念。我们把数据库中的数据,显示到屏幕上,或是传递给View时,这个过程其实是对data 做了一次copy。而且只要不是通过引用或是指针这些方式,通过值传递的方式都是对data做了一次copy。而这个copy的过程,非常类似Cache。通常建立一个Cache会遇到2种问题。· Cache情况A: 与original Data 数据不一致,没有及时更新· Cache情况B: 重复建立Cache让我们用这个思路来看我们的解决方案解决方法A这是一个非常典型的Cache情况B。数据库的数据并没有变化,但我们却多次重复计算cache解决方法B页面之间的关系可以用下面来描述这里我们隐隐能够感觉到问题,A的数据变化依赖于2个地方。不急,再往后看解决方法C解决方法D事情变得更糟了解决方法E和解决方法A类似,同样的重复计算Cache问题。实际上问题还会更糟现在还是一个简单的Model,如果project变得很大,那么就会变成这个样子每一个X都可能是一个Bug。我们似乎已经找到问题了《》&中,把这个图叫做information flow。我们的直觉会告诉我们,这个信息的传递,应该是自上而下的树或是森林,而且最好是一个层次平衡结构,要清晰,每一个位置都有相对于的职责。那我们就需要制定一个规则。在想这个规则之前,如果把上面的图背后的数据忘记,我们感觉这很类似内存模型。当然内存模型会比较复杂。但是我们可以借鉴很多”内存管理中的规则”,比如谁创建,谁销毁。同样,在我们的information flow中,我们希望谁创建Cache,谁更新Cache变化DAO的数据库似乎很难做这件事情,我们引入了一个新的元素dataSource(当然他本身又是DAO的一个Cache)。其中A、B、C3个都会显示数据,那么他们应该在一个层级,其中B、C会修改数据,他们会把这个数据返回给dataSource,而通过dataSource来把这个变化通知到A、B、C。这样带来的好处很明显,我们再添加一个D,也不会对其他地方的数据产生任何影响,我们的Unit Test、Mock也更加好写。我们之前的思路错在哪里呢?从局部来看,我们之前的思路都没有任何问题,但是整体来看却把问题隐藏化。关键的问题是在于没有找到Truth,找到问题真正的地方。而找到真正的地方,需要我们在大脑中有一个清晰的information flow或是data flow。了解之间元素的相互关系,才能建立一个个的层。才能坐到真正的解耦,解耦并不是仅仅一个个的Manager,更重要的是建立一套清晰的flow机制,或是消息机制,如果没有一套flow,中间引入的各种各样的方法,即便使用了各种设计模式,整个software 依然是深度耦合。疑问这个APP看上去交互非常复杂上面的model,有些同学还可能觉得这是交互上面的问题,这个交互看上去非常的复杂,不是一个好设计。我这里列举一个实际的例子:A页面要创建动画,动画背后包括很多数据,这些数据会在B,C甚至更多的页面,或是后台被修改。动画本身实际上体现在View,而这些view可能不仅仅在A中有,B,C可能也会有部分的View。单例怎么样当然我们可以用单例的法子。单例是个魔鬼,被很多滥用,这个场景用单例,其实仅仅是把全局变量合理的封装在了单例下,因为这份数据,并没有任何理由要一定是一份copy。recap在了解这个概念后,再看一些server的架构,规则时,也会更容易理解这些层之间的关系。包括为什么要规定那些层之间,不能相互调用,不能有静态方法。一个层之间的model,不能有重叠功能,不能连表查询。在哪个层才能调用另一个服务,而调用这个服务还必须要通过统一的接口software architecture 涵盖的东西非常多。这篇只是一个引子,介绍了设计之前的准备工作。但是在实际过程中,我们的模型可能要比我这里写的还要复杂很多。下一篇会介绍一种策略用来处理更加复杂模型的情况。最后附上一个完整功能的&(作者:朱宇光)
微信扫一扫
订阅每日移动开发及APP推广热点资讯公众号:CocoaChina
您还没有登录!请或
点击量6646点击量5944点击量4816点击量4543点击量3285点击量3068点击量2796点击量2552点击量2519
&2016 Chukong Technologies,Inc.
京公网安备89最近看了一些有关server的东西,一些很简单的东西,不外乎是一些文档规范,另外结合最近看的wwdc的一些video,觉得对软件架构(software architecture)认识又清楚了一些,这里记录下来。
software architecture 听上去是一个很大的概念,实际上也包括很多东西,里面的争议也很多。在我看来软件架构最好放在小的场景中理解。
我们有2个页面。
页面A:主页面页面B:详情页面
2个页面分别显示一个数字,这个数字应该相同。详情会修改这个数字,这里我们发现,详情页面和主页面数字不一样。
数据不一致
问题1 解决方法A
这里首先的感觉就是,详情页面返回,主页面数据没有刷新,导致数据不一致。 那么Fix这个Bug的方法,就是在主页面出现的时候刷新界面
- (void)viewWillAppear:(BOOL)animated {
& & [super viewWillAppear:animated];
& & self.displayLabel.text = [[CUDataDAO selectData].data stringValue];
现在来看,还不错。但是,我们调用selectData的次数则变得非常非常多。数据不是经常变化的。
问题1 解决方法B
我们发现既然数据的改变是在页面B进行的,那么页面B修改这个数据的时候,应该把数据变化”通知”给页面A,那么我们写了一个Delegate
@protocol CUDetailViewControllerDelegate &NSObject&
- (void)detailVC:(CUDetailViewController *)vc dataChanged:(NSNumber *)
在页面B修改数据之后,通过delegate 通知给页面A。
- (IBAction)changeButtonClicked:(id)sender {
& & int value = arc4random() % 100;
& & [CUDataDAO setData:value];
& & self.displayLabel.text = [@(value) stringValue];
& & if ([self.delegate respondsToSelector:@selector(detailVC:dataChanged:)]) {
& & & & [self.delegate detailVC:self dataChanged:@(value)];
到此场景1得到了不错的解决。
这时我们增加了另一个页面C。这个场景会稍微抽象一点,我们定义了3个数据
页面A的数据dataA页面B的数据dataB页面C的数据dataC
问题1中 dataA = dataB。在问题2中dataA = dataB + dataC;
问题2 解决方法C
也就是说页面C的修改,也会影响页面A的数据,那么我们是不是也要写一个XXXXDelegate呢?
这时我们的大脑嗅出了一些不好的味道,如果再来个什么dataD,dataE,我们要写这么多的Delegate么?对于多对一”通知”这种味道,很自然的想到了不用Delegate,而是用NSNotification来做。让我们未雨绸缪一下,定义一个Notificaiton
NSString *const kCUDataChangedNotification = @&CUDataChangedNotification&;
[[NSNotificationCenter defaultCenter] postNotificationName:kCUDataChangedNotification
& & & & & & & & & & & & & & & & & & & & & & & & & object:nil
& & & & & & & & & & & & & & & & & & & & & & & & userInfo:nil];
那这个变化broadcast到listener,看上去是一个很赞的idea。
过了一段时间,我们发现问题2的方法有一个Bug,当界面停在页面B的时候,切换到页面C,修改数据,B中再返回时,数据和页面A的数据不一致。
数据不一致
那也可以类比解决方法B,得到了下面的方法
既然A和B的数据不一致,而A的数据比B的新,那么保留一个B的指针,然后A变化的时候,更新B就好了。
- (void)handleDataChangedNotification {
& & [self updateLabel];
& & [self.vc updateLabel];
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
& & if ([segue.identifier isEqualToString:@&push&]) {
& & & & CUDetailViewController *vc = [segue destinationViewController];
& & & & if ([vc isKindOfClass:[CUDetailViewController class]]) {
& & & & & & self.vc =
页面C实在是太简单了,这次我们希望在页面C中显示页面A的数据。因为上次我们就产生了一个数据不一致的问题,这次我们注意到了,那么怎么修改呢?
在看了看整个APP各种通知之后,觉得挺麻烦,准备用一个取巧的方法。可以类比解决方法A。在页面C出现的时候,刷新数据,至于什么性能问题,不管了,先fix bug。
- (void)viewWillAppear:(BOOL)animated {
& & [self updateLabel];
- (void)updateLabel {
& & int dataB = [[CUDataDAO selectData].data intValue];
& & int dataC = [[CUDataDAO selectOtherData].data intValue];
& & self.dataLabel.text = [@(dataB + dataC) stringValue];
这时的数据需要不断的变化,我们在CUDataDAO加了一个timer 模拟数据变化,数据变化的原因可能是server push 一些数据。client 本地数据库更新了数据,需要在页面A、B、C中显示。
页面C的数据又不一致了。。。。
问题到底在哪里呢
走到这里,我们需要重新思考为什么这个问题会不断的重复出现呢?software architecture就是来解决这个问题的。但是在提出一个合理的方案之前,先思考一个概念。
我们把数据库中的数据,显示到屏幕上,或是传递给View时,这个过程其实是对data 做了一次copy。而且只要不是通过引用或是指针这些方式,通过值传递的方式都是对data做了一次copy。而这个copy的过程,非常类似Cache。
通常建立一个Cache会遇到2种问题。
Cache情况A: 与original Data 数据不一致,没有及时更新Cache情况B: 重复建立Cache
让我们用这个思路来看我们的解决方案
这是一个非常典型的Cache情况B。数据库的数据并没有变化,但我们却多次重复计算cache
页面之间的关系可以用下面来描述
这里我们隐隐能够感觉到问题,A的数据变化依赖于2个地方。不急,再往后看
事情变得更糟了
和解决方法A类似,同样的重复计算Cache问题。
实际上问题还会更糟
现在还是一个简单的Model,如果project变得很大,那么就会变成这个样子
每一个X都可能是一个Bug。
我们似乎已经找到问题了
中,把这个图叫做information flow。我们的直觉会告诉我们,这个信息的传递,应该是自上而下的树或是森林,而且最好是一个层次平衡结构,要清晰,每一个位置都有相对于的职责。那我们就需要制定一个规则。
在想这个规则之前,如果把上面的图背后的数据忘记,我们感觉这很类似内存模型。当然内存模型会比较复杂。但是我们可以借鉴很多”内存管理中的规则”,比如谁创建,谁销毁。同样,在我们的information flow中,我们希望谁创建Cache,谁更新Cache变化
DAO的数据库似乎很难做这件事情,我们引入了一个新的元素dataSource(当然他本身又是DAO的一个Cache)。其中A、B、C3个都会显示数据,那么他们应该在一个层级,其中B、C会修改数据,他们会把这个数据返回给dataSource,而通过dataSource来把这个变化通知到A、B、C。
这样带来的好处很明显,我们再添加一个D,也不会对其他地方的数据产生任何影响,我们的Unit Test、Mock也更加好写。
我们之前的思路错在哪里呢?
从局部来看,我们之前的思路都没有任何问题,但是整体来看却把问题隐藏化。关键的问题是在于没有找到Truth,找到问题真正的地方。而找到真正的地方,需要我们在大脑中有一个清晰的information flow或是data
flow。了解之间元素的相互关系,才能建立一个个的层。才能坐到真正的解耦,解耦并不是仅仅一个个的Manager,更重要的是建立一套清晰的flow机制,或是消息机制,如果没有一套flow,中间引入的各种各样的方法,即便使用了各种设计模式,整个software
依然是深度耦合。
这个APP看上去交互非常复杂
上面的model,有些同学还可能觉得这是交互上面的问题,这个交互看上去非常的复杂,不是一个好设计。
我这里列举一个实际的例子:
A页面要创建动画,动画背后包括很多数据,这些数据会在B,C甚至更多的页面,或是后台被修改。动画本身实际上体现在View,而这些view可能不仅仅在A中有,B,C可能也会有部分的View。
单例怎么样
当然我们可以用单例的法子。单例是个魔鬼,被很多滥用,这个场景用单例,其实仅仅是把全局变量合理的封装在了单例下,因为这份数据,并没有任何理由要一定是一份copy。
在了解这个概念后,再看一些server的架构,规则时,也会更容易理解这些层之间的关系。包括
为什么要规定那些层之间,不能相互调用,不能有静态方法。一个层之间的model,不能有重叠功能,不能连表查询。在哪个层才能调用另一个服务,而调用这个服务还必须要通过统一的接口
software architecture 涵盖的东西非常多。这篇只是一个引子,介绍了设计之前的准备工作。但是在实际过程中,我们的模型可能要比我这里写的还要复杂很多。下一篇会介绍一种策略用来处理更加复杂模型的情况。
最后附上一个完整功能的
Posted by studentdeng Aug 29th, 2014 ,
本文已收录于以下专栏:
相关文章推荐
最近看了一些有关server的东西,一些很简单的东西,不外乎是一些文档规范,另外结合最近看的wwdc的一些video,觉得对软件架构(software architecture)认识又清楚了一些,这里...
上一篇《iOS APP 架构漫谈(一)》简单介绍了information flow的概念。这篇文章简单介绍另一个在编程中非常重要的思想或工具——状态机(State machine)。 对大多数计算机专...
iOS APP 架构漫谈(二)
10:02 编辑:pockry 分类:iOS开发 来源:不会开机的男孩
上一篇《iOS APP 架构漫谈(一)》简单介绍了...
第一种:由导航栏来控制引导页面,登陆界面,和UITabBar
原文链接:/art/175230.html
HTTP/2,是HTTP协定发布后的首个更新,于日被批准。它采取了一系列优化技术来总体晋升HTTP协...
“ HTTP/2,是HTTP协议发布后的首个更新,于日被批准。它采用了一系列优化技术来整体提升HTTP协议的传输性能,如异步连接复用、头压缩等等,可谓是当前互联网应用开发中,网络层次...
刚开始觉得APP不能转让,但是当上传完APP之后发现有个功能在页面下面有一个转让选择:有了这个我们就可以轻松的转让APP了,这里选择填写要转让给谁,具体信息可以查看开发者账户里面的Account信息。...
他的最新文章
讲师:汪剑
讲师:刘道宽
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)

我要回帖

更多关于 sql优化常用面试题 的文章

 

随机推荐