Как отслеживать ссылки на объект?
В мире, где ручное распределение памяти и указатели все еще правят (Borland Delphi), мне нужно общее решение для того, что я считаю общей проблемой:
В данный момент на объект можно ссылаться из нескольких мест (списки, другие объекты, ...). Есть ли хороший способ отслеживать все эти ссылки, чтобы я мог обновить их, когда объект уничтожен?
4 ответа
Если вы хотите уведомить других об изменениях, вам следует использовать "Шаблон наблюдателя". Delphi уже сделала это для вас, потомков TComponent. Вы можете вызвать метод TComponent.FreeNotification и уведомить свой объект об уничтожении другого компонента. Это делается путем вызова метода уведомления. Вы можете удалить себя из списка уведомлений, вызвав TComponent.RemoveFreeNotification. Также смотрите эту страницу.
Большинство сборщиков мусора не позволяют получить список ссылок, поэтому они не помогут в этом случае. Delphi может выполнять подсчет ссылок, если вы будете использовать интерфейсы, но опять же вам нужно отслеживать ссылки самостоятельно.
Я не могу понять, почему вы хотите это сделать. Наверняка вы просто проверите ссылку на Nil, прежде чем ее использовать?
В любом случае, я бы рассмотрел два возможных решения:
- У управляющих объектами свои собственные счетчики ссылок.
- Создайте класс менеджера подсчета ссылок.
Я бы, вероятно, добавил бы функции AddRef() и ReleaseRef() либо к менеджеру, либо к справочному классу. Затем вы можете использовать их, чтобы проверить, сколько ссылок существует в любой точке. COM делает это таким образом.
Класс с поддержкой ссылок будет управлять только собственным счетчиком ссылок. Менеджер может использовать карту, чтобы связать указатели с целым числом для подсчета.
Вы пытаетесь отслеживать, кто ссылается на объект, чтобы вы могли очистить эти ссылки, когда объект уничтожен, или вы пытаетесь отслеживать, когда безопасно уничтожить объект?
Если последнее, то звучит так, будто вы ищете сборщика мусора. Я никогда не имел дело с Delphi, поэтому я не знаю, есть ли для него GC, но вы бы удивились, если бы их не было.
Если первый, то GC, вероятно, не поможет. Если Delphi поддерживает ООП / наследование (честно говоря, я не знаю, так ли это), вы можете сделать что-то вроде этого (псевдокод):
// Anything that will use one of your tracked objects implements this interface
interface ITrackedObjectUser {
public void objectDestroyed(TrackedObject o);
}
// All objects you want to track extends this class
class TrackedObject {
private List<ITrackedObjectUser> users;
public void registerRef(ITrackedObjectUser u) {
users.add(u);
}
public void destroy() {
foreach(ITrackedObjectUser u in users) {
u.objectDestroyed(this);
}
}
}
По сути, всякий раз, когда вы добавляете один из отслеживаемых объектов в коллекцию, эта коллекция регистрируется в этом объекте. Когда объект уничтожается (я полагаю, что вы вызовете destroy() в деструкторе объекта), тогда объект сообщает коллекции, что он уничтожается, поэтому коллекция может делать все, что ей нужно.
К сожалению, это не очень хорошее решение, если вы хотите использовать встроенные коллекции. Вы должны написать свои собственные объекты коллекции (они могут просто обернуть встроенные объекты). И для этого нужно убедиться, что вы регистрируетесь везде, где хотите отслеживать объект. Это не то, что я бы назвал "счастливым" решением, хотя для небольших проектов это, вероятно, не было бы слишком плохо. Я в основном надеюсь, что эта идея поможет породить другие идеи.:)
Есть ли конкретная причина, по которой вы хотите этого? У вас проблемы с указателями мошенников, или вы думаете, что однажды это может стать проблемой?
ИМХО, это не будет проблемой, если вы правильно разработаете свое приложение, и использование соответствующих шаблонов действительно поможет вам.
Некоторая информация о скороговорках: