سؤال

هل يمكن لأي شخص أن يفسر استخدام المراجع الضعيفة؟

ال توثيق لا يفسر ذلك على وجه التحديد، كما يقول فقط أن GC يمكن أن يدمر الكائن المرتبط عبر مرجع ضعيف في أي وقت. ثم ما هي نقطة وجود كائن يمكن أن تختفي في أي وقت؟ ماذا لو كنت بحاجة إلى استخدامه مباشرة بعد اختفائه؟

هل يمكن أن توضح لهم بعض الأمثلة الجيدة؟

شكرا

هل كانت مفيدة؟

المحلول

الاستخدام النموذجي للمراجع الضعيفة هو إذا كان لديك إشارة إلى B و B مرجع إلى A. دون جامع القمامة مناسبة للكشف عن الدورة، لن تحصل هذه الكائنتين على GC'D أبدا حتى لو لم تكن هناك مراجع إما من "الخارج". ومع ذلك، إذا كانت إحدى المراجع "ضعيفة"، فستحصل الكائنات بشكل صحيح على GC'D.

ومع ذلك، بايثون هل لديك مجمع القمامة للكشف عن الدورة (منذ 2.0!)، لذلك لا يعتبر :)

استخدام آخر للمراجع الضعيفة هو المخابئ. لقد ذكر في weakref توثيق:

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

إذا قررت شركة GC تدمير إحدى هذه الكائنات، وتحتاج إليها، فيمكنك إعادة حساب / الدفاع عن البيانات فقط.

نصائح أخرى

الأحداث هي سيناريو شائع لمراجع ضعيفة.


مشكلة

النظر في زوج من الأشياء: باعث واستقبال. يحتوي المتلقي على حياة أقصر من باعث.

يمكنك تجربة تنفيذ مثل هذا:

class Emitter(object):

    def __init__(self):
        self.listeners = set()

    def emit(self):
        for listener in self.listeners:
            # Notify
            listener('hello')


class Receiver(object):

    def __init__(self, emitter):

        emitter.listeners.add(self.callback)

    def callback(self, msg):
        print 'Message received:', msg


e = Emitter()
l = Receiver(e)
e.emit() # Message received: hello

ومع ذلك، في هذه الحالة، يبقي باعث مرجعا إلى طريقة ملزمة callback التي تحافظ على مرجع إلى جهاز الاستقبال. لذلك يبقي باعث الاستقبال على قيد الحياة:

# ...continued...

del l
e.emit() # Message received: hello

هذا هو في بعض الأحيان مزعجة. تخيل ذلك Emitter هو جزء من بعض طراز البيانات الذي يدل عند تغيير البيانات و Receiver تم إنشاؤه بواسطة نافذة حوار يستمع إلى هذه التغييرات لتحديث بعض عناصر التحكم في UI.

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

إزالة الاحتياطات يدويا هو خيار واحد (تماما مثل مزعج)، باستخدام المراجع الضعيفة هو آخر.


المحلول

هناك فئة لطيفة WeakSet يبدو أن مجموعة عادية، بل تخزن أعضائها باستخدام مراجع ضعيفة ولم تعد تخزنها عند تحريرها.

ممتاز! دعنا نستخدمها:

def __init__(self):
    self.listeners = weakref.WeakSet()

وتشغيل مرة أخرى:

e = Emitter()
l = Receiver(e)
e.emit()
del l
e.emit()

أوه، لا يحدث شيء على الإطلاق! ذلك لأن الطريقة المحددة (جهاز استقبال معين callback) يتيم الآن - لا باع باع باعث أو جهاز الاستقبال مرجعا قويا له. وبالتالي فإن القمامة التي تم جمعها على الفور.

دعونا نجعل المتلقي (وليس باعث هذه المرة) احتفظ بإشارة قوية إلى هذا الاتصال:

class Receiver(object):

    def __init__(self, emitter):

        # Create the bound method object
        cb = self.callback

        # Register it
        emitter.listeners.add(cb)
        # But also create an own strong reference to keep it alive
        self._callbacks = set([cb])

الآن يمكننا أن نلاحظ السلوك المتوقع: باعث يبقي فقط رد الاتصال طالما يعيش المستقبل.

e = Emitter()
l = Receiver(e)
assert len(e.listeners) == 1

del l
import gc; gc.collect()
assert len(e.listeners) == 0

تحت الغطاء

لاحظ أنني اضطررت لوضع gc.collect() هنا للتأكد من أن جهاز الاستقبال يتم تنظيفه على الفور. هناك حاجة إلى هنا لأن هناك الآن دورة من المراجع القوية: تشير الطريقة الملزمة إلى جهاز الاستقبال والعكس صحيح.

هذا ليس سيئا للغاية؛ هذا يعني فقط أنه سيتم تأجيل تنظيف جهاز الاستقبال حتى يتم تشغيل جامع القمامة التالي. لا يمكن تنظيف المراجع الدورية من خلال آلية عد مرجعية بسيطة.

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

def __init__(self, emitter):

    # Create the bound method object
    weakself = weakref.ref(self)
    def cb(msg):
        self = weakself()
        self.callback(msg)

    # Register it
    emitter.listeners.add(cb)
    # But also create an own strong reference to keep it alive
    self._callbacks = set([cb])

دعونا نضع هذا المنطق في وظيفة المساعد:

def weak_bind(instancemethod):

    weakref_self = weakref.ref(instancemethod.im_self)
    func = instancemethod.im_func

    def callback(*args, **kwargs):
        self = weakref_self()
        bound = func.__get__(self)
        return bound(*args, **kwargs)

    return callback

class Receiver(object):

    def __init__(self, emitter):

        cb = weak_bind(self.callback)

        # Register it
        emitter.listeners.add(cb)
        # But also create an own strong reference to keep it alive
        self._callbacks = set([cb])

الآن ليس هناك دورة من المراجع القوية، لذلك متى Receiver يتم تحريره، وسيتم أيضا تحرير وظيفة رد الاتصال (وإزالتها من باعث WeakSet) على الفور، دون الحاجة إلى دورة GC كاملة.

  • المراجع الضعيفة هي مفهوم مهم في بيثون، وهو مفقود بلغات يحب Java (Java 1.5).
  • في نمط تصميم المراقب، يجب أن يحافظ كائن مريح بشكل عام على مراجع ضعيفة إلى كائن المراقب.

    على سبيل المثال ينبعث من الحدث الذي تم إجراؤه () و B يسجل مع ذلك، فإنه يريد الاستماع إلى الحدث المنجز (). وبالتالي، عند الانتهاء () ينبعث، يتم إخطار B. ولكن إذا لم يكن هناك حاجة إلى B في التطبيق، فلا يجب أن تصبح تعرقلا في مجموعة القمامة في A (منذ تعليق الإشارة إلى B). وبالتالي، إذا كان هناك إشارة ضعيفة إلى B، وعندما تكون كل الإشارات إلى أ، فستكون B جمع القمامة.

  • انها أيضا مفيدة جدا في تنفيذ مخابئ.
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top