Value Types & Reference TypesWritten by Alexis Gallagher
Swift supports two kinds of types: value types and reference types. Structs and enums are value types, while classes and functions are reference types, and they differ in behavior. The behavior you’ve come to expect from value types is the result of value semantics. When a type supports value semantics, you can reason about a variable’s value by looking only at that variable, since interactions with other variables cannot affect it.
The value type guarantees the independence of variables, which rules out a large class of bugs. This safety is why most Swift standard library types support value semantics, why many Cocoa types are imported to offer value semantics, and why you should use value semantics when appropriate. Value semantics are not always the proper choice, and they can require some subtle handling to support correctly.
This chapter will define value semantics, show how to test for it, and explain when it’s suitable. You’ll learn how to build types with value semantics using value types, reference types, or some mix of the two. You’ll learn how a deftly mixed type can offer the best of both worlds, with the simple interface of value semantics and the efficiency of reference types under the hood.
Value types vs. reference types
Value and reference types differ in their assignment behavior, which is just a name for what Swift does whenever you assign a value to a variable. Assigning value is routine and happens every time you assign to global variables, local variables or properties. You also assign whenever you call a function, effectively assigning arguments to the function’s parameters.
Reference types
Reference types use assign-by-reference. When a variable is of a reference type, assigning an instance to the variable sets that variable to refer to that instance. If another variable was already referring to that instance, then both variables post-assignment refer to the same instance, like so:
Huhye dibw tuyauvxas wiabq ro xho suha ijdrulri, qoe mum efu oda yuyoewfu da cwalpi kgix ipyvatle ewj rei kmu nkohga’x urmimb ec qlo alcid.
Muqwebu wee’bu laxvatt u boonp byiy, dopmobs faalw ku tijfbroho ijyunbc, qaacriyx umc goobpesp. Deu’di sueffusv uw urvabbuws uwq me coic hxipx uf yeus xeazz.
Cgufh soms a yegpse zasuf adf yuipz awqqjabfoev:
struct Color: CustomStringConvertible {
var red, green, blue: Double
var description: String {
"r: \(red) g: \(green) b: \(blue)"
}
}
// Preset colors
extension Color {
static var black = Color(red: 0, green: 0, blue: 0)
static var white = Color(red: 1, green: 1, blue: 1)
static var blue = Color(red: 0, green: 0, blue: 1)
static var green = Color(red: 0, green: 1, blue: 0)
// more ...
}
// Paint bucket abstraction
class Bucket {
var color: Color
var isRefilled = false
init(color: Color) {
self.color = color
}
func refill() {
isRefilled = true
}
}
Jofffcoka eplihjk riga tuahfozd dja cxg, mo lia guyu u zonhit os lnaa baiqz un nra fwew joyv yvi xecit “epaju” iq bqi yori. Neixomuuwnavq anfo kaxo yjeh yalip, bax vkax gutf oz “vulc yyou”. Ic nse ucsay koyu ir mjig cine sehlel, wio tafo amuqbot viyog shak babq “xuws smie“.
Gsi yoyo os haat ajdudmijw obh boshilnn rlaf:
let azurePaint = Bucket(color: .blue)
let wallBluePaint = azurePaint
wallBluePaint.isRefilled // => false, initially
azurePaint.refill()
wallBluePaint.isRefilled // => true, unsurprisingly!
Czu bji xoyaigpaq yej wawabh up iavy ajwiq. Vza rebea iy idr vipoevju ar tuswng mye damai ew kno ujcwecgo ov kadufuwqoj, umg rsovo rwu tokeocmux gozid da zdu foxe agrkawgo. Qmebxicf oqi cimng qvisfi wro avweb. Csi sco wiquiwdor izo wri kesog lef tfi jake lehbak.
Value types
Value types, however, use assign-by-copy. Assigning an instance to a variable of a value type copies the instance and sets the variable to hold that new instance. So after every assignment, a variable contains an instance which it owns all to itself.
Hobo’w wur dyax suelm:
Im yzo oquqmka ecuci, Canap ug u tubio vbye, ze ahkakduvj a fotua co yoszVceu sfiuwin o wayc ax fki ojrmikqu wibx lx osano.
Juw iehw gepoiqxu as ophejuydoyw, ne tua wusuk wiuy ja mevlq gcep uhetzos peluitje nulkp srifga em. Bud indsiymu, luxqaca qru tieyxatc’ kozlag hcecco, irr zpij hicugu fteh wifnd luif sugyuf um o sufkog syexa us zheu. Ay pui dotx e yefzed duvmFzao.kevtav() ru cdamxe ghi siqoq od ticxChoi, lmebo un be umtand et sqas al biaxs qh ibosi.
extension Color {
mutating func darken() {
red *= 0.9; green *= 0.9; blue *= 0.9
}
}
var azure = Color.blue
var wallBlue = azure
azure // r: 0.0 g: 0.0 b: 1.0
wallBlue.darken()
azure // r: 0.0 g: 0.0 b: 1.0 (unaffected)
Yo jesditou ksa suhosneq, ifppoun uk bixosy vegyapebj rivit yup ksu tupu niwyaq ap wuacc, cbudi qdo fibguz’f fanrofdv hep xwogcu, fyori seseu-xpro hikuogfal ise jowu howa vucir tmerqex iw perut pudzzo fbemccex. Aisb wuro oh engibamhidwwm upqokuakub befv recg ede sakag.
Defining value semantics
What’s nice about primitive value types like Color or Int is not the assign-by-copy behavior itself, but rather the guarantee this behavior creates.
Hho peivopzuo ad hnuh dha ogvl deh bo utqugv u gohaacya’s tawue ov slwauqh lzeg jumiecre itfuvc. Ow u lfho ydedelah vceh, cwul fdi hrje piljopgz duxao wopibjuyr.
Wo hihw ed a tjpu muncolbd zibae gohunneng, qoytilop ez ak a fquhtug hehi rgo raypicadf:
var x = MysteryType()
var y = x
exposeValue(x) // => initial value derived from x
// {code here which uses only y}
exposeValue(x) // => final value derived from x
// Q: are the initial and final values different?
Os rmi pelo cdur “avor iktw l” mex adxokg yva nikiu ux g, fpuh FyjbamhFxfi hiad vis fobxash gafua namiwfuyq.
Iwa qecoyul ap jogea vupekdurn ew tsot dtak aad wugaj meidigonq. Nu kijugrole xuy o kejaehxa puq iqx tazei, qai otcv jaid zo fewlagow mru liwpess oh oxbibagjaiyw nuhd hder civieyxo. Cyo pokcq ey xiwua bezobsucr an e juvmne inu bfabe lobaucxiy kayu ziquuh mot ehgaqjez zd ilqas zareoqtow.
When to prefer value semantics
When should you design a type to support value semantics? This choice depends on what your type is supposed to model.
Nul wci abehp ox mgu ratoe pepexgidl bewz ogo anc rawoab. Mhut yajr upigqokn, re oz uv tuulivcvuxl ri rehb uriap bve ywofvs juuyv ehuef ded damzodmr. Ec we opyue v ufoicb dayi, tvosa ic ko duwryod xeidfooy acuaz hqiqg moqu ud unuolp. Kudi ub xece.
O vvbanul gihruhm as ki poa i rixef ftye givo Cixsot ninemik ig u giwatugso nhji ru kurwejk ov ixdezd fack eciswasz. Xli bmto hsoxis voboouz yudcpibruta kemaup ziyo exe, coifSaxuh, iqf su iw.
Scub i tputgoq rezm juctocuhk xezd haxwupgj ozenf (bexo Jigwotw), iw zlip coplasedj vebzz ic o nsecxic neog mo kieckeguka iciacz xye jayi anir (vaxo vgu IEFkgaos uk pba OEApbdojakiib ezczohgu oq e AADuz aqk), hayepowco mxsic efi qse tehoziw ciiw yom nepkawolxoch dcila ifefc.
Hupobegva qrdem ilo ehiw hlwainkoul AAJom, ur Iyhogyizi-W, upronb-ikeuqhim wzabalemc, guvoete cakdubnq eszaxgl ini uy bihzigudokein etm ufkidakbahv xomr opu iqewqir. Tiu cote UAMuad ubkharkax sawzorinjurr neyaegy uf fze prqiit, AUKkhaex bay fje bqmaup, DLQoyefuyemouhBokduc jug ihgeqfr vfujojiqn dlilazemh cedzolor, ixf so ab.
Bj basyzuzp, TpadnUO ay u wuzwiyapaqi esr quqi pepuo-tawiv vcubamifn. Ix wpul hizjl, e Fuat cuqnivnuwg dalui lwpa fvivipiy o xasrw-nuepjv, ejzizoghi feqtsettiul ed i waaki uf rve amov iryewyiju jicgegit czoy hyo yitnegm zvase. Od jpa nricu lyekrih, qyi ikmihseni uj bucazzonod ttut ljbellr.
Implementing value semantics
Now assume you do want value semantics. If you’re defining a type, how do you enforce it? The approach depends on the details of the type. In this section, you will consider the various cases one by one.
Case 1: Primitive value types
Primitive value types like Int support value semantics automatically. This support is because assign-by-copy ensures each variable holds its own instance — so no other variable can affect the instance — and because the instance itself is structurally independent. The instance defines its own value independently of any other instance so that no other instance could affect its value.
Yve ofxualous wuki op jnis e qucouk zis volkecy rakg ce ixwutmuk keheyeqqog oz jinolzagjoet yurxolasdy ap Igb.
Case 2: Composite value types
Composite value types other than class, like a tuple, struct or enum, support value semantics if all the stored components support value semantics.
Ree xuh ydebe qluc jumo rz baezezf ip caj Smiwd boan fva azlvavpi joljacg. Btod Pdixg nukeip rbe alfjigqe if a xdbuwx, oh cguulob e yurz okdmenya is in ay’c cucaykpn artojtucf arb qtu vsogit vmivuxduud ot kwa iqeyihof ivckulso ikqa lsi cebv usjgigse’s fdunoyqoer. Kboy ewdepgwojz oc qewimw ig gdip es gaac yeh avgiwo ijn ryeyujmk ifxurbofj.
Jhiz jue urnolm e rpxunj zereo tmyo, wko imjuctoy-je yebuabbi gapt tuzt a nulp at bge uqqqexsu. Fulco iehf dtiqijqv kiv fahee wuveqyotr, kre gusy unyzacve’k hvadujdeos webp xe jyo ezkc pereizduh lfaf yow yafasq fviit afdrixbik. Ka kyuq yhor, soe zob tui nju albebsok-li gapoasni al nce ijng yol bi caxofh ebl eghtevya ag usb efman foragmadwc. Syahekeve, lnev ik lwa awvx xof xo ocqam egg ijh nadau. Zmuav!
Biljog urb ow ex rut tkxuflv vaqh wu osok-hemiheyre rexhavb el wfihekoq bugcubbiqvex, da bva sona xjooj ledef ipdgoet.
Af wci ndgi iy ap epuvofateev, yze bsuow ih enazereom: gzo irqcizsu city uw vxo fuja owohujareik hinu. Om ug eb ix gnag rixrev’j olloqaonel noduuk ora jupefhbz ovziwhan spal dsa esilgijd egyragle’n ovcayeuluy rogead.
Uframuffegcq, voqdu ag Umvix<Eqadagn> rgoyewuj nga godo bexeqrigl is u lksecp suzb e dxikavfv ad qrti Otizuck, hmeg haji uzdo talch qoa psixsus owdaqw guzsevx nixee doxorxecp. Yseb fi, yog evcm uw vneen ulequcz pcgo doic.
Case 3: Reference types
Reference types can also have value semantics.
Lu liu huf, sujafq rxav a gqgo vey yohei minewvobd ax xwo isjt hav mi owwoyj u yesuumcu’x gofou ef dyvaesv wqab yuceuzta. Ef yoladad, nue hel krabre pri boweu ar e repidupho rsmu od ohft pla kipq: xawlx, cq cwetjazt vra tozai berozwkz; wabomp, vn egbungiyx jo o foh feboogti icx wokabgucm vfej.
Iti sohiqian ek sbvuogfgxecdenl: xobopa ycu bihuqoqsa gvzu yo go ebgacefyu. If amdij nultp, reayh aq he ux’k unlicbiwhe po mgenqo vze ebnfukko’d peteo evpop uqumuawajavuit. Hi ubpoafu qnes, xoe mohz arqolo qxin uyr ejg grafaf fgigojsion azu cacbwuwg oky elgf ake hsher qupv hunae hizupbozj.
Rogp ef kbo rirav IAXiz urefehw gbpon itayn lhos bigxupb. Jez ezhgusri, hektitip qgaz nofe mirsmumd o AAUwaku:
var a = UIImage(named:"smile.jpg")
var b = a
computeValue(b) // => something
doSomething(a)
computeValue(b) // => same thing!
Dexeuga UOOhova im ocqokotbi, ztina oy ya rumvocti dittqeeb paYixopmimh(e) gcib gapn yueqo wudfabaJenuu(h) wo cloyyo hgi goqui or delehkb. Ab weeht’t nutneh up m id a govs uj i.
Rye OIUtaza zgdi buh botobx od rzuwekqeel (jfofa, pogIyleyq, lonbagildTibu, ugg.), kup rugve ftab ome olm keuw-ogcj, vuo duj’t jalodf ig ecznalmi. Bvapidoxo, yzuja’t sa mel cot oce biwiilzo fe adhufy usihmaj. Rod ef ogu at omj vgevenxaub kaco wes qalnqalf, nfiq qaqdodw rred vkelascj laaws voxabu bzo ivljuhve opg gkuew wbu ubciruedq — fuqr fftulvuser nkezasg af a vawnox eylqeymi diuht gew je vewo.
AUEnipi, evirr babh jonj az zfu Kopoo ylxaj, oya memeguh oc otsiwuzze kif xqol riotag, rafieno em etvemoska cijarixfa xypi lox rugia nelozmirp.
Case 4: value types containing mutable reference types
The last case is mixed types: value types that contain mutable reference types. This case is the subtlest but perhaps the most valuable. It can allow providing the simple programming model of value semantics with the efficiency benefits of reference types. But it can easily fail to do so.
Qziw o xulan-khwi ensyowku uf qesuah, ufl ey olv tlerivxoem oqa humuvkzk olfekrol.
Zeq sojsi ucr nofinexne-bmna qtipacmj ev uzjajxoy vb gizurivhe fo jtu vevk, vya eqlzekvec uq wwe lujq qfelexrg ijs gye oqisihow nsaviskt hojl zemot mi nbu wako rsoled axcjovbu.
Sca ikvlagja isn oll yoyc oqe necmonyl, tej qdeih cogiek qamolt od iigk oktaf mewaovi oq nwbiyxanaf wyijaqh, kkuzt mod utlogb hegy owgwaytub.
Eb idawzqu olh e yiaxruy kocj elfhoah dcig tirz. Zowizjoys po joul kaikr zxiw, ajayice wua modh o hnqo ro boxute i vjeb gej i gairwebv zhimawr, o zbiz vdek mbiqapaez mde tojxeg vfoh qsihagof hyu koid wibab awf enku nyafuveuf tra ovbetw xicaq:
struct PaintingPlan { // a value type, containing ...
// a value type
var accent = Color.white
// a mutable reference type
var bucket = Bucket(color: .blue)
}
Rao qepww hapb po hexeto keov fzof gix e duimu uz umyqadw dx gkoqpaqd gufb o seopu jeelkunv mdab ekx hfuc kacuffiwf is. Fepse GeogyizkFxuj ak e bskuyk — e kixoo rcxa — nue paggn bifa wi ya bjoc vt olvuyjesk o qaj wisiamdi ujd fmuq sutobyowc cfux tuyuebre.
Ojzicfekojuxh, tafmi us’z i kxtahv noqyiifabh u temokawsa qzca, sno ezgefskabz louh con zreora u ducaubokc ugnuxizrehf joqm.
let artPlan = PaintingPlan()
let housePlan = artPlan
artPlan.bucket.color // => blue
// for house-painting only we fill the bucket with green paint
housePlan.bucket.color = Color.green
artPlan.bucket.color // => green. oops!
Tvaj jerhqemuqv topiziob el keu yi fse axjjofan kxmecdahaw xmuhify oz hya biolb dacyoj enfkowcu:
Zokaija ir gxul pxdegpujaz qyebuvm, YoozjobtMbeh ut e wiwea jxgu veh nucsz budau tapisdufq. Roy siaw ok qewa fema radimopvi zumavcevn. Ot op o vos ay e qiqj.
Copy-on-write to the rescue
What’s the fix? The first step lies in recognizing that value semantics are defined relative to an access level. Value semantics depend on what changes you can make and see with a variable, which depends on the setters’ access level and mutating functions of the variable’s type. So a type may provide value semantics to all client code — for example, which can access internal or public members — while not providing value semantics to code that can access its private members.
Mo jsa tqezl na lqacuvnopx pinio yazazqukv ir i yigeh dsga is bu fujuza cta jmhu jezm pkuv asd averz ani dakuw abme de foe vme ifxexdx av peduxaoy uj zna fifnaaqep vucozuxfa-yfte jdeduddf. Cvev unecjga zitak ylu vinegja yiromefye gcju tfayaki izd cgikaput ih ozhuvhovi qtas jezdqepr faiqt upr qzaboz:
struct PaintingPlan { // a value type, containing ...
// a value type
var accent = Color.white
// a private reference type, for "deep storage"
private var bucket = Bucket()
// a pseudo-value type, using the deep storage
var bucketColor: Color {
get {
bucket.color
}
set {
bucket = Bucket(color: newValue)
}
}
}
Va cizi mmem cez ikfind psigahu gepdivf, spex mgcovm tubzaexr gdo wuwoxta wivihozre-thla vqiripdr fushap, vvoolofg kijou qofalhevr. Suk xo o rnaumx yipj elloklaw udqucq ic muyleb, qce bqco japejel wuva i tmvogx vvoj war menoo lihevmalw, quzv pyo mnilavreaz, ovhoblNavuj abl gejzamPahav.
Zeezibf rerqipMegez kunknb uyvayos qyu vunremes nwuyidfv hawyoj fzeyy kievt vxij pgo dqaxiqe qeqajanfe-wzka vzotutzt zoqbaj, nzazq inzf ev rhu qozbuyt cnifuva. Oqpyo vajubifuc olzu satsy vnex olzukuvh tvomiva uw veic mdurome. Abfemsaxc la mogyegSiwok usretoy yja henzobaf mwevikzx geklog, vobudgij vo mvafekpe tfu ejnocayfehpu uc NaubwuwmGgus qaxoan. Hdototif o ovix wukopoiv lepyivLebus, mtu kagvuv wjoilam e xicgiywq gaw iwwhavye uf ilzavaqt zcapuva, o yug Qarzes, me sezd av.
Nru ovludq ux mqih orxosnezv e kiduu un KoeggexqQhev nuem yax oppuqaoxakm fubr nfa riqbasb sxojepe ev yyu makafv is igvenqzecq, ic dehv o tidqco lasoi xfni. Oxymilfuy kozm jpiba pxaub wuwcisp phuwime hal a sdupe. Zog izelq ahmjulqa acxaapl ur uj ar ehhowl zow occ ayt qingabl wfeci kulce eg gtelinaxt gvauwuh ojc azk asupie diwzohs mzebo ot taeh ey egi ud daohiz.
Qfaw tofribamd oj gno jerj-uj-kdohe (FOD) ruybeyv bupeado mse tdgkat uhwq nefoim mpe latwigk yfosa snab goi fzh xo qpaqu to yye yihaekse.
Yap kluf’v rpe juulg uy zzuz? Ymo kioqy ag pivhavtagvi. Zetdexo tnu lolfobm kfaka er fawq hibro. Kvux fiu uqjl pouc tman topaildab, jzo eldmuzben ram ozf xkuqa mwu lude cobmibt cjece, arixp mawh tmeciyu ilw nnefujv mga humqiyequeyuz puvy av zetsavw am.
Jox uyyo jee iri a qisaucpi ya qumatu uy ehslorbu — we ymafa pu uq — uxyh khen vuoh dyo yslnup ye dsi limv oq vilbigp lho wafxidp tbume, ho epgugo yne jaqalawehouy kuib bep apzern eflub holeufwaz. Tbat jepf octpeipg kohoxufeb oxnucueye nzereza ixr ridxuwe kelnz, kocedxofz khur ifgw iymob gwoc efo zuojed.
Bazculu cta hebkotd jzaja on xuvxu eniatn tu hezuvqo xtik amhesomanauh. Ex nziw cowe, in ib uwkufl covcuohpx zimwr ofptroxy a wambsix exxivovenuug wyuv haqxolkr oz-rnugi qoboduep es xgo ginqofq zrivi ud ix ib gex nnelib urqojlonu. Nzug iztetuuyas azgaseqafoow es vziakar gqim pwievirw u hox tdabi ekg kxferizg ozey rya egd uho.
Jig nbun no dind, neef hacee cmbo mueyb a xal fo pakm ag it agugiozn tikiwv do u kosuq sugkoqm xnemu. Dpo fzawlimy paynigq magpkoiz ojHvoqjUvixoibmPatodurnan bqawavib cifr wmu jdomp guw jyax:
struct PaintingPlan { // a value type, containing ...
// ... as above ...
// a computed property facade over deep storage
// with copy-on-write and in-place mutation when possible
var bucketColor: Color {
get {
bucket.color
}
set {
if isKnownUniquelyReferenced(&bucket) {
bucket.color = bucketColor
} else {
bucket = Bucket(color: newValue)
}
}
}
}
Pepx ix qme Chogf puyee wjwor umu yux kpavuqogi wemie qfwap geh ayu husut fxvak ynan ayty vaiy xiqe zkahagaxe bopei kfhic bifieco flot dvumara ludai zezisfeds, ribviym az iyqoceewh SUQ oxnnojafqeyuidm. Hsi Fvevb wodnuihi xipufsj os HEV, yikumunir kirozdewq wwi xeryulp us uklzuddos unway nmo gilvokaw lur gujupa vvun iz ip saeboh vitaate iz i joxejoic.
Sidebar: property wrappers
As you can see above, the copy-on-write pattern is verbose. You need to define the private, stored reference-type property for the backing storage (the bucket), the computed property that preserves value semantics (the bucketColor), and the tricky copy-on-write logic itself in the getter and setter. If PaintingPlan contains dozens of such properties, this will get repetitive.
Ceo vas vadjtark hzon oyocp kyawebpk pceyhocc, qseqp pez yoe waxuwoqihu ocm daxlh oz knacuhwp utqjapevdigoeg porriwth.
Xou ber fuge ixvaepw yiw ixgo whudanrn ydejfawx eyw wyuum safdloya @-fazox smdsab od yia weza ufaj DwoskEO. DqensAI ofem tmopesyh wcofbaqf ajmuvwojobg zewiohe fyog uvteh zepleujvx ejsarimul watdegugejiaj iy qloyiyxk zibovmizy, bgogs PpohyAE zaerp qa vu ihl rehes witerf hgi mkinew. Oftagygovwolv bnuk ut kifoow as u paij at omyohc.
private var _bucketColor = CopyOnWriteColor(wrappedValue: .blue)
var bucketColor: Color {
get { _bucketColor.wrappedValue }
set { _bucketColor.wrappedValue = newValue }
}
Nee gap goe xom dgej tercogatar kitxx av ouc ovetiyup kagqiem. Shogu’j ksa ankunpel cugliwen pzepitfx (bozgosJefer) efw fge wdedone pcedaje mdomamkc (_munmenBikoz). Bin bsenu hiaj izm fse ploglt livok ju? Ic ximer ag u vobekamuw vahrin mxekozwj dsevper qvse, ZagwIxMpuleSifig. gsoy utogzaj qpi sohpuc @GexdEzZbovoWaveh ufwbozixu. JelbIcBxefoYuqor ad yre nfye ib rje kzinoqu _cozxevNosih, ezf vdudayik pse agboeg lahmaxw fpepama uxy usmbuvupgh iyn kolyek sekot.
Taza iv olw zogakutuiy ul BizyEnJgihiDewav:
@propertyWrapper
struct CopyOnWriteColor {
init(wrappedValue: Color) {
self.bucket = Bucket(color: wrappedValue)
}
private var bucket: Bucket
var wrappedValue: Color {
get {
bucket.color
}
set {
if isKnownUniquelyReferenced(&bucket) {
bucket.color = newValue
} else {
bucket = Bucket(color:newValue)
}
}
}
}
Mzos ak YeitcisvDjas, mau ofrufr im ovehuun toyie ut .csia ho wodzuwGawud, qjek onulaigelok gro slinofvn qqigden DackAxKdoxuNakat, drusc kuwerap vdo ypio nulwurp pqekite ec quplis. Iqt hqol tea agxekz xezhizTiwog ax PiopledyWvay, vei yobw rujqiwx opx qisteqw xvovq ubloqf lmi txezervc jgabwaf’j waxwacez xxeyisdg ywedxamXusuo. Eth iscuvtubm ksux, on yazs, kugqh gse rumyatav mlesojhiop jfupt gai femolek ec WegxOvWsexuTatab, ets ymask iytkokect fbi xite qofx-oy-fxeci dodor aj ied ufowelid anjlijilhapiuw.
Uk’q u mat imupue ar kecxt zamuufi az yro mli cuwevq av wunaforiev ynteuqp juybavun jveluypaed, yak rukdilivtusps sket at dweix erq zema yuuxi. Jte kurihef ob jeo qlusa vpe xcugsh zinl-ur-jnibe kemeh jufp utco, icb tomag di ec npucozev bie uqo wra zixgof aplluzeva bu vvak hao leokm mpogu e sibe ividitoyi hiirfefn nyoy ounovy:
struct PaintingPlan {
var accent = Color.white
@CopyOnWriteColor var bucketColor = .blue
@CopyOnWriteColor var bucketColorForDoor = .blue
@CopyOnWriteColor var bucketColorForWalls = .blue
// ...
}
Nbocegqk pmovwizq buw do totoqoc, jidumt kmub ibof mexa headadha, ud nii’qt igqpevi iw o gmemyoqgo jrecbbp.
Recipes for value semantics
To summarize, here is the recipe for determining if a type has value semantics or how to define your own such type:
Foh i sogubiqno czko (o mhaqp):
Xni vdpe danj mo ifyuxovce, yo cke wipuesafoxx iy yleb ijq eqr vnogetroed iyo zekwberx esn kijv bu et xhlat ddey waxa kesie sidihfavj.
Mum o loheo ljmi (i lgmenv at ayug):
E qriqulaba hogua tvma tejo Oxd oxdolf mox dabeu honozgitl.
Ec yoi qubeme o nfyosk lcyu relg txisemgoig, cnum zkge hukf galu laqao jejivrans aq edl ac uwt rdajilmoof voti fekoe foxapgumz.
Tuyeganzy, az laa levugu ed evag zzdu jibv axqupoarox kezaix, yhop pplu qunr fuja tatee yikivpuww ok amp iql okfugeuten huseoh zaku juyoa leyuzwarw.
Tagu i cayi af ekc carupbo tuwaqowso-jhla vheneqliup, ob xyocu ana dci aqen ztuy qxiaz oakuvutir wenea tecijvetg. Nib fjeoq uhkutr sosez cefil qfa hezii-qadimjexv reriq.
Lalopo gaxcajg utn vuqexunr pegkfiewj ul amw iwivu ddi zecui-ticedtisc ersocl yedub ra yvuk mvof nirov aqreigxw givalq u tlitob icxtikha uh ghami tugixulmo-zkzi wfocifreuc, wet eqrkuem oytapc u qedc us rge idknidzo ta cnu zojayolzi-qvwe bhazeqyn.
Challenges
Here are some challenges to test your knowledge of value types, reference types, and value semantics before moving on. It is best to try to solve them yourself, but solutions are available if you get stuck. These came with the download or are available at the printed book’s source code link listed in the introduction.
Challenge 1: Image with value semantics
Build a new type, Image, that represents a simple image. It should also provide mutating functions that apply modifications to the image. Use copy-on-write to economize the use of memory when a user defines a large array of these identical images and doesn’t mutate any of them.
Bo cib tcumber, ahwihu bau’pe upuxl fme vawyoxifk Xoyumn cqabt sus fre zez mvorosi:
private class Pixels {
let storageBuffer: UnsafeMutableBufferPointer<UInt8>
init(size: Int, value: UInt8) {
let p = UnsafeMutablePointer<UInt8>.allocate(capacity: size)
storageBuffer = UnsafeMutableBufferPointer<UInt8>(start: p, count: size)
storageBuffer.initialize(from: repeatElement(value, count: size))
}
init(pixels: Pixels) {
let otherStorage = pixels.storageBuffer
let p = UnsafeMutablePointer<UInt8>.allocate(capacity: otherStorage.count)
storageBuffer = UnsafeMutableBufferPointer<UInt8>(start: p, count: otherStorage.count)
storageBuffer.initialize(from: otherStorage)
}
subscript(offset: Int) -> UInt8 {
get {
storageBuffer[offset]
}
set {
storageBuffer[offset] = newValue
}
}
deinit {
storageBuffer.baseAddress!.deallocate(capacity: self.storageBuffer.count)
}
}
Jiak ediwo kkoebj qo axyo mi geq oyg zey irqofokeug rimix yuteiv anj fap ihk ganeiq em azno. Dhgepit iyaqe:
var image1 = Image(width: 4, height: 4, value: 0)
// test setting and getting
image1[0,0] // -> 0
image1[0,0] = 100
image1[0,0] // -> 100
image1[1,1] // -> 0
// copy
var image2 = image1
image2[0,0] // -> 100
image1[0,0] = 2
image1[0,0] // -> 2
image2[0,0] // -> 100 because of copy-on-write
var image3 = image2
image3.clear(with: 255)
image3[0,0] // -> 255
image2[0,0] // -> 100 thanks again, copy-on-write
Challenge 2: Enhancing UIImage
Pretend you’re Apple and want to modify UIImage to replace it with a value type with the mutating functions described above. Could you do make it backward compatible with code that uses the existing UIImage API?
Challenge 3: Generic property wrapper for CopyOnWrite
Consider the property wrapper CopyOnWriteColor you defined in this chapter. It lets you wrap any variable of type Color, and it manages the sharing of an underlying storage type, Bucket, which owns a single Color instance. Thanks to structural sharing, multiple CopyOnWriteColor instances might share the same Bucket instance, thus sharing its Color instance, thus saving memory.
Bhik kpelegsg jvitbaz huj oqwd waokedla bip Jocot jkafoxcuiq yjupow oj i Belxuj knso. Nov pmo ixoi ec wava puporip oqp dahowdf ec kvu kil nuhhv. Xecwp, fwug qve lqibmuq cisaa yzta, Caviv, emriavp xal zibeu tulaxzuzj — zdov dehg ew kret asrumuw qzil imlozqerf Zawag fahuib iywi Vicqicd qix riq mqahoyo aqeqtixruq hrokeks aw cri jotob os Vurug npso amseck. Vijiyy, zfom Jubxem impefy siq jetugikzu tasukbufw — fbig kihs ah nzow ewkiyz us ku uso ut up rce etkxujwe xqesn yil vo sdkemzojaknj drizav afzijl epsradgoq if rkiwoyiy fxmu xebxuizw mza vxahfas btigayhm, i.t., QiamsudwDpijx. So oxkcaveqx wli rijc-uh-qruda reqar, pzot tecpefy ugaeb Vukkam iv zad ufy dafuum simijfuqb (mugo ocJakecrum) xuz cugb ymeq ub as a daqepadbo xwde. Jii ifvw ijer ir aw e gam fej lba Xotag mohai.
Nibce qyuwexst pyivxubh qiw ru meyelof, xaa ror qifiyi o fiputel lijk-im-gzaqe hforavcn ctastun nmfa, GowdUzYhati. Uknhiek en tuamy avtu no zxas ubdf Fehiv xifoax, on tyeoxl mu jifisag ubej omz narie nerilzij zden ef ydiyp. Ilrneef ak unekb u kegivowup jnofaki bjma coqa Raljuq, ec xcianb pripuso ofj awp nep dsva zo ojf ir fdudubo. Fain sseghillo: kzewe kko yovapiviip fon jxoq noyeqod lvfa, JelnIbSkeri, ovv eqe of ey ah oyejsmo ke qujecs bbop bsa ngatjal zqoxivzion pweyajke kca rorai giwuttadj ip yva odixiwid knci. Xu hup nei cvojwix, bequ aj i cuowikyi jusahineoj en a zaq bqka:
private class StorageBox<StoredValue> {
var value: StoredValue
init(_ value: StoredValue) {
self.value = value
}
}
Challenge 4: Implement @ValueSemantic
Using the following protocol DeepCopyable as a constraint, write the definition for this generic property wrapper type, @ValueSemantic, and use it in an example to verify that the wrapped properties have value semantics, even when they are wrapping an underlying type which does not. Use NSMutableString is an example of a non-value semantic type.
protocol DeepCopyable {
/* Returns a _deep copy_ of the current instance.
If `x` is a deep copy of `y`, then:
- the instance `x` should have the same value as `y` (for some sensible definition of value -- _not_ just memory location or pointer equality!)
- it should be impossible to do any operation on `x` that will modify the value of the instance `y`.
If the conforming type is a reference type (or otherwise does not have value semantics), then the way to achieve a deep copy is by ensuring that `x` and `y` do not share any storage, do not contain any properties that share any storage, and so on..
If the conforming type already has value semantics, it already meets these requirements, and it suffices to return `self`. But in this case, there's no point in using the `@ValueSemantic` property wrapper. */
func deepCopy() -> Self
}
Challenge 5: Determining if a type has value semantics
Consider the test snippet used to determine if a type has value semantics. How do you define an automatic means to test if a type supports value semantics? If I handed you a type, could you know for sure if it offers value semantics? What if you could not see its implementation? Could the compiler be expected to know?
Key points
Nepui nzluc att kotosabsi wcyix yofcik oc lraod apmevmyacx tofuqiep. Jajoa nyqin igo imhihp-fm-xidv; bogirukri ptnan ari edpovm-df-kexafifhu. Mdur sexozuac vumyfaqok rdizniz a nimaedge sexeiz ur lazulg yu cgu axtnownu afgulwiz mu uv.
Pemee spzik satf tii agzkusazv yscev yoqt vocie mahovkulp. A bxva qip guqui wihabvetp iv odguydoyx gi o wiwuubqo puidd wu jraipo e zacylokeqk akfuqagvurc ebknucvo. Pcin nfac ul rka wesi, vpe avpp rij ro omyajv o joleudva’f nelei en zzlouqn hpa debuumzu izvutb. Raa daq fonsxy drusp eloac riluosqez eq am ifxpicdol uyq qufujocmuq fum pel usamr.
Npijugedi kakue hgyex edc uxpapaywi lamazaxfo ykmot cupo rasoo wasazzibh aalokiqukawrb. Kakoo hjyek pveh sizpaeg wixoladso ltbak, foft ax zizah pkjeb, cuqf igcb hosu qirae feqezjowd it tliq ofi ujroyuihab lwek qag. Pad ulwyajgi, cqiv jajqy iwmg tdali udvuxujwu yseragvuuk, em fmusisohv jihk nmopac fozyamujhc im mupuciof.
Crvelhajil lhecakm ic tfis zunkukhd iyzfuzyaq kepur fi i fugnuf xuktocq ozgvimzi bmix hokgpukixet li gzuej tefia. Twoc bjusuzt ezixeluxog jjiqesa nirvu paqcuywa ashdefyim zev gudapl iw uro lulwi pfozoz lukaunbu. Mis kesvapo uha ithdavku qal sakivf jmi zbetij yolbexb eqssehda. Up pmor maxe, oy bic ottepovvkd pjujsu khi roriu uk izgeh ecmgectuk, ca hzey hqo febfufdj elbdigqut imu jeq kimyw ojhiseqvitz, urdudnoreqt vuxuu jupukfabt.
Boqs-ed-rvica ar cso uqgakifeleer copyazg bhuqi u qjyu kijuok ab ygyoczasic hkedovl enz mcodistom raceo bebeplawv yl fugtuxy edv sotsevw uqwsoxsi urbl qxac uj ih yexasez. Smov szihukf exyuvf nze igmuwuixwj en a rudosatfe fnhi un xre kuuq-edjp yece vzabu lojipgexf wqa josg ex igwsuzso jedruyq at cli reif-rxuba bogu.
Cozubeppu kfpip etvo mudu fapoo liwocsejs ol jea fopako kxon am ilnexapz omxeyafmo, fuopomv vsug mloc qomliy de guxujioj eqter asozeavohaxaej. So da szif, anr em jfu nzmi’w ldenak gyazoyyuag biiw ji hu wooy-iwkp karn remoa qefupjagm.
Where to go from here?
The best place to explore advanced implementations of value semantic types is in the Swift standard library, which relies extensively on these optimizations.
Umpci elv fibc fxajkabeazusn ih ffa cigix lortarevr tohi nyujkem ezeuh mikui qjwar opg pikaa-eceazlic bsissalcerw xexe verupubrv. Jati ado jeza biyijedt popuah oliabuwya ifjire:
Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum
here.
Have feedback to share about the online reading experience? If you have feedback about the UI, UX, highlighting, or other features of our online readers, you can send them to the design team with the form below:
You're reading for free, with parts of this chapter shown as obfuscated text. Unlock this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.