0Pricing
Swift Academy · درس

‏UnsafePointer و UnsafeMutablePointer

اقرأ الذاكرة الخام واكتب فيها بحذر.

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

المؤشرات ذات النوع في Swift

لا تكشف مراجع Swift الآمنة عن العناوين الخام، لكن عند التوافق مع C أو بناء هياكل بيانات منخفضة المستوى، يمكنك استخدام المؤشرات ذات النوع: UnsafePointer<T> للوصول للقراءة فقط، وUnsafeMutablePointer<T> للوصول للقراءة والكتابة.

يعرف المؤشر ذو النوع نوع العنصر، ولذلك يمكنه حساب المسافات وقراءة قيمة من النوع الصحيح.

// A pointer to an Int we can read but not write
func readFirst(_ p: UnsafePointer<Int>) -> Int {
    return p.pointee
}

خاصية pointee

تمثّل pointee القيمة التي يشير إليها المؤشر حاليًا. ويمكنك قراءتها وتعيينها عبر مؤشر قابل للتعديل، بينما يمكنك قراءتها فقط عبر مؤشر للقراءة فقط.

يؤدي الوصول إلى pointee في ذاكرة غير مهيّأة أو أُزيل تخصيصها إلى سلوك غير محدد — ولن يكتشف نظام الأنواع ذلك.

let p = UnsafeMutablePointer<Int>.allocate(capacity: 1)
p.initialize(to: 42)
print(p.pointee)   // 42
p.pointee = 99
print(p.pointee)   // 99

تخصيص الذاكرة

تحجز UnsafeMutablePointer<T>.allocate(capacity:) مساحة لعدد من العناصر يساوي capacity، لكنها لا تهيّئ هذه العناصر. وتكون الذاكرة خامًا، وتُعد قراءتها قبل تهيئتها أمرًا غير قانوني.

// Reserve space for 3 Doubles (uninitialized)
let buffer = UnsafeMutablePointer<Double>.allocate(capacity: 3)
// Must initialize before reading any element

التهيئة وإلغاء التهيئة

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

دورة الحياة هي: allocate → initialize → use → deinitialize → deallocate.

let p = UnsafeMutablePointer<String>.allocate(capacity: 1)
p.initialize(to: "hello")
print(p.pointee)
p.deinitialize(count: 1) // releases the String
p.deallocate()

احرص دائمًا على إقران allocate بـ deallocate

يجب مطابقة كل allocate باستدعاء واحد بالضبط لـ deallocate، وإلا حدث تسرّب للذاكرة. ويُعد استخدام defer مباشرةً بعد التخصيص النمط الأكثر أمانًا، لأنه يُنفَّذ مهما كانت طريقة خروج الدالة.

func process() {
    let p = UnsafeMutablePointer<Int>.allocate(capacity: 1)
    defer { p.deallocate() }
    p.initialize(to: 7)
    print(p.pointee)
} // deallocate runs here automatically

حساب المؤشرات

تدعم المؤشرات ذات النوع الفهرسة والحساب بوحدات العناصر، وليس بالبايتات. ينتقل p + 2 بمقدار مسافتين من نوع T. ويُعد p[i] اختصارًا لـ (p + i).pointee.

let p = UnsafeMutablePointer<Int>.allocate(capacity: 3)
p.initialize(repeating: 0, count: 3)
p[0] = 10
p[1] = 20
p[2] = 30
print((p + 1).pointee) // 20
p.deinitialize(count: 3)
p.deallocate()

تهيئة عناصر متعددة

بالنسبة إلى كتلة من الذاكرة، يعيّن initialize(repeating:count:) القيمة نفسها لكل موضع، بينما ينسخ initialize(from:count:) القيم من مخزن مؤقت آخر. ويهيّئ كلاهما النطاق بالكامل، ما يجعل قراءته آمنة.

let dst = UnsafeMutablePointer<Int>.allocate(capacity: 4)
dst.initialize(repeating: 5, count: 4)
print(dst[3]) // 5
dst.deinitialize(count: 4)
dst.deallocate()

للقراءة فقط مقابل القابل للتعديل

ينبغي أن تستقبل الدالة التي تفحص البيانات فقط UnsafePointer<T>، بينما تستقبل الدالة التي تعدّلها UnsafeMutablePointer<T>. ويمكن تحويل المؤشر القابل للتعديل ضمنيًا إلى مؤشر للقراءة فقط، لكن العكس غير ممكن — وهذا يفرض الغرض عند حدود واجهة برمجة التطبيقات.

func sum(_ p: UnsafePointer<Int>, _ n: Int) -> Int {
    var total = 0
    for i in 0..<n { total += p[i] }
    return total
}
// A mutable pointer can be passed here directly

استخدام withUnsafePointer مع المتغيرات المحلية

للحصول على مؤشر مؤقت إلى قيمة موجودة دون تخصيص ذاكرة، استخدم withUnsafePointer(to:) أو صيغته القابلة للتعديل. ويكون المؤشر صالحًا داخل دالة الإغلاق فقط — فلا تسمح له بالخروج منها أبدًا.

var value = 100
withUnsafeMutablePointer(to: &value) { ptr in
    ptr.pointee += 1
}
print(value) // 101

الأخطاء الشائعة

المؤشرات ذات النوع قوية، لكنها لا تتسامح مع الأخطاء:

  • تؤدي قراءة pointee قبل initialize إلى سلوك غير محدد.
  • يؤدي نسيان deinitialize مع الأنواع التي تحتفظ بالموارد إلى تسرّب.
  • يؤدي السماح لمؤشر withUnsafePointer بالخروج إلى إنشاء مؤشر معلّق.

يثق بك المصرّف هنا — ولا توجد آليات حماية أثناء التشغيل.

// DANGEROUS — pointer escapes the closure
// var escaped: UnsafePointer<Int>?
// withUnsafePointer(to: &x) { escaped = $0 }
// escaped!.pointee  // dangling, undefined behavior

مكدس يدوي صغير

بتجميع كل ذلك: خصّص كتلة، وتعامل معها باعتبارها عناصر باستخدام الفهرسة، ثم نظّفها. وهذا هو الأساس لبناء المجموعات المخصّصة والتوافق مع C.

let n = 3
let stack = UnsafeMutablePointer<Int>.allocate(capacity: n)
stack.initialize(repeating: 0, count: n)
for i in 0..<n { stack[i] = i * i }
// stack now holds 0, 1, 4
stack.deinitialize(count: n)
stack.deallocate()

اختبار سريع

استرجع دورة الحياة الصحيحة للمؤشر.

مراجعة

أصبحت تعرف الآن المؤشرات ذات النوع:

  • يقرأ UnsafePointer، بينما يقرأ UnsafeMutablePointer ويكتب.
  • تتيح pointee الوصول إلى القيمة، ويُجرى الحساب بوحدات العناصر.
  • دورة الحياة هي allocate → initialize → use → deinitialize → deallocate، ويفضّل حمايتها باستخدام defer.

سترى بعد ذلك كيف تغلّف مؤشرات المخازن المؤقتة منطقة متجاورة كاملة ضمن واجهة ملائمة لـ Collection.

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

هل درس «‏UnsafePointer و UnsafeMutablePointer» مجاني؟

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

ماذا ستتعلم في «‏UnsafePointer و UnsafeMutablePointer»؟

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

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

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

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

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

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

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

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

  1. ‏MemoryLayout والمحاذاة
  2. ‏UnsafePointer و UnsafeMutablePointer
  3. مؤشرات المخازن المؤقتة غير الآمنة
  4. ‏withUnsafeBytes والتكامل مع C
← العودة إلى Swift Academy