0Pricing
Swift Academy · درس

‏@dynamicMemberLookup

أنشئ وصولًا ديناميكيًا وآمنًا من حيث النوع إلى الأعضاء.

‏@dynamicMemberLookup درس مجاني في Swift Academy على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Swift Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Swift Academy 4 دروس في المجموع.

ما البحث الديناميكي عن الأعضاء؟

تتيح السمة @dynamicMemberLookup لنوع ما الاستجابة للوصول إلى عضو باستخدام الصيغة النقطية، حتى إن لم يكن هذا العضو خاصية حقيقية مُعلنة. عندما تكتبون value.something، توجّه Swift هذا الوصول إلى صيغة فرعية خاصة تعرّفونها.

يمنح هذا الأنواع المغلِّفة مظهرًا طبيعيًا، مع إبقاء الوصول في موضع واحد.

@dynamicMemberLookup
struct Settings {
    private var storage: [String: String] = ["theme": "dark"]
    subscript(dynamicMember key: String) -> String? {
        storage[key]
    }
}

let s = Settings()
print(s.theme ?? "none")

الصيغة الفرعية المطلوبة

يجب على النوع الموسوم بالسمة @dynamicMemberLookup تنفيذ subscript(dynamicMember:). عندما تكون المعلمة من النوع String، يتحول اسم العضو الذي تكتبونه إلى تلك السلسلة النصية وقت التشغيل.

لذلك يستدعي config.timeout الصيغة الفرعية بالمفتاح "timeout".

@dynamicMemberLookup
struct Config {
    let values: [String: Int]
    subscript(dynamicMember member: String) -> Int {
        values[member] ?? 0
    }
}

let config = Config(values: ["timeout": 30, "retries": 3])
print(config.timeout)
print(config.retries)

البحث باستخدام السلاسل النصية ليس آمنًا من ناحية النوع

يتميز البحث الديناميكي عن الأعضاء المعتمد على السلاسل النصية بالمرونة، لكنه يفقد التحقق وقت الترجمة. إذا أخطأتم في تهجئة عضو، فستظل الشيفرة تُترجم، وستحصلون على أي قيمة تُرجعها الصيغة الفرعية لمفتاح مفقود.

في المثال أدناه، يُرجع عضو غير معروف القيمة الافتراضية بهدوء بدلًا من فشل عملية البناء.

@dynamicMemberLookup
struct Config {
    let values: [String: Int]
    subscript(dynamicMember member: String) -> Int {
        values[member] ?? -1
    }
}

let config = Config(values: ["timeout": 30])
print(config.tmout)

بحث آمن من ناحية النوع باستخدام مسارات المفاتيح

لضمان الأمان، يمكنكم جعل الصيغة الفرعية تأخذ KeyPath بدلًا من سلسلة نصية. عندئذٍ يتحقق المترجم من وجود العضو في النوع المغلَّف، فيستعيد التحقق الكامل مع الاستمرار في تمرير الوصول.

يناسب هذا النمط الأنواع المغلِّفة التي تحتوي على قيمة ذات نوع محدد بقوة.

struct User { let name: String; let age: Int }

@dynamicMemberLookup
struct Boxed {
    let user: User
    subscript<T>(dynamicMember path: KeyPath<User, T>) -> T {
        user[keyPath: path]
    }
}

let boxed = Boxed(user: User(name: "Ada", age: 36))
print(boxed.name)
print(boxed.age)

لماذا تُعدّ نسخة مسار المفتاح أكثر أمانًا

بما أن الصيغة الفرعية تأخذ KeyPath<User, T>، فلا يُسمح إلا بالأعضاء الموجودة فعلًا في User. أما الخطأ المطبعي مثل boxed.naem فسيفشل الآن في الترجمة، بخلاف النسخة المعتمدة على السلاسل النصية.

تحصلون على سهولة الصيغة النقطية من دون أي من مخاطرها.

