Şərti kompilyasiya kompilyatora qurma mərhələsində məlum olan şərtlərdən asılı olaraq mənbə kodun hissələrini daxil etməyə və ya buraxmağa imkan verir. The Swift Programming Language (2026)-ya görə, #if direktivi maşın kodunun yaradılmasından əvvəl AST təhlili mərhələsində emal olunur. Şərti kompilyasiya tərtibatçılara təkrarlanma olmadan bir neçə platforma və konfiqurasiya üçün vahid kod bazasını saxlamaq imkanı verir.
Başlıca
Şərti kompilyasiya — kompilyatorun şərti kompilyasiya direktivlərini təhlil etdiyi və çıxış binar fayla yalnız şərtləri ödənən kod bloklarını daxil etdiyi mexanizmdir. Bu, müxtəlif hədəf platformalara və konfiqurasiyalara uyğunlaşan vahid kod bazasına sahib olmağa imkan verir.
Konsepsiya C/C++-dən #ifdef, #ifndef, #endif preprosessor direktivləri ilə gəlmişdir. Müasir dillərdə (Swift, Rust, Go) mexanizm ayrıca preprosessor olmadan kompilyator səviyyəsində işləyir ki, bu da təhlükəsizliyi artırır: şərti bloklar kompilyasiya olunmasalar belə, sintaktik cəhətdən düzgün olmalıdır.
Apple WWDC “Embrace Swift” (2025) sessiyasının məlumatlarına görə, Swift layihələrinin təxminən 40%-i bir targetdə iOS və macOS-u dəstəkləmək üçün şərti kompilyasiyadan istifadə edir. UIKit və SwiftUI ilə layihələrdə UI kodu tez-tez #if os(iOS) və #if os(macOS) direktivləri ilə ayrılır ki, bu da biznes mantiqinin təkrar istifadəsinə imkan verir.
Əsas üstünlük — kompilyasiya mərhələsində təhlükəsizlik. Uyğun olmayan platforma üçün kod sadəcə icra olunmur, həm də kompilyasiya olunmur. Bu o deməkdir ki, iOS-a xas koddakı səhvlər macOS üçün qurma zamanı özünü göstərməyəcək və əksinə. İcra zamanı yoxlamaları belə bir zəmanət vermir.
Swift dörd əsas direktiv təqdim edir: #if, #elseif, #else, #endif. C preprosessorundan fərqli olaraq, Swift bütün budaqlarda kodun sintaktik düzgünlüyünü tələb edir — kompilyator bütün kodu təhlil edir, lakin maşın kodunu yalnız aktiv budaqlar üçün yaradır.
Swift daxili yoxlama funksiyalarını dəstəkləyir: os(iOS), os(macOS), os(tvOS), os(watchOS), os(Linux), os(Windows). Bu funksiyalar tətbiqin qurulduğu hədəf platformanı yoxlayır. && və || ilə kombinasiya mürəkkəb şərtlər yaratmağa imkan verir.
// iOS, macOS və tvOS üçün vahid kod
import Foundation
class PlatformService {
func getSystemVersion() -> String {
#if os(iOS) || os(tvOS)
return UIDevice.current.systemVersion
#elseif os(macOS)
let vers = ProcessInfo.processInfo.operatingSystemVersion
return "\(vers.majorVersion).\(vers.minorVersion)"
#else
return "unknown"
#endif
}
}
Swift kompilyator versiyasının yoxlanmasını dəstəkləyir: #if swift(>=5.9). Bu, bir neçə Swift versiyasını dəstəkləyən kitabxana və freymvorklar üçün faydalıdır. Dilin yeni imkanları (məsələn, Swift 5.9-da makrolar) belə bir yoxlama ilə qoruna bilər.
// Geri uyğunluq
#if swift(>=5.9)
@MainActor
struct ModernView: View {
var body: some View {
Text("Müzasir SwiftUI")
}
}
#else
struct ModernView: View {
var body: some View {
Text("Köhnə SwiftUI")
}
}
#endif
canImport(ModuleName) funksiyası göstərilən modulun cari qurma mühitində mövcud olub-olmadığını yoxlayır. Bu, ən çevik mexanizmdir: konkret platformaya bağlı deyil. Məsələn, CoreHaptics istifadə edən kod yalnız bu freymvorkun mövcud olduğu cihazlarda kompilyasiya olunacaq.
#if canImport(CoreHaptics)
import CoreHaptics
class HapticManager {
private var engine: CHHapticEngine?
func playTapFeedback() {
guard let engine else { return }
// Taktil əks-sədanın implementasiyası
}
}
#endif
Kotlin bir dil olaraq preprosessor direktivlərinə malik deyil. Bunun əvəzinə Android ekosistemi üç alternativ təklif edir: BuildConfig sahələri (icra zamanı yoxlamaları), sourceSets (bütün faylların əvəz edilməsi) və expect/actual (Kotlin Multiplatform-da).
Gradle sourceSets müxtəlif fléyorlar və ya qurma növləri üçün siniflərin müxtəlif implementasiyalarına sahib olmağa imkan verir. src/debug/ kataloqunda debug üçün implementasiya, src/release/-də isə release üçün yerləşir. Qurma zamanı Gradle münasib sourceSet-i seçir və yalnız onun fayllarını kompilyasiya edir.
// src/debug/kotlin/com/example/Logger.kt
object Logger {
fun log(tag: String, message: String) {
Log.d(tag, message)
}
}
// src/release/kotlin/com/example/Logger.kt
object Logger {
fun log(tag: String, message: String) {
// Release-də No-op
}
}
KMP expect (ümumi koddə bəyannamə) və actual (konkret platforma üçün implementasiya) mexanizmini təqdim edir. Bu, kompilyasiya mərhələsində işləyən mexanizmdir: iOS üçün iOS sourceSet-dən actual implementasiya, Android üçün isə Android sourceSet-dən kompilyasiya olunur. Hədəf olmayan implementasiyalar kompilyasiya olunmur.
// commonMain — expect bəyannaməsi
expect fun getPlatformName(): String
// androidMain — Android üçün actual
actual fun getPlatformName(): String =
"Android \${Build.VERSION.SDK_INT}"
// iosMain — iOS üçün actual
actual fun getPlatformName(): String =
UIDevice.current.systemName() + " " + UIDevice.current.systemVersion
Android NDK vasitəsilə nativ kitabxanaların hazırlanmasında klassik C/C++ preprosessoru #ifdef, #ifndef, #define direktivləri ilə istifadə olunur. Swift-dən fərqli olaraq, C preprosessoru mətn səviyyəsində işləyir — aktiv olmayan budaqlardakı kod sintaktik cəhətdən səhv ola bilər.
NDK hər platforma üçün makrolar təyin edir: __ANDROID__ (Android), __APPLE__ (iOS/macOS), __linux__ (Linux). Arxitekturalar üçün: __arm__, __aarch64__, __x86_64__. Bu makrolar kompilyator tərəfindən hədəf platforma üçün qurma zamanı avtomatik olaraq təyin edilir.
// Android və iOS üçün nativ kod
#include <cstdint>
#ifdef __ANDROID__
int32_t getJniEnv(JNIEnv* env) {
return env->GetVersion();
}
#elif defined(__APPLE__)
#include <TargetConditionals.h>
int32_t getOsVersion() {
#if TARGET_OS_IOS
return "iOS";
#elif TARGET_OS_OSX
return "macOS";
#endif
}
#endif
NDK ilə işləyərkən yadda saxlamaq lazımdır ki, C/C++ preprosessoru mətn əvəzetməsidir. Aktiv olmayan budaqda sintaktik səhv varsa, kompilyator onu görməyəcək, lakin səhv #define səbəbindən aktiv budaq pozulursa — səhv üzə çıxacaq. #define zəncirlərini minimuma endirmək və constexpr sabitlərindən istifadə etmək tövsiyə olunur.
UniFFI və Mozilla Application Services vasitəsilə mobil inkişafda da istifadə olunan Rust üçün Cargo.toml-də öz mexanizmi — feature flags mövcuddur. Rust-da #[cfg(target_os = "android")] kimi bayraqlar Swift direktivlərinə bənzər şəkildə işləyir: yoxlama preprosessor deyil, kompilyator səviyyəsində aparılır. Bu, Rust-u bir kod bazasından Android və iOS üçün kompilyasiya olunmalı nativ kitabxanalar üçün cəlbedici seçim edir.
Şərti kompilyasiya ciddi şəkildə müəyyən edilmiş ssenarilərdə effektivdir. Səhv istifadə edildikdə, test etmək və saxlamaq çətin olan pis iy verən kod yaradır. Düzgün ssenariləri vɘ tipik səhvləri nəzərdən keçirək.
Birinci ssenari — platforma abstraksiyası: vahid fasad, içərisində şərti kompilyasiya platforma implementasiyasını seçir. İkinci — debug və profilinq: buraxılışa daxil olmamalı olan tərtibatçı alətləri. Üçüncü — geri uyğunluq: minimum versiya yenilənənə qədər köhnə əməliyyat sistemi versiyalarının dəstəyi.
| Ssenari | Dil | Şərt |
|---|---|---|
| Platforma abstraksiyası | Swift | #if os(iOS) |
| Debug | Swift/ObjC | #if DEBUG |
| Geri uyğunluq | Swift | #if swift(>=5.7) |
| Nativ kitabxana | C/C++ | #ifdef __ANDROID__ |
| A/B testi | Java/Kotlin | BuildConfig.FLAVOR |
Ən təhlükəli anti-nümunə — direktivlərin bütün kod boyunca yayılması. Hər ikinci faylda #if varsa, bu arxitekturanın yenidən qurulmasını tələb etdiyinə işarədir. Düzgün həll yolu platforma kodunu protokollar/interfeyslər arxasına çəkmək və Dependency Injection istifadə etməkdir.
Tez-tez verilən suallar
Şərti kompilyasiya kompilyasiya mərhələsində işləyir: aktiv olmayan kod binar fayla daxil olmur. İcra zamanı yoxlamaları (if / switch) həmişə kompilyasiya olunur, şərt icra zamanı yoxlanır. Birincisi təhlükəsiz və səmərəli, ikincisi daha çevikdir (yenidən qurmadan dəyişdirilə bilər).
Bəli, Swift funksiyalar daxilində, dövrlərdə və hətta ifadələr daxilində #if direktivlərinə icazə verir. Bu, Swift-in ilk versiyalarında olmayan imkanlardan biridir. Məsələn: let x = #if DEBUG 1 #else 0 #endif — düzgün kod.
Kotlin-in yaradıcıları preprosessordan şürurlə üz çevirdilər, çünki onu kövrək kodun mənbəyi hesab edirlər. Bunun əvəzinə onlar expect/actual (kompilyasiya səviyyəsində təhlükəsizlik) və Gradle sourceSets (fayl səviyyəsində izolyasiya) təklif edirlər. Hər iki yanaşma mətn əvəzetməsindən daha etibarlıdır.
CI-da müxtəlif bayraq kombinasiyaları ilə tətbiqi qurmaq. Swift üçün: müxtəlif Active Compilation Conditions ilə ayrı Xcode sxemləri konfiqurasiya etmək. Android üçün: ayrı Build Variants konfiqurasiya etmək və hər biri üçün testlər işlətmək. Avtomatlaşdırma məcburidir.
Swift-də #if şərti kompilyator direktividir. Şərtin özü sintaktik cəhətdən səhvdirsə (məsələn, os() adında səhv), kompilyator kompilyasiya xətası verəcək. C/C++-də preprosessor sadəcə makro tapmayacaq və şərt yalan olacaq.
Nəticə
Açar təslim mobil tətbiq hazırlayacağıq
IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.
Həm də oxuyun