|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
用代码控制view意思他们玩的都是高科技IB都是垃圾很多人也纠结到底用不用IB的确很多时候IB灵活度不行但是不需要灵活度的时候还不用IB那不是装X吗要是没人用苹果还开发IB干嘛早去掉了IB在很多时候节省很多工作量UINavigationController再说说NavigationController刚接触开发的时候不明白View和View之间怎么切换的本文次要切磋一些经常使用多义务的最好理论。包含CoreData的多线程会见,UI的并行绘制,异步收集哀求和一些在运转态内存吃紧的情形下处置年夜文件的计划等。
实在编写异步处置的程序有良多坑!以是,本文所触及的样例都只管接纳简便直不雅的处置体例。由于越是复杂的逻辑布局,越能彰显代码的头绪明晰,越易于了解。打个例如,假如在程序中利用多条理的嵌套回调,基础上这个它会有很年夜的重构空间。
OperationQueuesvs.GrandCentralDispatch
今朝,在iOS和OSX中,SDK次要供应了两类多义务处置的API:operationqueues和GrandCentralDispatch(GCD)。个中GCD是基于C的加倍底层的API,而operationqueues被广泛以为是基于GCD而封装的面向对象(objective-c)的多义务处置API。关于并发处置API层面的对照,有良多相干的文章,假如感乐趣能够自行浏览。
比拟于GCD,operationqueues的长处是:供应了一些十分好用的便利处置。个中最主要的一个就是能够作废在义务处置行列中的义务(稍后举例)。别的operationqueues在处置义务之间的依附干系方面也加倍简单。而GCD的专长是:能够会见和操纵那些operationqueues所不克不及利用的低层函数。概况参考低层并发处置API相干文章。
延长浏览:
- StackOverflow:NSOperationvs.GrandCentralDispatch
- Blog:WhentouseNSOperationvs.GCD
CoreDataintheBackground
在动手CoreData的多线程处置之前,我们倡议先通读一下苹果的官方文档”ConcurrencywithCoreDataguide”。这个文档中排列了诸多划定规矩,好比:不要在分歧线程间间接传送managedobjects。注重这意味着线程间不仅不克不及对不属于本人的managedobject做修正操纵,乃至连读个中的属性都不成以。准确做法是经由过程传objectID和从其他线程的context信息中猎取object的体例来到达传送object的效果。实在只需遵守文档中的各类引导划定规矩,那末处置CoreData的并行编程成绩就简单多了。
Xcode供应了一种创立CoreData的模版,事情道理是经由过程主线程作为persistentstorecoordinator(耐久化和谐者)来操纵managedobjectcontext,进而完成对象的耐久化。固然这类体例很便利并基础合用惯例场景,但假如要操纵的数据对照复杂,那就十分有需要将CoreData的操纵分派到其他线程中往(注:年夜数据量的操纵大概会堵塞主线程,长工夫堵塞主线程用户体验很差而且有大概招致使用程序假逝世或溃散)。
样例:向CoreData中导进大批的数据:
1.为引进数据创立一个独自的operation
2.创立一个和mainobjectcontext不异persistentstorecoordinator的objectcontext
3.引进操纵的context保留完成后,关照mainmanagedobjectcontext往兼并数据。
在样例app中,要导进一年夜组柏林的运输线路数据。在导进的过程当中会展现进度条而且用户能够随时作废以后导进操纵。守候条上面再用一个tableview来展现今朝已导进的数据同时边导进边革新界面。样例接纳的数据签名CreativeCommonslicense,能够在此下载。利用公然尺度的GeneralTransitFeed格局。
接上去创立NSOperation的子类ImportOperation,经由过程复写main办法来处置一切的导进事情。再创立一个privatequeueconcurrency范例的自力的managedobjectcontext,这个context必要办理本人的queue,在其上的一切操纵必需利用performBlock大概performBlockAndWait来触发。这点相称主要,这是包管这些操纵能在准确的线程上实行的关头。
1
2
3
4
5
6
7
NSManagedObjectContext*context=[[NSManagedObjectContextalloc]initWithConcurrencyType:NSPrivateQueueConcurrencyType];
context.persistentStoreCoordinator=self.persistentStoreCoordinator;
context.undoManager=nil;
[self.contextperformBlockAndWait:^
{
[selfimport];
}];
注:在样例中复用了persistentstorecoordinator。一般情形下,必要初始化managedobjectcontexts而且指定其范例:如NSPrivateQueueConcurrencyType,NSMainQueueConcurrencyType大概NSConfinementConcurrencyType,个中NSConfinementConcurrencyType不倡议利用,由于它是给一些遗留的旧代码利用的。
导进前,按行迭代运输线路数据文件的内容,给每个能剖析的行数据创立一个managedobject:
1
2
3
4
5
6
7
8
9
10
[linesenumerateObjectsUsingBlock:
^(NSString*line,NSUIntegeridx,BOOL*shouldStop)
{
NSArray*components=[linecsvComponents];
if(components.count<5){
NSLog(@"couldntparse:%@",components);
return;
}
[StopimportCSVComponents:componentsintoContext:context];
}];
经由过程viewcontroller中来触发操纵:
1
2
3
ImportOperation*operation=[[ImportOperationalloc]
initWithStore:self.storefileName:fileName];
[self.operationQueueaddOperation:operation];
至此为止,多线程导进数据到CoreData部分已完成。接上去,是作废导进部分,十分复杂只必要在汇合的疾速列举block中加个判别便可:
1
2
3
4
if(self.isCancelled){
*shouldStop=YES;
return;
}
最初是增添进度条,在operation中创立一个progressCallback属性block。注重更新进度条必需在主线程中完成,不然会招致UIKit溃散。
1
2
3
4
5
6
7
operation.progressCallback=^(floatprogress)
{
[[NSOperationQueuemainQueue]addOperationWithBlock:^
{
self.progressIndicator.progress=progress;
}];
};
在疾速列举中加高低面这行往挪用进度条更新block:
1
self.progressCallback(idx/(float)count);
但是,假如你实行样例app就会发明统统都出格慢并且作废操纵也有迟滞。这是由于mainopertationqueue中塞满了要更新进度条的block。经由过程下降更新进度条的频度能够办理这个成绩,
比方以百分之一的节拍更新进度条:
1
2
3
4
5
NSIntegerprogressGranularity=lines.count/100;
if(idx%progressGranularity==0){
self.progressCallback(idx/(float)count);
}
UpdatingtheMainContext
我们样例app中的tableview前面挂接了一个专门在主线程上实行取数据义务的controller。如后面所述,在导进数据的过程当中tableview会同期展现数据。要告竣这个义务,在数据导进的过程当中,必要向maincontext收回播送,要在Store类的init办法中注册CoreData播送监听:
1
2
3
4
5
6
7
8
9
10
11
12
13
[[NSNotificationCenterdefaultCenter]
addObserverForName:NSManagedObjectContextDidSaveNotification
object:nil
queue:nil
usingBlock:^(NSNotification*note)
{
NSManagedObjectContext*moc=self.mainManagedObjectContext;
if(note.object!=moc)
[mocperformBlock:^(){
[mocmergeChangesFromContextDidSaveNotification:note];
}];
}];
}];
注:假如block在mainqueue中作为参数传送,该block就会在mainqueue中实行。运转样例,此时tableview是在导进停止后才会展现导进了局。也许那末几秒钟,用户的操纵会被堵塞失落。因而,必要经由过程批量操纵来办理这个成绩。由于但凡导进较年夜的数据,都应当接纳渐渐导进的体例,不然内存很快就会被耗光,效力会奇差。同时,渐进式的导进也会分离mainthread更新tableview的压力。
至于说公道的保留的次数基础上就得靠试。存得太频仍,弱点是重复操纵I/O。存得次数太少,使用会变得常常无呼应。经由屡次实验,我们以为本样例中存储250次对照符合。改善后,导进历程变得很光滑,更新了tableview,全部历程也没有堵塞maincontext太久。
其他考量
在导进文件的时分,样例代码将全部文件间接读进内存后转成一个String对象接着再对其分行。这类体例十分合适操纵那些小文件,但关于年夜文件应当接纳逐行懒加载的体例。StackOverflow上DaveDeLong供应了一段十分好的样例代码来完成逐行读取。本文的最初也会供应一个流体例读进文件的样例。
注:在app第一次运转时,也能够经由过程sqlite来替换将大批数据导进CoreData这个历程。sqlite能够放在bundle内,也能够从服务器下载大概静态天生。某些情形下,真机上利用sqlite的存储历程会十分快。
最初要提一下,比来关于childcontexts的争辩良多,其实不倡议在多线程中利用它。假如在非主线程中创立了一个context作为maincontext的childcontext,在这些非主线程中实行保留操纵仍是会堵塞主线程。反过去,如果将maincontext设置为其他非主线程context的childcontext,其效果与传统的创立两个有依附干系的contexts相似,仍是必要手动的将其他线程的context变更和maincontext做兼并。
现实证实,除非有更好的选择,不然设置一个persistentstorecoordinator和两个自力的contexts才是对CoreData多线程操纵的公道体例。
延长浏览:
- CoreDataProgrammingGuide:Efficientlyimportingdata
- CoreDataProgrammingGuide:ConcurrencywithCoreData
- StackOverflow:RulesforworkingwithCoreData
- WWDC2012Video:CoreDataBestPractices
- Book:CoreDatabyMarcusZarra
UICodeintheBackground
起首夸大一点:UIKit只在主线程上实行。换句话说,为了不堵塞UI,那些和UIKit不相干的可是却十分耗时的义务最好放到其他线程上实行。别的也不克不及自觉的将义务分到其他线程行列中往,真正必要被优化的的是那些瓶颈义务。
自力的、耗时的操纵最合适放在operationqueue中:
<p>1
2
3
4
5
6
7
8
__weakidweakSelf=self;
<p> |
|