struct User { let name: String; let age: Int }

@dynamicMemberLookup
struct ReadOnly {
    let value: User
    subscript<T>(dynamicMember path: KeyPath<User, T>) -> T {
        value[keyPath: path]
    }
}

let r = ReadOnly(value: User(name: "Lin", age: 22))
print(r.name, r.age)

الأعضاء الديناميكية القابلة للكتابة

إذا وفرت الصيغة الفرعية أداة تعيين وأخذت WritableKeyPath، فيمكنكم أيضًا تعيين قيم للأعضاء المُمرَّرة. يمرر الغلاف عملية الكتابة مباشرةً إلى القيمة المغلَّفة.

ينشئ هذا غلافًا شفافًا قابلًا للتغيير.

struct Profile { var name: String; var score: Int }

@dynamicMemberLookup
struct Wrapper {
    var value: Profile
    subscript<T>(dynamicMember path: WritableKeyPath<Profile, T>) -> T {
        get { value[keyPath: path] }
        set { value[keyPath: path] = newValue }
    }
}

var w = Wrapper(value: Profile(name: "Mo", score: 0))
w.score = 99
print(w.value.score)

التمرير إلى كائن مغلَّف

يُعدّ البحث الديناميكي عن الأعضاء مناسبًا جدًا للوكلاء الخفيفين الذين يمررون معظم العمليات إلى قيمة داخلية، لكنهم يضيفون سلوكًا بسيطًا. وتحافظ الصيغة الفرعية على منطق التمرير في موضع واحد.

يمرر غلاف التسجيل هنا عمليات القراءة، ويطبع العضو الذي جرى الوصول إليه.

struct Account { let id: Int; let balance: Double }

@dynamicMemberLookup
struct Logged {
    let account: Account
    subscript<T>(dynamicMember path: KeyPath<Account, T>) -> T {
        let result = account[keyPath: path]
        print("accessed a member")
        return result
    }
}

let logged = Logged(account: Account(id: 7, balance: 100))
print(logged.balance)

الدمج مع أفكار الاستدعاء الديناميكي

ينسجم البحث الديناميكي عن الأعضاء جيدًا مع الأنواع العامة. ويمكن لصيغة فرعية عامة واحدة تمرير أي نوع من الخصائص، لذلك يخدم غلاف واحد قيمًا ذات أشكال متعددة.

يعمل Proxy نفسه هنا سواء كان العضو من النوع String أو Int.

struct Item { let title: String; let count: Int }

@dynamicMemberLookup
struct Proxy<Wrapped> {
    let wrapped: Wrapped
    subscript<T>(dynamicMember path: KeyPath<Wrapped, T>) -> T {
        wrapped[keyPath: path]
    }
}

let proxy = Proxy(wrapped: Item(title: "Book", count: 4))
print(proxy.title, proxy.count)

البحث بالسلاسل النصية عن بيانات شبيهة بـ JSON

ما تزال النسخة المعتمدة على السلاسل النصية مفيدة لتمثيل البيانات غير المحكمة البنية، مثل JSON الذي جرى تحليله، حيث لا تكون الأعضاء معروفة وقت الترجمة. يتحول كل وصول إلى بحث في قاموس.

تكشف قيمة صغيرة شبيهة بـ JSON حقولها هنا من خلال الصيغة النقطية.

@dynamicMemberLookup
struct JSON {
    let fields: [String: String]
    subscript(dynamicMember key: String) -> String {
        fields[key] ?? ""
    }
}

let json = JSON(fields: ["city": "Oslo", "country": "Norway"])
print(json.city)
print(json.country)

قيود يجب وضعها في الحسبان

يؤثر البحث الديناميكي عن الأعضاء في صيغة الوصول إلى الأعضاء فقط؛ فهو لا يضيف خصائص مخزنة حقيقية. تعمل أدوات مثل الإكمال التلقائي مع نسخة مسار المفتاح، لكنها لا تعمل مع مفاتيح السلاسل النصية العشوائية.

