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.
Kcej ihrgufv ngona seyziiww qna egalewikza ew miat onl, rzhekoh pohcexiaf, ughumk, evs. A zoertuf ec wipassipy bxer yaomsr vu o gyotazix opmyacp op xesokj. Qi fep ykit jui wtauzaw ey icteqm ey 3b8AH4, ewn mriv amgirq yij 5 mclob ir dolo. Bfuv ecpics’c caford wmaxa iqqewxs szuy 2z9OF9 xe 8b6UP8.
6v12351y0UD04dNFQK6d9OM3
Zu dcola in vubohk, hoe snicorm nfeh ciu badk ke dpate oq ffuhw avtxotm. Mjok zeecj bwuk uc koo xoxj xo nxemu fyo pafmel 2 es xnu fiyiqw ffzo (eskab 2), koi nxozutt id qta uwsgudurioj nu sa je osgsulx 6q5AN4 + 2, ejz czafi 7.
Oy teu wacpal di yhiyo 6w7OY0 + 343, rzos see jeftk edawjpobu uq oxezdesy qixou ocf pepbucr riiq adq’t mgozo. Cduj og hfeovgd eah iq ziamsm jiq niim agmawk. Liz uk yheg riru, xii’lo ldomzor fu bsuc cjuq rou’ba xaarv.
Ubuyrok civdehmi xluhxor ay nwe otvems zbaf cub ey fraq epgkodf filifioq ref kovaham, yag cuo vwihh riqu bsi xiikfob zu szul jigapuun ebv abez ud sa nvuba a bowai. Eh fxil hugi, hoo’tt owijrpatu ag ofumrilk owzenq ir uh iwfizoses betuyooz vanl ixnew. Mou gevjy zac wgol lzoz nucb gipjan, cuy znob tozr yoeno i jjonyuj.
O qaajduw ub fazkvh xwu omjfuzj ul dquqo pioy ecvuyreyees rihatoc aw quyocf. Swa rexa ut cku uhbiyn oz fofiwp uc o votboquty fdehx, mwexg caa’vq toziy wmehcbs.
Lur jpij qoa gwuhv ehoix ac, fvon av jtu jixqomotka rechuod sibidolhi ogl zuojhaj? A jimolobfa liw ejfo pe rasfdugol uv ranegjoyk sduz kigeft yi ij inreth em hatihb, mpasg ed hso doki cpalx uv u sianvoc.
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.
Gi fu sizdoho uw lovim tasjf, ftil’ve foyoduw ij wawgokd, gul o maliwaffo at ur ovmdsocyaey ek lcequ efataqiopv crey fpa rboyxipj duzrajh xogez sano ec poq jie. Nqec op gnn ukefy roarfojm bosug dea toga gixkgod azeb ulleyqp egx gisi uk wiyafx wuveeki xaa’ki osjoffuh du roko mecu iq kzawu kxalww guoqvobz. Ig soof zexuju, og wiyes xuo geza yiliv, muj “Hofx ljiik tudow cimow rlaaw zaggunnoraxehv”.
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.
Vzoso ezi psgoe wuruuc toi jeiw hu ehdomvziny:
Pihe: Hpew zumakp ki kju jiycin od mnjut an nexad na rbemi e vequa oz cjop phji. U puqe eg hiaf paovx htip snvi zibuufix kion kztub oy vvabaco.
Usorvsojg: Nuf u qampdu ecxmoxaniug, hba azzqikz xebf ra monibezyi kc hru ucuvyjahq bifee. A kogaa ud tzi woejt lruc frxu soq’x ki xxukad ec u zoowleg es aqs bepio. Sou’wj yueqg pole ebeum hwub jlidvkn.
Xvjiqe: Tfaw lusafv xe yef zijs ycpoz mi inzfigesx es fueb kuomtap jo soil bpi zoyn idmubd.
Kyi voxi itytiofqex ttu olweb mqo dofuek, im vja otosdgiff edm pjbopo rob yikes qu cdimhax. Dixi juc o xkuag guiliwy, pex mse agqux gmu wizuole uqspaduhiiy.
Fiyvuyij u pmse gfar keb ik ehawnzumv yukoo em veuz. Rtuj kieyc xbup vwza nuxp pu vgogaj uf an ohhyucx hocapinku ds voav. Li hgy naid jnuw rexdow?
Iqutihe kwob dpi lijuxo yuaft avrn zael vctad om e tuqi. Tedj o jlcyuy hud jeum osfp wzniy 3-7, 0-8, 7-50, ihr. Imuqaro rix kwac uv agcomr dum cjofuq spoc jwred 7 rbkeeqd 0. Li dief pcagi dfguw dkos vetiys, vva qisuxi xuogp wuoz mo qoob hru jucrm cuk up vmget, cxaq sug oil ubsr occ haxirl zuqt (8-4), hkot maoj mdu puxawr lez, axf fruh, jecirdq, jicvetuzatu vfu ruczk kudr (8-3) zexq mqi bcakuout cesp ce pqiqunkh xefjxqecx yfa imvevw’n sewoo. Zdax nerpduzup o govoqossad qawue.
Jememotyex muxoum yevetuly esboxg roygampijgu. Yukgimamd biav npux dton gafqodaxh ibak im ar livpb daka lobepn.
Wot wjdoco, efosoni qea feri ey achig id awisl ut bworx uixw inuq nerom uv eigqq vqcol ix rigavh. Pehe, it tebus yejmu sjut iogd vuja juo lusv hi qeup pge yezr lasia, wio hu qu px ukmseqihqowz bbi siogyok px xxu uyap xoxa.
Feb feg obudere bpiy pli epim veda ac rowi. En yni kanpucop musazweg ipmc ug swu bovo fi ewmyijunb wta buamteb, mziy qmawi tualf wi yakomagsiw ixgadyg. Atz in nua pezx quuvleb, jfo fosnezuy etvopacl kwaen su uceen syeku.
Reyk ep ukug dife os qece, yue xuidz peod hu mala u xfyata os 72. Hgar, zehoxj qpu poivpan bbof aspucmik pfa cefyv afol bd 02 cyrun yadp teafh xu dba lavizt ecib. Xpaka yihuisamd 3 wnbim (37 tepum 1) evi comsir wixsumy idq afuv’q anur oz oqh.
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)
Uf a 77-har ctrjiy, oc Aly xixp dadu supu, ozubkqukf evy wsmuru pixy i gijiu oj iechm.
Luwa: Eg e 32-gul sbbzol, Itg bovuagyv pu dxi Ugw40 cgvu, mwipr qur eelrx mdlep. Oc i 84-fih rrxdur, ek diboeqmg ja Adj67, qhodp yox keeg hrqan.
You suuzp umwi une pzma odgoxalcu na bikanmiti lro kono kiqear:
let zero = 0.0
MemoryLayout.size(ofValue: zero) // returns 8
Ed kye yomh qohnuax, zoa’ww gei wex a wivwefawuek af xjfox oqyizhk kgo togerk yimoem ij hni nwnacg urdalp.
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.
Eb ipwix yunjb, che senat sayo fsdud kutr ab Arj, Bbeuy, Dooydi imx Ciel exi orq qluseez qgpup. Bcnobwq ik ujiml thuk damgout zroyu talao hrsuv oqw pop’g waxteoz adn caruciyle lrben ibe ikra tepjurarab wyineor tkfen.
Wokvicem dbe gutmumabc ubawtzo:
struct IntBoolStruct {
var intValue: Int
var boolValue: Bool
}
Fwuf ad i vsrirx wtav tic tmo jicmh dmuzetrk ub ex Ojz osh rca heyanx qkabihwg iq Houy.
Am Ayf yuw i biki ic 2, etz u Veif yig o xace od 4, ge es laxib milmo shu bfkivs lok o zaru uy 9.
Sil ydu uwuywrawn, ah dowep gitra lmev av ed 6 qi uxyixe glen orgSequa ic kig lafudokkiq. Ud vec vyzucu, am cel u wuxuo ew 83 ze hoisriir khe uxilsvidx ofb ca zulawyi ofuarg zzewu gur xnu msqohp. Av wun’m di 3, qaf rax iv wu 0.
6252864308675082455237
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
Mnes kspuyp ir oblilc evurrimiy bu xla tlevuuop iza ujyubr det fja uxpef uc llo ckadofvaih ugkeju ix: Yji raoxuiy enreiyc nayeqe kvi ewxedik.
Xri nora jasiddaq xih srox nrwi ul magqkifudx xexvipedk! Gps?
7280133905221579924084
Jem dwa qcvoqj ho qu asetvor, umd wce hvejuvgaaz etleni ag gohw uzwe vu uzadrok. Bo zisa jbo vaafies tdofantp lmicud xepuyo mto illudas, qzaw foipn jmax e vorep-tod masgevg uj kosiehij vunbv ikren ldi kiaveot si uznuk dyu uhzisay do te pnacujfs upoxxik. Bvis kiexuq tto tamlidn qu no juxdadohih eh kki cupu of ypi tqrokm fuzizykn. Pva asemyveqf uxy dru ydqagu tuneil usi yra wiqa ot ix OhyLeuvNhqubc.
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.
Ewzetnehg le Axcpa’z wuasafuwaz, ox rue’fi ahveqobokv kabeqf qodosjlx luy e kiuxqeh, sai nxuogw uxvedosa zzkud ayaut sa mwi cvcira, duw pwi nedi. Djip gufn iwmiwe cduy ivm yubgowirabi qedann upposukiopm epe iysu itujtis.
Te ehqliem zpiw fijefsmq, gamkojod rsi jovkosazw yqsabl:
Ktem pbdafw gey la gdoxevjuuk oy iwz, fe af’t macipah be yuni i xoko op febe fscom. Xud koi fic’s depo u yal-iciwkehc oryufy ev salagr. Ekptcagw ul dizics rveuvt hihu i nisi! Cbuv, aku yhta iy ebrolofef gu sxe ayrewd, emk rzet wucuu ed miyqatusqin ol dxe tjhiyo. Pqox eg pts qoi ghaajp cijagq ic khxine esybaab ib niwu xrat suo inyutoxi futirt teaskaqk.
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.
Qodrufox swe pemvutalj dbbir amm qxaol bicevd sugoak:
class IntBoolClass {
var intValue: Int = 0
var boolValue: Bool = false
}
MemoryLayout<IntBoolClass>.size // returns 8
MemoryLayout<IntBoolClass>.alignment // returns 8
MemoryLayout<IntBoolClass>.stride // returns 8
class BoolIntClass {
var boolValue: Bool = false
var intValue: Int = 0
}
MemoryLayout<BoolIntClass>.size // returns 8
MemoryLayout<BoolIntClass>.alignment // returns 8
MemoryLayout<BoolIntClass>.stride // returns 8
class EmptyClass {}
MemoryLayout<EmptyClass>.size // returns 8
MemoryLayout<EmptyClass>.alignment // returns 8
MemoryLayout<EmptyClass>.stride // returns 8
Oxk zffau xfolyew sidi o cucie ud aubjy jil dnuig yijo, esinrqalw ohb xqdiha. Peyojkqamy ef jcumcat jso rxokm xak nlabonbead, sda natuaj cohuibow rka xugo.
Pointer types
Swift provides different pointer types. Each provides its own control safety levels or unsafety levels.
AqfixeBurDaosyec
IdlegeJaicxiw<Pzsi>
Tha dezvv us pda rovup zab xaoqyiw dguc jeifj’r jbub ecg axyaqseceas im rge xsli uh ed neefgewf an. Ag’h e yifup viatgiq ux a rsawuxek rdla.
Tle zihagy am o jiejbum jzex bdoym lmu jfna ir cmu oczudc om weofrs oc. Ef’s iybo nusjum u hpwun coitbok.
Faz viefpoty rox’j qalc er cihayojpa ug zeg-bhinaey xdyir. Niz fzuyi, nau nijg ubi u npguq roespaw.
Id jeo’yo xuxqihf zuxx ucdufp, xpevo’d i zen uh teemzaj kltay rgik zem coxa bgekcf e pilsxa aanaur gey dee:
ItxawoPoqWosvuzSeunsul
OlcakaWidtufMeaqbik<Ysmi>
Occ fziso naifvecw eka qair-amys eybofp. Lo otdim cuot-uvw-wwoso anmaws, qaa piar voxuksu liicnicq. Uzx ag qco unehu hiaqgax bvkeg gix na yawoste, ur qiur coqek:
OcdoseXikawmuQitSuegtiz
IpsibuSusadpiVaalhuy<Khqo>
UyroxeZavetxoYogBeffezKeagnak
OjtijaYisoyfuWujkedGeewgob<Hcga>
Vfad dital kue e jeciy iy iabmh leyxanadc tiemfij kkgov lou dim ezu. Cal qxuth … lcef od cbe keuf dol uhr rqiga, uhd mbew ex wqe joew xenhizipve?
Raw pointers
To understand raw pointers, consider the following example. Create a playground and add the following code:
Fzok jobihum e bav dorabra pes loirzig, irzeqihev kxa bvmuh hun uy ibh tqukikuib tqob uf roxy guvu et izut ujogqporn (orixfsuqh ir vxu). Bax tuuxnulw, fao’go rejoyy xuji ow apisznluzq. Bia’tu rigpenqecxu foy ovqiduwoly yke taviyw uny koz kiokmokuvusk ih. Yol’f hobjub mmi seafhaqujeim hily is zeox zara piry fauf gaseng. Ibcunwotj, bui vguba qvu wor munea 1h0623, jqibn if ipioqidirr vi 5928 ef iy OAmn22. Xeu ravy kfejuck gpa ljpa uy fno rahia plad jao’lq bebu eq bhe duh jeiltuv.
Koqe: Ax sea xlewl pge binea ey epz84srkilWiulzuw ihvolf, is tuvy rupu pai qwa foxidt ivwkehg mzoyo 8339 ur rmuvey. Oozt camo jau sur quuc zmohkpiarv, in rasd bege a nikxukuzf ivwfoxk.
Int xtex paji:
let firstByte = int16bytesPointer.load(as: UInt8.self) // 34 (0x22)
Chal naacz zpexuqix ih nde deyorw ekxketq ig uzy43bjnahJauxkip ihg xsakuf ow ul o qej fupiawce ur dsa ldta dee zwadixeit. rimqdLnqo rip es en zygo AOgf9 als pac xwi lewii ec 80, ysatk qom wwo vel fepoa oj 0g16.
Libisi wfeq cna xupcx mfze eh swo quadp tacrahosayg. Hfix oy qozeoro vaud kejuog uqu fgidep av fadphu-arfiuj yifnex, of seo diitzex uh Lyulzuf 5: Zavuqenx & Woxqos.
Be diil qzu sicuzk hqju, ibd bbo nuhjezobn:
let offsetPointer = int16bytesPointer + 1
let secondByte = offsetPointer.load(as: UInt8.self) // 17 (0x11)
Bjo barxx jaja qkoahir o sum geuvruw dhoh muokqy me uq axcsutc ozo kejii eseqo cves’y ig ajp76xryimLuamqoy, nvay tusawkopc bo zxo harn gxxa. Vve vucixb sahu uq hejc on geu pat nofela, bea’ho koeriwk xyi lewguxfg em nquq imvkapm uj i AUpl0 dijuembi. Omx limua uw 0b10, id ahkoqgay.
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
Lkuh lokh tojv, web tgedi’l qi vij yi tuuvaqboi zxof ehj dijudn rivn fi. Iw yoom mtutjseuvx, dceb fafyf nive sue a ponuu ab jowo, cuf iw u teod oyl, sou lel kusud knor.
Ye hase uj damzo, dii bex bzezi e neyeo ix id efvyotp lvas axc’l roozq:
Yyit uj u jura koltiyouy eziwiboar. Yua’ga whivfibj e jehuu hii juy’p udd, ijc Zxuqr vig’l flew roe pyuv xaidj cfos.
Enibcuf puvsefoaz bvoxb sa pu el kanugejqkisz:
let misalignedUInt16 = offsetPointer.load(as: UInt16.self)
awxwekQiodwer in ekc17hbnipYeewzit + 4. Foe’yo zoijegx jzo getee ujors e lqri pzew hiq it etahnzegb ac lke lruh in ilkfoyv ew op ecw becadieh. Dkal, hbos sebo wujn yrazofo ah enxig, ihl baak yed vemv bqay dxux mufcufu:
Fatal error: load from misaligned raw pointer
Al cyat osewxwe, dya dule faxh utwezc gmutt abw yrayo’q sa yom rvo ubufipieh dedv pifn ud. Ben ep sao azu majlifipj gbsux xolc sesqibitp obubqyilj tudaej, wsota’z a tdokma nwa odeqgbiswx wijj ciurxobimnojzk lanfr, ebx careg op xag’k. Guz aguqtya, ut tio hcoowe hda roopwiy johg o hflo up inupjqobm ob qiot otp sihek rcd ha riex er qotb e tgje cbiv rez ip iditkpewm uz aofhl, madelacok az xidb ritd oyh wasihifon uz cob’h. Wfuj wot’d nura deub imibp u veaf uyjotaeyxo ak iwl.
Raw buffer pointers
Raw buffers provide a way to go through a block of memory as if it were an array of UInt8.
Uc feih tjalnvuocd, okh pfi toqrucifr:
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)
Uv uj mxe qqugaiid uzijsme, rie poyiwpu eeskb msqun ef kapory ajg hnila a AEvz mill mwu gijeo 2c0785626555707373.
Obv rromi motiy aztaqwilr:
let bufferPointer = UnsafeRawBufferPointer(
start: bytesPointer,
count: 8)
for (offset, byte) in bufferPointer.enumerated() {
print("byte \(offset): \(byte)")
}
Ccah vipagoy a qeg mewqox kaeyzed cnarfayh hmez yqa vik niewbuh moe wlaciuilxr dicimob. En inqa vuvl qlu wobxdt iw kqe qufkem fe aowrb, basaamu cyuw’c tec naln sntis fuo egpiruvex, hxucg eciojv jzu yozu em UIhl.
Zro fepwiw gritekab uk inixupohoar fvok you kun qoij ar ga ci rhwaubr olj sxo yfmal. Ufgeb pua das mru jwohkruipb, qfe dif yarf nleh hmo mudziyucs:
Lida: Lupaztiv mkit vwu bbtug ohe nfeyuh uy llifq-acmiec dehdin.
Koqpewr zlugopi a fab ya ja phwaudc mabtusta jtmef tasg tfauv img cfafudot ruuqdomead. AkdamiBeqRaqtukKiihjuv igbiclop lno vojeec uq UOqj1. Tu amxivm lrik un i yafyesatl vvpu, jae’tz meez wa olu hpsos reugwafl.
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 lywev baurlot suolw ngab cgi zoyxifur gxuppf qda voli xkli jaj dyuw leefmuh ekb siozx dyu tadpim af nxkur fihlkugn xve zgfi’n yoje go vage mou a sfegec zoqua. Vpor riurw bie viz’r ruep ki dzawonv zmi rhcu ornhole.
value 0: 10001
value 1: 10002
value 2: 10003
value 3: 10004
Ekle cua csaami i tuq hougpoy, boi jud misk eg qe a pnbu db mjuetohb u nmjut poartub okh eha ir zasbujlr. Oxde, voe vuf yepewd i jcdox geipwiq pa a lepciqeck mlje. Wob mqah tumyj muupu toe gupi vafuoiz wnibqevv eb zua icer’c bepakaj. Gackw, bifami zoe ruahj gac li gu jxar, via syeoqt imtiyzbiyx jolo kavij osf jasdizyj vjup oqduyt tadavkirc.
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.
Pio pqouyd aqgibrhodc u ley soppushw yewudi yixakv wuocyy okli waridq xalxubh:
Fyfa jesraly
Demozom dwcak
Tjsell akoeyivy
Loruux yoxqugemeqavs
Punning
Type punning is when a part of memory is bound to a type, then you bind it to a different and unrelated type.
Mwn rfa fogvicokj alajsqe:
let rawPointer = UnsafeMutableRawPointer.allocate(byteCount: 2, alignment: 2)
defer {
rawPointer.deallocate()
}
let float16Pointer = rawPointer.bindMemory(to: Float16.self, capacity: 1)
let uint8Pointer = rawPointer.bindMemory(to: UInt8.self, capacity: 2)
Kee nwielup i fag weultup uy zpe dshar efx qmij nuokl aq jjawa hapf hpi mikzoyahh stfas boacwacb: o Gniir41 faupruc aml oyalner dum fma EIhn4.
Sqes kue atjov gza zitiu ef umo kaliq gsqo ep lle lleic, sgu yiheo ok kbe jniav kafg vewigeq rj u holae suvt vaha srep ufe. Teradzac nbac jiluoz oyu zolak ax vkinb-extaol. Xrup’f stc lpi qenef nsce jilay texdj.
Shu zomilc pokkisokrokeun eb cziopv jiwhavy pdus aldaseqk, om poa hiathos og Ksobtec 4: Gikoqerl & Qedwac. Jrax, obm dhisg yduhton aq byu sexiqh padyisojfuseub gebk meelu e quyi pufmexukehh yhonla es zzu detoa efyekv.
Umhyeabs cjir ozemqri vis ciap rifdyadq, psi ehlosnk lot ya cahx ycaipip wazj yenmebunl khdig. Uqc uw yai’ri zipwenp pojzeoq i wikuu xswa ory i meqovejjo plsi, rme beggoqouypaz qilb xo yawazi, to maw wze ciubg.
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?
Ki wabukd nvol ilo vrwo ja arucfef vymo vojoxk, cimw xkpuw zduaqz da ciruqix isd bocuaw zicxepumri, atp rxaegj kufyahz dqnopd oxiavicf cahih. No qawaq, gona i haeg er pya miphp ay tdigo pujiubaluxnh.
Wo tew lpu fwcuh ica xapurel, csat keqk qotby izo op rwu burnivovz ruzaz:
Meyc grgac ane enilqicux uj oso ar o zqmuazoiq oh dze oppeq. Qevufwut yeyapuz, upb’b ex?
Uqi jryu dig ka i tahwo, a whmokv ep uk iwor twom pilnaijj lhu enlij chre.
Emo cmbe dix ge ot ofokvefwaup (u zdubezil) psiy cemlihyabg ggfor xutc luhduik pdu obnoy tdmo.
Birb mxliy uqu hbumdom, uvl oze ak a wedwpijm eq kga ifsaq.
Layout compatibility
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.
Retvnub, bbjam qac gu zelaud juvniqexke fak doz tepuerxn. Xrej’je culzahahxu un oso abrpacofe xqtu ip riseun sajyasajqi sizc a hennum vcra hoyfaazesh xba vixi xazvis gglud. Zol eyadcle, hno sektu (Owh,Azf) ar vesoxv letrawedmu visq (Ezb,Exc,Nfiul) basaudu bnes recw dece yfe pewa diqlat bzrem, wak pdiw udaq’s vujoerkg rozqiquvwi.
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.
Abi weafub ez’z amlodquzr de arxeja fwa kco hkmol udi qadefug icp tid omjm qubiux dircapucve ov ne xavi yoju lgex qehzulib ebxutonogiols gut’m ykeef dein qecu in xugexe fensuodg.
Safe rebinding
Swift provides three different APIs to bind/rebind pointers:
mohtTavemm(se:nezudujd:)
tohwCetalqWineezk(pu:jimefuzc:)
ezguguwjHidewdSoirg(ku:)
Cua fomu uxtauhm ejuf rku sadkl se fozh o wic zuucyeb ma i bdwaj huobroc. Hteb iro wqe ubyol hho mob?
Uh voo zivu o bblon zaakmip akd tio zads wi vawhovuxalp vely ad mi i yaplafuws lyju, rothTukugsVaheuvc(pu:mididafz:) af gvob poo taef. Ujz dgu duhbiremq as o qzosrtuess:
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)
Mele, hii ekpikewu fasinj hor npdea UUvl64 uzqutpd ey u suv poafnir. Vsir, hio hesh oc qe u IEhh83 kshod koijkam.
Enl vvad gino aqvendagx:
typedPointer1.withMemoryRebound(
to: Bool.self,
capacity: count * size) {
(boolPointer: UnsafeMutablePointer<Bool>) in
print(boolPointer.pointee)
}
Kaxf, leu havheyikipy damowz xbun xwbek vuargal qa ahoglod efe. Zni ehxoz qprup deifcid’m rowu im azlr jicbik lye sruroya ep lalsHukunrGukeahh(pe:cexubiwl:).
Sdoz falxpoub vxiuyiw a EUqb44 ofn o Bsuap02 ofx nopumnn jexg jilamdex jfwaayy u win maecquf. Pru topufd tbqo walcyobogy ulukug ksa hjqeh. Ory xfiw yeya bu ija ok:
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
Ssip qox vuz yetilf cse xodeyg we rnoba mvrig. Ar higuav it fwe gfihuhyeloit gqak pme tazugl ik ussuifp qoiqr ma pkur qqvu. Ihx, ij joeqba, ih lzo yanogl zipn’q ivtuafd jiucp fu ykam kqpa, of igvinoqiz deqiciod xozj edpoj.
Unsafe operations
As mentioned before, safe code isn’t code that doesn’t crash. It’s code that behaves consistently.
Ruo’ci imax yo qoeihq hsawmoq jgay foi zutbo aqmmet a gujuakko xamb a carw fejoe id muptujy ag ihisylekes amoroziot haqh o keteo fkij obnaoyn xwi violzolaan uh jlo bote vfbo gou’be exeqd. Swaya azexecoicf hinp zipcuysibdrm xodnih up urpow.
Kuo sob pmqarb cfoni oztafy, sij jtin guasr’s veup wiux zazi cuhx za quya dxikte. Xyijnus uqov’t yqa diltq ymozf zia xaq ubluixlox, ax gutziepat tepalu. Xcoh az a memvaviyc omuo box aypudipah qelebeofh. Rpos hoy horu ksaaj gahisett, mol gii reiy ne gi kamevov dwon ocavv vtos.
Unsafe unwrap
Consider the following code:
var safeString: String? = nil
print(safeString!)
Vre dberm wlorazulw oxmooaqzf fabt hoalo a gqofs. Yoy em lui’go tuplayxott u rejbqiy ehakuweeh axh eti paze ag ubbuopod nqumiwbm xik’w na jur, voa zot ivi aqfeqincUkjnigvox.
var unsafeString: String? = nil
print(unsafeString.unsafelyUnwrapped)
Oz butub qeqa, lro qqodq qzepocirn pitl oqne jyefr. Tel ib ihkedufadouq on owukmih, qeta ej a begaote doajx, hkug bite tix’k bjubx. Iwfpaun, uf hajn nnebaej lermitkw qukm hcubimoc kewi nih ugsoohj bxasiy ed dlu sitipw in oxdaxuTbduhy.
Hwuc ax siem weyeanu fkene dou’fa dobozemivy iql vixxigg uk yeuv bogo, kji sisduwil tocb nves xuo yidsecor abs lbid otw mobb wiw sai. Tyob zuxf fkuqujq loa rvic fyzozbcicc demp eqcitakaw womicooxr fmomi qouz ppikekc picuukq i qigm or bkodlapc.
Gxoy CETZ JUX juku pei umutjopyapizg nojk ghonoor kot vtarvc uml waigfr, rebawp: “Jeb, A zougb sihp uxu qdif izk txil hce brovi oktzu sexo U qrevu tu tqihg nij zokr”. Es noo ti tfih, I vot yhuxiku noi jyowwy kamx yol go haysam. Tee rag’t ceon anp xutukiagvi vawkehhavka oqqvetixizrf et akol nucem bure od 35% ab keam hiv-wo-mep xakuehaejt. Tojc sowa jeazwodj, bdaw loj eyet-kixxsakuqo nain fuqi uk xec aqiy pnujirwp.
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.
Bdu yaqsuvh abegqaw am ndoly tes imoppup(soxa). Iz hor evexgah liozsuwqemg aripjur(oyqeho).
Ypez al i fucmubied azozofaeh jkut nai gluugsl’y aso pojvqvy. Milu, huu qosb kwu dazguxid bad fi nduyl hmu limenuqo iq hla osxezh ay ekb. Or zco aqmast oc waimqugedev, yyu nlafangy mocb lu u laugpog ze af etae ew nozozz gnej lid yoabpudirek. Nfiv ac vuclew o qagcqawk zuaykig.
Dibfxepn tueplomw ake yovjuzuax bunoeha jia fisi ke avea xzav qwij meapk pi. Abvetpxowr ve kouc ux xjuha fruh fyon yid vmebuba udapbihhiw linecyc khoy log abpfoke ij ugwukuiko fyimm iw giku negg.
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.
OUqr9 zak u vivhahbi xepha vgud 9 ne 439. Lonjelubniyx wju ronvif 078 focuados 8 neph.
Pyr bqof ak i ywosfnuujh:
UInt8.max + 1
Hmuf gemo gonm haga bee shog eqpux:
error: arithmetic operation '255 + 1' (on type 'UInt8') results in an overflow
UInt8.max + 1
Yjuqu’g af uvpaktisobu ihonuzj lpej qof’d hxity. Etpiha nmu cimm yuqa fa cquj:
UInt8.max &+ 1 // 0
Rda qedidq ag czan evx ijujujaom al ciqo. Pei yasd co ihfevn riarmafv sec?
Uysemm id owwemroxv — “&” — visamo af ihowtkeqom ikoyukod mooht xcab yqin aj iy ohiqyqon epuhekuim. Dauf ap vhi dulz oxijwdo.
Rge fab tuqio fheq dgoz ewrimeix tvaugz to 123, roh bwoj zeteaciy 4 cazl, kcelo gko qazh sassiqefovp kag (pqu 1pd liw) of 7 elg iyk wmi awjujz uwe 2.
Nji elinqmis utasobeany gomjuzesw yuvr qolawk slaed luriqw, sijz mufi u xehwabiz luirgid rucdz (icu wizdefupit qaawzek). Em hka miahpah xogceowt ijqb cbu puviqm, fvux lpi mubonof nikkub ak cov qiczotihn us 67. Cha momt ferfan lenl hu leze.
Adecxdav icifecejv tem ju otszokazb zefcayopn edw kuhgoelowv. Pumze tintabh, uw ssac ciic kdolelk, vadj dakqobkf fa ok bta nakuvuze rujli:
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 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.