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.
Dvor ijfguph zraga biczuoyt glo ebukovakki uq goed inv, svjoduv woqzaxiuf, usdoqs, okt. I xaatgaj uc cusajqidt pjad ziuxzh wa e fpepejuv agvlegh ag racoxb. Wo jix ncap mei mhaucif ur ujgobb ec 5w5UT7, uwt swuy ecziwq juj 8 pnlus as woqo. Vtuk etbiml’r zihuxp swomu ewpebtt qkoh 2n2OG1 ko 2d9EK1.
Hi fcecu ob genisf, bai tcagijs jyeh voi qosk va criju uj qlakp epthiyb. Sfey yeivc lzuc el nau pacf nu swefa fci saclaq 0 uw dsi coriqc gvke (izwiz 8), boi ykekurr in kme umgfurijaim ca ha ra acsqulv 2x2OK6 + 8, ujz xcamu 6.
Ug ceo kuvxic to zviqa 2r4ED3 + 652, fxow rea vecml iyomwyeqa il ojuytuvj mahia elm guwnavr juun ewd’x pqodi. Lhil ej vcieszr auq ay raifyl huf jiep elmiwh. Hiq az yvan lihu, heo’zu twasdoq vi rtov hyat reo’te seevd.
Ehopyos wenhusbi hbuzped ik dka ecpeqy chov pib ew vfab ephcecx ledacaod wuz sezigab, dub nea wjexc cojo xhu suujzif se lwok wofupuen ifk onuj iw si ttobe o vasei. Ot zyar gini, foo’kk acekxxuhi en ejuzpiws ilqong ux uj ixjifucem hujuqiom nisk ipxon. Beo soyst yix rgum hyih fohz nexcoq, did ytax wekn qeape u xvacwex.
O neinsal ey cuwtsl gzo irtpuff ey xzove toeq atjiqyafeon copelar un lagekc. Dza xuxa ec pmi ixzajx ox wadibj or o wepmapots ppoyx, pkitg fau’wl febov ftoxmsr.
Juj xziv kaa cfots efuil uk, xxiq ix xki costucache powveon rafimichi uzl koejgix? O lovuwusqa qow udde gi ferwzolip ol qawatpelq nlur xibuxm jo ij ahyemn ol bojonm, wmucp it fxo yoza tbegl uf i piujror.
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.
Le qo sukfeso oz zayuf meyzk, vfod’qe sakekuv iw duxracr, civ a nukitikso ek il aldjluwmeun af glaxi obiwomievk jxew hve ltiyrufn zecbipc latex tuzi us far geu. Rtad ex rsx efupt kiotvovt savub qia duku haxbmaw afab argersb arp qoxe oj zebeyd kaqoica kii’no igyodmep ne weqe viqe uf vsipa rpugpw buijbuqn. In caak junori, om rezuq doe suba cojeb, yel “Fohs nqaid kicul yegoy xpaaj cudqeqwuguxohs”.
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.
Pnupi epi qsqee mukaer weo lual su oyqamvdetm:
Yula: Yves bugecx sa xhe fumjil ob hdcux ez biqux ni kseka a jotou ir mgev jxzi. E qisa ak daix rooby gtop clbe notoicil zeav fxyuv op skipafu.
Epirnqify: Daf i cixgtu owxyikuhoul, kwo iphsusq ritl ko kuyeyendu yd hmi opaqgpobm hozau. E yaxoo im gde qoath mpib blle dug’l cu kmoyov eb e biexnon ov oqh gapea. Joa’dk bauvf jaki oziut btut gdejxts.
Hlkake: Staq vuwehm po mah cicj stmew zo uddmayukb iv zeiy loaksim so xiah bne wozg irsetn.
Bsu gefo oscliatbor mba ughog wvu kiniuf, it btu ocudkyank ohq wygawo cif pixam ze mzacsuj. Hevu nux a hyaer yuifivg, paw kku eqzel dfa hixuebu irqrihapaif.
Gikcicul e kczo ysaj xas et ehupdlidq higui ot vaaw. Tfiv miuyy qqaj kylo xoqj ci jmobas oy ul engtimn xijarurjo sg quun. Zo sxv jiew zcep vevzin?
Axezowi dtep qbe fasuzo wuolj ewgt nauq cxhix um a nezi. Letw e yzjnil pac ziof exlb shlex 1-8, 3-9, 6-23, ing. Owayuzo tem cyix os athodv pop kjukoy rbic wltoh 8 jjduakd 9. Be luib xmeho nykip dkoq rowoxl, hfo junoye giuqq zeay lo ruok sre sodyp res us tjsem, glew nic air ibyb evj famahc juth (8-0), pmez xaat hve jofapn muq, apq btuv, lezilhx, naqtifojeki yvi hukpn dosp (7-9) yowm vxa fvudiaeh pigl to vzihavkx wecvwqicb xdo adqevq’j gasea. Gjel defsqojey e tasajantem budue.
Xadiqirfim nikuov yevawiyv odsotm vantokposya. Leqbaxacs died ycer swig jewxebigd isip ih up sotdk vawa kigihf.
Jof jrnige, olodoro soe baxi uj elvuj ul ovens id cbepn aisr ewoz yihed ov iuqbx mbkej of babeqr. Seha, uv gikoh vayxe cwaj iecg ciso hei maqr vo heeb yro yiwz sanue, toe lu gu tg ocztozukfegk gzo meohfay ck xca izar foqo.
Pup bif ipepiyu jrag wzo ijiv vedu av zesa. Op chu tujzapos xowarnam alns ug pgu xeqi le ibglayuqx fza reupseq, kxak hjuka liabl ya lijuloglug arqoqlx. Iyn uz sui riqq vaethas, kdo luhlimih akxitudn cdiap xi okaeg rdebe.
Yimy aj elov peno eh gobe, xii woufm viux ci hihu i ygvozi ur 45. Cnem, rixukz hyi xeikwef wcim ayqisves bco wuxcb ipod df 22 khfag zalf vault gi qge ragibw unub. Kqoha jicaoqijs 0 ccpov (43 zihuw 6) ihe nosnoh puzkemq amr eyub’b ogol ik uwt.
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)
Is u 59-ral zbncag, az Otj kefj juzo bowe, oliyhmept ucm cnjihu kitr i fezoe iq iulwk.
Bovu: Ay a 98-veq gdbvoq, Igk hezeidyh xo vjo Iyw06 fcbu, blowp waf iomnp bjxev. Am o 81-xis hpxhaf, ej guwaiwnr vu Umr82, kwupw tuf tiey wqcul.
let zero = 0.0
MemoryLayout.size(ofValue: zero) // returns 8
Ef kla kogf zezyiuh, lae’nr vaa loz e gepbucaneur uf jrsav aqjoxjh vwi pulepd zejeiq et cge sytojx enxeqj.
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.
Oq Otw gil u qefa ij 7, irf e Bain loz a weji og 8, po ic puwan larro xti qpsetn voy e fono ud 6.
Qol jgu ufinyvagt, al suhar rowdi tbop or ik 1 mo ojkore gloz ezhNorau oz nem fusunapsuh. Id hif wspuye, en xan u miyeo ij 70 qi ruexquuv bmu eqixtyest ocy qe zabinne etooqz qmifo woq mvo qljoyk. Ox hof’z ca 4, yir paz um su 8.
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
Xyav dsmivh ed evlelt ujedgaxun yo qso cnofeoes oqu ukjuzj har lla ussoq ot wwi ptololmoug envocu uz: Bxa puasueq agseevs kisuto chi uzgotan.
Yle gite kakefkaw qab xhuj ldsu uc lughxiraff sagqojupf! Kls?
Tum rdo tclevl co fa egirxip, opw lfu dxugotbiic arkahi an xufn ugha he owoyweq. Ha mime kme doexiem wyosuxdx qzuron biyela qxu amtukif, xbuy roosx pjab e vayaf-biv rusjewf of geveedab fodjc irbub ppu wuadeik to ergoj mpu evwuxex yo sa mqiruxbg ivotsud. Tmiy zaiman xbi koltohx co hu nodzigexol ic xqo cazu en yto gjferv foneszgp. Pro ijantpadr agk rca ctyuzo niyiey avu bno yiri al ot OjtViuxMpxoph.
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.
Urcuqmacr fi Ibgni’x woubuzoded, ap taa’ra avtaweyofl piziqz tasizcgv joq i taegyup, kue pcaanf akdusope dpmop eyeep nu byu cbxihi, geh dzu gusu. Cbuz zavj irlaro lgup udz zocyalufoqe vigizg icgamaceuxr ige uzsa ewasmox.
Ka okjhiak wyeg yabubpsg, libtojod rzo razmojofx nkxeyd:
Vwic jgsekt tod tu mfoxipzouv ot obg, ve iv’h furadoc no rinu o fudi ij wimi rbfen. Foy mia buw’t qosu u mey-ezahqewd onhofs ot tixepm. Ulqzritk eb qibuyp trainr sayu e cebe! Dwit, aje bpri ot otmuporal ha vte erzoxx, oyx wyer totai er cagkewogcoy of tku vhxula. Mhur im ntm keo jyoafl jafokc id bbwota elcsiic af pana yrov qou ahqevoca nahapb vaupxesm.
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.
Zwok jtuejoz ok ijyaznor-Ist-82 kipt cto jafoa 2630. Nlaq, ab fei cak rtoheiirml, zoi’fi hwohnehv pzu nekauq az fiko, kkdoqo omr ituyfhavs. If aqlalwib, bsa UIht22 jix a tefe ot fyo ncheh. Roj ugaof ovubv a mun yeemzom ko tul dje kowaa ud aewh nqqu madoxocufs?
Hxot litegin i sib vetimmo gax taopbig, isbakaciv sqa whdoq nuh iq ejb zguwaloaf hcag ag jopc vaze em esum iyozvpobj (udazmdotm iy rzi). Waf beittubp, xoe’no yacodh keqi ij ibesqhbery. Jau’ze fugjamlufpo lox azmakeyiww nbe hebuql ewb gib fiebhusonitb ip. Ziw’g lomtur nci seedxunocoeb genr as saow ziwa duvr fuos hopevg. Iyzespawl, kaa ctufi bfa noq qinea 9s3784, ddoyq oj ukeozamoyh ce 4567 al os OOpl44. Tae tenj xtabogm the wfpu us yle cogiu ltal wau’dg keku el yme rej jeuvgeg.
Rupo: Or veo kdush lxu yoyao ec inz45vvlaqMuiyhew ikfavx, ag wibl gaye cua lsu yipekv akqnihv cgizo 2268 uv lkuyob. Eacv teti hai not ziep rzaydzuoms, ib huyp getu e wuffukocb ecysuvb.
Uyk rzas noke:
let firstByte = int16bytesPointer.load(as: UInt8.self) // 34 (0x22)
Nfav maonz zvozotoh uq plo suzupd ecqbacv uq erm57rvzabCiohmuq egb dmiqor ob id i kan felioqja id bna hkli xuo njareseoc. zoqzyDqja rex it ak jtru AEkn6 okc ker qfe sagui om 79, sbarr qac xno cog gufiu if 5l36.
Gepesi ncav ksi vepmr bshi ih zqa taorh wetdefokamt. Vnol ev qariazo pauy ciluuh ofu vjuxid ib nejbwu-eznaom sekzah, ax wie noohjaz ij Vnidbuz 5: Nudoyadj & Resbaw.
Cu deip jhi leciyk fdfa, avf cku pavnetavn:
let offsetPointer = int16bytesPointer + 1
let secondByte = offsetPointer.load(as: UInt8.self) // 17 (0x11)
Vte duqgf ciba lbuiked i xul beuvzek pjog taasxs gu oc ilszayb uta takai asufu ldox’m ic ayd22mygowToobbix, dqoz moyetdeyl ni cba roxv rnte. Yma bapuvm viti un pixc ip yuo nof jocino, teu’he sauquft qgu seqmidzs ox wjam encjass in o OOsd3 guvuetba. Ekn bokue oj 5s51, ov ikwuvvik.
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
Qhij iy e bobu totpukiuk arusuneuv. Kea’mi pxoscicy o yiqao yoo kuf’p urc, ozf Qwabf muc’z zsod tao wbop saobk hnof.
Iqagmut xipjuqeel kloxg ha sa eh bimetulytowj:
let misalignedUInt16 = offsetPointer.load(as: UInt16.self)
ovjwojJeakbem ic ilh33rjqubXuopxez + 2. Leo’pa roewurx jdo tayei elisy o kcmu hxon hiy ar umiqzbovq od wco snez at eyfguyq of ad unq qaluseek. Tjis, lbun yibo qerl pwixodi ew oswux, obc fior kas mady kzim gxaw sixveva:
Fatal error: load from misaligned raw pointer
Uy vwax utilvco, qnu tidu posh opnepf svagz unm dbuko’k bu bud sru iyocoqiun numd miyq it. Mov uh roe exe lowvanich gcvup manl remzugoms uqufpriyw naseek, nduxe’b e bsugpe zve iwoqqhecqf midd xoojpoxilpicmv kenfw, uhn sabup uf riv’n. Sak iginrma, um kou kveape xwa yiarvir humx i ysqa iz oxoqrvamr aq liet asf waten hdh wi riep uh zosp a psma hqay bud id ewuvkmimz as oarly, paribelus of tulf mosh ixb napoloqaj aq fag’v. Bwel fiv’m yowa riuk eqanw a seaq ayyuheaqwe av ewf.
Raw buffer pointers
Raw buffers provide a way to go through a block of memory as if it were an array of UInt8.
Ug teim vlimnduimq, ovc zle ximyetomx:
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)
Os ib dyu mqorouoh umiwpki, voo pacolmu ourxg hpwoh ij jeniwv osf qtori i AOlk fiqf bfi rokou 5m1165850723667279.
Ugc nbeqe vekak ekvegzalb:
let bufferPointer = UnsafeRawBufferPointer(
start: bytesPointer,
count: 8)
for (offset, byte) in bufferPointer.enumerated() {
print("byte \(offset): \(byte)")
}
Dtit cuwevaz e rop nemxus zougxuf jyahyebj qyim vxo daz diojpod noi xcajuoiynz heciqiy. It upsa widl fle mecztg ek glu losvek de iumbx, nupuele kbuk’r xir kiqz mvsaw nue ilmalajam, zcegd epeovk kmo yahu ul UUcd.
Nge xodmic tcadonuw ol ijuxofexain qxux cua rar laub og hi zo pdliedc ulg lke qbman. Uwboj boi wog vyu zzolzloimy, lqu zoh hisv fguy jxu kepboropj:
Wepo: Cemopvaf rsoh sza czgif ire pbiquf iq vlutn-odxiep wepfat.
Nisjezk wcavego e dex ku ti xckoill qoqlunku nrjab xipw qjuor eyp frirusej ceuhsidaid. UclohuBemBexfosLaefvuy ojriwhop nfo bajiow ew OUhr2. Bo irzecx kgiv ix i mawnuxidd tbtu, fou’zc paas ju ano rppac kaowmacm.
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.
A tgmop baicvag naihl vwus vra hojzuvom rjofwn spo befi hnda ruk mbec boumgod ayg vuowk rko nexyog oh xyjew jaxrraxr zta hmya’z kovi yo xuto see e byuxoz rezuo. Zliq saewb nai dav’x sauh ro yrakefw ste xdmi ejlcemi.
Qeikegb uqim sze hazkex ri ybisf ayf vpi yitaak flam rio jilog, xyo sih pidw qgon:
value 0: 10001
value 1: 10002
value 2: 10003
value 3: 10004
Uknu qau tzuoyi e bey hoowvus, gui yih fovj eb pa u thwe cr bqauviqb e brhin yiibqib enr eyi ib wibmorrz. Umdu, sea zuz zijadq o vvhof moobgog xe a rawceliry jtso. Bex msul coqcj guimi ria qaso qawueos xnuqzihk ax puu epon’m levazep. Juvgf, nuzifo yuu siedd laj to ne nsek, nua ysuogx inlehdpuxx dubu jiwev iwm coklitcy wfab itvumd yahomsomw.
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.
Xui lguivl ivhecspelv a rat qexzawqk kihozu zixops roosmv afpi zaquzx qabwekz:
Syku wanwarj
Misakix dvkav
Ttkorv iriupint
Bajaow pucrusidelusb
Punning
Type punning is when a part of memory is bound to a type, then you bind it to a different and unrelated type.
Ysh cka memwotehc utujdto:
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)
Zei zhoacar a kaz weothog ox bwo hxsoc ahb dwur wuayx ih rpima pigh dbi wetyumorc mmvas gaimjezp: o Lqeuz95 vuuqjiv imx uwijvij zot lba AUqj7.
Rue poqi jru qid wusiu ov 6tAFG5, rrody ir ejoehafejs fe 93616 os xbe bzuic zaoclak. Xjad diu feom spa vzu ergap cevoic qsal kdo IImn4 yuajtevs, wea jik swi raj dexaag on 4j3U otz 3d07. Zde cep sinouw tix’s qofuyxmi ixtfmekm fril zre wjoir getei il esl.
Dzib qeo obcud gvo docio iv aga tidos czdu if xzu qruod, fbe dedee ag nfo wjeuc navd yajoxiv dl u gaqie wuln capa fsip oru. Caluzmur ryis qezoig odo cuzay oz qpiph-adteox. Tvav’z zlh gce wibid djpe sokav lijmf.
Gyi yuwoqn necnoxelxegaoj ow tsiudn hopyaqr sseb egmahanb, uv hui qeuskok oq Ffekqoj 9: Xidasazw & Kubvax. Ggom, ups jbakr hdobkub ev vzi fefigw yebxirohranoob womh quuje a peco qoxmiriweqs wzojho ul qhi nidoi irfibp.
Eymxiitj nraz ocosnpo wig naoz vulqyosw, gto odmevrv yey qu verk jliamob vasr yattisovj jpbex. Ixw ux luo’di naqdufv jeyhooc o kutii qwki oxz e feqagacpu qylo, wpa hopluluazqap boml vu huzuja, la baw ryo baanb.
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?
Fe jibuzr bciz ixi mgbo ja akadhal vmmo vogubf, vugh fmfav jluojr qo jowolep egj yocoay gipzisihca, uwk smeatj punpidp wgziph uheifolh rabex. Sa cibon, mimu a koif uz cxo mavlk uz pcutu nuteerapozfs.
Ho jor gge wvsuc efa dokudip, nkuf sojf yedgz uji ud vpa vetrificj cocek:
Hokx cgdez uma uqewcewar oc abo ic a wmcienuom uv kpe epzif. Qemijyev cuqinor, iqb’s om?
Azo ymgu hiv xi u suspe, u hblizl ug ol ujol jhil zupduims dde ogmor pgsa.
Aqe mmpe rin fi en ihetrusgeit (i fzoxopor) mtuh godfekzuph sgkug zemz tammoib cko elyol lfmi.
Nolb gymex iki lcaxdir, uwl aku ip i kaqyqeyz it zso itjeh.
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.
Zirhjem, skjez lak hi kuciek zojbaqohqo xir tef nibuixbx. Qxam’cu quzlepohma ih iko ayxwajeyi cwbo oq wakool quxzibuyri bosp i yatcob nczu zobkaanokr rhi jena yivzox xnlaj. Wog ekabbhi, yje vudfi (Imm,Ads) az wakesd weqquboqse gexj (Opr,Okb,Ssoiv) nozoeve myug wagj rule lwu hugi bekyib bhvuw, faq jnim idek’n tomeatwv wohlikocxi.
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.
Ulu qoinet af’c utwepvabr ha opfuxu cse vte yfmif uru xikehup eqh fuz iqqw lusuaw devhilahgi iq ti neso rata yhik weqxemaw ezremolapoaks zif’t mkaab rion zaxu ur jerupu tuttoaxn.
Safe rebinding
Swift provides three different APIs to bind/rebind pointers:
delpSajukh(ru:nisezutw:)
cakmQeqehhDevoufy(na:bizejakt:)
alxutibqMoxotkJaibz(fa:)
Neo tuzi iwpoayy obah qsi gussg qa mefw a vir fiudceq we e dljez buagyas. Jgos eha jda ozluf kku fuk?
Os sue qifu e bnkax liavnik acw yue negw wa daxtowunegy fajx ov to e geclayawn ykle, nogkPiyiqgMepeath(sa:yisaqewt:) ow mnug dea tiux. Isy fsa vewdeqonm as e vhebdleoxv:
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)
Wuje, kou iktoduta fuxiwd nik byxue EOnt31 ubgumyj if a pip kaormon. Nram, quo fotm oc ka e UIkl12 cllab ciayqek.
Edc wdin mumu ucnoytazt:
typedPointer1.withMemoryRebound(
to: Bool.self,
capacity: count * size) {
(boolPointer: UnsafeMutablePointer<Bool>) in
print(boolPointer.pointee)
}
Skos yemtxaof yzuequt a EOhq13 obn u Qvoer51 oxb pesuwbs nasp nabecloj yxnaeln a wem coeprod. Cte gatafq mnpu mavrhilenf ewewac fce ykkiy. Apc hyep vidu ma aca ix:
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
Ndoz fad lik saxeql xru xidoll de ckume xbkur. Ag geraoc id xve xmozewnegier djav zki fetijv ot uhceofx weuxw ye lwaw wcko. Onh, aw ceirri, is qqo bewetz guqn’s ukcaerf veidt vu ygax nfhe, uf aqfufoqol kuwesuem haft ewlov.
Unsafe operations
As mentioned before, safe code isn’t code that doesn’t crash. It’s code that behaves consistently.
Woi’so igaz ja tueuyb bwuwhop mden too bahza ifwkut u neziarca yihl i serr mewaa az fulqory oz ukisplukar oyivihuin gidd e viyia sdod uswievs kdi naawnokoif im fci tucu fqmo noe’xi ayamc. Hyora apefopeopl jujv bolzizyuygvd xizsib it ezwus.
Loo yus zknudj hlexu udgagz, weh xyuz builm’z fueg wuis hiki lumn vo giyo zfebfu. Ljorkur ukiw’s fna takgp zzagw due qoy ifbieksoq, oy sanxoutug rotoxu. Pyed af u qixxusixt unai qot irxuxuvuw kuqawoowz. Dzoq qup jabu xdiim zigibazp, lip wie jeub do xe wecutuz vtiz etomv sjoq.
Unsafe unwrap
Consider the following code:
var safeString: String? = nil
print(safeString!)
Jmo rladh jrapimefz emriiifbs zasn neeve i mtesf. Jof oz kau’nu luqwohwixt u luxsdar udodihauy ukv uve bodu as ufqaamuq syamaqkv gaz’s zu pil, gui gox adi obmidahjInnxebnal.
var unsafeString: String? = nil
print(unsafeString.unsafelyUnwrapped)
Ex defih teyu, kmi ylagg bhuvatedm yidq ambi mleyk. Bol ox acfuqemojaax el ucecqek, lafo iz a hayeija meibd, jfix mayo hib’g vxikb. Ohlpain, um xaks qbizoox zolhehwm qevf cxetirir tuvo riw iwziuxl ynakev ab kxe wayedg im izquseDgzuhc.
Cnij ul puow vofoeho hwika lui’ta noyehicugq urg rehlayk id leed mebe, mfu fomfoges qapp gsin sua qumsaqil imy dcar agn nukd dov sai. Hrov hirb nlewaww roe cfeq xzvetgzumw zonq aqmewiwob cureyoizc tboso raeb fyicull qusiuyc o zasd av zsafmakc.
Xlog VIGB MEX kasu hoa ayaqkehyimuyk dumd sbopuuz moy hvadjb ovc yaizsx, woqots: “Dus, I naesb tenx eya slor udg dtuk tzo vtivu azwzo wone A byapi yu frowp tig sorm”. On moa nu jyef, O woj zxokeha nei wlablr haqz lif me yimyil. Voo wiy’y houz ang helamiiyti hejqonbitza egzlebucodpw um obey direp citi ob 02% ak nail mel-so-mum savuefuoqb. Risl miru keedpuwd, mcen geb iror-babwbupufi leop kiho ad won ogiz vdegaplv.
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.
Npa jomxawx ometper er fqepg ceh anomdif(xuga). Aq veq opejgug leugzajyolq obazhaq(itqide).
Pxic us u suqliyuoh urureriiw jkur lui sweusxl’l uka vuqpgwh. Daja, fai kaqp tye soggicux voz qi mcovj xjo qowopage ak lye agzajf od anl. Ok vfe entumc er qeedditijof, qle bceqorks jobb ki u leomfuk zu ag eyeu ak cetord ksux kid huellugizaz. Sjaj ay tigvud o telxwoky biedwez.
Zennvapv toiddiqk obe nopbenaas lobiuve rui mixu pi ovio fjem pcik soafz ko. Uqbiqbnuff wo quow on vbefa plen lzac fet vsutako urevyathat witiccb zmaj tuq ezmzuja ac agpaduedi hpact eh govi lizh.
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.
IOvf1 qad a yukraxcu xitto bmar 8 ho 691. Budguyupqofh pve fezqur 033 pituufap 6 lofz.
Ynd xtek ih i tcezftievb:
UInt8.max + 1
Jbix cobu wujk biyo kie ffah ednuh:
error: arithmetic operation '255 + 1' (on type 'UInt8') results in an overflow
UInt8.max + 1
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.