فضّلوا نسخة مسار المفتاح كلما كان شكل البيانات الأساسي معروفًا، واحتفظوا بالنسخة المعتمدة على السلاسل النصية للبيانات الديناميكية فعلًا.

struct Point { let x: Int; let y: Int }

@dynamicMemberLookup
struct Safe {
    let point: Point
    subscript<T>(dynamicMember path: KeyPath<Point, T>) -> T {
        point[keyPath: path]
    }
}

let safe = Safe(point: Point(x: 3, y: 4))
print(safe.x + safe.y)

مغلِّف عملي

بعد جمع العناصر، إليكم غلافًا مدروسًا يمرر كل قراءة لخاصية إلى قيمته الداخلية، مع عدّ مرات الوصول. تستخدم الأطر الحقيقية هذه الفكرة للمراقبة والتتبّع.

تبدو الصيغة النقطية على الغلاف مطابقة تمامًا لاستخدام القيمة مباشرةً.

struct Sensor { let temperature: Double; let humidity: Double }

@dynamicMemberLookup
class Tracked {
    let sensor: Sensor
    var reads = 0
    init(_ s: Sensor) { sensor = s }
    subscript<T>(dynamicMember path: KeyPath<Sensor, T>) -> T {
        reads += 1
        return sensor[keyPath: path]
    }
}

let t = Tracked(Sensor(temperature: 21.5, humidity: 0.4))
print(t.temperature)
print(t.humidity)
print(t.reads)

تحقق سريع: البحث الديناميكي الآمن من ناحية النوع

تذكّروا أي معلمة في الصيغة الفرعية تجعل البحث الديناميكي عن الأعضاء آمنًا من ناحية النوع.

مراجعة: البحث الديناميكي عن الأعضاء

لقد تعلمتم كيف تعمل @dynamicMemberLookup:

  • توجّه هذه السمة عمليات الوصول باستخدام صياغة النقطة إلى subscript(dynamicMember:) عندما لا يكون العضو مُعلنًا.
  • يمنح معامل من النوع String وصولًا مرنًا لكنه غير متحقق منه، وهو مثالي للبيانات الشبيهة بـ JSON.
  • يعيد معامل من النوع KeyPath أمان الأنواع الكامل والإكمال التلقائي.
  • يتيح WritableKeyPath مع دالة ضبط تمرير عمليات الكتابة عبر غلاف.
  • إنها تسهيلات نحوية تنسجم طبيعيًا مع نمطي الوكيل والمزيّن.

أصبح لديكم الآن مجموعة الأدوات الكاملة لمسارات المفاتيح: القراءة والكتابة واستخدام المجموعات والتمرير الديناميكي.

struct User { let name: String }

@dynamicMemberLookup
struct Wrap {
    let user: User
    subscript<T>(dynamicMember path: KeyPath<User, T>) -> T {
        user[keyPath: path]
    }
}

print(Wrap(user: User(name: "Final")).name)

الأسئلة الشائعة

هل درس «‏@dynamicMemberLookup» مجاني؟

نعم — نص درس «‏@dynamicMemberLookup» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Swift Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Swift Academy 4 دروس في المجموع.

ماذا ستتعلم في «‏@dynamicMemberLookup»؟

أنشئ وصولًا ديناميكيًا وآمنًا من حيث النوع إلى الأعضاء. تتمرن على Swift Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Swift Academy؟

لا تُشترط خبرة سابقة. Swift Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.

كم من الوقت يستغرق درس «‏@dynamicMemberLookup»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Swift Academy هذا؟

نعم. كل درس في Swift Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. أساسيات KeyPath
  2. استخدام KeyPaths في المجموعات
  3. مسارات المفاتيح القابلة للكتابة والمرجعية
  4. ‏@dynamicMemberLookup
← العودة إلى Swift Academy