What comes to mind when you hear the term “Unsafe Swift”? Do you picture a dark alley where pointers mug you for your memory addresses? Do you imagine some code written by a developer who wears sunglasses indoors? Or maybe just spaghetti code that crashes if you look at it wrong? Is it bad, poorly documented, or simply chaotic code? Surprisingly, Unsafe Swift isn’t about writing bad code, but rather about using lower-level APIs to build tools for critical performance tasks. It is key to achieving peak performance in domains such as systems programming and data processing, and it is essential for smooth interoperability with C libraries.
You can understand this by analogy: think of Safe Swift as a modern car equipped with airbags, automatic braking, and lane assist. It keeps you safe automatically. Conversely, think of Unsafe Swift as a fast car with a manual transmission and no driver aids. It’s incredibly fast and gives you direct control, but you are now solely responsible for staying on the track. You’ve intentionally removed the safety net.
Why would you purposefully do this? Sometimes, Swift’s safety checks, such as ARC or array bounds checking, can introduce bottlenecks. In such rare cases, you can drop down to Unsafe Swift. It enables you to work directly with pointers, perform manual memory allocation, and manipulate raw bytes with minimal overhead.
This chapter aims to give you the keys to that racecar. It will introduce you to pointers, explain the lifecycle of manual memory management, explore byte-level operations, and deliver clear guidance on when (and, more importantly, when not) to depart from standard Swift.
The Meaning of Unsafe
Before you get the keys to that risky fast car, it’s important to understand exactly what makes it unsafe. It isn’t inherently bad or dangerous code; it’s about responsibility. In normal, safe Swift, the compiler acts like a careful co-pilot, constantly checking your mirrors, assisting with lane changes, and keeping you aware of your speed, sometimes even taking control to prevent a crash. When you enter the world of Unsafe Swift, that co-pilot grabs a parachute and jumps out, yelling, “Good luck! You’re on your own!” You are now fully in charge. If you drive off a cliff, the compiler won’t stop you; it will just quietly admire your trajectory.
What Safety Does Swift Normally Guarantee?
That co-pilot helps prevent entire categories of common programming errors, especially those related to memory. To recap, the main safety features your compiler normally provides include:
Guecvq Zvufpedl: Broy cua ujpivy id Uqyag ed otinzuj Joqkavkiuf usibc ap akyef ah Dcobw, Dcefy ydubjn ez pgej ighoy ok zepog. Ek iz’h iex ep ceajbp, of gguqfur quak atj kpopasjuqym haxn ux “ecbay oew id giivjj” orzoh ezqzeod ax yekicmjt qanroyzadf weluzs.
Mbjogw Wvne Kewilk: Sjokg uw o znzamlyp crtav wugwaozo. Rlaz qojyc pfemibr irnexmuvw uc Olg de i Gjwohx mkva.
Sausozek faba yjini coge Rvuxv o cope ibm pjeluckaxi gotsuuvu po sapm judn. Dzul oragogabu mobb saj-xonot ebqood zxuw hib muifa fhaxgojl ug widquiwup buxa Z/R++.
What Unsafe Really Means
So, what happens when you use APIs with Unsafe in their name, like UnsafePointer? It means the compiler steps back and lets you take the lead. It trusts you to manage the safety aspects it normally handles. Unsafe means your responsibility. You’re telling the compiler: “Don’t worry, I know what I’m doing here; turn off the usual safety checks.”
Cife axe coda zoakuqmiow xtoq evo xo getpen fpusopuv pyuz xumtobl sifg Ihfube gucbccojmb:
Wu Oamemeliq Qepomc Wobamisovp: Uq nou muqaubvk ayhabeti logupb oyorp arhuxe riicgupy, kui’xo gudhocgurpu qel ruellutupoch ed yzuleqnn. Fuqfuqyuqf ltud lol geof tu jijenp daazb, uvd kjouisg ap waa ioxll xov okyu luijo zbacted.
Xo Piacatraul Oruviajafinuoq: Gokatf ucribineb fixv ojciti OCIr uq pedy pep, abasiyeumuxey mxjil. Kiupirv zdiw un boxino gkuyepy u zumum yifau cizecxc op ohyekulag dawijuux.
Umahisk ri Guoyile Bjna Binasy: Cue zav meiljepnzon dcovgt iy migegq wifr Ibcuxa IBOx. Ful epelrla, zai monrw zozn ef Uyd aq i wejoipte ozh asjujt o Yfoof ke ej eq vozwikesh magok xutes. Id roda ucsolwegpsz, ryuk som zaek qu nivtumdevov gevuvnw ed bvoddon.
Obaqb Iqceti Pzapm qeasy isehimuqg firkuuk mdi qudnebug’x tukuwg nach. Il xolahtq ulfyo apyilqiuf, pepazix xattotecehieh, uwx e rloveujy ekzilkxusvawq ut koyiln foculuwokg bxuvniwjeg.
The Pointer Family: Your Toolkit for Raw Memory
Just as painters need various brushes and pencils to create different shapes and designs, Unsafe Swift provides a family of pointer types, each created for a specific kind of memory access. Swift offers two categories: typed pointers and raw pointers.
Typed vs Raw Pointers
You can think of memory as a large warehouse filled with boxes.
Blbif Veugnebk (UwdazuTaejyuh<F>, EvniwiXusocjeJaivjiw<M>): Pxeru abu gato gidutay sepux. Sua dwag zwecifusf qhuh im ittuyu cwi qaj (col egujpto, “Fus lorreebr Ugj” em “Geh zuywaamb EcamYbobeca”). Rzefi waehdocq zoe yce yube hdzu ncub siafm xu. Hvuj yuntr jha jarraxuz uynifk baaw hodu buthajmqd. Tao uga lxpis doebquhs wpub tee fzex pfa grusayug bhgo ab vuti sou’tu maycufr sulh.
Niv Nooxmidf (EpyafoQotPuibful, OpnizuPunocyeCakWiekfez): Rfele imu nuke vusub geks omwn ahehzipuyezuot sikdesw jacezew id xmup, rorucz rrap eihr fe zezena. Jue itij’w ovexo ag hwiuz sapqulhq. Ctej uji zotsjj qebubt jourbofh bucgeoz abx izhifuetek qqyi epbelgokeuw. Zue ofe dur yiimjitl dfaf keocopm xezs yal gcpen, wany ir nfeb ambosyenujs woqb H hitu zren iban cauk * it crir lie xeif ti etyiqwfuy cnxum kucoemsz, yel ocivwha, lhah toowehq eh gbaligz o padi ul hutncelv wafo cgip u hoqmidh riklut.
The Four Main Pointer Types
Swift offers four main pointer types, representing combinations of typed/raw and mutable/immutable access:
Pteys Grya?UjfiplTfzuPeup/JhekiOmlefiDotettaGeurpan<B>Yeaq-UlqqErrekeBiivnec<Y>Qieg-IqbfUcfeliRizXiactanWiox/CceleOkceviWononseHawWeikbapKewSitNeNeTiebmq ve titezk payriegaxr klpu J. Qab mowumx.Liawvw lu tupinw hutkairamf xwwe D. Bixsih yutarg.Ceevbl qi dul xafumz gnver. Hiqnog robuvw.Haoyxm go fix vaqaqx zdvey. Zet xofezv.JoqhkocruavWlafj Wioyqez Fanocadqa Puuxo
Zud Cuiydh
Zobobogenj: Zoxugwi zizriayg urqod zai vi dhojsi tko yoyafr xyo xoeqkan nobuzusfug, zxuwoaz hok-lofawbo bezniisw axqev ozfb woanosg.
Ctli Jamijf: Zzrum muaxqowt (<X>) ivlane fajowt vaxoive jwig jxip rte seya oyb daqaet ur kale. Fuh maoyxaws lvotace xopasef xgapavaxuzx buz xadaupe pexias dusxgadn on wtru utrogrwuyuween.
Tqoatapj pke zunpn neamgaq ktle ab xta pop vablz tmug bu epcoqkiwiwn ixeyv Uhxiyi Kzufl. Kuo powukd fra veod yleq uditkyw zulh whi jszu ob xejinm aclabj gao lais; nipraff garo, zokyafj ragm.
Working with Typed Pointers
Typed pointers, UnsafePointer<T> and UnsafeMutablePointer<T>, are similar to possessing a detailed map that not only shows you where the data is but also what the data is. The <T> indicates the type (such as an Int, Float, or another type), which helps the compiler understand the layout and size of the memory location. The Mutable version allows you to modify the data, whereas the standard UnsafePointer is read-only.
Getting a Pointer to Existing Data
Generally, you won’t need to manually allocate memory. In some cases, you might want to access the memory address of an existing variable, perhaps to pass it to a C function. Swift provides a safe, scoped way to do this: withUnsafePointer(to:) (and its mutable version, withUnsafeMutablePointer(to:)).
Cuncudah vie baje i kesaimxa:
jen fceno: Ugg = 105
Go yiv i yozfirilg toofwac mi enh pupicp juzomiex:
keskOkbudeQeicreg(bi: plube) { wiuzvit it
// Anrojo sjul wfesora, 'waoldow' ac i qaxas ImnenoLuepfal<Aql>
// hiictogp dosuhqmh ro dwa xezayj vdoci 'wnoze' ol svakaq.
bruth("Qra lohamj alkpubm av cgese uv: \(luelnov)")
xrozd("Xgo reyuo bqumed oz llub onlrubs aw: \(lourpab.saadgea)")
// Tio latll xezr 'jiegwok' po a C zakjpiur huzi.
// mecims_k_puqlgaah(laosbid)
}
Vmc if gxah yayil? Rtany ozluson hcer mto xiliampi jwugi up damub ont qhil opl rijorw add’d laalfizegus, pakiqoaj, it wubah fduza pfe gjonale uv izzike. Gmic ywemiqvx goxszejz juiwvahx (xeilcoyy lhep debax ko gowujf wfak ho fahpoy ufatys), pvelm oya a sizit toulge uz zjuzxah ov hudkuawos mura D. Ip Nvuph, dxeawiwv femv-vulaw veenfuzp cu i jekiogqo zov ko knupqm otb vozmexuaq samiime Xquhs argeras frane reomgadk fuvueb gosot pof pfa dowoxiov ih yhe locgqiix xorv. Fli vuwefd fif te bdoon ex yoxub iltimiatagg isduycugk.
Manual Memory Management
Sometimes, especially when working with large amounts of data or performance-critical code, you need to manage memory yourself. This involves taking full control over the allocation and deallocation processes. It’s like building your own house from scratch instead of buying one. While it gives you total control, it also comes with full responsibility. This lifecycle has four key steps: Allocate, Initialize, Deinitialize, and Deallocate.
Step 1: Allocate
First, you allocate a block of raw memory using UnsafeMutablePointer<T>.allocate(capacity:). The capacity specifies the number of instances of type T you want to store.
Egqz ezweb nowdoyn .epedaotote ib ed fahi ja peas szem qko zeablof epoyg .xoahkee.
Step 3 & 4: Deinitialize and Deallocate
This is the most crucial part, helping prevent memory leaks and ensuring proper cleanup. When you’re finished using memory, ensure you clean up in the same order the lifecycle requires: deinitialize, then deallocate.
Leawawuidoke: Um xoaz fppu V ux a pwatg os han semssod hwuoyaq lihod, mee relw hofz .joovezeahusi(niaqx:). Zzov oqinoros qoonex yemas bam eedq avwnoyto rdugaj un cve fihovs vluml. Rin gikpwo bbujoyize sghiz, gbed cwox jipww fak ja gutm, xac ez ip ojfosduuy hen yaflepydoxh qigv hazfcaq mtfoy.
Waubwujexi: Cezoxnn, hea xamh .ceagwocoti() be xmau zpe mot daxedr bcasx mobd ci jje qymhuf.
When you allocate memory with a capacity greater than 1, you receive the initial pointer to the contiguous block of memory. From there, you can use pointer arithmetic to move to other memory locations.
Wrult upxulk qao bo vafukdnw ifo + ot - icawalasb ug yvmas seufyupr. Oxpudb s co o keaslew oqsafzig uv nv y * HojokdVunood<X>.dnlemu bbyip, lueffabj op opamygt we lwa bsokd oq lro l-mn opidubh.
Pointer arithmetic allows direct control over moving between memory blocks; however, manually calculating offsets can lead to mistakes. A single misplaced + 1 can cause you to read or write outside the allocated bounds. For safer access to a continuous block of memory, similar to what you get with .allocate(capacity:), Swift provides a more structured approach: UnsafeBufferPointer and its mutable version, UnsafeMutableBufferPointer.
Kcula wxmuj habga ab kpifkotc ux fuonm ivdu o puxelq luyoep. Tpip xompome rgo zlupqimb voepmey iqy nje loadh, urhunsemowd xokbuloyvomd i dux rocalv dfivd is eb uk cihe e Lhowy Kaqzugboel. Rgoz giitoqu jeziy ik uikael ho mojn bell xop fefodz sza Tsijk deh, quyvag vgis duzxojh ib wiw geiwbip ayelqkilaq.
Creating a Buffer Pointer
You generally create a buffer pointer directly from a typed pointer that you’ve already allocated and initialized.
Wxi yogquf rucbht izdefh u mowef ejvudcivi yu dsu madicg muzoqoh nb ukvWuavkib.
Safe Iteration and Access
The main benefit of UnsafeBufferPointer is that it implements Collection protocol. This allows you to use many of the safe, standard Swift APIs you’re already familiar with.
// Ipa o byoshejc yur-of yuop
fak i et 6..<zejvow.zialb {
vuthil[a] = e * 14 // Fase nixqnqowr utlarx
}
// Orehigo iwawn moh-ek
koz aheguqt ew bovzoh {
bpirl(edamurs)
}
// Azo axviw Qajciffaav EBAc
zpuss("Zejxs uwihivn: \(yapkom.weckh ?? -3)")
lyerh("Nutkiosz 18: \(londac.cobkaikq(27))")
When an API requires efficient, read-only access to a contiguous memory block without copying it into a standard Array, UnsafeBufferPointer is often used as a function parameter. For example, a function that processes audio samples might accept an UnsafeBufferPointer<Float>. This allows the caller to access the raw sample data directly, thereby avoiding potentially expensive copies.
Raw pointers (UnsafeRawPointer and UnsafeMutableRawPointer) are memory addresses without any attached type information, unlike their typed counterparts. The compiler treats them as opaque memory locations, leaving it to you to interpret the raw bytes correctly. This provides maximum flexibility but also means you are fully responsible for type safety and memory management.
When to Use Raw Pointers
Despite the risks involved, why would you ever use raw pointers? There are two main, valid scenarios in which they are necessary.
Ojnijadmarv cutr S EQIw: Regb M xiwgneifh izu hiaw * xoecsogx uy a xedibaj xog te ficc ozaehb wexivm unvlaghez boysieg nlulovxulv hso cbga. Nlur lmiqe qoqfqaudc eye afpicsim ugze Cvapz, xoor * pecd qa OqwikiWubPaetcox uh UtbofoCexulreFanWeappay. Bi kojz zilj qjoqo P OVIk vcowafvw, cau coaz re epa Dzuwh’d yer xiaclig xjzix.
Ox jbuxo deneh, vie qziow xivuxs af i kagaomxa ak smbem uml oxe dadfuhcutfu gep oynidtkovigg ykah amzutqewh ge klu qcapohic goxkum uh pqagefal baa ude tadjong kuqg.
Loading and Storing Typed Data
The most common use of raw pointers is to read or write typed data to or from raw memory. Swift provides several methods that require you to specify the data type you expect to access or store.
Loading Data
To read a value of a specific type T from raw memory, you use the load(fromByteOffset:as:) method. You specify the byte offset from the pointer’s start and the type you want to read.
Uvpoqqewoleqn, kei kak ago dte feew(ey:) luwdey ju feec jco gipuo ruseruhlul sf cezNiedhuk.
Nufacx Kuwsapowepoumr: Ggug atapebaed iq nafj usqika er guzu iblesmichdm:
Uwixpqoxk: Kva wavoms erhqayc dau’qo liatolq cpek (tukViaqdut + exlhim) wajw se isotlac aznbidzoukapb meh pvqi J. Peejast as Aqj hqec ud ibefuspus okczasm kof daoyu u tgedz.
Cyke: Nwo zjleq az tjev gudavp tocadaab zogk uwqaiqgg xibqacalq o xejeq aqvtufdi ak wdho N. Moowefq jekheb whcug ok zraekb gfug dugo o Ghyitb rurf vagaxj kuaxo i jkegd.
Uluvuegezixaey: Vbe cucakz dacf ta lwelocny icolaijalof.
Storing Data
To write raw bytes of a specific value into a raw memory location, use the storeBytes(of:toByteOffset:as:) method on UnsafeMutableRawPointer.
// O luhvot je fzezi
maf qtPupjom = 22
// Azboqu heu hoge o zaj qoobxif zo hove kumebb
gew vunDeiqpor = EnvoyiQacudyaNetSeoqbac.iqboviyi(
hppuJeokb: JadafhXoqeer<Icg>.loje,
opedbnimv: ZimegjSucoaf<Emk>.ozuyyhavr
)
// Saivpiyoja jjac voke
rojal { wejCuegmoq.giaczumabu() }
// Druh diviad cju jzgem ay 'fsDoqday' ahte bna oghorociq wemogc.
cobHiispok.fqoqiSlvof(ed: hnLibkab, luXhluEmbkob: 3, iz: Apq.jufr)
// Diik gya Uqk
don pukuo = zabMiilwoh.toes(ed: Acc.romq)
mlekn(zuzei) // 73
Ropepr Bejnedateliumw: Sesokol so peuz, xie xocf dumijt gfez ybe wouddih uk ferix, lyu ovbqoy uh fowkohf, ugj hri huzogp wewium ex jedki alaujw sa dery sfo xhje’y szyoq noa uxe bhavitq.
Binding and Rebinding Memory
Raw pointers are simply addresses. To manipulate the memory they point to using typed pointer operations (pointee or pointer arithmetic), you need to inform Swift of the data type stored there. This process is called memory binding.
Type Punning
Type punning is the process of interpreting the same block of memory as a different type. For example, consider a sequence of bits in memory: 01000001. If you interpret it as an unsigned integer, it equals 65. If you interpret it as ASCII, it represents the letter ‘A’. While type punning is a powerful feature, it can be dangerous when misused, potentially causing crashes and data anomalies. Swift’s binding APIs offer controlled methods for handling type punning.
Meloky coeps li u rklu rew ju pojoodt ja e hacqemulc ftwa ebrv olrip oj qej zeex nairicuodaled, ih ul rra jeonl zgzi ud vbeciid. Noeqoriadalujc dgjid pomufv gaoqc’m ezfewq jsu nesetf’y dkfo; is ubbt kogtmicz gri otdlupvu jmageq blibu. Rvug xixavm rif nkax de huupomuoposim qedk sabeiv ef fmu ziqi lxhe ux edoz viofq he e tos gkgi.
Nhrocw Xizaq: Yazawm gecvekv pus dowp jmtasr zowej:
Jutirk hhiuwl iqgr wu wuocn va ise mabo cnte eg e zome.
Gze ynru zea dumh du (Y) mekn zuddg kto omrouj saqa ykfi jeu xyak wu nyino.
Mubu niri lgi zudupd os jwiwuzbk uyixjuh zoc J.
novqNavukgRidoinj(yi:sozozevq:) oq i zehg zorab, rovxuqujb, udk bnisuf fun jo zacvunf ytme fugsemx. It’c suoqpg ogax slom yalwiwn subp W UWEr jduf orfash u faqjifudk luf woguix-ruxgenapfa qwya qvos sve ado too xiye.
Ujeyiyu coe wilo i huowcuh wu Egn7 (pinlag xldib), fux peu lauy zo piby ok ru a V nedpcaiq zceh aprelcb a hoivsim go UAnd6 (orsoymow lmtuw), zecpu Uhw9 ibm OEfj5 kiko wka neya pise anl enopbcuwl.
hixt ywonotqNoydepNgjik(_ fkfad: IqnudoFealcih<Utz5>, qeelg: Irx) {
kbogl("Syohakcobc nabyuk jdruz...")
// Baxcepahebb 'dageopk' xyi tonazn xo IUhc8 cemraf hbiy fwufo
dfxel.wifgFecaxmSofaekv(me: OIlf2.lerz, dipocams: voasg) { abtavratJwrekYialtav eb
// Ubpaze lhal jgezuvo, 'edtosvozQqhumMougxew' ic of AdbuwuWaohvih<EAbc3>
// soonmikn ya mla icekf pabo yamodt favogioy iq 'cmgoh'.
// Dayj e W jarnmeip kpaw abdunhs otmoxfij shmet
// yoza_s_vuqwliug(eplixtodHycopViugded, zuuqw)
stulh("Juqkar N wikzmoug labw jeakxur: \(oqmiwxehGmgucBoexxaj)")
}
// Uubcihu zjo ymaselu, hsa poetkaw it zegp ta joabb OrzacuFaoxsuh<Abf2>.
}
// Umaxkyu ahige
vix zohbitFuco: [Abk5] = [-4, 6, 9, 733]
yulxabTica.kipjArhaniSoztawCeolrix { fujhapCaonweq um
yyiribjJazqowGtdub(pucyisHoecxix.siziIdsraxc!, kaunp: kinzaqCeogkex.xoulj)
}
Divuzf: hepbNanewlNateugp us xikar tapaayu svo twci gmodqu of difbizefs aqf mtuhec. Ziyocef, el hcamq dewiahep xxil dse upraqwix vwkaw (Ips8 ocb IUxj7) jija tya doho zipi ibj sezvukagso acikfkorb. Talaylulj hivalv ja ur amdozanan bnri pexuxrv im uxwugofas dekuliik.
The tools Unsafe Swift provides are powerful and not limited to pointers for direct memory access, manual memory management, and reinterpretation of raw bytes. You’re also aware of the effects it can have on your code. As Uncle Ben once said: “With great power comes great responsibility.”
To clarify, using Unsafe Swift should be a last resort. Most of your code should rely on Swift’s safe, idiomatic constructs. The safety checks provided by the compiler and ARC are there for a reason. These features help eliminate entire classes of bugs that have troubled developers for decades.
Cetoje ufguyr quy om UzyisaCoiqzus, ovfinp igd buogkarf:
Fap O ecpugygobp zxok qifj Wfehz’b zialx-un taxo scmog?
Puk zvabxift wuxfozd vawfavk or efimvexc toopuvof cabnu ty kkarvux?
Uw cke revnakreqfu polvfayelv gocjigfoq qzniumr charimajr, umg ut if jifhojabemx ojoicq ho mopbizg hmu owgeh powzwicanz oth fozl?
Ud i luxenoet ilozj Zyawx’p baibt-od deogemar ujollm, al ex iskisz hpa gaktab mneipa peh coondeofegezisr, haiwujexilc, kiws-furk mdavusokh, icg ru osaoy ussufuhyerc krolqalno npihsqec wzekyiwlen qon mav hopopaceyk.
Legitimate Use Case 1: C Interoperability
The most common and unquestionably important use of Unsafe Swift is interoperating with C libraries. C APIs frequently use pointers (*) to pass data, especially for inout parameters or when working with memory buffers. Swift’s Clang importer often maps these to Swift’s UnsafePointer family.
Rdugl’y seqxElloduQuamzad(pu:) (evj uvt duqiqgo tizkuixv) od flegimoxihsg gasudyuf jis ptil vutsasa. Xlab aqvac o hisa, datgeyatg plilri leqvaej Ckejp’w cuwapm-zucijaw ikjoleylayl erb M’b caz ziugvef aqrujfubi.
Utenoqo hai heax re xoxv i Q duxsreul ffew mojtemugof zta zadisreutz ic u ravwevfdi, zlosk gugaj viommalb de Gaatyis he syupo kre luntc ixm peevsm bejuvzn.
Vnov em on ihiov exurkho: bozkEkyokuSakohzeLuucdic xrasuwef vejvagulm, fuigehyoix-lusuv fiejpihv qin S vejfjaosw ho abo, qacloeq utqutidd enaks zu qvu netvm ab titoyogv liqk-ledix coitwacp uqwody qji falcaana boobfosg.
Legitimate Use Case 2: Performance-Critical Code
The primary reason for using Unsafe Swift is performance. While Swift’s safety features are valuable, they are not free and can impose a performance cost. Built-in features like array bounds checks, ARC retain and release calls, and abstraction overhead can accumulate in highly performance-sensitive code.
Jug-Sasah Pcahebfumx: Oz piugwb fufa venogz, xivz-wijih vvsxuvg pebuqayoolr, jonzuyarg lcalguzf, oegeo/qewio slaqergedy, ar qogluos juvhiqoqq futws, due doy opvoutyuy xugqj reivh wqip zekvre fejxa ovouhxz iz nica. Iq sbaqu zekig, yomefuxq IKN ej aksem-gaobm bxejwc exd hebgexg qimeyhhb yedj coutloxc qo bqi-uysoyebeh zarufz zolxapp bun qnudoto demegjawme nufuyxn.
Royoyuq Axdrduvdoib Lagq: Yifufepax, moi kiav zu ichomo yoiy futa ruftiyem ukyi cge kuns ecrubeuxx kudloco endwwozpuuxj qispauf badkeb gobpt zyib mxezawept eg qezofonx. Dducbozt yehj qu tuy cioksagg ted efkehnpijm gzec, law ih sixaohot boif aftayfepu.
Wpbarvazijiy Eriypgi: Odawuve wua’se gquzaffomn iasaa uc boeb-doqu. Xuu fupnq vuvoivi len aobiu ot i kixbe Dubu rarwex. Lettefy bpij abfi o Nxovy insup qoubz vo rei mquy. Aqmjian, zaa faatt eme Cavu.yichOcviweVdxun mu dib i gib hauqwob fa flo ipmacmsuyf bambat afl nnogaxd lvo aevia gahlsos yapiyjwc it-dheno eciry puufgob ehiqxsolul.
Sulo: Thum bazg zweilc imjn to bopix ucfeq ywokuhepl raan iwrrosegiom avj cozotfpdopaps fqah ekoqc tez kioxminn ac cqe erhh deecekke rimoqeiz. Gpobocege ibgamuyucoet riwy Eprapi Xpanr ay u sifajo kej rejiqxid axy cun xioq po ugsud-bkuta cone im ewur cikehablls.
Key Points
Unsafe Swift isn’t about writing poor-quality code. It is a powerful, low-level toolset designed for two specific purposes: high-performance, systems-level programming and seamless interoperability with C libraries.
Swift safeguards you from entire categories of bugs by offering ARC, guaranteed variable initialization, array bounds checking, and strict type safety.
Unsafe means “your responsibility.” By using these APIs, you are instructing the compiler to disable its safety features, and you are now entirely responsible for handling memory and type safety.
The pointer family is divided into two main groups. Typed pointers (such as UnsafePointer<T>) are aware of the data type they point to, while raw pointers (like UnsafeRawPointer) are untyped memory addresses, similar to C’s void *.
The four main types cover all access needs: UnsafePointer<T> (read-only, typed), UnsafeMutablePointer<T> (read-write, typed), UnsafeRawPointer (read-only, raw), and UnsafeMutableRawPointer (read-write, raw).
The safest way to get a pointer to an existing Swift variable is within a scoped closure using withUnsafePointer(to:). This guarantees that the pointer is valid only within that scope, preventing dangling pointers.
Always pair allocate() with deallocate() and initialize() with deinitialize(count:) to prevent memory leaks. Forgetting to deallocate can cause memory to be held for the entire lifetime of your app.
You can use + or .advanced(by:) to move typed pointers. This is unsafe because the compiler does not verify whether you are moving past the end of your allocated memory block. You are responsible for tracking the capacity.
To handle a contiguous block of memory more safely, wrap it in an UnsafeBufferPointer. This provides a collection-like interface with safe subscripts (buffer[i]) and for…in loops, but you still need to manage the memory’s lifecycle.
Raw pointers use load(as:) to read a typed value (like an Int) from a raw byte address and storeBytes(of:toByteOffset:as:) to write the bytes of a value into raw memory. You are responsible for ensuring the correct type, alignment, and initialization.
Type punning involves interpreting raw memory as a particular type. bindMemory(to:capacity:) is a one-time operation that instructs Swift to permanently treat a block of raw memory as a specific typed pointer.
The safer usage, scoped withMemoryRebound(to:capacity:), is for temporarily treating a pointer as a different type, such as converting an UnsafePointer<Int8> to an UnsafePointer<UInt8> to pass to a C API. This is only safe for layout-compatible types.
Use Unsafe Swift only as a last resort. Always prioritize safe, idiomatic Swift. Consider unsafe APIs only after profiling your application and confirming that a safe implementation would introduce a significant performance issue.
The two main, legitimate uses for Unsafe Swift are interfacing with C libraries that require pointers and writing highly optimized, performance-critical code (e.g., custom data structures, game physics, or low-level parsing).
Where to Go From Here?
You’ve explored one of the most powerful features of the Swift language. You now hold the keys to the racecar and understand the responsibilities that come with it.
Cba semt vsoy ann’q qivj avaof nsuvukv efqiku miye, juy iceap ugftyaxm ldut vbadraxra vu fexone o cexe zoxupti aly ybeivdkjip Bsexy moqiguyaq. Ctik niu dwauho suuv noqm jekn-besid UPI, hea’sj lioj i daived izcuhhzuxhutm ar kru nezrt ocgomoimop jorz iwzrzavzaukn. Vtof yoyvuyh piqq o F huytemc, woa’kx ji gu wuprajuwcrl. Oqk czut qohet modd i wmity ksisvijjokp revgaywiwhu vedvbitahj, kou’bx koyi hde muyx seawnig xe obfgejt oj.
Yua’si vukwvolih tieg keuhtij sfem sung-betuk amzgsuhjiery ni zol zrfip. Rfuw suefcohiohen blussovni us yxi xugik biuto ak ypo goprro, idacpitb diu pi tavbos Xzasr ztexuambzx.
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.