Управление памятью с помощью Objective-C Distributed Objects: мои временные экземпляры живут вечно! - PullRequest
1 голос
/ 26 марта 2010

Я играю с Objective-C Распределенные объекты , и у меня возникают некоторые проблемы с пониманием того, как управление памятью работает в системе. Пример, приведенный ниже, иллюстрирует мою проблему:

Protocol.h

#import <Foundation/Foundation.h>

@protocol DOServer
- (byref id)createTarget;
@end

Server.m

#import <Foundation/Foundation.h>
#import "Protocol.h"


@interface DOTarget : NSObject
@end


@interface DOServer : NSObject < DOServer >
@end


@implementation DOTarget

- (id)init
{
    if ((self = [super init]))
    {
        NSLog(@"Target created");
    }
    return self;
}

- (void)dealloc
{
    NSLog(@"Target destroyed");
    [super dealloc];
}

@end

@implementation DOServer

- (byref id)createTarget
{
    return [[[DOTarget alloc] init] autorelease];
}

@end


int main()
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    DOServer *server = [[DOServer alloc] init];

    NSConnection *connection  = [[NSConnection new] autorelease];
    [connection setRootObject:server];
    if ([connection registerName:@"test-server"] == NO)
    {
        NSLog(@"Failed to vend server object");
    }
    else
    {
        while (YES)
        {
            NSAutoreleasePool *innerPool = [[NSAutoreleasePool alloc] init];
            [[NSRunLoop currentRunLoop] runUntilDate:
                 [NSDate dateWithTimeIntervalSinceNow:0.1f]];
            [innerPool drain];
        }
    }

    [pool drain];
    return 0;
}

Client.m

#import <Foundation/Foundation.h>
#import "Protocol.h"

int main()
{
    unsigned i = 0;
    for (; i < 3; i ++)
    {
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
        id server = [NSConnection rootProxyForConnectionWithRegisteredName:@"test-server"
                                                                      host:nil];
        [server setProtocolForProxy:@protocol(DOServer)];
        NSLog(@"Created target: %@", [server createTarget]);

        [[NSRunLoop currentRunLoop] runUntilDate:
             [NSDate dateWithTimeIntervalSinceNow:1.0]];
        [pool drain];
    }
    return 0;
}

Проблема заключается в том, что любые удаленные объекты, созданные корневым прокси-сервером, не освобождаются, когда их аналоги-прокси в клиенте выходят из области видимости. Согласно документации :

Когда удаленный прокси-сервер объекта освобождается, получателю отправляется сообщение, уведомляющее его о том, что локальный объект больше не используется в соединении.

Поэтому я ожидал бы, что, поскольку каждый DOTarget выходит из области видимости (каждый раз вокруг цикла), его удаленный аналог будет выделяться, поскольку на удаленной стороне соединения нет других ссылок на него.

На самом деле этого не происходит: временные объекты освобождаются только при выходе из клиентского приложения или, точнее, при недействительном соединении. Я могу принудительно освободить временные объекты на удаленной стороне, явно аннулируя объект NSConnection, который я использую каждый раз вокруг цикла, и создавая новый, но почему-то это просто чувствует неправильным.

Это правильное поведение от DO? Должны ли все временные объекты жить так же долго, как и соединение, которое их создало? Поэтому следует ли рассматривать соединения как временные объекты, которые должны открываться и закрываться при каждой серии запросов к серверу?

Любые идеи будут оценены.

Ответы [ 2 ]

0 голосов
/ 23 апреля 2010

Старайтесь вообще не называть "autorelease". Просто позвольте «createTarget» вернуться без сохранения и предположите, что объект будет освобожден, когда прокси-сервер будет освобожден на клиенте. Когда объект Connection выполняет свое действие и возвращает клиенту прокси-сервер, он сохраняет объект local-server в своем атрибуте localObjects. Таким образом, когда прокси-сервер клиента выходит из области действия, Connection освобождает локальный объект, не требуя автоматического освобождения.

Я не совсем уверен, что я прав, но это похоже на правильное объяснение, и хотя кажется немного странным не вызывать авто-релиз, это DO, который немного отличается. Хотя сервер фактически создает объект, он не владеет им, потому что клиент создал его (хотя и удаленно).

  • Клиент владеет объектом, потому что он его создал
  • Клиент несет ответственность за сохранение / освобождение прокси
  • Прокси и объект в жизненном цикле сервера - это одно и то же
  • Все управление памятью должно осуществляться одним и тем же объектом, который инициирует выделение (в данном случае клиентом)
  • Никогда не используйте «удобные» методы при удаленном создании объектов, так как это вызовет автоматическое освобождение.

Довольно жалко, как мало объяснений того, как ДОЛЖНО обрабатываться память при создании клиентом.

0 голосов
/ 26 марта 2010

Пул авто-релизов на вашем сервере никогда не очищается, поэтому ваши автоматически выпущенные объекты никогда не выходят за пределы области действия. У вас всегда будет дополнительная ссылка на них. Настройте ваш сервер аналогично тесту, который вы настроили на своем клиенте (с выполнением цикла выполнения, сбрасывающего каждую секунду или около того) и сливайте и каждый раз устанавливайте новый внутренний пул. Затем вы увидите ожидаемые результаты.

...