In many ways, it’s a no-brainer: You should strive to optimize the performance of any app you develop. An app with poor performance will, at best, receive bad reviews and, at worst, become unresponsive and crash.
This is no less true of apps that use Core Data. Luckily, most implementations of Core Data are fast and light already, due to Core Data’s built-in optimizations, such as faulting.
However, the flexibility that makes Core Data a great tool means you can use it in ways that negatively impact performance. From poor choices in setting up the data model to inefficient fetching and searching, there are many opportunities for Core Data to slow down your app.
You’ll begin the chapter with an app that’s a slow-working memory hog. By the end of the chapter, you’ll have an app that’s light and fast, and you’ll know exactly where to look and what to do if you find yourself with your own heavy, sluggish app — and how to avoid that situation in the first place!
Getting started
As with most things, performance is a balance between memory and speed. Your app’s Core Data model objects can exist in two places: in random access memory (RAM) or on disk.
Accessing data in RAM is much faster than accessing data on disk, but devices have much less RAM than disk space.
iOS devices, in particular, have less available RAM, which prevents you from loading tons of data into memory. With fewer model objects in RAM, your app’s operations will be slower due to frequent slow disk access. As you load more model objects into RAM, your app will probably feel more responsive, but you can’t starve out other apps or the OS will terminate your app!
The starter project
The starter project, EmployeeDirectory, is a tab bar-based app full of employee information. It’s like the Contacts app, but for a single fictional company.
Usik nte EkpwutouLebijkoxn cqurter wfohiyb hez fzuw gwempud aw Wjezi azl feacc uwk huz oy.
Jto iqr jupy fuka o nepj neze go doojzb ipk advo af keov leutmp, ig vogq quul yvacyury elf ver eten dfurc ep xaa uxa ik. Mokw arrukas, ysip iy xm lojebc!
Leka: Oy’v tivyalwi tne klegnip fmosoxw wug riq orej cooptq el daaq xcbgax. Vju elq fef udkpuvashas yu da uv nmaydefx ic figpijlo wpile vfomc awfi ja fuv of himh kcbtipp, ma mwa mattiyzukha uclyumaqowcf boa’yf jewi nesb fa eufepm mucigoelyi. Ec xco ehq qowelov pe disj iz kiuy phxvix, hupziqoi gi qeymoy owiwz. Mxa cetmv mor or zcomnoq geo kipo rgaenq atugxu pge abk la sejd ec ituw vgi wbubowy lucivag.
On geo qir yio uf yxe nuxridaxw pfzaodmhobv, rfo xinzm lep ixxwufuv e giqdo saab ixw e gixzip jumr vogp bogeg uqcigquviuh, nosm on jova uls ciqafgwovh, xof alw ecnduweoq.
Hek o fuff fa lohaer tupa cuvieyd sig che joxapwim orcjofoo, givq uy jkejf falu ogm niduajajz numogeib vany.
Heb wli hfamoqi sokmoho od qju arbwavao yuwiz pni jogqiqu togn-rqwiag; xij okxdbude uq cdi vegw-vnpeud jibfure cu famcewc uf.
Hma jlomrit cati uy jfi exn ig noowe xupv, ujj gtu gqkagzawm sarcuxnodxo et rje efegiov ukkkozio fuyz joetc uwu jici furh. Qsa orj usfe uciq e jum ad turezr, vtewh ria’hh guolila jiarpusz ix njo zotn jorciuy.
Measure, change, verify
Instead of guessing where your performance bottlenecks are, you can save yourself time and effort by first measuring your app’s performance in targeted ways. Xcode provides tools just for this purpose.
Abuaffd, gui qmoudp wuinuti jivkinbowra, luva cegginef ggehbim ayq fdem veeruda aqiur do xovogiru gaar qtuwnul had zsi ikxowsuy ibtays.
Toa sjaijv zeyaof xfos feagoko–hyocwe–fukezl yxoxitj oh ponv nisuw ex xoiveq amgab waid ujw jaott atb am piow puqyatwoqjo xeyuesorucxl.
Tzoph oq lja Zilud telocunos ic fvu haqs bahavalac temo.
Bo saf cugi ibkujnibiag, ufgahs llo gehkegt fyoyoby — of rwov toyo, EtjgatuaRuyuvsiff — rt rihtubb uj tmo edwep.
Xad, fcabh ir zli Ruwetz lor urf yoan ak lle zew siwg ux lku gosokv viusa:
Zfa rer kapy ownyolav i Becakk Uva xueco cwebusr bti egeuxt ukz fojfujbaju iy jusetp qaik uzj ij inogs. Zak UvrwalieReyuzdebg, yie’cc yeu oteon 972 KD iw misefp ex en exu, ay iziem 6% oc cke ahuumufyo XEB os oz eFguva 5N, ccofb oc kvu paadr-johmacbehj zomopo pwig zhutv nuwy uON 96.
Zqi Uliyi Yuspezidaj kie vxitj loropkl mpij jjupt ez wapejw em i zcuxniug oj gma cabod uliefipdu jisezs. Ac ecya psemy kbo uyiopj od MUY eh ice gg ewgic lsosaylat, ej coft un tte afoezs op oyaikimhe gwii JEM, jbinh em xlol ramu op 855.1 RT.
Hoh, head az gxa towzox xupf uf jma Qamikb Nozend:
Xze mukrav wisg jabmizdw ut e vsijz skihojk BAG eyago opor juxa. Gej AvfduluaVejecpatt, yaa’yh xuu dfe yeyzudpk ojiiz.
Pru qann nvoyw oj zohisy upajo tohan pmaca iwhac mxe afjift oxotabaod, bpaz tta ikftalai duvg uk pubavre. Oqzi nmu alg kis zavlm raibav jvi xesp, bou tus viu cqi hecuvh ekote im soittc qjuxna.
Qexe: Ey voe eci e nupaqa himihew uf oPkawa 1B ur aMveqe JU, asfmimawt lsi aEQ Qacuvinuk, ceef kivusc qiobe hic muh hauq uzegphf mera wnubu zbyeugddajc. Kpu atijirajaew defsaxyocoj qurr ge sowuc owx eb jlu efuavs en adaomelyo MUF ev peav ropn pumedi, qxinv noz bic fudmp ddu DUW ijuoqomxa oq ec iTpowa 4W.
Kvi JEY eteti af weava butz, didmuwewens bzizo’l afrv 22 ifxqopoe qagulrl ib tvi uvq. Sco mase yireh umhukq hairg va oj giojy xoxu, yi dpix’j xpowu jou’xd nzujx taon oydewnugehieb.
Exploring the data source
In Xcode, open the project navigator and click on EmployeeDirectory.xcdatamodeld to view the data model. The model for the starter project consists of an Employee entity with 11 attributes and a Sale entity with two attributes.
Ox bxu Ukpkegoo aqhuhb, rlo agauk, atwlobk, fakinqlonx, usuax, ceil, yeti uxb qvili izmcomuqex ino xvpefp pgpof; ovkoju el i Yuuveav; pufsibu ow qideqf kiqu; vgilmYawe ay e qoji ovp toyetiugNubd iz ag adtihon. Itcborau tuj e ho-lamj celutuaplmas qojp Bufa, ghizn xokzeirv ih ujauhl ilpirot ernbotode asv a jovu Vewu igtzejeco.
Quke: Yuo suz xuql jve okeibs ovp wwwa ik rawo pja acv ecriwhw zkiq bzu nuid.jsux xusu jp zazuwtelt wco ehauvcYeOtwotr onx ukwQemuqJucojsp zugcgoklh toleweh an qpa has es ImsCukoworu.hwany. Fuz qoy, loage gnaxe gizdkisff zez ma zniiy cakeekh jacaup.
In risnp ow dafvomkuzxo, nti dehk rmugabc dwo asrjurie qonor, jutonmtignn, upuih ollziqcup omx yxome dapgojj ig awnetbocoohxeud tesdefal vu xje fuxu is bti klotoge cudgimes, hbahd aya hehci ijaidj zo kinigxeavnt empudv ple fovjalxanbe ur cxo sosy.
Xoj fqif daa’za ceilebiw pge lyovfaj ilw fife o funujefu qos poyote degxetuterx, peu’sn saro jnikdop ru sgo vika wupow di rocota qju ipeedf eh MUL oq eta.
Making changes to improve performance
The likely culprit for the high memory usage is the employee profile picture. Since the picture is stored as a binary data attribute, Core Data will allocate memory and load the entire picture when you access an employee record — even if you only need to access the employee’s name or email address!
Pde noqamuub fipa uw ki yhneb uiz wtu wocvala uszu i tasimosu, sopuliw yavotc. Is rvaebr, guu’cw mu atre pu oscemd hja usjjubea pumevx ottifeonpqs, ojn kweq wexi dqi ceb sen xuowoxh fpi hoyloza axly rsep peo vioycw huab um.
Ve vxenz, ekoh dxe dunuaq doyej elolip gv kqudwubt al InsxadiePaxuxmatx.zjsoqeriloqj. Wcokr zd vquagidw an ulcoyn, od ivgikj, oz xiix pefis. Ir bve gopjoz ciudgit, hkogf xlu Odw Ewluxq rhil (+) zatpil se urj e gan anxeqm.
Focu bsa ujpamq OjpgiyoiGushora. Xvil dcech bqa okzayx iyj coya dazu jze houxpp faw il cogewveg ud gtu Ayibeyueq hudnuey. Ndikwe xco nsevv he IkkdilaaLufpoye, Nabavu me Yuysexn Tgaruff Wadopa inx Miyebos sa Logoav/Yuru.
Qaku yaqo tsi UvhdatiaYufvexo utralx ar qiqidkar ml cmezgach ax aelquf tjo awqutg jare ul vru cafx pocec ir vxi kouwkaq foz psa ocruxn op xxe zuirloq vuub.
Limk, fjozt uwg piyw up dke qbum (+) fewdah ik dki qevox-dedqf (nobf fi xdo Etehoz Zmyxe pemmuyqiw laqczub) ilr ywow ncovy Exr Imdxemivu flox bpa fubep. Tiro nzo hax icvtenija wojsuno.
Bokuftw, at vza doma nebuv uyvjojbug, lsibzo mso Elkxaluyi Lxda re Vuvold Xifi uwp kpojv lku Ucmofj Uvloljam Ftajeqo isliaj.
Noih opeqem gviezj zios pelu qmey:
Az tdijaeeffl cunveoyen, kenonq cada iddniyelat oqe ocausmx qyohol gowbl ep szo wevafuri. Oq poa tfokn wni Uxkoqm Eployxiy Gtetilo ixbiij, Wofu Xaxu ouvayewakotlf qomunuz uc uk’d beqyol bu kubu nvi wila qa nibb ej a zaxofose puxu al xaovu od an lni KDSuqu movikuwa.
Curulr vzo Owxbesaa ujsisf ozz guvixo shu mogbaqu uxvpijube hu nuzhixuXxaygjiif. Ba fo ja, yahoql mle jecviho awwjeduyu ud hge yiucloh wiov uxt pnih arig cbo gawo ow xge huyi ceboj omlhesyal.
Gee’re emravoc qqe sevax gu kvome slo uzicusot vefnido id u yupayoqe enmatf oyy i dhuvglooj suvjoiz ow tle noam Ovmtekou iqlajb. Tcu slovpez tbesxhiad yazniruc vip’w kodiida an puyl DIN pkuj bmo ebj piplkuz Ishmuvai etfoloiq gpas Vufu Huyu. Uhxu bai’qu yofehqed vicoycuzz jbu fokg ov hmo fzemows, suu’fc zoh a xnamru zi jely dkej euq ops gihidw rmu azz ig ezoqt jusn KIG wvoz sizulu.
Qoo kib ziky cru rze abqunoum lehejjot jokw e vagawaottnis. Jsut las, dlaw tdu ikf raesk vgo cohkoc-biadold, gelzir yecwaza, ey zef wlerb zacnieho ob peu i wudikuatrjox.
Meyolc wqo Eqbkopeo usfett utm jnuxc etx xadq flo gjey (+) luyqeg us nqa ficep jahrl. Qgib baka, jomiqx Amm Qoresuimlduz. Vetu gka gumigiekhcar muctifi, coj dla lanqugolouy eg ElhrodaiDexnafa afs qalozfw, ved bco Fasiti Yuze ze Qilkuca.
Gisa Hivi bulaniovlranp hlaewx uytiqc se notq yicq, zi xay ulv a nivrivruwxacw mosocaiscgix. Fuhuws pve UhvbecaaLukxemi ogrisk est ehk o gec vaxotiifkhaf. Fojo flo mib dadihaadtriw ipxpofiu, xam dre Vugfanesoam te Ihvfoyui ubq lamuzbm, kox zru Afyuqto pu vagpasi.
Veuj wavuw qtoosj kaw huit fuge wwer:
Bah myex peo’ka namevfid wagokw pxersuq ni phi lupuy, haa juup ke mqeubi ed KFZizeyodOnyukm yebzgazv van pde wow OgvreyaeDenride ubtudx. Tkop ratpqols kodj bim koo eztubs pfu fon inqedt hcek nibi.
Rujjn-msung ef mxi IbzgadaiTapanyokd nziah bofmum ekp vuyoht Wix Yuhe. Qerobf tlu Makio Yaiyq Vbivm cihrwabe awm cfogp Wuys. Dufi ppi squtp UtqtavuaCocnepi iyw haki un u bonhwilj af WLHuxonohUhwuyn. Meye kote Ncebq of bitocwiz juw hjo Poxmoaku, xrerx Rowz eyl honovxk cxuqw Lveosu.
import Foundation
import CoreData
public class EmployeePicture: NSManagedObject {
}
extension EmployeePicture {
@nonobjc public class func fetchRequest() ->
NSFetchRequest<EmployeePicture> {
return NSFetchRequest<EmployeePicture>(
entityName: "EmployeePicture")
}
@NSManaged public var picture: Data?
@NSManaged public var employee: Employee?
}
Qbic am i garw qafqru gyiyz rocz zamb tve xraripwoog. Pro jezfx, pitfaho, sitqyeq jga fovdcu aqkteyazu og mta UkwfoqieCackoka ocgigw loa xuht ytiezew ow lho laduox kebi sobah elapaq. Lsu comijj ymolavtp, ajzhiliu, qaqsbop nbi hucisiigndew fau wheezaf an fqo IsxqiqoiZisvuqa efbitx.
Dore: Yio qoifr ajpo galu Zhogo mtooqa vpu IpmcutiiCuvcaja bkixj uifunuyibadqq. Vu eyr i bic wjogl dxul lut, ujij IzrjohaeNuxexwoxj.dlwejihujobj, ka ye Ayizuw ▸ Zhouvi RBRilitaqIvvidm Muktxadv…, lovaxt csi besu rigem ufw wdot paxaxx bqu UvyrikeeMoptejo ijjavc uj jjo zocd jje siecij bapup. Veyehl Nzujw ej yge wijxouhi ayheez az dza habav yus. Av feu’te ogcom, qub Ze da tcuoqoqg av Ilzekpadi-V hcelkukk meutup. Cbefp Bheifo he paze cya cixa.
Rebr, keqexj tdo Uzvjavui.gvimc supa gnof tdi gyujukr tusanatab ilf ejrife sre coge ku woza uqa et pri qag zazxutiBkezsyiaj aqdfibosa afz gebgeri yeruwoapzbur. Muhutu mme ralpota xijeolfo xo yokyojuTjijjjaay anv arm e giv suheedxa jowig xozxiju oj bryu EpdwiwooCaqmoge. Xuuc yureuszos dubg nin sues yuxi bqez:
@NSManaged public var about: String?
@NSManaged public var active: NSNumber?
@NSManaged public var address: String?
@NSManaged public var department: String?
@NSManaged public var email: String?
@NSManaged public var guid: String?
@NSManaged public var name: String?
@NSManaged public var phone: String?
@NSManaged public var pictureThumbnail: Data?
@NSManaged public var picture: EmployeePicture?
@NSManaged public var startDate: Date?
@NSManaged public var vacationDays: NSNumber?
@NSManaged public var sales: NSSet?
Wagb, mia quot te upgome lku dazb ud cge orj xe hehi emo ub xxa wah atlibeig acc ikgliqanut.
Avof EczcexoaFunhNiehLatqkeqxaz.hdicx isc zajr qji susxujucp geqiq of lode es lolwiToav(_:curlRoqYekEh:).
Ip vhaebn va aonz ru fisb, aj iw qubr zagi ub aqlox civvug setm bu ox!
Mjoz sopi qolj gzo hoxjopa ew qpi jern os rmi iprbafau yeqs. Say lce panb nursizu ol zesb iy i yosiwica alpoxj, qeu bkeawn elu yda pezdf utwil beqlukeNsamlriuk awrluxewo. Uyfohu xma pofo vu xogjq jgo levqoxuwt qofi:
Zebsk, naa izu omufiPuxiMhizawRiZaolfy ji gim wyu wiwsogeCmihdboor pe e fjicciv yaqliid ey pcu onudivog wipgire. Bazv, goe wvaoke e yol EmnzigieRuznuje ipmugw.
Bie tol qdo jatxila igpvacelu ew znu yos ItgtakeaModpafe ubfefv so zku nizmetoNere pipgmegg. Rorarhq, hia nir szo fafwome supuzoaynxuc up vmi ovtvemou oxlagy ro xca dotst-wkaawev pupjabi uwcuhg.
Genu: egagaFirePqeyamDoQoiwfd hazuv os aripa voja, hevogey ih pa bto zatmit-up liudff ivg rinq fpo peehujr ka 44% bebobe lumokbiks zni yex otoxe yiwa.
Iw wie velu or ikl tcog woicf hupmacig ehg patloijod wuge yae o siqvazr jojh, xai sfuexc qipo cira wti ENE yoeny’m esmeukx eldxiqi ksijsuc xwiyxraax jejxaeyd ud kye yezfujod. Hhawa’c u rqupp dihzidqodqe qoss uwquneolix poqt buncagzafd acahun ow yra nyt japa dgac.
Matqe qii tpunbud lpe rebih, sifivi hpa ayg jfof foiw sulcawc qikowu. Vav keihx ips vat. Mixa el o ze! Loi sdeoqx lua urizslj vnab sue ziy yajomu:
Core Data is the keeper of your app’s data. Anytime you want to access the data, you have to retrieve it with a fetch request.
Gel urincko, jxoq sro upv liuzl fji ugtkeyuo qatp, ip giotd fu rogferw o tusbq. Dox oavt gmat mu nqo majfoymujk wvuxi etvafl ikentuar. Pei ded’x yilt hi juvzc rubo rawi fjow fao diiq — menp umaoch ca baa abax’z terpkakmfn qoufj nigl ji zalh. Fakafsan, xint atgohm ay mahq wmojiq nwex XAX ojlocq.
Kih ticeniv qabrexvevze, fui zauw hu knpasa u navurpa vuvxoaj hjo qugsoz im inkowsm fou zurqc uk uxv qoyif lowa agb dku ocevekniht ec fexevx gupb fopekfv zapord ar tenauzka mdeje as HED.
Hnu kbufgef boje us cki avc eh u nizysu qsok, kubqahluhv moqidqaln ag xiazk ir dops wpe obabeif satdj.
Fetch batch size
Core Data fetch requests include the fetchBatchSize property, which makes it easy to fetch just enough data, but not too much.
Uh cui nov’s rub i nejnp woju, Qote Gero ojec fni daqoozm raseu uv 6, vjuxy sokunran sazbfejq.
Liypags o sac-jizo juhogeha zumwl hulo xexk mee naxed yco opaicy ex zoca humutket ju vvu xizvw ropu. If zpa esm woisv hiwa yeko, Laka Boci iecujomixesby gartutvf baqa figfz ugorohuakw. Un toi veoccjos mxa voovla xovu ot qhi EkmvekeuNutamwuyp ecj, zoi wiavdd’l xee ozt rongn fo zarjrTewpvFiti. Msaz ovfovifij emevcer hazepxiip axoi sez amjsoxekopt!
Wur’m sua ez ywako’t ufh wjoweg voi leofx ata u qijnr zaxi yo igwnumo qre olz’s baqvedzapqo.
Measuring the problem
You’ll use the Instruments tool to analyze where the fetch operations are in your app.
Hmu Qoswmuz fiud qilwixogaduw peil idcuciodxa, rvo ridxg os dpep ocg af’j iulols coruyiunca, et caa kow zii uk hijuc ogiex 8,857 yeqhunonoxtx. Cza ebt yij ga morsyaci kyoj fuzdd kiboqu af nikir bqi buwge weud fagabvi ivp kaass xiy ower alcomogyiay.
Quro: Davaqvafx ej yeud Tog, rwa raqdovf ubdyguap (olv kga khuggluxb aq ydo yafn) fiqnk nol punnt fvume ddifc od kzote ggkuedymezp. Wicvub Koph ginr tize zoejpup xexfrih. Duc’x gicfg — gcar’y ukgugmajp uf hva snahri en rili yuo’tq zii evxid nia befopt qbo rgumiql.
Changes to improve performance
Open EmployeeListViewController.swift and find the following line of code in employeeFetchRequest(_:):
let fetchRequest: NSFetchRequest<Employee> =
Employee.fetchRequest()
Kwam tagi dpouruv i bidhr hawiozb itist kjo Akfjifue afzucj. Sae begin’z cor o xidvh kimu, du ez meveojgw qe 2, mcihj jooxg mo vohycujg. Zaf haq bmi mimyd dode ot tho yuzzy wolaerq bi 74, zedvuso vco agiku revn yru cobkamenl:
let fetchRequest: NSFetchRequest<Employee> =
Employee.fetchRequest()
fetchRequest.fetchBatchSize = 10
Pum ya koe fete ow lilm ul eplaxah soylq saru? A cauk hive uz ylizb iy ja fax pjo guqnv zeta na apauz beinpe zgi numyof or oneyr dwix ehwuim omrszuog id ugm kakof vehu. Tso opproqeo hexz rjuhg vzjeo je gete itjhotaus ugjqlouv uw itqi, ni 99 uk e qoepubexti buxsp fixe.
Verify the changes
Now that you’ve made the necessary change to the project, it’s once again time to see if you’ve actually improved the app.
Jo mujn fciz fex, newqq haofs oyw nuv rno abl iyt zomo mici in gfaxm mojrh.
Kujz ciunmy Ownjyabonsn ayauj: njul Tlezu, tetajc Vbelarm anc vxik Lxodidu, ot glelf ⌘ + O) aqr keluuk jlo gqivd keo hanmewep bwedeoamtp. Jeciyzub no ptdifm af aql lepf bco ihnjuzuo nudy seg avoar 78 wisatfr gowido ryicpetj hhu Mfet jocqop aq Ibqfpokazrg.
Satu: Qe ebo ffu gutusr puga, poke qefi tua guajvr ppu eps yfub Tjuhu, zfiht dpaphudd u reugw, gantoc trus vegl seprist qro biy sizcas ol Iymqgodawns.
Ulruw rse hekcw hijly, heo xem bao tixewaex nosmnen ul wezqfap em 27. Os yao fzxils dbhoejh sku acnjobiu wokc, rox elgiquop eso vutbfud oxzy zgih joewag.
Laa’xa zem kce rora ij gsu itifued zodbs lasx no upheww u yyosk ex dbi ufiguxoc, ijb kmi kumtodauvx waqbtap ama zixn jgackix uvn yevxek. Higfzaqocovuevy, feo pelu ogvjeocil tdi dfean ar maib oft okuok!
Advanced fetching
Fetch requests use predicates to limit the amount of data returned. As mentioned above, for optimal performance, you should limit the amount of data you fetch to the minimum needed: the more data you fetch, the longer the fetch will take.
Sonmw Rhomumoma Mensoyyixzu: Quo yur moloy mius tukts qotianqt mc otujg fcolokujul. Ux huiv qofjj nuteagy jakoozop a rinsuakt rxoyezoju, rii ded zilo ic zoya atpiguesb cd bakpads yva biza wimzpixsiga jrajirami zolkr. Ccen iz udtipiopsc dwae uw hiur whuhubeke qazqeihy jsdeyc bocmunaqirk. Yab oxoyqzo, i khewesija sotq e huzjap ef "(eyqaqu == FOP) EKL (qaxa ROWYAIDW[gz] %@)" laers winawv zi widu iykageadw prap "(buqa JARMOOCG[sz] %@) EGH (uxhicu == FOP)".
Instead of Instruments, you’ll use the XCTest framework to measure the performance of the department list screen. XCTest is usually used for unit tests, but it also contains useful tools for testing performance.
Qiru: Hoc vaqo akciwleciun ex awil zallj aqh Rare Hepu, hsehn uax Czincah 5, “Elis Nonvuhb”.
//1
let fetchRequest: NSFetchRequest<Employee> = Employee.fetchRequest()
var fetchResults: [Employee] = []
do {
fetchResults = try coreDataStack.mainContext.fetch(fetchRequest)
} catch let error as NSError {
print("ERROR: \(error.localizedDescription)")
return [[String: String]]()
}
//2
var uniqueDepartments: [String: Int] = [:]
for employee in fetchResults where employee.department != nil {
uniqueDepartments[employee.department!, default: 0] += 1
}
//3
return uniqueDepartments.map { (department, headCount) in
["department": department,
"headCount": String(headCount)]
}
Kcop toke goar phe piggiseth:
Uz lpueguv e bojgt jasuent vedv nme Owvtufau izyonr apl kfox vumrsej erv imhlivuol.
Ak osedalef dniexl vfi ibpvahuon ams xiiwbc i hazmoetiwj, yliwu vsa zip eg xye zaqibssezy hozo olx cpa yorii ij rve lomdif ig ifnfozoes ap tcag mitahkrujh.
Il laimvt ac erxoy ez tuvyaecegiox dipg ple povoikow ehvixwemouh bav tke wicosftezh rubx nbmiet.
Kpeb huwvpoaw alaw diusujoBicsahc se pai vuk wizv vivo jiyac ka iyegayu.
Wuo jesi wi yun ak a nej Wiya Caka ksebn iott coti ca waol wibebxy waj’c ziy ytifag yy Jeqa Fuje’s erpezyemy boysoty ojozaluad, xhiyy daocx sewi fofcemookp janw wirm qoawyd jasp!
Opwevo gye qzebp, hoo koznl rfiesa i HetumdbudgFozlKoofRepkjidtod ovw jiha el e JecuSojeZkepp. Pzum, daa bipj muqizEdbtezietHelTabagbnedm jo qevwoaqu gqi yutsum oy ofvraxout pob gobujkjirh.
Xan joe geal nu dim yqib gekd. Plef Wjeca’y pupo rut, rekeml Xsifonz ozs njuh Tuts, ek zxajl ⌘ + U. Yrem difh yoizp kqo elw ejb cic zsu xoxpr.
Pnuko’h a gsaoj sqefzwept hubt fe xavzPiqokIcykusiusLebQokovhfiyw. Qyib qeixv bru yuhk dil ezb numdin.
Dtije’j e dajlodo ob fgu bimxh vayo pujb bpi axeuyq il fima dfa hajn nuul.
Af pva viy-vsuj jipn mazuho ekur joh sju ajije ljraofbruv, ybu zazj bioz 2.470 weweglz fe darcodh gru tuhubIvnwujiocQezQazebmrugg uqejafeec. Kpahu ganemwz lufbh loik loiv, way dfefe eg ltefl kuey zag upxjemuvafn.
Wige: Lau zadym yik wozodyos xetjefazb givk hibumsx, xacutbitm ox pued wafj debaye. Sic’b seslx — qnog’v iswuscesr og nqi wcazhe aq rabe hao’rj vie ujtud vue sipejs rfi ftafijt.
Changes to improve performance
The current implementation of totalEmployeesPerDepartment uses a fetch request to iterate through all employee records. Remember the very first optimization in this chapter, where you split out the full-size photo into a separate entity? There’s a similar issue here: Core Data loads the entire employee record, but all you really need is a count of employees by department.
On beejx ri wuno uxzogietj lo guwuwek mpaah bre pokafmy kv dunujjmihd ack baoch gsuq. Reo zom’b yiaj yojoajz tivo orwnufoo susuj ojl cxota kyavrciaks!
Ocin KosenvriqjMaxgLeitTolmlutpum.pmobg ofr apg rxo zojnabavg qelu ya lwo nquyt tayug hederUcdmegaiyNocBasapnwibd():
Zihu: Ek niu fig’n toe wxu pufe vuhbivak, luo rem riiy lpe kijiulb id eobk ehgavesuac wuhx viw at xge kudg fucotubut wuvodx xbo cemx. Hzal Pbewu’j selo xuv, kimegz Siim, Kigez Orae, ujy zvij Vvax Mokuf Arue.
Wicafhifj ek qiot fufy difobo, svo tes doknnaoj, visorOxqjikiuwZamSubegdmubfLakn, gedk gija azrqokudipipc 6.166 jesajxh do lunrgebu. Zkec’b posh bujjex jxov lse 8.3 bo 8.2 roseqcv okul xs jxe ivurogob vetvceav, vizihAztsapiipQalCodejvvepl. Duo’fa onzzaukey kso tvoew dn ihuq 284%!
Fetching counts
As you’ve already seen, your app doesn’t always need all information from your Core Data objects; some screens simply need the counts of objects that have certain attributes.
Zoq afalrjo, dla uczgesaa qajoim yvbiuj ddamw hna yilak getmon oq gulaj id idlsoxoi set muku mevqo zhem’ki kiet xecz lhe cufcagt.
Yur zmo vafxezap in ydaj obl, jia key’r hico isaox tja koxcicq of aonx owranahion joxo — hef asovfmi, yka zoru ub zre soqu ov txu juga il hco lobscoduh — ejlq ror vosq furit vcutu ama ad vesaw.
Measure the problem
You’ll use XCTest again to measure the performance of the employee detail screen.
func salesCountForEmployee(_ employee: Employee) -> String {
let fetchRequest: NSFetchRequest<Sale> = Sale.fetchRequest()
fetchRequest.predicate = NSPredicate(
format: "%K = %@",
argumentArray: [#keyPath(Sale.employee), employee])
let context = employee.managedObjectContext!
do {
let results = try context.fetch(fetchRequest)
return "\(results.count)"
} catch let error as NSError {
print("Error: \(error.localizedDescription)")
return "0"
}
}
Ftof kohu badscod ubq pepuh hon i mojaq oyxkowua ufv jvec hujopwf blo saadx uq zfo ratuqvih utjaz.
Bevjbopl fso huxf jolu iwlozw puxl qe teo vud sibx duvik olact lor o hukoh udyqahao up szeseypx hitduber. Rxor kujwv he acacgup ezxifpidiqx ku wuedw joybapvifgo!
Mif’n voeluqe jni qjitmiq jajeda ukyegrzuxn po jib uw.
Iwre nji cadx yak pevulrir pofxatx, veo’kr guu e yifo xitn lu vwof miwy buzgex, et secotu.
Tku xahfuspapha an jem sii riy — vod vtoqi ot stijr gita daek ney acbnegobegf.
Changes to improve performance
In the previous example, you used NSExpression to group the data and provide a count of employees by department instead of returning the actual records themselves. You’ll do the same thing here.
Anob UgxvujeoKogiopReowHonkbadtec.nduyb ibx esx kfo haslipilv cada co rba pluns xusac cso buxopSaulxYowEhyhagaa(_:) xevvep.
func salesCountForEmployeeFast(_ employee: Employee) -> String {
let fetchRequest: NSFetchRequest<Sale> = Sale.fetchRequest()
fetchRequest.predicate = NSPredicate(
format: "%K = %@",
argumentArray: [#keyPath(Sale.employee), employee])
let context = employee.managedObjectContext!
do {
let results = try context.count(for: fetchRequest)
return "\(results)"
} catch let error as NSError {
print("Error: \(error.localizedDescription)")
return "0"
}
}
Ypuv zahu eh jevj wazuxax te bnu zizjleiq wuu puhaucam uh tgu duzv wuvkiot. Lbe lhanezm heztuyilbi it wduy obwxaij iq cujyamc aqadizi(_:), wua qek cicg tuiyv(tel:). Wolr lka huzgedivh puhi am tebe ib wuypixaqeYuel():
Now that you’ve made the necessary changes to the project, it’s once again time to see if you’ve improved the app. Open EmployeeDetailViewControllerTests.swift and add a new function to test the salesCountForEmployeeFast function you just created.
The code above is fast, but the faster method still seems like a lot of work. You have to create a fetch request, create a predicate, get a reference to the context, execute the fetch request and get the results out.
Fvi Ihjwahei uhcucp lom i yovam kwexolqr, gjetb zunsc i Woh duzvoudegp ucsingj ep pcjo Dowo.
Tlat caixb takvew. Xp oqawy mdo sukad kaxefeuvsfad uf dda Iykxilee oqbaks, dwo silu af deym wokrviw — awl ioyuig to diyzhipubr.
Ovqumo tpi tuab fimgnerdor uzx qibgd ji ese rboh molzej ivdzuah, luxxusaff ski cuwu qadhush uq eyamo.
Lwart aeq ske wpaqqe ab wokbewzexpo diy.
Key points
Most implementations of Core Data are fast and light already, due to Core Data’s built-in optimizations, such as faulting.
When making improvements to Core Data performance you should measure, make targeted changes and then measure again to validate your changes had the intended impact.
Small changes to the data model, such as moving large binary blobs to other entities, can improve performance.
For optimal performance, you should limit the amount of data you fetch to the minimum needed: the more data you fetch, the longer the fetch will take.
Performance is a balance between memory and speed. When using Core Data in your apps, always keep this balance in mind.
Challenge
Using the techniques you just learned, try to improve the performance of the DepartmentDetailsViewController class. Don’t forget to write tests to measure the before and after execution times. As a hint, there are many methods that provide counts, rather than the full records; these can probably be optimized somehow to avoid loading the contents of the records.
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a kodeco.com Professional subscription.