Kuidas kirjutada kaasaegset Swifti hästi: väärtustüübid, Swift 6 andmevõistluste turvalisus ja konkurentsus.
Kogu oskus — vaikeversiooni hoitakse GitHubis; sinu muudatused jäävad sinu enda klooni.
Kui see oskus esimest korda ilma konkreetse küsimuseta käivitatakse, vasta ainult:
Olen valmis aitama sul kirjutada kaasaegset Swifti nii, nagu see keel tahab olla kirjutatud.
Ära anna mingit muud teavet enne, kui kasutaja küsimuse esitab.
Kuidas kirjutada Swifti nii, nagu keel tahab olla kirjutatud, ajakohane kuni Swift 6.4-ni.
Tööriistaahela lähtetase: Swift 6.3 (praegune väljalase 2026. aasta augusti seisuga). Kõik siin kompileerub versioonis 6.3, välja arvatud ⚠-ga märgitu, mis tähistab avaldamata Swift 6.4 funktsioone. Samaaegsuse juhised eeldavad Swift 6.2 mudelit — kui projekt on versioonil 6.1 või vanemal, ei kehti §3 reeglid async ja @concurrent kohta.
Läbiv mõte: Swift on järkjärgulise avalikustamise keel. Alusta kõige lihtsamast, kõige staatilisemast ja kõige ühelõimelisemast lahendusest, mis töötab, ning osta dünaamilisust — samaaegsust, viitesemantikat, eksistentsiaale, ebaturvalisi viitasid — ainult seal, kus saad põhjusele osutada. Iga allpool toodud reegel on selle rakendus.
Modelleeri see vaikevalikute hierarhia. Liigu tasemel allapoole ainult siis, kui oskad põhjuse sõnastada:
| Vajadus | Kasuta | Liigu allapoole ainult siis, kui |
|---|---|---|
| Andmed | struct / enum |
vajad identiteeti, jagamist või pärilust |
| Abstraktsioon | konkreetne tüüp | sul on tüüpide vahel korduvat koodi |
| Polümorfism | some P (geneeriline) |
vajad heterogeenset salvestust → any P |
| Täitmine | peaaktor, sünkroonne | profileerimine näitab hangumist → async → @concurrent → actor |
| Mälu | Array, String |
profileerimine näitab kulu → InlineArray, Span |
| Ohutus | turvaline API | C-koostöö või mõõdetud kuum rada → Unsafe* |
Väärtustüübid on Swiftis vaikimisi valik, mitte erijuhtum.
struct-i ja enum-it. Kasuta class-i ainult identiteedi, jagatud muudetava oleku, päriluse või ressursi eluea jaoks. Aknal, andmebaasiühendusel, renderdusmootoris hoitaval olemil on identiteet. Point-il, Drink-il, Material-il ei ole.let; var ainult siis, kui muudad. See on sama distsipliin nagu some enne any-t ja väärtus enne viidet: alusta kitsalt, laienda põhjusega.private salvestatud omadus copy-on-write'i taga.isKnownUniquelyReferenced(&storage); kui see pole unikaalne, kopeeri esmalt. Täpselt nii töötavad Array, String ja Dictionary.isSharing, selectedRows, shareTarget) ühe enum State-iga, muutuvad kehtetud kombinatsioonid väljendamatuks ja olekumuutus atomaarseks, mitte omaduste kirjutamise jadaks, mille lõpetamine võib ununeda.struct Material { // value semantics preserved
var roughness: Double
private var _texture: Texture // a class
var color: Color {
get { _texture.color }
set {
if !isKnownUniquelyReferenced(&_texture) { _texture = Texture(copying: _texture) }
_texture.color = newValue
}
}
}
Mittekopeeritavad tüübid (~Copyable) väljendavad ainuomandit: failideskriptor, pangaülekanne, avatud ressurss. Kopeerimise keelamine muudab „seda ei tohi kaks korda käivitada“ väitest kompileerimisveaks ja annab structi deinit-ile tähenduse. Märgi lõpetav meetod consuming-uks, et kompilaator tõestaks, et see on viimane kasutus. Parameetrite omand muutub selgesõnaliseks: borrowing (ainult lugemine, vaikimisi), consuming (võtab ära), inout/mutating (ajutine kirjutusõigus).
Swifti veakäsitlus toetub kolmele punktile: veaallikad on märgistatud, et need sind ei üllataks; vead kannavad piisavalt konteksti, et nende põhjal tegutseda; ja taastatavad vead erinevad programmeerija vigadest.
throw. Programmeerija viga → precondition/fatalError. Ebaõnnestunud võrgupäring hoiab programmi töös. Piiridest väljas indeks tähendab, et kood on vale ja peab peatuma enne, kui viga turvaprobleemiks muutub.case duplicateFriend(String) on parem kui case duplicateFriend — kontekst ongi kogu mõte.guard veatingimuste jaoks, sest see sunnib väljumisraja peale. if let tavalise lahtipakkimise jaoks.throws(MyError)) on mõeldud sisemistele funktsioonidele, vigu edasi suunavale geneerilisele koodile ja piiratud keskkondadele, kus any Error-i karpimine on liiga kulukas. Avaliku API puhul säilitab tüübitamata throws sinu vabaduse veatüüpi hiljem muuta. Pane tähele ühtlustust: throws on throws(any Error) ja mitteviskav on throws(Never) — see võimaldabki map-il abstraheerida mõlema üle.!-le teatega ebaõnnestuvat #require/precondition-it.Selle jaotise teevad agendid kõige sagedamini valesti, sest mudel muutus Swift 6.2-s.
Alusta iga rakendust täielikult põhilõimel. Ühelõimeline kood viib kaugele ja enamik rakendusi ei pea sellest kunagi lahkuma.
Edenemine järjekorras. Ära jäta samme vahele.
async/await latentsuse varjamiseks (võrk, ketas). Ikka veel pole sul omaenda samaaegsust — SDK API-d nagu URLSession.data(from:) tõstavad töö sinu eest mujale.@concurrent, et tõsta sinu kulukas töö põhilõimelt ära — ainult pärast seda, kui Instruments näitab hangumist.actor, et tõsta olek peaaktorilt ära — ainult siis, kui liiga palju peaaktori olekut sunnib ülesandeid pidevalt tagasi hüppama.Lülita esmalt sisse õiged ehitusseaded. Lülita igas projektis sisse Approachable Concurrency. Rakendusmoodulite ja kasutajaliidesega seotud moodulite puhul määra ka Default Actor Isolation väärtuseks MainActor — see on Xcode 26-s uute rakendusprojektide vaikeväärtus ja kustutab enamiku sinu @MainActor annotatsioonidest. Paketis: swiftSettings: [.defaultIsolation(MainActor.self)]. Ära määra main-actor-by-default'i üldotstarbelisele teegile — teegid peaksid tarnima nonisolated API-sid ja laskma klientidel otsustada, kus töö jookseb.
Swift 6.2-s ei vii funktsiooni märkimine async-iks seda praeguselt aktorilt ära. See jookseb seal, kust see välja kutsuti. Just see teeb „kõige loomulikumalt kirjutatava koodi“ vaikimisi andmevõidujooksudest vabaks.
@concurrent — lülitub alati samaaegsele lõimekogumile. Kasuta seda oma protsessorimahuka töö jaoks.nonisolated — jookseb seal, kust see välja kutsutakse. See on teegi API-de jaoks õige vaikevalik, sest otsustab väljakutsuja. nonisolated tüübil muudab kõik selle liikmed nonisolated'iks (Swift 6.1+).nonisolated struct PhotoProcessor { // decoupled from the main actor
@concurrent // guaranteed to run in the background
func process(_ data: Data) async -> ProcessedPhoto {
async let sticker = extractSticker(data) // two independent jobs, in parallel
async let colors = extractColors(data)
return await ProcessedPhoto(sticker: sticker, colors: colors)
}
}
UserDefaults väärtuse lugemiseks maksab rohkem, kui see säästab.await on peatamispunkt ja see lõhub atomaarsuse. Olek võib peatatud olekus olles muutuda ja võid jätkata teisel lõimel. Kontrolli eeldusi pärast iga await-i uuesti. Ära kunagi hoia lukku üle selle. Ära kunagi toetu üle selle lõimelokaalsele salvestusele.Aktorid tagavad vastastikuse välistamise, mitte tehingud. Kahe await-i vahel samal aktoril jookseb muu töö.
await-i juures järjepidevasse olekusse.await allalaadimine → kirjuta vahemällu. Kaks ülesannet mõlemad ei leia, mõlemad laadivad alla, teine kirjutab esimese üle. Kontrolli pärast await-i uuesti või dedubleeri pooleliolev töö.AsyncStream-i, mitte aktorit.Sendable märgib tüübi, mida on ohutu isolatsioonidomeenide vahel jagada. Kompilaator kontrollib seda iga ülesande ja aktori piiril.
Sendable, kui nende salvestus on seda — mitteavalike tüüpide puhul tuletatakse see automaatselt. Avalikele tüüpidele ei tuletata sendability't kunagi: avaliku tüübi märkimine Sendable-iks on lubadus sinu klientidele, seega paneb Swift sind seda ise kirjutama.@MainActor klassid on kaudselt Sendable, sest nende olek on isoleeritud.@MainActor ega Sendable. Hoia need teadlikult mitte-Sendable-ina — see hoiab ära olukorra, kus pool mudelist muudetakse põhilõimel ja teine pool taustal. Kui need peavad peaaktorilt lahkuma, tee need nonisolated-iks, mitte Sendable-iks.Sendable objekti saab domeenide vahel siiski saata, kui saatja lõpetab selle kasutamise. Tee kõik muudatused enne üleandmist; selle puudutamine pärast seda ongi viga.@Sendable-iks ainult siis, kui see tõesti domeene ületab.@unchecked Sendable on lubadus, mida kompilaator kontrollida ei saa. Jäta see tüüpidele, millel on tõeline sisemine sünkroniseerimine (Mutex, lukk). Sama kehtib nonisolated(unsafe) kohta globaalsel muutujal — viimane abinõu, mitte hoiatuste vaigistaja.Kui saad andmevõidujooksu vea, liigu läbi see loend:
Sendable väärtustüüp, nii et „jagamine“ on tegelikult kopeerimine.Mutex/Atomic moodulist Synchronization (salvesta need let-omadustesse) või @unchecked Sendable.Globaalsed ja staatilised muutujad on kõige levinum veaallikas. Eelistuse järjekorras: tee sellest let; pane see @MainActor-ile; mähi see Mutex-isse; nonisolated(unsafe). Pane tähele, et Swiftis initsialiseeritakse globaalsed muutujad laisalt ja atomaarselt — erinevalt C-st.
Vanade tagasikutse-API-de sildamine: annoteeri delegaatprotokollid @MainActor-iga, kui need on sinu omad. Kui ei ole, märgi meetod nonisolated-iks ja kasuta MainActor.assumeIsolated { } — see kinnitab, mitte ei hüppa, nii et see kukub valjult kokku, selle asemel et vaikselt võidu joosta. @preconcurrency vastavusel on sama asja lühivorm. Kasuta @preconcurrency import-i, et ajutiselt vaigistada sendability hoiatusi moodulist, mis pole veel migreeritud; hoiatused tulevad — õigustatult — tagasi, kui see on tehtud.
Eelista alati struktureeritud ülesandeid.
Struktureeritud ülesannetel (async let, ülesandegrupid) on lokaalsete muutujate sarnane skoop: need ei saa plokist kauem elada, neid oodatakse automaatselt ja need pärivad ülesandepuu kaudu tühistamise, prioriteedi ja ülesandelokaalsed väärtused. Struktureerimata ülesanded (Task { }, Task.detached) ei anna sulle midagi sellest automaatselt.
async let fikseeritud, staatiliselt teadaoleva arvu samaaegsete alamülesannete jaoks.withTaskGroup, kui arv on dünaamiline. Ülesandegrupid vastavad AsyncSequence-ile — itereeri tulemusi nende saabumise järjekorras. Kasuta withDiscardingTaskGroup-i, kui alamülesanded ei tagasta midagi: see vabastab iga alamülesande ressursid kohe ja tühistab esimese vea korral õdeülesanded.Task { } ainult siis, kui töö eluiga ei mahu skoobi sisse — reageerimine delegaadi tagasikutsele, nupuvajutusele, vaate ilmumisele. See pärib aktori isolatsiooni ja prioriteedi; tühistamist pead ise haldama.Task.detached peaaegu mitte kunagi. See ei päri midagi — ei isolatsiooni, prioriteeti ega ülesandelokaalseid väärtusi. Kui vajad lahtiühendatud juurt, pane ülesandegrupp selle sisse, selle asemel et korduvalt lahti ühendada.Tühistamine on koostööpõhine. Tühistamine seab lipu; see ei peata midagi. Kontrolli Task.isCancelled või try Task.checkCancellation() enne kuluka töö alustamist, ka sünkroonsetes abifunktsioonides. Töö puhul, mis on pigem peatatud kui jookseb (AsyncSequence'i next()), kasuta withTaskCancellationHandler-it — ja pea meeles, et käsitleja jookseb kohe ja kehaga samaaegselt, nii et olek, mida see puudutab, vajab tõelist sünkroniseerimist (atomaarset muutujat või lukku, mitte aktorit — aktoril ei saa järjestust tagada).
Piira oma samaaegsust. Ära hargne piiramatu loendi peal iga elemendi kohta üheks alamülesandeks. Käivita N alamülesannet ja lisa uus iga kord, kui mõni lõpetab.
Ülesandelokaalsed väärtused (@TaskLocal) levitavad konteksti — päringu ID-d, jälituslõiku — ülesandepuus allapoole, ilma et peaksid parameetrit läbi iga signatuuri vedama. Tee need valikuliseks, et sidumata lugemistel oleks mõistlik vaikeväärtus.
Tagasikutsete sildamine: withCheckedContinuation / withCheckedThrowingContinuation. Leping on jätka täpselt üks kord igal rajal — jätkamata jätmine paneb väljakutsuja igaveseks ootama; kaks korda jätkamine on fataalne viga. Hiljem käivituvate delegaat-API-de puhul salvesta jätk ja sea see jätkamisel nil-iks. (Swift 6.4 — avaldamata — lisab tüübi Continuation, mis kontrollib ühekordset jätkamist kompileerimise ajal.)
AsyncSequence: itereeri for await / for try await abil. Kohanda olemasolev käsitleja- või delegaadipõhine API AsyncStream / AsyncThrowingStream abil — loo allikas sulundi sees, tee käsitlejast yield ja korista onTermination-is.
View on @MainActor-isoleeritud ja nii on ka kõik, mida see sisaldab, sealhulgas sinu @State. Peaaegu kunagi pole vaja kirjutada @MainActor vaatele või vaatemudelile — ja main-actor-by-default'iga saad olemasolevad kustutada.@Sendable API signatuuris: visualEffect, Shape.path(in:), Layout nõuded, onGeometryChange. Kui saad mõne sellise sulundi sees isolatsioonivea, ära saada self-i — kopeeri üks vajalik väärtus sulundi haaramisloendisse..visualEffect { [pulse] effect, proxy in // copy the Bool, don't capture self
effect.blur(radius: pulse ? 2 : 0)
}
withAnimation olekumuutus sünkroonsesse tagasikutsesse; ava Task ainult sellele järgneva pikaajalise töö jaoks.Ära alusta klassist. Ära alusta ka protokollist.
Töövoog: kirjuta konkreetsed tüübid → märka nendevahelist korduvat koodi → tõsta ühine võimekus protokolli → kirjuta selle vastu geneeriline kood. Peaaegu identsete kehadega ülelaadimised on signaal, et on aeg üldistada.
GeometricVector<Storage: SIMD>, mitte GeometricVector: SIMD.)any P-d, kutsub välja laienduse oma. Kui tüüp peaks saama midagi kohandada, tee sellest nõue.some vs anysome P. Muuda see any P-ks, kui pead salvestama suvalisi tüüpe. Sama distsipliin nagu let enne var-i.some P — üks fikseeritud aluseks olev tüüp skoobi kohta. Säilitad kõik tüübisuhted, sealhulgas seotud tüübid, ja kompilaator saab spetsialiseerida.any P — tüübikustutatud karp, dünaamiline tüüp varieerub käitusajal. Vajalik heterogeensete kogumite, aluseks oleva tüübi valikulisuse ja abstraktsiooni täielikuks peitmiseks. Selle eest maksad: seotud tüüpide suhted kustutatakse nende ülempiirideni ja väljakutsed on optimeerijale läbipaistmatud.any P peal ei saa välja kutsuda meetodit, mis võtab seotud tüübi. Kustutamine töötab tootvas positsioonis (tulemus kustutatakse ülempiirini), aga mitte tarbivas. Lahendus on anda eksistentsiaal edasi funktsioonile, mis võtab some P — kompilaator pakib selle lahti ja selles skoobis on tüüp jälle fikseeritud.some Collection<Element>, any Collection<any Animal> — lasevad peita LazyFilterSequence<[Animal]>-i, eksponeerides samas elemendi tüüpi. Deklareeri oma protokollidele esmased seotud tüübid (protocol Container<Item>) tüübi jaoks, mille väljakutsujad tegelikult annavad, mitte implementatsiooni detailide jaoks nagu Iterator.where-klauslites on viis, kuidas protokollide vahelisi suhteid kinnitada (where Self.CropType.FeedType == Self). Ilma nendeta ei läbi „kasvata, siis lõika“ tüübikontrolli ja valed vastavused kompileeruvad.Selgus kasutuskohas on eesmärk, mis on siin kõigist teistest tähtsam.
get asünkroonsetest alternatiividest ja kõigest, mis tagastab tulemuse otse. persistentPosts, mitte getPersistentPosts.private (fail), internal (moodul ja vaikimisi), package, public. Piiril selgesõnaline olemine sunnib tegema ülaltoodud sendability ja API arengu otsused.UUID String-i asemel.@Argument, @Published, kaitsev kopeerimine, laisk, lõimelokaalne), nii et deklaratsioonikoht väljendab poliitikat ühe sõnaga. Kombineeri @dynamicMemberLookup-iga võtmeteel, et projitseerida läbi mähise (nii töötab $binding.title).Madala taseme Swifti jõudlust domineerivad neli kulu. Tea, millist neist maksad.
Aga tee esmalt algoritmiline töö. Iga kord, kui kirjutad tsükli, proovi see asendada algoritmi väljakutsega. Suurimad võidud pole peaaegu kunagi mikrooptimeerimised:
Array.remove(at:) on O(n); selle tsüklis väljakutsumine on O(n²). removeAll(where:) on kokku O(n). Data koostamine baidi kaupa ümberlõikamise teel on O(n²); popFirst() on O(1). Mõlemad olid 100×+ regressioonid, mis peitusid puhta välimusega koodi taga.map/flatMap/filter eraldavad iga etapi jaoks massiivi. Elegantne ≠ kiire. Kui konveier jookseb kuumas tsüklis iga piksli või elemendi kohta, määra väljundi suurus üks kord ja kirjuta sellesse.platform_memmove, tähendab see juhuslikku kopeerimist; miljon lühiajalist eraldust tähendab vahemassiive; swift_beginAccess tähendab käitusaegseid eksklusiivsuse kontrolle; swift_retain/swift_release tähendab viiteloenduse liiklust.Konkreetsed hoovad, umbes selle järjekorras, mida need annavad:
final klassidel, mida sa ei kavatse alamklassida, muudab dünaamilise saatmise staatiliseks ja võimaldab sisseehitamist (inlining). Terve mooduli optimeerimine laseb kompilaatoril paljudel juhtudel seda sinu eest tõestada — ja võimaldab geneerilist spetsialiseerimist, mille puhul geneerikud lakkavad midagi maksmast.any P eksistentsiaalil on 3-sõnaline reasisene puhver. Väärtused, mis sinna mahuvad, elavad reasiseselt; suuremad eraldatakse iga koopia jaoks kuhjas. Kehtib sama tehnika: anna suurele tüübile kaudne salvestus copy-on-write'iga ja see mahub jälle puhvrisse.[MyModel] on parem kui [any Model] — tihedalt pakitud, tüübiinfo antakse üks kord, spetsialiseeritav. [any Model] on paindlik, kuid läbipaistmatu valik; kasuta seda, kui seda vajad.T: AnyObject) annab kompilaatorile teadaoleva esituse ka ilma spetsialiseerimiseta.InlineArray<N, T> (Swift 6.2) fikseeritud suurusega salvestuse jaoks: elemendid salvestatakse reasiseselt, suurus on tüübis väärtusgeneerikute kaudu, kuhjaeraldust pole, viiteloendust pole, unikaalsuse ega eksklusiivsuse kontrolle pole. Vale valik, kui seda kopeeritakse või jagatakse.Span / RawSpan / OutputSpan (Swift 6.2) asendavad withUnsafeBufferPointer-i otseseks ligipääsuks järjestikusele salvestusele. Need on mittepõgenevad, nii et kompilaator seob nende eluea konteineriga — saad viida jõudluse ilma elueavigadeta ja retain'id/release'id kaovad.@inline(always) (paari meetoditel final-iga) ja @specialized(where T == ...) (SE-0460), et geneerilist eelnevalt kuumade konkreetsete tüüpide jaoks spetsialiseerida.borrow/mutate aksessorid get/set asemel suurte salvestatud väärtuste jaoks, UniqueArray/UniqueBox ning Ref/MutableRef, et korduv otsing tsüklist välja tõsta.Asünkroonsed funktsioonid hoiavad oma olekut ülesandepõhises plokieraldajas, mitte C-pinus, ja jagunevad igas peatamispunktis osafunktsioonideks. Kuluprofiil sarnaneb sünkroonsete funktsioonidega, kutse lisakulu on veidi suurem — mis on veel üks põhjus mitte teha async-iks midagi, millel pole midagi oodata.
Hüpped peaaktorile ja sealt tagasi maksavad tõelise kontekstivahetuse. Koonda: lükka tsükkel funktsioonide loadArticles/updateUI sisse, nii et need võtavad massiive, selle asemel et iga iteratsiooni kohta kaks korda hüpata.
deinit jookseb, on varjatud viga.weak/unowned on viitetsüklite katkestamiseks — mitte millekski muuks. weak viite lugemine pärast tugeva omaniku viimast kasutust võib õigustatult anda nil. Valikuline sidumine on seal halvem kui sundlahtipakkimine: see muudab valju krahhi vaikseks valeks vastuseks.weak: ära ehita tsüklit. Tõsta jagatud andmed kolmandasse tüüpi, millele mõlemad pooled viitavad, muutes tsükli puuks.withExtendedLifetime töötab, kuid paneb korrektsuse sinu kanda ja levib koodibaasis — käsitle seda paiga, mitte disainina.deinit-i kõrvalmõjud lokaalsed. Mõõdikute avaldamine või globaalse efekti käivitamine deinit-ist järjestub optimeerija otsuste vastu. Kasuta selle asemel kutsekohas defer-it ja jäta deinit kontrollimiseks.Kasuta uute testide jaoks Swift Testingut. XCTest on endiselt vajalik täpselt kolme asja jaoks: kasutajaliidese automatiseerimine (XCUIApplication), jõudlusmõõdikud (XCTMetric) ja testid, mis peavad olema kirjutatud Objective-C-s või mis püüavad Objective-C erandeid.
@Test mis tahes funktsioonil — globaalne, staatiline või eksemplari oma; async, throws ja globaalse aktoriga isoleeritud töötavad kõik.#expect(...) võtab tavalisi avaldisi. Pole XCTAssertEqual-stiilis funktsioonide perekonda, mida pähe õppida — #expect(a == b), #expect(list.isEmpty), #expect(!x.contains(y)) kõik haaravad ja kuvavad ebaõnnestumisel alamavaldiste väärtusi.try #require(...), et peatada test ebaõnnestumisel ja pakkida valikuline väärtus ohutult lahti. See asendab continueAfterFailure = false ja laseb valida ootuse kaupa.struct-id. Iga testfunktsiooni jaoks luuakse uus eksemplar, nii et olek ei saa testide vahel lekkida. Kasuta seadistuseks init-i; kasuta class-i/actor-it ainult siis, kui vajad koristamiseks deinit-i. Pesasta komplekte rühmitamiseks.@Test(arguments: [...]) käivitab iga juhtumi sõltumatult, paralleelselt, eraldi uuesti käivitatavana, ja tulemustes on ebaõnnestunud argument nimetatud. Kaks argumendikogumit annavad täieliku ristkorrutise — kasuta zip()-i, kui soovid selle asemel sobitatud paare..enabled(if:) / .disabled("reason") tingimuste jaoks (ära kunagi kommenteeri testi välja — keelatud test ikka kompileerub), .bug(url) jälgimiseks, .tags(...) testide seostamiseks failide ja sihtmärkide vahel, .timeLimit, .serialized, kui test tõesti ei saa paralleelselt joosta. Kasuta käitusaegse #available kontrolli asemel @available-it, et testimisteek sellest teaks.withKnownIssue { } testile, mis ebaõnnestub millegi tõttu, mis pole sinu kontrolli all — see jätkab kompileerimist ja jooksmist ning annab teada, kui probleem on parandatud, erinevalt .disabled-ist.confirmation tagasikutsete jaoks, mis käivituvad N korda; withCheckedContinuation ühekordsete tagasikutsete jaoks, millel pole asünkroonset ülelaadimist..serialized järele.#expect(processExitsWith: .failure) { ... } — katavad precondition/fatalError rajad isoleeritud alamprotsessis. Ainult macOS, Linux, FreeBSD, Windows.XCTFail-i mähkivaid abifunktsioone välja kutsuda Swift Testingu testidest ja vastupidi; määra režiimiks complete või strict (mitte limited ega kunagi none), et raamistikeülesed probleemid jääksid vigadeks ja suunaksid sind Issue.record asenduse juurde.Kasuta makrot, kui kirjutad koodi, mille kompilaator saaks tuletada — ja ainult siis.
#foo) toodavad avaldise või deklaratsiooni. Kinnitatud (@Foo) täiendavad deklaratsiooni ühes viiest rollist: member, peer, accessor, member-attribute, conformance. Rollid on kombineeritavad — @Observable on member + member-attribute + conformance.assertMacroExpansion-iga. See on kiireim tsükkel ja nii väldid vigu koodis, mida keegi ei loe. Sea expansion-isse murdepunkt ja tee süntaksisõlmele po, et selle kuju tundma õppida.context.addDiagnostic-ut hoiatuste ja parandusettepanekute (fix-it) jaoks konkreetses kohas. Ära kunagi lase makrol vaikselt genereerida koodi, mis ei kompileeru.Logger moodulist os, mitte print. Loo üks iga alamsüsteemi ja kategooria kohta. Sõnumeid salvestatakse optimeeritud kujul ja renderdatakse ainult kuvamisel, nii et logimine on piisavalt odav, et see sisse jätta.privacy: .public abil ainult andmete puhul, mis tõesti pole isiklikud. Kasuta .private(mask: .hash), kui pead väärtusi seostama neid paljastamata.debug (ei salvestata kunagi, kiireim — sõnumi koostamine optimeeritakse täielikult välja, kui voogedastust pole), info, notice (vaikimisi), error, fault (kõige püsivam, aeglaseim). Logi tasemel error/fault asju, mida tahad veaaruandes näha.log collect --device --start ..., seejärel filtreeri Console'is alamsüsteemi järgi.format: ja align: on tasuta — kasuta neid, et logid oleksid loetavad ja veergude kaupa valitavad.await-ist üle lõimede läbi, swift task info näitab prioriteeti ja alamülesandeid ning nimega ülesanded ilmuvad nii silurisse kui ka Instrumentsi Swift Concurrency malli.Span-i Unsafe*Pointer-ile. Alates Swift 6.2-st on olemas turvaline, mittepõgenev ja sama kiire viis järjestikusele salvestusele ligi pääseda. Jäta toorviidad C-koostöö jaoks.@diagnose (avaldamata) võimaldab selle üksikute funktsioonide jaoks sisse lülitada.~Copyable-ina). Swift 6.3 atribuut @c eksponeerib Swifti funktsioonid tagasi C-le (koos @implementation-iga, kui deklaratsioon on juba päisfailis olemas). Võta Swift kasutusele üks fail korraga; ära kirjuta ümber.Agendid kirjutavad tavapäraselt kõigi nende vanemat ja pikemat vormi.
⚠-ga märgitud read on Swift 6.4, mis pole veel välja tulnud. Praegune väljalase on 6.3.x. Nende ettepanekud on vastu võetud ja põhiharus implementeeritud, nii et nende ümber on ohutu planeerida, aga täna on ebaturvaline neid kirjutada — kontrolli enne kasutamist projekti tööriistaahelat ja eelista vanemat vormi, kui sihtversioon on 6.3 või vanem.
| Selle asemel | Kirjuta | Alates |
|---|---|---|
Pesastatud ternaarsed operaatorid; kohe väljakutsutud sulund let-i initsialiseerimiseks |
if/switch avaldised |
5.9 |
| Ülelaadimised 1, 2, 3… argumendi jaoks | parameetripakid (each T) ja for üle paki |
5.9 |
ObservableObject + @Published igal omadusel |
@Observable |
5.9 |
| Objekti küsitlemine muudatuste suhtes | Observations { ... } — tehinguliste uuenduste AsyncSequence |
6.2 |
NotificationCenter stringipõhise userInfo-ga |
konkreetsed teavitustüübid (MainActorMessage / AsyncMessage) |
6.2 |
Process + torud skriptimiseks |
pakett Subprocess (AsyncBufferSequence.strings() reakaupa väljundi jaoks; 1.0 saabub koos 6.4-ga) |
6.2+ |
| Käsitsi tehtud stringiindeksi arvutus | Swift Regex — literaalid lühiduse jaoks, RegexBuilder struktuuri jaoks |
5.7 |
Fikseeritud suurusega [String] kuumal rajal |
InlineArray<N, T> |
6.2 |
withUnsafeBufferPointer |
.span / .bytes (RawSpan) / OutputSpan |
6.2 |
Käsitsi Task.isCancelled žongleerimine kirjutamise lõpetamiseks |
Task tühistamiskilp (SE-0504) |
6.4 ⚠ |
| Sõnastiku käsitsi ümberehitamine võtme kasutamiseks | mapKeyedValues |
6.4 ⚠ |
@available(iOS ..., macOS ..., tvOS ..., watchOS ..., visionOS ...) |
@available(anyAppleOS ...) |
6.4 ⚠ |
Rocket.SaturnV, kui tüüp varjutab moodulit |
moodulivalija Rocket::SaturnV |
6.3 |
| Üldine „hoiatused vigadeks“ | @diagnose deklaratsiooni / hoiatusgrupi kaupa |
6.4 ⚠ |
@unchecked Sendable weak var-i tõttu |
weak let; või väljenda mitte-saadetavust ~Sendable-iga |
6.4 ⚠ |
| Binaarvormingute käsitsi parsimine viitadega | Swift Binary Parsing (ParserSpan, ületäitumist kontrollivad parsimisinitsialiseerijad) |
6.2 |
| Kohmakad testfunktsioonide nimed | toorsed identifikaatorid: @Test func `fruits have a tropical climate`() |
6.0 |
Veel tasub teada: Swift Regexi parserid on kombineeritavad Foundationi päris parseritega (.date(...), .currency(...)) — ära kunagi kirjuta kuupäeva või numbri parsimist regexi sees käsitsi. Määra lokaat selgesõnaliselt, selle asemel et süsteemi oma pärida. Ja kasuta NegativeLookahead-i või Local-it (atomaarsed grupid), et takistada mustril kogu sisendi ulatuses tagasi otsimast.
Järjekord on oluline ja sammude segamine on see, mis migratsioonid seisma paneb.
var globaalsed muutujad, mis peaksid olema let, vabad funktsioonid, mis kuuluvad @MainActor-ile, üks avalik struct, mis vajab : Sendable. Üksainus rida võib kõrvaldada kümneid.Võid range kontrolli uuesti välja lülitada ja tarnida; iga tehtud parandus on tõeline täiustus, mis jääb alles. Lülita Approachable Concurrency ja rakendusmoodulite puhul main-actor-by-default sisse enne alustamist — mõlemad vähendavad dramaatiliselt nähtavate vigade arvu ja Xcode tarnib migratsioonitööriistu, mis rakendavad paljud muudatused sinu eest (swift.org/migration).
| Vajadus | Kasuta | Mitte |
|---|---|---|
| Andmetüüp | struct / enum |
class ilma identiteedi või jagamiseta |
| Jagatud muudetav olek | actor või @MainActor klass |
class + lukk, mida pead meeles pidama |
| Töö tõstmine põhilõimelt ära | @concurrent func … async |
Task.detached, DispatchQueue.global() |
| Teegi API isolatsioon | nonisolated |
@MainActor, @concurrent |
| Fikseeritud arv paralleelseid töid | async let |
N struktureerimata Task-i |
| Dünaamiline arv paralleelseid töid | withTaskGroup (piiratud) |
üks ülesanne elemendi kohta, piiramatult |
| Alamülesanded, mis ei tagasta midagi | withDiscardingTaskGroup |
withTaskGroup, mida sa kunagi ei tühjenda |
| Kasutajaliidese sündmusega seotud töö | Task { } tagasikutse sees |
tagasikutse muutmine async-iks |
| Andmevõidujooksu parandamine | lõpeta objekti jagamine | @unchecked Sendable |
| Jagatud mudeliklass | mitte-Sendable või @MainActor |
Sendable + käsitsi lukustamine |
Blokeeriv primitiiv üle await-i |
mitte midagi — struktureeri ümber | DispatchSemaphore, NSCondition |
| Polümorfism | some P |
any P, kui sa ei vaja salvestust |
| Heterogeenne kogum | [any P] |
klassihierarhia |
| Jagatud käitumine, kohandamiseta | piiratud extension |
uus protokoll |
| Kohanduspunkt | protokolli nõue | ainult laienduses olev meetod |
| Viitetsükli katkestamine | struktureeri ümber puuks | weak + withExtendedLifetime |
| Sobivate elementide eemaldamine | removeAll(where:) — O(n) |
remove(at:) tsüklis — O(n²) |
| Otsene ligipääs järjestikusele mälule | .span |
withUnsafeBufferPointer |
| Fikseeritud suurusega puhver kuumal rajal | InlineArray<N, T> |
Array |
| Uus test | @Test + #expect |
XCTestCase + XCTAssertEqual |
| Sama test paljude sisenditega | @Test(arguments:) |
for-tsükkel või kopeerimine |
| Testi peatamine ebaõnnestumisel | try #require |
continueAfterFailure = false |
| Ajutiselt katkine test | withKnownIssue |
.disabled või väljakommenteerimine |
| Diagnostika tarnitavas koodis | Logger + korrelatsiooni ID |
print |
| Optimeerimise otsustamine | Instruments profileeritud testil | intuitsioon |