الانتقال إلى المحتوى الرئيسي
الوحدة 3 · الدرس 5 من 515/50 في الدورة~12 min
دروس الوحدة (5/5)

"تأجيل": تنظيف مضمون

defer يؤجل تنفيذ المكالمة ** حتى وظيفة التضمين يعود **. يتم تشغيله حتى إذا خرجت الوظيفة عن طريق الذعر. عدة defers في نفس الوظيفة، يتم تنفيذها بترتيب LIFO (آخر دخول، أول صادر).

إنها الآلية الاصطلاحية لـ تنظيف الموارد: إغلاق الملفات، فتح كائنات المزامنة، وتحرير الاتصالات، واستعادة الحالة.

حالة الاستخدام النحوي والأساسي

Go
f, err := os.Open(path)
if err != nil {
    return err
}
defer f.Close()   // chiamata SEMPRE all'uscita della funzione

// ... usa f liberamente ...
return nil

نموذجي: f.Close()، mu.Unlock()، db.Close()، tx.Rollback()، محمي_INLINE_9.

تم تقييم الحجج على الفور، وتم تأجيل التنفيذ

عندما تكتب defer foo(x)، يتم تقييم x في وقت defer، ليس في وقت التنفيذ:

Go
x := 10
defer fmt.Println(x)   // cattura 10
x = 99
// quando la funzione ritorna stampa "10", non "99"

طلب LIFO

يتم تجميع المؤجلات وتنفيذها بدءًا من الأحدث:

Go
defer fmt.Println(1)
defer fmt.Println(2)
defer fmt.Println(3)
// output: 3, 2, 1

وهذا يتوافق مع دلالات الموارد: آخر ما تم فتحه هو الأول لتكون مغلقة.

defer + recover: التعامل مع الذعر

defer هي الطريقة الوحيدة لاعتراض panic:

Go
func safe() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("recuperato:", r)
        }
    }()
    panic("boom")
}

المأزق: defer في حلقة

يؤدي وضع defer f.Close() في حلقة إلى تأخير ** حتى نهاية الوظيفة**، وليس كل تكرار. في حلقة طويلة يمكنك فتح العديد من الملفات وأغلقها فقط عند خروج الوظيفة: خطر تسرب واصف الملف.

Go
// MALE: defer accumulati
for _, path := range paths {
    f, _ := os.Open(path)
    defer f.Close()   // chiude TUTTI alla fine della funzione esterna
    // ...
}

// MEGLIO: estrai in una funzione
for _, path := range paths {
    elabora(path)   // ogni chiamata ha il proprio scope di defer
}

جربه

تمرين#go.m3.l5.e1
المحاولات: 0جارٍ التحميل…

اطبع "inizio"، ثم قم بتأجيل طباعة "الغرامة" مع التأجيل، ثم اطبع "mezzo". يجب أن يكون الإخراج: inizio / mezzo / غرامة.

جارٍ تحميل المحرر…
إظهار التلميح

يتم تشغيل التأجيل عندما يكون main() على وشك العودة.

الحل متاح بعد 3 من المحاولات

تمرين#go.m3.l5.e2
المحاولات: 0جارٍ التحميل…

اكتب ثلاثة تأجيلات متتالية تطبع 1 و2 و3 بهذا الترتيب في الكود. سيكون الإخراج 3/2/1 (LIFO).

جارٍ تحميل المحرر…
إظهار التلميح

ثلاثة تأجيلات بالتسلسل؛ تذكر أنها تنفذ بترتيب عكسي.

الحل متاح بعد 3 من المحاولات

Quiz#go.m3.l5.e3
جاهز

ماذا يطبع هذا؟ (ملاحظة: يتم تقييم وسيطات التأجيل على الفور)

Go
x := 10
defer fmt.Println(x)
x = 99
خيارات الإجابة

خلاصة

  • defer call() يؤجل التنفيذ حتى تعود الوظيفة.
  • التأجيلات المتعددة = أمر LIFO.
  • تم تقييم الوسيطات على الفور، وتم تأجيل التنفيذ.
  • يستخدم للتنظيف الحتمي: الملفات، وكائنات المزامنة، والاتصالات.
  • defer + recover() يعترض panic (نادرًا، عند الحدود).
  • لا defer أبدًا داخل حلقة طويلة: فهي تتراكم حتى تعود الوظيفة الخارجية.