Swift is an easy language to learn. It can take care of a lot of things for you and help you keep your code safe and clear to minimize bugs. If you were to compare it to C++, many people would say C++ is harder. Swift takes care of type checking, memory allocation, and many things on your behalf so you can focus on what you want to do in your code, and not how the machine will handle your code. But C++ gives you more power and more control. As we’re told in the Spider-Man comics and movies, “With great power comes great responsibility”.
By default, Swift is a memory-safe and type-safe language. This means you cannot access uninitialized memory and can only treat an instance as the type it was created. You can’t treat a String as if it were an Int or a Numeric and vice-versa. But this doesn’t cover completely what the word safe means.
For a more general description, Swift validates any input, whether it’s valid or invalid, and behaves accordingly. So storing a number in a string property, for example, will fail. Additionally, forcing a value from an optional that doesn’t have a value is not a valid behavior. Neither is storing a number that exceeds the maximum allowed value of your variable. All of those are different cases related to safety.
In some cases, you might need your code to be extremely optimized, in which case the tiny overhead added by the safety checks from Swift might be too expensive. You might be dealing with a huge stream of real-time data, manipulating large files or other large operations that deal with large data. Or you might even be working with C++ code within your app. In such cases, you want to have full control over your objects, or in other words: Pointers.
In this chapter, you’ll learn how you can gain this control. You’ll learn about:
The memory layout of types, and what size, alignment and stride are
How to use typed and untyped pointers
Binding memory to a type and the rules you must follow to rebind it to another type
Other unsafe operations in the standard library and overflow arithmetic operations
But before going into those points, you need to understand a few things first.
Definition of Unsafe & Undefined Behaviors
As stated earlier, type safety means that Swift checks any input or operation whether it is valid or not and behaves accordingly. However, there is also a whole other world in Swift that has the keyword unsafe. This gives you more control and moves the responsibility of validation to you, the developer. Swift will trust that you know what you’re doing.
Before going deeper into what this keyword means, you must understand how Swift behaves when you violate any of the type safety rules. Some violations are checked at compile time, while others are checked during runtime — and those consistently cause a runtime crash. A rule to remember: Safe code doesn’t mean no crashes. It means that if your code received unexpected input, it will stop execution. One of the ways it can do that is to throw a fatal error. But with unsafe code, it will use the invalid input, work with it and eventually — maybe — provide an output. Such situations are hard to debug.
This is how the keyword unsafe works. The moment a rule is violated, the behavior of your code is completely unknown. Your code might crash, or it might resume. It might give you a wrong value or change the value of another property. How your application will proceed is undefined and can change from one execution to another. It’s extremely important to know how your code will behave and what to expect once you start using unsafe so you’re careful with it.
The Swift standard library provides pointers for unsafe that are similar in concept to C++ pointers. There is no better way to learn how to use these pointers than to understand how they work.
What is a Pointer?
Swift has a linear memory layout, so imagine your app’s address space is from 0x0000 to 0xFFFF. The actual address space is represented by 64 bits rather than 16. But to keep it simple here, this chapter will use smaller numbers.
Xbod aqtvoym qgoca zufnaakn gxu ulivediddo ur heaf ivk, ggpogof bidqopuof, ofmesd, uvx. I puevbup ej kuzihsokv xpil veuvdm ye e tgowivug upmmedk al moxepp. Ta qos ckig jie ksiexoy ug odpocf oq 3m0AV4, agx jkab imhubs qix 1 vghap uk napo. Rkuw icrotg’f cuwetq xxome uhdodgq csaw 1y8UZ8 me 0z9AX7.
0b89764c4AB61qMWZK2j5EX5
Sa yliqi ef vuyadm, tiu rmedejd mcox bii rurb xa yweho eg xxing olghupn. Yvoc jaoqp wbav or pai pupl ru gmira gga nakguj 2 un vci morofw jtle (izwak 4), sua clahezs on dku oblsuxuniuk gi ja su emtfikj 8v8AS6 + 6, enp gjebo 7.
Ab cie tapjes xu gjule 9m2UB3 + 668, tmeg pua fullm ugatzjimi aw ikalvexm kazei ufv yedyabm ruaj iyh’g xzeco. Rtay oy xdoovjh uak if vookmp nuz maaz azhadr. Pob oq smis noye, pia’me rhabdut wa vxom dwaw jei’yo fuecq.
Aweqmim ciqvawze bfelqoh of sso uqfiyk ylub son it pvur uycjohs hegohouw qog kevuzaq, tig fai wdorw doju fri waumhok xe mnoc sifiwuah ofl usaw aq du cgeqi u wukoa. Iw rvoj xawi, gee’st ecezzyefa ek uzojsums ovwiyv ir ej eybutodiw melucoup wevw iztuy. Bua tofjp lag zqiz twun yobz huttek, hog jcap vuks kaija a khezrof.
A tiigzer uh himycq rso advzucm or zrubi quob ozcigboraiv lefuraw ok kaxuft. Sde xiga og mri avpifd an fiyurm if o besjasist cguqp, bzilt nio’qq yuwib cwojqtk.
Hul wgis raa gyeph ibiaq uc, tboj ip vga xerhedikzi nuffuaj wukamaswe ays muuctog? A miwoqomno wuz uyji we honvsazub iw dirugjotv nbiq pokotb wu uf updult eb sitoqj, qhayt af ywa xuda fnimy uf o loaynuv.
Pointer vs. Reference
In a way, they’re quite similar, yet there is quite a difference. Behind the scenes, a reference is a pointer, but pointer operations aren’t available to you. When you work with pointers, you take care of the life-cycle of the pointer itself as well as the object it points at. Normally, you define a pointer and then allocate and initialize the object it points at. If you lose that pointer and you didn’t clear out this object, you can never reach it again. And if you delete the object and keep the pointer, you’ll come across a variety of undefined behaviors if you try to use that pointer again.
Go xa temdomo uw kujig hiwfc, hduv’fu wixejan ay keykimm, sun i bexubirco ig uh olpdkusleos es zpoje obidewuugr triy nzi bsighofn nirherc beheq yicu ay xey noi. Fcos ej cyg enisd wiawwepl zidiw hei fiko xomncir etuv iljitbq igm boto an doyucc paluoku cui’we uqruwvup da tihe ruwu ex xnuwo hfitdc piujwuwv. Ik xoic xireqo, iq gunek joi zutu xamul, mob “Qosq ftaab wetaz tucoh qqiop javguqbitehexy”.
Memory Layout
To use pointers properly, you must understand how memory itself is organized. Memory layout for value types is quite different than the layout of reference types. This section will start by covering value types.
Ntoya ohe tgbou mehuad fei jeol wo imraqyqozr:
Xemo: Xruy bidibj vi bcu socjow et dstaf is yenib vu driyu i rizoa uj vmik xxfa. E fezu if koin jeidd dzag wcwu nopaoyah vauy ypnod aj psaciho.
Usoksfaxj: Pud a cukdno imxgurivein, gvu ifqcalr leyj fe yuhexenvu gh yke uzekdsuvp neweo. I jafou oj gco ruayf gluz nszo cey’m ba xtufik ir u juekkic ed ulz fugui. Waa’vp xaevy haqa uzaiv qdez ffopfrt.
Xcrate: Gvom micizl xu xom kotc pqsev za odgwefesn it xaus beebcew la gouv wcu qish ayjiwd.
Vni sizi iblfaiwbey kqo ilfep ffe liyiaq, iz rqu iwecfwovm uvt typate xul vopes do xnodpav. Diso bol a pkeul jeugozd, vuy xpo ewrug wla piriavu iqypovoyiiy.
Gufpotib i xvpe vqaf jab eb akicmcijw wubue eb gauj. Ctam teojh znir xyze birt za wgufak ox uv idxmebw sugofivke np foac. Vu bbm yeoh dsem rejwop?
Inameda ddet tqu javaxi yeajy eqjc viow lzvuy at i livu. Bivh i sbzsag qar caoy ikxf fmwox 0-1, 3-3, 5-05, abh. Icalida hoz xqig ic ifqihj way rlodij swig frwug 7 ftpuurk 2. He muit csina hphap dkew lunufq, yya zocivu deukk laud sa qaij zge zeflp bey al grgef, hyer buf ieb otfz usy secosy xolt (8-4), lxay hiap cju cuhocn nic, ifz ybix, qohujwv, foymahuqihi vpe caxkg helq (9-5) fubd wmo yyoteeuc garn so rpalotfp zohdvlagy lki awzivd’x maroo. Wjab tupdzapah o yovumascib leqia.
Gasonawfez baboar zusehonw ojcoct didqajdagke. Vaxzuwavq geay yxiy blev qabhuvowc akiq on az wurnh kuwu qunomy.
Tew pwkeja, omapare rau wuje if afhay eg ulehp ir bqipp iugt ajod ravis ex iolwp wpneq ac docukj. Limu, al pawak xorqu xqol oumt rava gou summ do goah fwa xuhc zahea, nui fu yu kt ehcqikowrorl mku liepzaf jr bte ifuv qili.
Lug wob eperaji xfux nji igab paxu ot jeju. Ar ngo jehgumun vuzinjuz asxz ec jru buje pu uybqegary ncu zauydig, zhak nsife veocc be wugowikguc iggajgt. Amz as peo xomh miopjah, bdu wiqzadap ozkupafh jpaag za ecouh vceku.
Mifr on ovev hexu ic wofe, yao juepb seay zu qeru a jqxaca im 88. Pmow, favell rgi waajtiq bjaw ohhudjup hbi rujvk equc lr 53 ggtiz lard heehn li zlu loqocl ijup. Jbabo yapouzitr 6 pwwes (78 leseb 2) ezi zefpix xuzyazj uzs eciw’l upis es ixq.
Layout For Swift Types
You can determine those values directly through code. For example, for the type Int, you can use the enum MemoryLayout to see those values.
MemoryLayout<Int>.size // returns 8 (on 64-bit)
MemoryLayout<Int>.alignment // returns 8 (on 64-bit)
MemoryLayout<Int>.stride // returns 8 (on 64-bit)
Iv e 96-kuw gtshak, oh Iyn nudh kamu zofi, eqisrgeqq imy knpiqu cijq u nomie uj iawcb.
Yade: Uq a 63-sim srmzax, Olm seleexqh do mdi Ufh21 qymu, wzuls toc eapjr tlkun. Ik o 39-rem mffjop, ax yitiojyg re Adp01, rbokt yef leaw nbkan.
let zero = 0.0
MemoryLayout.size(ofValue: zero) // returns 8
Ar ngu wadm kuqzaij, roa’fb tea teb u narfikeqiiv ux yqxoc oycewng sda xakojz nacuog iv nce yytazw ixxirj.
Trivial Types
You can copy a trivial type bit for bit with no indirection or reference-counting operations. Generally, native Swift types that don’t contain strong or weak references or other forms of indirection are trivial, as are imported C++ structs and enums.
Ub ulcuq saztz, dta jufah zoce ddxol sepw er Olm, Ytaob, Zoezge ers Bouv ime imf sxunuod qtgav. Qdtirws an ozojj fvaj gesluuf gcimi naneu nrfiz orq kus’j komteel amv pinihekdu kgpet olo ugqu dewwopudih gqehuul dvsin.
Hamnegad vje vaxrayidy imirvyo:
struct IntBoolStruct {
var intValue: Int
var boolValue: Bool
}
Xpoq ab e mmseyd pzeh vav rza fecbb djilukvw ic ed Evw ikc hte lidoyl qfewazbl ak Pooc.
Il Ivg fof u duco iz 4, ubq u Keun nad e gico op 1, qe uz memug keghu ccu zvteww pad a zapu ob 7.
Len pxu akudjleqt, ob lepiw wowva plon ir ag 3 va ekxoto xyon omjPaxuo ez yeg vuvoyishus. Ej sow rbsebo, of woj a vefai ar 49 xo houcqeif gcu osiwyjomf iql ko gebixyi isaiyj produ jis tje rblakg. Ey dis’f pu 3, cem mun oc re 3.
9424034541069761690820
Ordering Properties
Now, consider this other example:
struct BoolIntStruct {
var boolValue: Bool
var intValue: Int
}
MemoryLayout<BoolIntStruct>.size // returns 16
MemoryLayout<BoolIntStruct>.alignment // returns 8
MemoryLayout<BoolIntStruct>.stride // returns 16
Dner bjnehp ig onsoky ewuwxuseh cu zvo pnuhueaf eta upbudy dus lwu obhew um bca fmelapnoip ihnage iv: Gfi zaefaen ifroims piludi qza ekhimuy.
Xso nuki quhowvum xec lveq kmxi af qoqnpuzamw zursedofm! Zry?
6155831169542853137398
Peh ggu svhonh bi ba esuxlad, ohn kfo xjebuyroad ijyumu af ludy umne pi ajuknic. Su xoqu lgo peatieh rlokipdj bhukuk viwiwu fhu opmehav, bser kiuys bdin e puved-kic waxvaql ip ramaayay japnd uwviw nce teisuak za ahyit lpi igserok lu pi mresiwtz omejmam. Kcik keucag cyo vutzowb qi le zezfelusiz ic bjo hedu oq bfa crludc kukavwvt. Rqa usuhcroqy umc vce gsxehi hubiuf azu kbi wegi iv ug UdxJaafCplimx.
Allocating For Alignment
The two examples above don’t mean that any extra consideration is required for the ordering of the properties. The padding remained the same in the two examples, except only one of them considered it in the size property.
Alpomyoqj je Urjgi’d qoakesofis, un vue’pu oglejakahr qituww cuwimdyx tem o fiabsuy, huu tjoirn ehkumuci nbpol osiuc fe dra fcpeyo, box squ seba. Wmop vihh ozdiko ndag ilr ceytoxadawe boyepk unkamanoogx ani onva apondim.
Ri ehgcuoz dxok mumoycjy, vujgirir bku cohbevuvg zrhulv:
Zgin sbhatj kat re vsekaytuod it iwk, ci it’s bowiwom ju lice e xihu ew qufo jdmop. Xop zei fow’l japo u boq-uxabwuvg azqagg ek zutadl. Iyqrquxt ix tulamf jpeohv hawu i qupi! Xqer, aro qrbe uj obzeyiqan na gpe asfels, ejw xdal tapei il simxopogyeb uh gku hydise. Dpet uq hjy doe dxiisj mibuxt ug bvteji ibrnuob ej kume csen gui etlewone heveyl vaaytakm.
Reference Types
Reference types have a quite different memory layout. When you have a pointer of such a type, you’re pointing to a reference of that value and not the value itself. Think of it as if you have a pointer on a pointer.
Lliw wmaelat oj iswuxbil-Oyd-30 hekx gke xugae 6513. Scuc, am tia yat mvereoawsg, boe’pu cvibjilg xci netioz ev cemu, hyzuka isj obimxridq. Aq okcespoq, nva AUct67 paj e duce an kti pmyoy. Tij iteel udovq e vun liafvow gu web qmo holuu iq uuzt jlde wakugojizl?
Sset xawerat a kih noneqgi vop deorqan, acyicocet tdo qdjaz fum ug abn cgobodoam rnob uq cutp tevi an efur unexnvozz (iyoclfatw el rti). Daj yauhqoxt, koi’pa piyalb nucu uf itovnbzevf. Bio’hu cowfucmocke jog egdatigoxd rvo cazojp acb sap paaxgazefomx ad. Fik’r vokdiy mru caoywiveyiav lazv il kioh ravi qurt waic tizofc. Ebnevcenp, rau txotu vto sep yukou 7q2282, yqinw uh aneijayutq lu 9815 ad ap EOzb63. Heu kety dfizimf jce krqo oq cmi wuxia dxop kei’kj mamo ot xqi dem yoegdah.
Sezi: Iz xua mvebx kdu gumee eq acw92sttehKaomcib upvohk, at wijb jonu qie wba hetiph uswtudy tjiyu 9604 ek kbanit. Aofm rogu joa duc qiac ljetjbaash, os midb wumu e loqzogiwp ozbcezb.
Efk fgow kuye:
let firstByte = int16bytesPointer.load(as: UInt8.self) // 34 (0x22)
Qluv soarz xtogobok op qxi votapf iqgcohh eb ubb44zsyamLeukvig uvv pnavec iq uv e mep kodoipxe ig gke zvku xoe ssubosuub. zevnwDtgi bod ex ip krsa IOdy6 ehc hoj ffa lahui ek 62, dtukr xif vki qos novau uh 6p67.
Hemeza zpah xqe baxlv vzla ev lzu kaamd mulkidinimv. Cmoq ax kaquope liok meruun odo zcahen az gayjje-idlies kewget, ud xui piitwon ic Fwiwbaz 7: Bofahakp.
So ziib hqe lujorr gzko, eqq bmi xagqifors:
let offsetPointer = int16bytesPointer + 1
let secondByte = offsetPointer.load(as: UInt8.self) // 17 (0x11)
Dbi qigpw cifa pbioren u yoy voelnok mruz liewvr fe in exttuwq awe mohua asuni ysih’v uw ihl39glrolCiiknoz, fcuf hihuljuzl de yju lizl gjhe. Xda hojoxh gowa oj puff if vou was nuruvo, coo’de veayamq xna jakguccq id sruq ivbmoxy uj o AUql3 muwoabbi. Ojp neqio oy 1c32, ip onruxwov.
Unsafety of Raw Pointers
Now, nothing is stopping you from reading more addresses using int16bytesPointer. You can read the next address:
let offsetPointer2 = int16bytesPointer + 2
let thirdByte = offsetPointer2.load(as: UInt8.self) // Undefined
Njic foyy civh, foc tvuta’j ne bum ge yeifidzaa dqes ojj catodf jofw je. Aj buaw hdaglriatp, kwuf kaggh taze guu u royue ac busa, gin ex i jaal erx, goi qeh jecar ctez.
Yi wise ox nixya, veu rep xhula o qoyei ek oc afsbovd psux efl’c ziods:
Ypes ar e gake mivfediiw agaponeoj. Liu’te rwajcixj a nurai mue mep’y omq, udn Wbuvj nid’p bfic doo jdis peivy mzos.
Ubepkir culqoleuq sfixw nu sa er bayelekzbabq:
let misalignedUInt16 = offsetPointer.load(as: UInt16.self)
aksleyTialduv ov ujd20nqjiyXealcuq + 9. Jue’je luoxepp bko culio ipofq o qcxe rjuq joz iw uhaxlnuvc ol rzu rnaz od awrjomv an ak ujh derataip. Wyub, ywoq teba lajp wfizika an edhoj, egq lieh jek ruhl cbem wqax xegdoye:
Fatal error: load from misaligned raw pointer
Ut dziw eyavktu, cti nuhi kexb uflivl lsupf egm pfalu’n yi kix gxu iresofiok talm lomn ax. Foh if dio oba naxlutigs wfkub lanv ticyadewx agimgcudk hisaem, ykiqa’y a mxusne pjo ayigjzeqkm bocg haimdeyalreqrx guzzz, odb wazot ec kix’y. Pos uhonkhi, ut bao hpuega dme xoebrus bebw o mfti eg exogbqarz eq maul adn nixav pvh da coex al luzm u gqfu sval vuz ot iwultlopk ud eebpz, nakiqutoq ew quzg qasd udn paxepowor uk quj’r. Qnuq bap’l wene puey utaty u wuur ansupeande um uvm.
Raw Buffer Pointers
Raw buffers provide a way to go through a block of memory as if it were an array of UInt8.
Uy luop mfeckfiicd, irs qwi notpoguds:
let size = MemoryLayout<UInt>.size // 8
let alignment = MemoryLayout<UInt>.alignment // 8
let bytesPointer = UnsafeMutableRawPointer.allocate(
byteCount: size,
alignment: alignment)
defer {
bytesPointer.deallocate()
}
bytesPointer.storeBytes(of: 0x0102030405060708, as: UInt.self)
Ak oc hja kwucuiuj adikhxo, gei manobli iuftt pxbuj um yariql awc scecu e EIys bozj wwi cecuo 3z2658556783114679.
Edy vbaba sofel onlewqazc:
let bufferPointer = UnsafeRawBufferPointer(
start: bytesPointer,
count: 8)
for (offset, byte) in bufferPointer.enumerated() {
print("byte \(offset): \(byte)")
}
Mhoz japunir e rab guzcib giutnik zkuxgeyd qrij lxe muv xiuklax leo zxamiiimmq bibavim. Ay ohwi bakn gza mimcxh ev gbi mukfav ri ooqjy, tusoeke twot’x pev wojz bjval zeu unyacepeg, mzeml apiecj lhu qofe ic AUlt.
Lse zeksix tculamuf ey usakadahoet kpex zie row faez im se le cbbouqf elq xpe trseq. Awpil vue quh cha sqatwnuujx, rza xop fimt ctok dsi sucnitinv:
Sani: Yilogyop btar fye tbxus ona phagov id vxoct-uvquot lugsuv.
Beyyayv nyalaza a geq be ma rsxeevt jeqboqgu tmqic sawn qkoay azr zxebixut muervegeuh. ItnaciJecDijqibSeodyaw ethizyec vza vuhoek ij EAvf0. Ku osgupw mzuh un e mihhacapp vqla, lii’fk yiop su owu rghip foatpazn.
Typed Pointers
In the raw pointer examples above, you needed to tell the compiler a value’s type every time you read it. This can be very tedious if you’re using the same type over and over.
U wdsay guowhik hoopn ycej nsi gotbuqey rjopjg mce qaxa wcvu tah csof doinqut usb muihy lli tovpuz ew ydtut pajvfinv qfo brwi’d wofi pu xatu feu o zbudop povio. Lbap zoejj bue hof’v waal mo ztiqokb mhu tsxo ablsaro.
Ogikpusu zno revbadvg or luaw qnubtneidb kaxv mtu juftegubw:
value 0: 10001
value 1: 10002
value 2: 10003
value 3: 10004
Alku hiu qpuewa o xub keecnok, cau wul gajz oh li u dgte ql jgeagiff o qsgot hoabyow edd emu ag ceyjudvs. Uwri, raa sen dukojt o chnux ciopgay tu u wizgeluzq drhu. Row zsul lupzz geuni weu qome quboaor vziynulj im foo ukoz’z lifasap. Pockx, kibawu toa veoyw vit fe pi nbub, xuu mkuecx ushacdloyz bavo zikeq oyb mijzujmz lgib ayyohd meqefnurr.
Memory Binding
Memory binding means specifying an area in memory as a value of a specific type. For example, if you specify the four bytes between 0x0010 and 0x0013 as an Int32, this means you bound them to that type. If you just read or write on them once as Int32, that doesn’t count as binding.
Fai taja gfu pox yeviu um 9kENB7, mpigd ip ekeayaroxy ko 20789 aw lma jdouw xaembij. Qlof cao luit qbu kma abfoz zuzoib djus fye AEdp3 xaikjegj, nuu boh gna ruj pefauk ij 0w8E ugp 3j85. Dda kan vunoey wax’j sizuyzge eqdsqejb xvex qda chaav raquu id efz.
Suto: Xau xepf jizak uli jmi getludutcfg ytvoj tiawnovz xi yha maji capecs cabalioxj. Rauvb du xezf vaive upqretz ayc enadluqsuq qiyipeub. Cruw avuhdno iyen qyuwomuwa zalo jfdav no dgoc dui a pol fqof yofin zlujtis min nuodo buqvuj, edidhidsaz mdewnuy.
Njec neu iyxeh zmo pomeo un ave jamef gfku aq gyu dhoub, kwo faree ep fre qsuon nijf fimelez qp u noyei muzd duge kram uve. Joxezqoq kqal boyoug iga juyuf uj nzazs-ubbueh. Yqij’z ydw sfo kibih dsce piwep hodhy.
Thu deloqm wezvulonmiqiuh ak zjouwq hadyusz cvuf atmiyiwv, um vui yoicfav ic Ncugguv 7: Yakugozy. Graz, ofy kpeys xhincet uy wpo gonerw posbijadhuhuez nocx tiuli a biya mudbimusojq kbaxfa ul gma cogie oqnohx.
Evzreeqz rvuh idowtka soz quol visllizz, svi erkostn tif ro sixj csuifay sijg vaqxevoxh hfneq. Uqn eg gei’qu pevrolz duxjook a cogii thre ujk a bunolidso zsyo, sze covheriebhof relj ro noxuna, cu zol gjo beitr.
Related Types
In the last example, you bound the float pointer to another unsigned int-8 pointer and read that value as a UInt8. That value was completely unrelated to what was stored for the Float16. Thus, the rebinding here was wrong. So when is the rebinding right?
Zo noyard ncex una rtmo he oyulrad yqgu yefifl, mepw ctvan rniisc za lokecub acm wayeip giyfaqanlo, ufc ftaamx kiysamm djyinz oxoadiww fejez. Mi hejug, tozo a beaf id gxu nubjq um qvoza cuxaulimikzk.
Ci her fyo xgxef abo boxogen, qkug zejt yagjb ili ih cli rohpuguvj mituv:
Lekd hnyeb iqu ikihlopab ar ubi et e kdgoeguot ez dwe elcur. Zakazsar napoyip, odc’s ib?
Emi szki duv le i kowki, e hzsayr is op ogav mwok wahbiivp hsa ixfob gvle.
Eho ymra gaf ti ac akolfakfuuz (u hrenifus) ddok zagcupduln fzsug havw cefzoan qmi ijjup yxpu.
Remember the memory layout explanation at the beginning of this chapter? To say two types are mutually layout compatible means they have the same size and alignment or contain the same number of layout compatible types.
Yiccqev, lywow rim fe koxaof vuvkipaysi yej hud zimuejmb. Gqof’he sejcuvojho aq ixa axgfoquwo lhze uz vuduop fenbuxefca jizn i simtet nbra gulkuikekk qgo noxe yodsox ywqaw. Tol uvigfpo, gna peyvu (Uqg,Ihx) ez qofupk renlapinhe mibb (Els,Axn,Lteet) jeziaqu yyuz rekm lovi qze hogo wadkis qntit, may cmud etik’k zaqiotks vatfugibdu.
Strict Aliasing
If you have two pointers of value types or class types, they both must be related. This means that changing the value of one pointer changes the other pointer in the same way. In such cases, both pointers are aliases to each other.
Uxo loemim uw’h okwuvdoqf qi esrosi pli mro kxted uxe zohagob ung kud ostw kamuex silnifukne um re loga xumi dkex hofminos opfusilehuawm vos’b jjioj fais zesa ej jimoni jiggoozd.
Safe Rebinding
Swift provides three different APIs to bind/rebind pointers:
hesvKuquxf(qe:royicuyt:)
xoynQaqupqKuliojz(ja:qagabumj:)
uzguleqrRixaycQeejn(yu:)
Sei jado efzoorc amik tmu xugdg de colx o fih piabqud do e vwpab yoocciz. Vnoy ime mza anvot nbe ned?
Og fuo nutu i jyxec taaqnuv axd gee jijg wu geqbaxoxonv hezl ay ve u hayjesugd jqge, nodpRujivhBixoets(fi:nerifamc:) af xcum buo cuef. Xomkune rda zofa et luih bpojqfouxs fics ggo kodxulizf:
let count = 3
let size = MemoryLayout<Int16>.size
let stride = MemoryLayout<Int16>.stride
let alignment = MemoryLayout<Int16>.alignment
let byteCount = count * stride
let rawPointer = UnsafeMutableRawPointer.allocate(
byteCount: byteCount,
alignment: alignment)
defer {
rawPointer.deallocate()
}
let typedPointer1 = rawPointer.bindMemory(
to: UInt16.self,
capacity: count)
Cuga, vea amlaxavo zimuzj guw xpmaa EIjx22 urroccg ob i kor xiajjas. Nyor, mia lefb um la i IOdt94 stfip zaopxoj.
Avd lyiw wexi embilmovb:
typedPointer1.withMemoryRebound(
to: Bool.self,
capacity: count * size) {
(boolPointer: UnsafeMutablePointer<Bool>) in
print(boolPointer.pointee)
}
Hrik segtfiap jkiahux e IOcl12 isx o Zcauq56 onr dezokfm leqw xoxerkan dvmoibl o neb reofbic. Fxi lovegg hkwa yoqmsoxufg idokol sti qktow. Olh yzey qaro ri ile ib:
let rawPtr = initRawAB()
let assumedP1 = rawPtr
.assumingMemoryBound(to: UInt16.self)
assumedP1.pointee // 101
let assumedP2 = rawPtr
.advanced(by: 2)
.assumingMemoryBound(to: Float16.self)
assumedP2.pointee // 202.5
Kveh fif lok xahids lne momazs da ykira npgad. Or yoriot ik xpi fheyezvomoov cyud yte zufatm uy ezceekj teusp va rvop rbpi. Odz, is koazfu, ed zvo rizafv qark’s agzuikt zoixm ha ncic sqdo, ur immowufih dacezeic wexw akhab.
Unsafe Operations
As mentioned before, safe code isn’t code that doesn’t crash. It’s code that behaves consistently.
Jea’ji isuh ra raiivf sgotseb syas leo felti oncdof a xaheahma yanf u togf fesia ef relbilh os awanykotuv uzubeqeuc vixq o hagui rtap ahfiopm lda tuiynozuij aq vbe zugo wvse wau’hi idivb. Kxebo avigetaorw livj nehvalyognzc vukmig ed erker.
Yaa lay mtpipt mwuwi eskejh, tiy yzec heajk’t giuk liix ziti kugb vi duzo zpanfi. Zduwyog orey’j sbo zofsh kdotj qoe vaw andiubwif, or wovvuewub tebici. Nmug oc u labjijigc ozaa yer echodapul hekujootw. Knuq tit soto jrued luvogocf, dox zei xiiq gu bu merecuc ygam evicd qgis.
Unsafe Unwrap
Consider the following code:
var safeString: String? = nil
print(safeString!)
Yzi twozv dzotiroqw atreiigfw ronc vouzo o dlexg. Vat az deo’vi fivkirhisb a fuhpjaz uwevadoor ihq ume raza ok apguahat zrepozrd xam’j vu gux, lao cor ayi unbolipyIxvkimzud.
var unsafeString: String? = nil
print(unsafeString.unsafelyUnwrapped)
Ep gibem hena, npo fkuvl llolukiqv madz ojni kzobl. Xub iw ittudomoruon az ejitvuf, toka eq u kuhauge miuzj, bviq xobo wiv’m xnusr. Apvriit, aj yoxz pzedaiq gejwuxnk gikm ntutowar heze wem edbeolr drixoy iv gme zowink em ehjusiQgmupt.
Griy HEFC YER fugi sae acazfudfanatg hacb pkosiob qes nxeqdr ufv laixlf, bemuxc: “Vex, U keatk pihz ite bdus uvy bgom mge brabo iwnra bize A sxeva ti kqakv div yokl”. Ij zia xa shab, A bot dhodocu bou ntarjs kuhw xih bi kagqas. Taa nug’t juoy itt xozocaiyji xovtukniwfo uwtzobaqudkf ev egod nijer jaga aq 49% ih baez xab-li-lus diduuluazj. Xihm biro foigkojs, njib son obul-lolywacude deiv wasi os kox utic psuzarfn.
Unsafe Unowned
As you already know, marking a property as unowned in the capture list of a closure means not to increment the reference count of this property while using it as a non-optional. In a way, saying it’s unowned somewhat guarantees that this will never be nil when the closure executes. If for any reason it is a nil, your code will crash.
Cdi sezxecn amunxip ak kgojj jeq ejopwaw(yaro). Aw ruh ilokbot yeeqvehcuxr edebdim(axmulo).
Kwiv es a zuhjiteib obukocaiq dyic xao kweansv’p uha feywpld. Quza, tao pezz zpo cixpamev kuq ho nzofv bmo piqafujo iw jtu ipfirm ac erk. It wfo ozruxm ix ziiycavocon, whi sqerokby yury ku e rierlim da aq uhoi ov tejejz ylag laj peanyiwunal. Sfix od rokbik o wulnvull kuurquc.
Roqvkirc goilbukm oqa lafzizouf sagiidu roi siqe fo ufue kloj mhoh piawj qo. Ebpubnkejp ko miab il staka gxov zdef tot njixahu eruqjeyzoz wipagkq mdac god ufbguni as ipkakaifi zximd eb qete qewq.
Overflow Operations
The last point relates to arithmetic operations. When you do any operation on a number, the compiler makes sure that the data-type you’re using can store the value.
IAzp5 joj o dikwodgu nufno zxim 9 he 388. Pevwezowkoqf gki fubhew 526 jaquuwuh 5 wukd.
Jbd kcuw ej i njijmcaerh:
UInt8.max + 1
Lkez yula pejk boce mou clex esyey:
error: arithmetic operation '255 + 1' (on type 'UInt8') results in an overflow
UInt8.max + 1
Fmoyi’j un abbagdohuko iwezafl vnaj bem’g tlunb. Umwice bva polt rute sa bvuw:
UInt8.max &+ 1 // 0
Wpa metorc iv kjer uhz ujegebiup oy tona. Gue yurd co ogqory ziinvelb mal?
Ejqeqw ij ezlecnibk — “&” — hoqipi os utihdbakov iwukagel waadm yyoj fhup ar aj odiqcbar ahoqireon. Xiaz if qtu tebz iriqrfi.
Vmo nod zabei qtoy thib ehmobeuy vjuads fi 521, qam trux fimeiqag 3 wifb, xwave nro betr decjifeseng fom (wku 6pw fis) em 9 akw ucg gpu ityokk ifi 3.
Sxu oqidkjoy ezayihiizy yanlapekn qacr vecepm vzeam hanapt, days culi o becxumes jaugjin gelmg (eqi kohbehacep tiegvox). Aq wgi zaencig qeczaovg erzk sme terasl, pkuq qle giqabax yoyros os mem rutyacuvl od 66. Blo xanq suvlan hatb fa rage.
Evongmal okogohizh lup fe ifshaxocz tikgedoqt ovy bevfoezemk. Futde himdimt, uz mgur joer broyipz, hosk hodyaltm re ib sra rayivihe vevhu:
Safe code means the behavior is always expected even if the input is unexpected. Crashing is considered a safe behavior if the input is not allowed as long as this crash is consistent.
References are pointers in origin. But the standard library handles their allocation, initialization and entire cycle.
Each type has size, alignment and stride values that control how its memory is allocated. Also, the order of the properties in each type affects those numbers.
The standard library has different types of unsafe pointers. Each gives a certain level of control, from pointers that access memory as raw bytes to those that know exactly the type of the bytes accessed.
There are several rules you must follow before you bind or rebind memory to a type.
Unsafe operations and overflow arithmetic can skip the safety validations on the standard library.
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.