Memory management with Objective-C Distributed Objects: my temporary instances live forever!
I'm playing with Objective-C Distributed Objects and I'm having some problems understanding how memory management works under the system. The example given below illustrates my problem:
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;
}
The issue is that any remote objects created by the root proxy are not released when their proxy counterparts in the client go out of scope. According to the documentation:
When an object’s remote proxy is deallocated, a message is sent back to the receiver to notify it that the local object is no longer shared over the connection.
I would therefore expect that as each DOTarget
goes out of scope (each time around the loop) it's remote counterpart would be dellocated, since there is no other reference to it being held on the remote side of the connection.
In reality this does not happen: the temporary objects are only deallocate when the client application quits, or more accurately, when the connection is invalidated. I can force the temporary objects on the remote side to be deallocated by explicitly invalidating the NSConnection object I'm using each time around the loop and creating a new one but somehow this just feels wrong.
Is this the correct behaviour from DO? Should all temporary objects live as long as the connection that created them? Are connections therefore to be treated as temporary objects which should be opened and closed with each series of requests against the server?
Any insights would be appreciated.
The autorelease pool on your server is never drained, therefore your autoreleased objects never go "out of scope". You will always have an extra reference to them. Set up your server similar to the test you set up in your client (having the run loop dump out every second or so) and drain and establish a new inner pool each time. You will then see your expected results.
Try not calling "autorelease" at all. Just allow the "createTarget" to return without retaining and assume that the object will be released when the proxy gets released on the client. When the Connection object does it's magic and returns a proxy to the client, it retains the server-local object in it's "localObjects" attribute. This way when the client's proxy goes out of scope, the Connection will release the local object, no need for autoreleasing.
I'm not really sure I'm correct but it seems like a valid explanation and while it seems a little strange to not call autorelease, this is DO which is a bit different. Even though the server is actually creating the object, it does not own it because the client created it (although remotely).
- The client owns the object, because it created it
- The client is responsible for retain/release of the proxy
- The proxy and the object on the server's life-cycle are one and the same
- All memory management should be done by the same entity that initiates allocation (in this case the client)
- Never use "convenience" methods when creating objects remotely as this will call an autorelease
It's rather pathetic how scarce any explanation of how DO memory should be handled when created by the client.
精彩评论