garbage collection and NSConnection

  • hey,
    I have a project that uses Bonjour for some of its communication,
    theres a server and a client, and I was having tremendous difficulty
    getting it to work, pouring and pouring over my code, only to discover
    some weeks later that for some odd reason, NSConnections do not work
    when the project is set to support or require garbage collection.

    As a test I set garbage collection to: Unsupported, and the app
    compiled, and the NSConnection returned the proxy object as expected.
    But the app obviously failed to do much else, because I had no retain,
    release, or autorelease method calls.

    I am not a lazy person, but I am a novice programmer, and the retain,
    release, autorelease stuff in cocoa is horrible. I was very happy when
    garbage collection was added to Cocoa, and my projects became much
    easier to develop, and maintain. Now however, I find myself with a
    project riddled with memory problems that did Not exist just a few
    days ago, and as far as I can tell, I don't have any choice... My app
    either gives up Bonjour, or I have to retrofit the whole thing to
    manage its own memory with a system that is, lets face it, poorly
    envisioned.

    can anybody shed any light on this? am I really stuck managing the
    memory myself? This is intolerable.

    cheers,
    -eblu
  • Hi there-

    I encountered the same issue some months ago, and posted my questions
    to this list.  An Apple engineer did reply off-list that this was a
    known issue with garbage collection and that there was no known
    workaround at that time.

    I was just playing with GC for fun and reverted back to "regular"
    memory management.

    You might inquire of Apple DTS if things have changed at all; my
    incident was in the 10.5.0 days.

    John

    John Pannell
    http://www.positivespinmedia.com

    On Jun 30, 2008, at 11:33 AM, <eblugamma...> wrote:

    > hey,
    > I have a project that uses Bonjour for some of its communication,
    > theres a server and a client, and I was having tremendous difficulty
    > getting it to work, pouring and pouring over my code, only to
    > discover some weeks later that for some odd reason, NSConnections do
    > not work when the project is set to support or require garbage
    > collection.
    >
    > As a test I set garbage collection to: Unsupported, and the app
    > compiled, and the NSConnection returned the proxy object as
    > expected.  But the app obviously failed to do much else, because I
    > had no retain, release, or autorelease method calls.
    >
    > can anybody shed any light on this? am I really stuck managing the
    > memory myself? This is intolerable.
  • Hi Chris,
    I'm not terribly sure what you are asking for here. From my
    experience (limited experience admittedly) theres really only one way
    to use NSConnection.
      its a pretty elegant class, which is simple, and works as expected,
    except for when garbage collection is enabled.

    heres what I do just after NSNetService finds a service:

    // Sent when a service appears
    - (void)netServiceBrowser:(NSNetServiceBrowser *)browser
            didFindService:(NSNetService *)aNetService
            moreComing:(BOOL)moreComing
    {
    NSMutableDictionary* newDict = [[NSMutableDictionary alloc] init];
    [newDict setValue:aNetService forKey:@"theService"];

    [serverArrayController addObject:newDict];
    [aNetService setDelegate:self];
    [aNetService resolveWithTimeout:5];
        if(!moreComing)
        {
        }
    }

    // NSNetService Delegate method:
    - (void)netServiceDidResolveAddress:(NSNetService *)sender{

    id proxy = nil;
    NSData *addy;
    NSSocketPort* socket;
    NSConnection* connection;
    NSString* hostname;

    int a;
    int i;

    hostname = [sender hostName];
    socket = (NSSocketPort*)[[NSSocketPortNameServer sharedInstance]
    portForName:@"BKOtherPort" host:hostname];
    connection = [NSConnection connectionWithReceivePort: nil sendPort:
    socket];
    @try{
      proxy = [connection rootProxy];
    }
    @catch(id exception){
      proxy = nil;

    }
    addy = [socket address];
    if(proxy){
      // app level stuff if the proxy exists
    }
    }

    pretty straight forward,
    and every time I ran it with garbage collection on, the NSConnection
    initialized, but NEVER returned the proxy. it returned nil.
    all my instance variables were populated, everything on My end was
    correct... or at least behaving as expected. it just wouldn't return
    the proxy object (or the root for that matter)
    All I did to fix it, was to turn off garbage collection.
    That part runs like a champ now.  the rest of the app won't do
    anything anymore, as it was built on garbage collection.

    cheers,
    -eb

    On Jun 30, 2008, at 3:37 PM, Chris Hanson wrote:

    > On Jun 30, 2008, at 10:33 AM, <eblugamma...> wrote:
    >
    >> only to discover some weeks later that for some odd reason,
    >> NSConnections do not work when the project is set to support or
    >> require garbage collection.
    >
    > How are you using NSConnection?  NSConnection and Distributed
    > Objects definitely *does* work with Objective-C garbage collection,
    > at least in the situations in which I've used it.
    >
    > Some code examples might be illustrative.
    >
    > -- Chris
    >
  • Le 30 juin 08 à 22:10, eblu a écrit :

    > Hi Chris,
    > I'm not terribly sure what you are asking for here. From my
    > experience (limited experience admittedly) theres really only one
    > way to use NSConnection.
    > its a pretty elegant class, which is simple, and works as expected,
    > except for when garbage collection is enabled.
    >
    > heres what I do just after NSNetService finds a service:
    >
    > // Sent when a service appears
    > - (void)netServiceBrowser:(NSNetServiceBrowser *)browser
    > didFindService:(NSNetService *)aNetService
    > moreComing:(BOOL)moreComing
    > {
    > NSMutableDictionary* newDict = [[NSMutableDictionary alloc] init];
    > [newDict setValue:aNetService forKey:@"theService"];
    >
    > [serverArrayController addObject:newDict];
    > [aNetService setDelegate:self];
    > [aNetService resolveWithTimeout:5];
    > if(!moreComing)
    > {
    > }
    > }
    >
    > // NSNetService Delegate method:
    > - (void)netServiceDidResolveAddress:(NSNetService *)sender{
    >
    > id proxy = nil;
    > NSData *addy;
    > NSSocketPort* socket;
    > NSConnection* connection;
    > NSString* hostname;
    >
    > int a;
    > int i;
    >
    > hostname = [sender hostName];
    > socket = (NSSocketPort*)[[NSSocketPortNameServer sharedInstance]
    > portForName:@"BKOtherPort" host:hostname];
    > connection = [NSConnection connectionWithReceivePort: nil sendPort:
    > socket];
    > @try{
    > proxy = [connection rootProxy];
    > }
    > @catch(id exception){
    > proxy = nil;
    >
    > }
    > addy = [socket address];
    > if(proxy){
    > // app level stuff if the proxy exists
    > }
    > }
    >
    > pretty straight forward,
    > and every time I ran it with garbage collection on, the NSConnection
    > initialized, but NEVER returned the proxy. it returned nil.
    > all my instance variables were populated, everything on My end was
    > correct... or at least behaving as expected. it just wouldn't return
    > the proxy object (or the root for that matter)
    > All I did to fix it, was to turn off garbage collection.
    > That part runs like a champ now.  the rest of the app won't do
    > anything anymore, as it was built on garbage collection.
    >
    > cheers,
    > -eb
    >

    And did you try using CFNetServices instead ? As it is CF based, it
    probably does not have the same GC problem and is it far more easier
    to replace NSNetService by CFNetService than rewriting the whole
    application without GC .
  • Le 30 juin 08 à 22:19, Jean-Daniel Dupas a écrit :

    > Le 30 juin 08 à 22:10, eblu a écrit :
    >
    >> Hi Chris,
    >> I'm not terribly sure what you are asking for here. From my
    >> experience (limited experience admittedly) theres really only one
    >> way to use NSConnection.
    >> its a pretty elegant class, which is simple, and works as expected,
    >> except for when garbage collection is enabled.
    >>
    >> heres what I do just after NSNetService finds a service:
    >>
    >> // Sent when a service appears
    >> - (void)netServiceBrowser:(NSNetServiceBrowser *)browser
    >> didFindService:(NSNetService *)aNetService
    >> moreComing:(BOOL)moreComing
    >> {
    >> NSMutableDictionary* newDict = [[NSMutableDictionary alloc] init];
    >> [newDict setValue:aNetService forKey:@"theService"];
    >>
    >> [serverArrayController addObject:newDict];
    >> [aNetService setDelegate:self];
    >> [aNetService resolveWithTimeout:5];
    >> if(!moreComing)
    >> {
    >> }
    >> }
    >>
    >> // NSNetService Delegate method:
    >> - (void)netServiceDidResolveAddress:(NSNetService *)sender{
    >>
    >> id proxy = nil;
    >> NSData *addy;
    >> NSSocketPort* socket;
    >> NSConnection* connection;
    >> NSString* hostname;
    >>
    >> int a;
    >> int i;
    >>
    >> hostname = [sender hostName];
    >> socket = (NSSocketPort*)[[NSSocketPortNameServer sharedInstance]
    >> portForName:@"BKOtherPort" host:hostname];
    >> connection = [NSConnection connectionWithReceivePort: nil
    >> sendPort: socket];
    >> @try{
    >> proxy = [connection rootProxy];
    >> }
    >> @catch(id exception){
    >> proxy = nil;
    >>
    >> }
    >> addy = [socket address];
    >> if(proxy){
    >> // app level stuff if the proxy exists
    >> }
    >> }
    >>
    >> pretty straight forward,
    >> and every time I ran it with garbage collection on, the
    >> NSConnection initialized, but NEVER returned the proxy. it returned
    >> nil.
    >> all my instance variables were populated, everything on My end was
    >> correct... or at least behaving as expected. it just wouldn't
    >> return the proxy object (or the root for that matter)
    >> All I did to fix it, was to turn off garbage collection.
    >> That part runs like a champ now.  the rest of the app won't do
    >> anything anymore, as it was built on garbage collection.
    >>
    >> cheers,
    >> -eb
    >>
    >
    > And did you try using CFNetServices instead ? As it is CF based, it
    > probably does not have the same GC problem and is it far more easier
    > to replace NSNetService by CFNetService than rewriting the whole
    > application without GC .

    Sorry, it the DO part that does not works, not the Bonjour discovery.
    I read it a little to fast.
  • On Mon, Jun 30, 2008 at 9:10 PM, eblu <eblugamma...> wrote:

    > hostname = [sender hostName];
    > socket = (NSSocketPort*)[[NSSocketPortNameServer sharedInstance]
    > portForName:@"BKOtherPort" host:hostname];
    > connection = [NSConnection connectionWithReceivePort: nil sendPort:
    > socket];
    > @try{
    > proxy = [connection rootProxy];
    > }

    Have you tried the rather more simple:

    proxy = [[NSConnection
    rootProxyForConnectionWithRegisteredName:@"BKOtherPort" host:[sender
    hostname]] retain];

    ?

    Hamish
  • El 30/06/2008, a las 19:33, <eblugamma...> escribió:

    > hey,
    > I have a project that uses Bonjour for some of its communication,
    > theres a server and a client, and I was having tremendous difficulty
    > getting it to work, pouring and pouring over my code, only to
    > discover some weeks later that for some odd reason, NSConnections do
    > not work when the project is set to support or require garbage
    > collection.
    >
    > As a test I set garbage collection to: Unsupported, and the app
    > compiled, and the NSConnection returned the proxy object as
    > expected.  But the app obviously failed to do much else, because I
    > had no retain, release, or autorelease method calls.
    >
    > I am not a lazy person, but I am a novice programmer, and the
    > retain, release, autorelease stuff in cocoa is horrible. I was very
    > happy when garbage collection was added to Cocoa, and my projects
    > became much easier to develop, and maintain. Now however, I find
    > myself with a project riddled with memory problems that did Not
    > exist just a few days ago, and as far as I can tell, I don't have
    > any choice... My app either gives up Bonjour, or I have to retrofit
    > the whole thing to manage its own memory with a system that is, lets
    > face it, poorly envisioned.
    >
    > can anybody shed any light on this? am I really stuck managing the
    > memory myself? This is intolerable.
    >
    > cheers,
    > -eblu
    > _______________________________________________
    >

    Well, it always takes a risk to embrace a technology that is just
    released, such as garbage collection. Cocoa APIs and the Objective-C
    language was not designed from its origins with garbage collection in
    mind, so it must have been a really tedious and bug prone process for
    Apple to convert all the frameworks to properly work in a managed
    memory context. This is unlike Java and C#, which have been created
    from the beginning with such feature. In any case, Garbage collection
    is neither good or bad, it certainly allows you to not have to care
    about Zombies and Leaks, but you will still have to think about the
    lifetime of objects. You will still have to remove observers, or
    unbind objects, and think that your objects will be eventually
    reclaimed by the garbage collector, in an undefined order, so you will
    have to implement finalize methods.

    From my experience, at the end of the day, coding in a garbage
    collected environment, is not such a better deal. Obj-C, unlike C++,
    has a very clean way to deal with memory: the release/autorelease/
    retain way to manage your memory, along with autorelease pools, and
    the consistence of the Cocoa APIs with respect to returned objects,
    are heaven compared to what you have to implement in C++, or other not
    managed memory languages.

    Basically, all you have to do is to always return autoreleased objects
    from your methods, and always send release to objects that you created
    with alloc or were returned by any method containing "new" or "copy".
    Also, if you only use the standard accessor methods for setting
    properties, (which send release to the old object and retain the new
    one),  you will not incur in any memory problem. It should not be that
    hard, and at the end your application will potentially perform better,
    and for sure it will eat significantly less memory.

    Joan Lluch.
  • On Wed, Jul 2, 2008 at 6:09 PM, Joan Lluch (casa) <carbonat...> wrote:
    > Basically, all you have to do is to always return autoreleased objects from
    > your methods, and always send release to objects that you created with alloc
    > or were returned by any method containing "new" or "copy". Also, if you only
    > use the standard accessor methods for setting properties, (which send
    > release to the old object and retain the new one),  you will not incur in
    > any memory problem. It should not be that hard, and at the end your
    > application will potentially perform better, and for sure it will eat
    > significantly less memory.

    I have to object to this bit at the end. It's a common cliche that
    garbage collection makes your program perform worse and use more
    memory. And certainly this is *possible*. But it's far from a given
    these days.

    In Cocoa you do lots of retaining and releasing. These operations
    aren't free. They involve a lookup into a global hash table and some
    sort of atomic increment/decrement operation. They're pretty fast, but
    there's certainly some cost there. Garbage collection lets you
    eliminate all of this code, so you get a speed bonus there. Whether
    this is overcome by the overhead that GC gives you will depend on the
    specific program.

    The question of memory usage is far from a given, especially in Cocoa
    where you do lots of autoreleasing. The pervasive use of autorelease
    essentially means that objects don't go away until you go back to the
    event loop. This can result in large spikes in memory usage during
    event processing. The collector isn't constrained by the event loop
    and can run any time it wants to, so it can potentially prevent these
    spikes from occurring. This also has a performance impact, as a more
    memory efficient program is also a faster program due to having a
    smaller working set.

    Cocoa's GC also has another interesting advantage: most of the work is
    done on a background thread. This means that on any multi-core machine
    (which is any Mac sold in the past couple of years) that isn't already
    loaded at 100%, this bulk of the GC work essentially comes "for free".

    Now, this is not to say that GC will make your program go faster. This
    will depend greatly on exactly how your program is designed and
    implemented. But while GC won't automatically make your program go
    faster, it also won't automatically make it go slower.

    Mike
  • El 03/07/2008, a las 3:33, Michael Ash escribió:

    > On Wed, Jul 2, 2008 at 6:09 PM, Joan Lluch (casa) <carbonat...>
    > wrote:
    >> Basically, all you have to do is to always return autoreleased
    >> objects from
    >> your methods, and always send release to objects that you created
    >> with alloc
    >> or were returned by any method containing "new" or "copy". Also, if
    >> you only
    >> use the standard accessor methods for setting properties, (which send
    >> release to the old object and retain the new one),  you will not
    >> incur in
    >> any memory problem. It should not be that hard, and at the end your
    >> application will potentially perform better, and for sure it will eat
    >> significantly less memory.
    >
    > I have to object to this bit at the end. It's a common cliche that
    > garbage collection makes your program perform worse and use more
    > memory. And certainly this is *possible*. But it's far from a given
    > these days.
    >
    > In Cocoa you do lots of retaining and releasing. These operations
    > aren't free. They involve a lookup into a global hash table and some
    > sort of atomic increment/decrement operation. They're pretty fast, but
    > there's certainly some cost there. Garbage collection lets you
    > eliminate all of this code, so you get a speed bonus there. Whether
    > this is overcome by the overhead that GC gives you will depend on the
    > specific program.
    >
    > The question of memory usage is far from a given, especially in Cocoa
    > where you do lots of autoreleasing. The pervasive use of autorelease
    > essentially means that objects don't go away until you go back to the
    > event loop. This can result in large spikes in memory usage during
    > event processing. The collector isn't constrained by the event loop
    > and can run any time it wants to, so it can potentially prevent these
    > spikes from occurring. This also has a performance impact, as a more
    > memory efficient program is also a faster program due to having a
    > smaller working set.
    >
    > Cocoa's GC also has another interesting advantage: most of the work is
    > done on a background thread. This means that on any multi-core machine
    > (which is any Mac sold in the past couple of years) that isn't already
    > loaded at 100%, this bulk of the GC work essentially comes "for free".
    >
    > Now, this is not to say that GC will make your program go faster. This
    > will depend greatly on exactly how your program is designed and
    > implemented. But while GC won't automatically make your program go
    > faster, it also won't automatically make it go slower.
    >
    > Mike
    >

    There is a couple of points I think you are missing here.

    First, GC makes programs go slower not because of the overhead of the
    garbage collection itself, which I concede that may be comparable to
    the retain/release calls in a non-managed environment, but for the
    extra memory overhead that it is used. The crucial difference between
    a non-managed app and a GC app is that in a non-managed app the memory
    is released very soon after the life of an object has expired.

    Yes, you are right that Cocoa does produce memory usage peaks (though
    you are not fair by not saying that you can eliminate them with a
    proper use autorelease pools), but in a GC collection environment
    these peaks not only are much bigger, but you don't have any means to
    avoid them. The problem with GC is that memory used by the app will
    eventually suck all the available resources of the system, and from
    this to severe performance issues due to virtual memory page faults,
    there is only a very short path. However, all of this problems can be
    effectively avoided by the traditional self-managed memory Cocoa
    programming. The brain cost of having to set some release/retain
    messages here and there, does compensate in my opinion (and
    experience) the unknown performance issues that a GC app will have as
    it is run in a system with limited memory resources. GC works just
    fine if you have lots of memory, but not if you have limited
    resources. And know think about why Apple did not implement GC in the
    iPhone SDKs.

    Joan Lluch (casa)
  • Le 4 juil. 08 à 11:25, Joan Lluch (casa) a écrit :
    >>
    >>
    >
    > There is a couple of points I think you are missing here.
    >
    > First, GC makes programs go slower not because of the overhead of
    > the garbage collection itself, which I concede that may be
    > comparable to the retain/release calls in a non-managed environment,
    > but for the extra memory overhead that it is used. The crucial
    > difference between a non-managed app and a GC app is that in a non-
    > managed app the memory is released very soon after the life of an
    > object has expired.
    >
    > Yes, you are right that Cocoa does produce memory usage peaks
    > (though you are not fair by not saying that you can eliminate them
    > with a proper use autorelease pools), but in a GC collection
    > environment these peaks not only are much bigger, but you don't have
    > any means to avoid them.

    I don't see your point.

    Using standard memory management, you can create you own pool, and
    using GC, you can manually trigger the GC. What prevent you to avoid
    memory peak ?

    > The problem with GC is that memory used by the app will eventually
    > suck all the available resources of the system, and from this to
    > severe performance issues due to virtual memory page faults, there
    > is only a very short path. However, all of this problems can be
    > effectively avoided by the traditional self-managed memory Cocoa
    > programming. The brain cost of having to set some release/retain
    > messages here and there, does compensate in my opinion (and
    > experience) the unknown performance issues that a GC app will have
    > as it is run in a system with limited memory resources. GC works
    > just fine if you have lots of memory, but not if you have limited
    > resources. And know think about why Apple did not implement GC in
    > the iPhone SDKs.

    You cannot compare iPhone with a desktop/laptop computer. That's not
    the only facility Apple has disabled on the iPhone for optimization,
    and that does not mean that all thoses features sucks.
  • El 04/07/2008, a las 11:38, Jean-Daniel Dupas escribió:

    > I don't see your point.
    >
    > Using standard memory management, you can create you own pool, and
    > using GC, you can manually trigger the GC. What prevent you to avoid
    > memory peak ?

    Bonjour, Jean.

    Try it yourself. With the correct use of an autorelease pool you can
    turn a simple loop to hog all memory resources to not use any
    perceivable amount of memory. It is exactly as this because it was
    designed this way.

    However, this is hardly possible with GC even if you trigger the GC
    manually in the middle of the loop. It simply will not release the
    memory at the rate you expect because it follows its own rules and as
    far as free memory is available it will not free anything unless it is
    a reason, according to its algorithms, to do so. Furthermore, the fact
    that the memory is released in a parallel threat running at lower
    priority, allows for little chance that the memory you would like to
    be released at a particular point, actually gets released on time.
    When things go really bad, the GC simply stops the app (in a user
    noticeable way) until it has been able to free enough resources for
    the app to continue. I might add that I really hate this, and this is
    why I turned back to retain/release in my Cocoa development.

    Look at it the way you want, but you simply won't have control of the
    memory resources used by your app in a GC environment. Just keep a
    look at Activity Monitor and compare the memory usage of similarly
    complex apps, running on GC and not. Usually the difference is huge.

    On the other hand,  a badly designed "normal" application can
    eventually consume a lot more than a GC enabled app. But my point is
    that you *can* control it and you can keep it down, however you
    *can't* in a GC App. My point is also that it is not *that* hard to
    deal manually with your app resources given the clever release/
    autorelelase/retain features of Cocoa. However, it is true that GC
    will always keep global memory usage in control (in all cases), and
    will never crash the app for that reason, because it is obviously
    designed to do so.

    > You cannot compare iPhone with a desktop/laptop computer. That's not
    > the only facility Apple has disabled on the iPhone for optimization,
    > and that does not mean that all thoses features sucks.
    >

    Agreed, the iPhone is tiny compared to a desktop computer, and as you
    already concede, several features have had to be disabled to keep with
    performance requirements. However I didn't want to imply that GC does
    suck, It is already a matured technology used by virtually all modern
    languages, which can't even be disabled in most of them, and therefore
    it is here to stay, and certainly a needed improvement to Cocoa
    programming in order to keep with the latest development trends.

    Joan Lluch (casa)
  • On Jul 4, 2008, at 2:25 AM, Joan Lluch (casa) wrote:

    > First, GC makes programs go slower not because of the overhead of
    > the garbage collection itself, which I concede that may be
    > comparable to the retain/release calls in a non-managed environment,
    > but for the extra memory overhead that it is used. The crucial
    > difference between a non-managed app and a GC app is that in a non-
    > managed app the memory is released very soon after the life of an
    > object has expired.

    Please do not spread misinformation about Objective-C garbage
    collection.  What you're essentially asserting is that Objective-C
    garbage collection will always increase the high-water mark of an
    application, and that is not the case.

    Memory in a GC app is released very soon after the life of an object
    is over -- sometimes, even sooner than it would be under manual memory
    management.  That's because Objective-C garbage collection runs on a
    separate thread.

    Under non-GC, an object's memory may not be reclaimed until the
    current autorelease pool is drained.  However, under GC, an object's
    memory can be reclaimed as soon as the collector can tell there are no
    more references to it -- no matter when that is.

      -- Chris
  • On Jul 4, 2008, at 9:32 AM, Joan Lluch (casa) wrote:

    > However, this is hardly possible with GC even if you trigger the GC
    > manually in the middle of the loop. It simply will not release the
    > memory at the rate you expect because it follows its own rules and
    > as far as free memory is available it will not free anything unless
    > it is a reason, according to its algorithms, to do so.

    This situation should work.  If it does not, that's something to
    report to Apple via the bug reporter http://bugreport.apple.com/ and,
    if possible attach a test case that demonstrates the issue.

    Do not assume that it's simply the collector "follow[ing] its own
    rules."  The collector should -- and DOES -- collect objects that it
    can tell are no longer referenced.  In fact, we've had occasional
    threads here about objects used at the beginning of a method being
    collected while interior pointers that they've handed out are still in
    use, so it's not just hypothetical.

      -- Chris
  • El 04/07/2008, a las 22:54, Chris Hanson escribió:

    > On Jul 4, 2008, at 2:25 AM, Joan Lluch (casa) wrote:
    >
    >> First, GC makes programs go slower not because of the overhead of
    >> the garbage collection itself, which I concede that may be
    >> comparable to the retain/release calls in a non-managed
    >> environment, but for the extra memory overhead that it is used. The
    >> crucial difference between a non-managed app and a GC app is that
    >> in a non-managed app the memory is released very soon after the
    >> life of an object has expired.
    >
    > Please do not spread misinformation about Objective-C garbage
    > collection.  What you're essentially asserting is that Objective-C
    > garbage collection will always increase the high-water mark of an
    > application, and that is not the case.
    >
    > Memory in a GC app is released very soon after the life of an object
    > is over -- sometimes, even sooner than it would be under manual
    > memory management.  That's because Objective-C garbage collection
    > runs on a separate thread.
    >
    > Under non-GC, an object's memory may not be reclaimed until the
    > current autorelease pool is drained.  However, under GC, an object's
    > memory can be reclaimed as soon as the collector can tell there are
    > no more references to it -- no matter when that is.
    >
    > -- Chris
    >

    Sorry if I my post did not sound appropriate. It never was my
    intention. However, let me copy an excerpt of the Cocoa documentation
    on the GC algorithm that Cocoa uses. This is *exactly* what the
    collector does and this is why the memory used by a GC app is *that*
    much compared to a well designed traditional Cocoa app.

    <begin excerpt>
    The collector is conservative—it never compacts the heap by moving
    blocks of memory and updating pointers. Once allocated, an object
    always stays at its original memory location.
    The collector is request-driven, not demand-driven. The Cocoa
    implementation makes requests at appropriate times. You can also
    programmatically request consideration of a garbage collection cycle,
    and if a memory threshold has been exceeded a collection is run
    automatically.
    The collector runs exclusively on the main thread of the application.
    At no time are all threads stopped for a collection cycle, and each
    thread is stopped for as short a time as is possible. Threads may be
    blocked for a short time while all unreachable objects are formed into
    the garbage list and weak references zeroed. Only threads that have
    directly or indirectly performed an[NSThread self] operation are
    subject to garbage collection.
    The collector is generational (see “Write Barriers”)—most collections
    are very fast and recover significant amounts of recently-allocated
    memory, but not all possible memory. Full collections are also fast
    and do collect all possible memory, but are run less frequently, at
    times unlikely to impact user event processing, and may be aborted in
    the presence of new user events.
    <end>

    Basically for performance reasons, the G. collector "preffers" to let
    memory usage grow (while it is still available) in order to avoid too
    many collections or to try that the user does not notice it, and in
    practice it generally succeeds at it. But you will definitely notice
    some annoying sporadic app response delays of half a second or so
    specially if your app is very complex and maintains lots of objects
    referencing each other in complex graphs.

    On the other hand, the documentation also states the following

    <begin excerpt>

    Garbage collection simplifies memory management and makes it easier to
    ensure thread and exception safety. It also avoids common problems
    such as retain cycles, and simplifies some code patterns (such as
    accessor methods in Cocoa). Together these make applications more
    robust.

    <end>

    Which, of course is also completely true !

    Joan Lluch
  • On Jul 5, 2008, at 3:00 PM, Joan Lluch (casa) wrote:

    > However, let me copy an excerpt of the Cocoa documentation on the GC
    > algorithm that Cocoa uses.
    >
    You haven't updated your documentation since the beginning of November
    last year.

    > <begin excerpt>
    > [...]
    > The collector runs exclusively on the main thread of the
    > application. At no time are all threads stopped for a collection
    > cycle, and each thread is stopped for as short a time as is
    > possible. Threads may be blocked for a short time while all
    > unreachable objects are formed into the garbage list and weak
    > references zeroed. Only threads that have directly or indirectly
    > performed an[NSThread self] operation are subject to garbage
    > collection.
    >
    This is not correct for the current implementation of the collector:

    The collector is both request and demand driven. The Cocoa
    implementation makes requests at appropriate times. You can also
    programmatically request consideration of a garbage collection cycle,
    and if a memory threshold has been exceeded a collection is run
    automatically.

    The collector runs on its own thread in the application. At no time
    are all threads stopped for a collection cycle, and each thread is
    stopped for as short a time as is possible. It is possible for threads
    requesting collector actions to block during a critical section on the
    collector thread's part. Only threads that have directly or indirectly
    performed an [NSThread self] operation are subject to garbage
    collection.

    The collector is generational (see “Write Barriers”)—most collections
    are very fast and recover significant amounts of recently-allocated
    memory, but not all possible memory. Full collections are also fast
    and do collect all possible memory, but are run less frequently, at
    times unlikely to impact user event processing, and may be aborted in
    the presence of new user events.

    <http://developer.apple.com/documentation/Cocoa/Conceptual/GarbageCollection
    /Articles/gcArchitecture.html
    >

    mmalc
  • On Jul 5, 2008, at 3:00 PM, Joan Lluch (casa) wrote:
    > Basically for performance reasons, the G. collector "preffers" to
    > let memory usage grow (while it is still available) in order to
    > avoid too many collections or to try that the user does not notice
    > it, and in practice it generally succeeds at it. But you will
    > definitely notice some annoying sporadic app response delays of half
    > a second or so specially if your app is very complex and maintains
    > lots of objects referencing each other in complex graphs.

    Only if some of those objects force the main thread to block (which
    can happen as certain objects do require finalization on the main
    thread -- these are considered to be bugs and will be eliminated as
    time permits) or if you have written classes with complex finalizers
    that cause your secondary threads to block.

    And it isn't that the collector "prefers" to let memory usage grow.
    More so that the collector strives to achieve a balance between memory
    use, responsiveness, and CPU cycles consumed, with user responsiveness
    being the most heavily weighted of the three.

    If there are ways for the collector to collect more efficiently
    without impacting user responsiveness, said ways will be implemented....

    b.bum
  • El 06/07/2008, a las 2:05, mmalc crawford escribió:

    >
    > On Jul 5, 2008, at 3:00 PM, Joan Lluch (casa) wrote:
    >
    >> However, let me copy an excerpt of the Cocoa documentation on the
    >> GC algorithm that Cocoa uses.
    >>
    > You haven't updated your documentation since the beginning of
    > November last year.
    >

    Oops, thanks. Looks as it is time for me to update things... I will
    give the Cocoa GC another try before adding another comment, (my
    apologies, I might be testing an earlier version)

    By the way, how do I know for sure that a set of documentation
    corresponds to a particular version of the SKD. I am using XCode 3.0
    for mac development since I believe it is the latest non pre-release
    version of the dev-tools and the documentation files that came with
    it. Is 3.1 "already" intended for use for mac development?. Since this
    post will go out of topic I am also posting this on the Xcode lists,
    where I think I should receive a more appropriate answer.

    Joan Lluch
  • On 7/4/08, Chris Hanson <cmh...> wrote:

    > Under non-GC, an object's memory may not be reclaimed until the current
    > autorelease pool is drained.  However, under GC, an object's memory can be
    > reclaimed as soon as the collector can tell there are no more references to
    > it -- no matter when that is.

    I think Joan's point is not that there are circumstances in which the
    GC will never reclaim, but that it is not possible to ensure
    reclamation deterministically.

    > From the docs:

    --------
    collectExhaustively
    Tells the receiver to collect iteratively.

    - (void)collectExhaustively

    Discussion
    You use this method to indicate to the collector that it should
    perform an exhaustive collection. Collection is subject to
    interruption on user input.

    Availability Available in Mac OS X v10.5 and later.
    Declared In NSGarbageCollector.h
    --------

    "Collection is subject to interruption on user input" -- with no
    mention of when it might be re-started.

    Hamish
  • El 07/07/2008, a las 0:18, Hamish Allan escribió:

    > On 7/4/08, Chris Hanson <cmh...> wrote:
    >
    >> Under non-GC, an object's memory may not be reclaimed until the
    >> current
    >> autorelease pool is drained.  However, under GC, an object's memory
    >> can be
    >> reclaimed as soon as the collector can tell there are no more
    >> references to
    >> it -- no matter when that is.
    >
    > I think Joan's point is not that there are circumstances in which the
    > GC will never reclaim, but that it is not possible to ensure
    > reclamation deterministically.
    >
    > [...]
    >
    > "Collection is subject to interruption on user input" -- with no
    > mention of when it might be re-started.
    >
    > Hamish

    Thanks Hamish. That was exactly my point, and that citation of the
    documentation gives more plausibility to it.

    (my native language is not English, nor I live in an English speaking
    country so it can be sometimes difficult for me to express complex
    things or to know the more appropriate word to express a concept, this
    is why I tend to paraphrase on my writing, and I understand that it
    can be difficult to read).

    Joan Lluch
  • On 7/6/08 11:18 PM, Hamish Allan said:

    > --------
    > collectExhaustively
    > Tells the receiver to collect iteratively.
    >
    > - (void)collectExhaustively
    >
    > Discussion
    > You use this method to indicate to the collector that it should
    > perform an exhaustive collection. Collection is subject to
    > interruption on user input.
    >
    > Availability Available in Mac OS X v10.5 and later.
    > Declared In NSGarbageCollector.h
    > --------
    >
    > "Collection is subject to interruption on user input" -- with no
    > mention of when it might be re-started.

    There's always the lower-level:

    objc_collect (OBJC_EXHAUSTIVE_COLLECTION |
        OBJC_WAIT_UNTIL_DONE);

    --
    ____________________________________________________________
    Sean McBride, B. Eng                <sean...>
    Rogue Research                        www.rogue-research.com
    Mac Software Developer              Montréal, Québec, Canada
  • On Mon, Jul 7, 2008 at 6:33 PM, Sean McBride <sean...> wrote:

    > There's always the lower-level:
    >
    > objc_collect (OBJC_EXHAUSTIVE_COLLECTION |
    > OBJC_WAIT_UNTIL_DONE);

    If this were called from the main thread, would it guarantee that the
    collector run without interruption, given that user input would be
    suspended?

    Otherwise, there's still rather a difference between "do" and "wait
    until done"...

    Hamish
  • Some minor factual corrections:

    On Jul 2, 2008, at 18:33 , Michael Ash wrote:

    > In Cocoa you do lots of retaining and releasing. These operations
    > aren't free. They involve a lookup into a global hash table and some
    > sort of atomic increment/decrement operation.

    The hash table is only used by NSObject subclasses that do not
    implement their own inline reference count.  Most Foundation objects
    do implement such an inline reference count, so there are no hash
    lookups involved, just the atomic increment/decrement.  I would
    strongly recommend that you implement an inline reference count for
    your own objects if they undergo frequent ownership changes.

    > They're pretty fast, but
    > there's certainly some cost there. Garbage collection lets you
    > eliminate all of this code, so you get a speed bonus there.

    GC does not eliminate this overhead, it replaces it with the overhead
    of the write-barrier functions that are called when you do an
    assignment.  These calls are generated automatically by the compiler,
    so you don't see them in your code, but they are still function
    calls.  This is in addition to the scanning overhead.

    > The question of memory usage is far from a given, especially in Cocoa
    > where you do lots of autoreleasing. The pervasive use of autorelease
    > essentially means that objects don't go away until you go back to the
    > event loop.

    They generally go away whenever you want them to go away.  The top of
    the event loop is a good default, but it is just that:  a convenient
    default.

    > This can result in large spikes in memory usage during
    > event processing.

    If you see such spikes, simply add autorelease pools to your
    processing.  Also:  don't gratuitously create and/or autorelease
    objects if you don't have to.  Objective-C object-creation is pretty
    heavy-weight compared to other OO languages, regardless of wether you
    are using garbage collection or reference counting, so programming-
    styles that do a lot of object-creation will suffer, performance-wise.

    With RC, there are ways to mitigate the effects ( see http://www.metaobject.com/blog/2007/08/high-performance-objective-c-i.html
      and  http://www.metaobject.com/blog/2007/09/more-on-mpwobjectcache.html
      ), whereas I haven't yet found a way to achieve the same effect with
    GC.

    > The collector isn't constrained by the event loop
    > and can run any time it wants to, so it can potentially prevent these
    > spikes from occurring. This also has a performance impact, as a more
    > memory efficient program is also a faster program due to having a
    > smaller working set.

    You can achieve the same effect by inserting autorelease pools during
    processing.

    > Cocoa's GC also has another interesting advantage: most of the work is
    > done on a background thread. This means that on any multi-core machine
    > (which is any Mac sold in the past couple of years) that isn't already
    > loaded at 100%, this bulk of the GC work essentially comes "for free".

    ...as does doing the work during the top of the event loop when the
    machine is waiting for user input.  Of course "free" is actually not
    free (a) in terms of power consumption and (b) in terms of memory
    bandwidth, which is a shared resource between the cores and frequently
    the bottleneck these days and (c) if you have other work for that core.

    Cheers,

    Marcel
  • On Thu, Jul 10, 2008 at 12:17 PM, Marcel Weiher <marcel.weiher...> wrote:
    > Some minor factual corrections:
    >
    >
    > On Jul 2, 2008, at 18:33 , Michael Ash wrote:
    >
    >> In Cocoa you do lots of retaining and releasing. These operations
    >> aren't free. They involve a lookup into a global hash table and some
    >> sort of atomic increment/decrement operation.
    >
    > The hash table is only used by NSObject subclasses that do not implement
    > their own inline reference count.  Most Foundation objects do implement such
    > an inline reference count, so there are no hash lookups involved, just the
    > atomic increment/decrement.  I would strongly recommend that you implement
    > an inline reference count for your own objects if they undergo frequent
    > ownership changes.

    Atomic updates are still a pretty big hit on a multiprocessor system
    (all of them, these days), and implementing your own is a fair amount
    of work that you simply don't have to do in a GC environment.
    Especially since you can't reasonably implement it once and share the
    implementation, you'll have to manually insert the implementation into
    your classes individually.

    >> They're pretty fast, but
    >> there's certainly some cost there. Garbage collection lets you
    >> eliminate all of this code, so you get a speed bonus there.
    >
    > GC does not eliminate this overhead, it replaces it with the overhead of the
    > write-barrier functions that are called when you do an assignment.  These
    > calls are generated automatically by the compiler, so you don't see them in
    > your code, but they are still function calls.  This is in addition to the
    > scanning overhead.

    No, it *does* eliminate this overhead, and it has this *other*
    overhead. It's not a replacement, because the overheads are not
    identical, neither in time nor in location. For example, in the very
    common scenario of creating temporary objects which never leave the
    stack, a write barrier is never generated.

    >> The question of memory usage is far from a given, especially in Cocoa
    >> where you do lots of autoreleasing. The pervasive use of autorelease
    >> essentially means that objects don't go away until you go back to the
    >> event loop.
    >
    > They generally go away whenever you want them to go away.  The top of the
    > event loop is a good default, but it is just that:  a convenient default.
    >
    >> This can result in large spikes in memory usage during
    >> event processing.
    >
    > If you see such spikes, simply add autorelease pools to your processing.

    Yet more work that you don't have to do in a GC system. And
    autorelease pools, while quite cheap, are not free.

    > Also:  don't gratuitously create and/or autorelease objects if you don't
    > have to.  Objective-C object-creation is pretty heavy-weight compared to
    > other OO languages, regardless of wether you are using garbage collection or
    > reference counting, so programming-styles that do a lot of object-creation
    > will suffer, performance-wise.

    You can bend your programming style to micro-optimize the language's
    speed if you want, but that's not the kind of thing I prefer to do.
    I'd rather look at how GC compares to refcounting in typical usage,
    not this kind of carefully optimized usage that rarely happens.

    >> Cocoa's GC also has another interesting advantage: most of the work is
    >> done on a background thread. This means that on any multi-core machine
    >> (which is any Mac sold in the past couple of years) that isn't already
    >> loaded at 100%, this bulk of the GC work essentially comes "for free".
    >
    > ...as does doing the work during the top of the event loop when the machine
    > is waiting for user input.

    This doesn't work when you're compute bound, which is of course the
    only time that performance actually matters anyway.

    > Of course "free" is actually not free (a) in
    > terms of power consumption and (b) in terms of memory bandwidth, which is a
    > shared resource between the cores and frequently the bottleneck these days
    > and (c) if you have other work for that core.

    That would be why I put it into quotes. The point being that while
    it's not really free, it takes no additional wall-clock time in the
    common case.

    Mike
  • On Jul 10, 2008, at 9:50 , Michael Ash wrote:

    > On Thu, Jul 10, 2008 at 12:17 PM, Marcel Weiher <marcel.weiher...>
    >> wrote:
    >> [hash tables not generally used + internal refcounts]

    > Atomic updates are still a pretty big hit on a multiprocessor system
    > (all of them, these days),

    Yes, they're definitely not free.

    > and implementing your own is a fair amount
    > of work that you simply don't have to do in a GC environment.

    Not really, no.

    > Especially since you can't reasonably implement it once and share the
    > implementation, you'll have to manually insert the implementation into
    > your classes individually.

    There are several ways to share the implementation:

    1.    Do nothing, CF and Foundation already do it for most of their objects
    (and they share their implementation...probably unreasonably...)
    2.    Implement a common superclass
    3.    Implement a function, inline function or macro that takes a pointer
    to the refcount ivar.

    >>> They're pretty fast, but
    >>> there's certainly some cost there. Garbage collection lets you
    >>> eliminate all of this code, so you get a speed bonus there.
    >>
    >> GC does not eliminate this overhead, it replaces it with the
    >> overhead of the
    >> write-barrier functions that are called when you do an assignment.
    >> These
    >> calls are generated automatically by the compiler, so you don't see
    >> them in
    >> your code, but they are still function calls.  This is in addition
    >> to the
    >> scanning overhead.
    >
    > It's not a replacement, because the overheads are not
    > identical, neither in time nor in location.

    Why do you say that?  The write barrier code gets called when when you
    store an object into an instance variable, same as for a retain.  They
    are actually at pretty much precisely the same time and location.

    I am guessing you are referring to the scanning overhead, about which
    you are right:  it happens at a different time and in a different
    place, and in addition to the checks.

    > For example, in the very
    > common scenario of creating temporary objects which never leave the
    > stack, a write barrier is never generated.

    Yes, just like objects don't get retained when they are stored in
    local variables, that happens when you store them into instance
    variables.

    >> Also:  don't gratuitously create and/or autorelease objects if you
    >> don't
    >> have to.  Objective-C object-creation is pretty heavy-weight
    >> compared to
    >> other OO languages, regardless of wether you are using garbage
    >> collection or
    >> reference counting, so programming-styles that do a lot of object-
    >> creation
    >> will suffer, performance-wise.
    >
    > You can bend your programming style to micro-optimize the language's
    > speed if you want, but that's not the kind of thing I prefer to do.

    Taking into account the programming style a language supports is about
    as far from a micro-optimization as you can get.  It is an
    architectural concern that informs how you structure your system,
    changing it after-the-fact often turns out to be impossible.  At least
    that's been my experience over the last 20 years or so, YMMV.

    > I'd rather look at how GC compares to refcounting in typical usage,
    > not this kind of carefully optimized usage that rarely happens.

    You might have heard about the 80/20 rule, which is actually more a
    90/10 or 95/05 rule:  most of the execution time is spent in a very
    small portion of your code.  Being able to go in and *really* optimize
    those hotspots actually gives you the most bang for the buck.  The
    "typical usage", meaning the bulk of the program, generally does not
    matter.

    This is one of those areas where Objective-C really, really excels:
    the ability to combine very high-level, very abstracted code with
    small bits of highly optimized code to get an optimum balance of
    expressiveness and performance.

    >> ...as does doing the work during the top of the event loop when the
    >> machine
    >> is waiting for user input.
    >
    > This doesn't work when you're compute bound, which is of course the
    > only time that performance actually matters anyway.

    The latter part is only true iff your apps do not need to responsive.
    I prefer apps that are.  The first part is where that 95/5 rule comes
    in and being able to tune those hotspots really comes in handy.

    Cheers,

    Marcel
  • On Thu, Jul 10, 2008 at 7:33 PM, Marcel Weiher <marcel.weiher...> wrote:
    >
    > On Jul 10, 2008, at 9:50 , Michael Ash wrote:
    >
    >> On Thu, Jul 10, 2008 at 12:17 PM, Marcel Weiher <marcel.weiher...>
    >> wrote:
    >>>
    >>> [hash tables not generally used + internal refcounts]
    >
    >> Atomic updates are still a pretty big hit on a multiprocessor system
    >> (all of them, these days),
    >
    > Yes, they're definitely not free.
    >
    >> and implementing your own is a fair amount
    >> of work that you simply don't have to do in a GC environment.
    >
    > Not really, no.
    >
    >> Especially since you can't reasonably implement it once and share the
    >> implementation, you'll have to manually insert the implementation into
    >> your classes individually.
    >
    > There are several ways to share the implementation:
    >
    > 1.      Do nothing, CF and Foundation already do it for most of their
    > objects
    > (and they share their implementation...probably unreasonably...)

    This, obviously, doesn't work for your own classes.

    > 2.      Implement a common superclass

    This doesn't work if you're subclassing something other than NSObject already.

    > 3.      Implement a function, inline function or macro that takes a pointer
    > to the refcount ivar.

    This works, but still leaves you to copy/paste glue code everywhere.

    >>>> They're pretty fast, but
    >>>> there's certainly some cost there. Garbage collection lets you
    >>>> eliminate all of this code, so you get a speed bonus there.
    >>>
    >>> GC does not eliminate this overhead, it replaces it with the overhead of
    >>> the
    >>> write-barrier functions that are called when you do an assignment.  These
    >>> calls are generated automatically by the compiler, so you don't see them
    >>> in
    >>> your code, but they are still function calls.  This is in addition to the
    >>> scanning overhead.
    >>
    >> It's not a replacement, because the overheads are not
    >> identical, neither in time nor in location.
    >
    > Why do you say that?  The write barrier code gets called when when you store
    > an object into an instance variable, same as for a retain.  They are
    > actually at pretty much precisely the same time and location.
    >
    > I am guessing you are referring to the scanning overhead, about which you
    > are right:  it happens at a different time and in a different place, and in
    > addition to the checks.

    I'm not referring to the scanning overhead. There are many scenarios
    where a refcount modification is made which do not produce a write
    barrier in GC-land. For example:

    - Temporary objects get a 'release' at the end of their life, and
    usually an 'autorelease' at the beginning.
    - Paranoid or thread-safe accessors do a retain/autorelease dance
    before returning.
    - Jumping objects across the end of an autorelease pool by retaining
    them before destroying the pool.

    >> For example, in the very
    >> common scenario of creating temporary objects which never leave the
    >> stack, a write barrier is never generated.
    >
    > Yes, just like objects don't get retained when they are stored in local
    > variables, that happens when you store them into instance variables.

    They do get released though, which is a refcount operation that
    doesn't happen in the GC world.

    >>> Also:  don't gratuitously create and/or autorelease objects if you don't
    >>> have to.  Objective-C object-creation is pretty heavy-weight compared to
    >>> other OO languages, regardless of wether you are using garbage collection
    >>> or
    >>> reference counting, so programming-styles that do a lot of
    >>> object-creation
    >>> will suffer, performance-wise.
    >>
    >> You can bend your programming style to micro-optimize the language's
    >> speed if you want, but that's not the kind of thing I prefer to do.
    >
    > Taking into account the programming style a language supports is about as
    > far from a micro-optimization as you can get.  It is an architectural
    > concern that informs how you structure your system, changing it
    > after-the-fact often turns out to be impossible.  At least that's been my
    > experience over the last 20 years or so, YMMV.

    I'm not sure I understand what you're saying here. My point is that
    ObjC makes it very easy and natural to create temporary objects
    without worrying about their lifetimes. In my experience, code which
    goes to great lengths to avoid autoreleased objects is messy and much
    more bug prone than the "normal" way. Thus, yes, you can avoid many
    autoreleased objects if you want, but this is a painful micro
    optimization, not the standard way to do things.

    >> I'd rather look at how GC compares to refcounting in typical usage,
    >> not this kind of carefully optimized usage that rarely happens.
    >
    > You might have heard about the 80/20 rule, which is actually more a 90/10 or
    > 95/05 rule:  most of the execution time is spent in a very small portion of
    > your code.  Being able to go in and *really* optimize those hotspots
    > actually gives you the most bang for the buck.  The "typical usage", meaning
    > the bulk of the program, generally does not matter.

    I did my master's thesis on high performance code and optimization; I
    am more than vaguely familiar with these concepts. My point is merely
    that GC can help you without you needing to change your code in any
    way. This, to me, is more valuable than peppering my code with lots of
    painful manual memory management to make it go faster.

    > This is one of those areas where Objective-C really, really excels:  the
    > ability to combine very high-level, very abstracted code with small bits of
    > highly optimized code to get an optimum balance of expressiveness and
    > performance.

    I agree, but I don't agree with your proposed methods. I've optimized
    lots of ObjC code in my time, and I've never found it necessary or
    even particularly helpful to perform these refcounting or allocation
    tricks you discuss. In my experience, object allocation and
    refcounting are never hotspots.

    Your experience may differ. In fact, I know that it *does* differ,
    because we've had that conversation before. But, to be perfectly
    frank, your experience is not going to change my mind.

    >>> ...as does doing the work during the top of the event loop when the
    >>> machine
    >>> is waiting for user input.
    >>
    >> This doesn't work when you're compute bound, which is of course the
    >> only time that performance actually matters anyway.
    >
    > The latter part is only true iff your apps do not need to responsive.

    This is absolutely not true. If your computation is impacting
    responsiveness, then you are compute bound for that constraint, pure
    and simple.

    Also let's remember the context here. I said that running the GC on a
    background thread means that it comes "for free", and you said that
    doing work at the top of the event loop is also "for free". It's
    obviously *not* for free if there is another pending event waiting to
    be processed. In other words, doing a bunch of memory management work
    in the event loop is much more likely to impact responsiveness than
    doing GC work on a background thread.

    Finally, please recall that I never said that GC is better for
    everything or that it will provide more performance in all cases. I
    only said that GC is not necessarily slower, and that which one is
    faster depends greatly on exactly what your application is doing and
    how it's written.

    Mike
  • >> There are several ways to share the implementation:
    >>
    >> 1.      Do nothing, CF and Foundation already do it for most of their
    >> objects
    >> (and they share their implementation...probably
    >> unreasonably...)
    >
    > This, obviously, doesn't work for your own classes.

    But a lot of the objects you will use will tend to be Foundation
    objects, especially if you've adopted the temp-object-heavy style.

    >
    >> 2.      Implement a common superclass
    >
    > This doesn't work if you're subclassing something other than
    > NSObject already.

    Non-NSObject subclasses tend to be things like NSViews which are
    fairly heavy-weight and not that temporary.

    >> 3.      Implement a function, inline function or macro that takes a
    >> pointer
    >> to the refcount ivar.
    >
    > This works, but still leaves you to copy/paste glue code everywhere.

    You don't even have to do that if you don't want to.

    Not every solution works in every context, but combined, they cover
    the bases rather well, making sharing the implementation quite easy.
    In my experience.

    > [not referring to scanning overhead]
    >
    > - Temporary objects get a 'release' at the end of their life, and
    > usually an 'autorelease' at the beginning.

    The cost of a single refcounting op is negligible compared to the cost
    of object allocation, so these two are quite irrelevant.

    > - Jumping objects across the end of an autorelease pool by retaining
    > them before destroying the pool.

    In my experience, this is rather rare, and the cost once again tends
    to be completely negligible compared to the cost of destroying the
    pool and the objects in the pool.

    > - Paranoid or thread-safe accessors do a retain/autorelease dance
    > before returning.

    This one is actually a problem.  Don't do that, it isn't actually
    thread-safe and can cause at least as many problems as it 'solves'.

    >> Yes, just like objects don't get retained when they are stored in
    >> local
    >> variables, that happens when you store them into instance variables.
    >
    > They do get released though, which is a refcount operation that
    > doesn't happen in the GC world.

    Once again, the -release is completely negligible compared to the
    actual deallocation.

    >> Taking into account the programming style a language supports is
    >> about as
    >> far from a micro-optimization as you can get.  It is an architectural
    >> concern that informs how you structure your system, changing it
    >> after-the-fact often turns out to be impossible.  At least that's
    >> been my
    >> experience over the last 20 years or so, YMMV.
    >
    > I'm not sure I understand what you're saying here. My point is that
    > ObjC makes it very easy and natural to create temporary objects
    > without worrying about their lifetimes.

    That is exactly my point:  this is one case where the comparative ease
    is deceptive, as creating lots of temporary objects is not something
    that Objective-C supports well.  Objective-C is a *hybrid* OO
    language, not a pure OO language.

    > In my experience, code which goes to great lengths to avoid
    > autoreleased objects is messy and much
    > more bug prone than the "normal" way.

    Yes, if it is autoreleasing you avoid, not object creation in the
    first place.  The extra autorelease only costs you maybe 30%, the
    extra object allocation costs you an order of magnitude or more.  So
    for example, my standard pattern for initialization is something like
    this:

    -init {
    self=[super init];
    [self setFoo:[Bar bar]];
    return self;
    }

    > Thus, yes, you can avoid many
    > autoreleased objects if you want, but this is a painful micro
    > optimization, not the standard way to do things.

    Once again, avoiding temporary object-creation is not a micro-
    optimization, and creating lots of temporary objects is definitely NOT
    the standard way to do things in Objective-C.

    >> You might have heard about the 80/20 rule, which is actually more a
    >> 90/10 or
    >> 95/05 rule:  most of the execution time is spent in a very small
    >> portion of
    >> your code.  Being able to go in and *really* optimize those hotspots
    >> actually gives you the most bang for the buck.  The "typical
    >> usage", meaning
    >> the bulk of the program, generally does not matter.
    >
    > I did my master's thesis on high performance code and optimization; I
    > am more than vaguely familiar with these concepts.

    Glad to hear that!  However, in the sections above you were
    continually treating ops that differ in cost by an order of magnitude
    or more with equal weight, which makes me somewhat dubious of your
    claimed credentials...

    > My point is merely that GC can help you without you needing to
    > change your code in any
    > way. This, to me, is more valuable than peppering my code with lots of
    > painful manual memory management to make it go faster.

    ...as does this.  Once again:  this is not about randomly "peppering"
    code with "lots of painful manual memory management", this is about
    (a) adopting a coding style that is clean, (b) flows with what
    Objective-C provides and is good at and (c) allows for the highly
    focused optimizations that actually make an impact, rather than
    wasting it on the parts of the code that don't matter.

    >> This is one of those areas where Objective-C really, really
    >> excels:  the
    >> ability to combine very high-level, very abstracted code with small
    >> bits of
    >> highly optimized code to get an optimum balance of expressiveness and
    >> performance.
    >
    > I agree, but I don't agree with your proposed methods.

    I haven't seen any indication that you know what my proposed methods
    are.  They certainly do not involve peppering code with micro-
    optimizations or

    > I've optimized lots of ObjC code in my time, and I've never found it
    > necessary or
    > even particularly helpful to perform these refcounting or allocation
    > tricks you discuss. In my experience, object allocation and
    > refcounting are never hotspots.

    I've given some references to back up my claim...the Postscript
    interpreter I wrote in Objective-C is 20x faster because of object-
    caching, turning it from laughably slow to competitive with the
    industry standard.  MPWXmlKit is around 10x faster than other XML
    scanners, again due in large part to object caching.

    > Your experience may differ. In fact, I know that it *does* differ,
    > because we've had that conversation before. But, to be perfectly
    > frank, your experience is not going to change my mind.

    "I have my opinion, who cares about facts or evidence"

    >>>> ...as does doing the work during the top of the event loop when the
    >>>> machine
    >>>> is waiting for user input.
    >>>
    >>> This doesn't work when you're compute bound, which is of course the
    >>> only time that performance actually matters anyway.
    >>
    >> The latter part is only true iff your apps do not need to responsive.
    >
    > This is absolutely not true.

    It is.

    > If your computation is impacting responsiveness, then you are
    > compute bound
    > for that constraint, pure and simple.

    This is true, but it doesn't make your above statement true.

    > Also let's remember the context here. I said that running the GC on a
    > background thread means that it comes "for free", and you said that
    > doing work at the top of the event loop is also "for free".

    Exactly.

    > It's obviously *not* for free if there is another pending event
    > waiting to
    > be processed.

    Just like stuff on the background is not free if it ties up bus
    bandwidth (which it does, it is scanning after all), or you could have
    used the CPU for other means or you are counting power.  In two of
    those cases, doing the work at the end of the event loop is actually
    better than doing it concurrently.  And let's remember that once you
    are compute-bound, you get much more bang-for-the-buck from targeting
    the 5% hot-spot than from off-loading extra work to another thread.

    > In other words, doing a bunch of memory management work
    > in the event loop is much more likely to impact responsiveness than
    > doing GC work on a background thread.

    That's a strong assertion.  Care to back it up with some evidence? My
    guess is that the opposite is the case:  once the user has her
    response from the system, the wait-time to the next user event will
    typically be much greater.

    > Finally, please recall that I never said that GC is better for
    > everything or that it will provide more performance in all cases.

    Please recall that I never said anything here about the qualities of
    GC or RC in general.  I just corrected some "minor" factual errors.

    > I only said that GC is not necessarily slower, and that which one is
    > faster depends greatly on exactly what your application is doing and
    > how it's written.

    Exactly.  In Objective-C, a temp-object-heavy style will almost
    invariably make your application significantly slower, unless what it
    is doing is sufficiently light-weight that it simply doesn't matter.
    With the problem there being that when your users start filling your
    app with data, what you thought initially would be sufficient will
    turn out not to be.

    Marcel
  • On Fri, Jul 11, 2008 at 3:21 AM, Marcel Weiher <marcel.weiher...> wrote:
    >> [not referring to scanning overhead]
    >>
    >> - Temporary objects get a 'release' at the end of their life, and
    >> usually an 'autorelease' at the beginning.
    >
    > The cost of a single refcounting op is negligible compared to the cost of
    > object allocation, so these two are quite irrelevant.

    A quick test of this claim would appear to disprove it. On my Mac Pro,
    an alloc/init/release cycle of NSObject costs about 290ns. Adding an
    extra pair of retain/release costs about 480ns. I'm not sure how I can
    reasonably measure the object allocation by itself, or the cost of
    just retain or just release. But it seems clear that these refcounting
    operations are quite significant in cost.

    >>> Yes, just like objects don't get retained when they are stored in local
    >>> variables, that happens when you store them into instance variables.
    >>
    >> They do get released though, which is a refcount operation that
    >> doesn't happen in the GC world.
    >
    > Once again, the -release is completely negligible compared to the actual
    > deallocation.

    Apparently not!

    >>> Taking into account the programming style a language supports is about as
    >>> far from a micro-optimization as you can get.  It is an architectural
    >>> concern that informs how you structure your system, changing it
    >>> after-the-fact often turns out to be impossible.  At least that's been my
    >>> experience over the last 20 years or so, YMMV.
    >>
    >> I'm not sure I understand what you're saying here. My point is that
    >> ObjC makes it very easy and natural to create temporary objects
    >> without worrying about their lifetimes.
    >
    > That is exactly my point:  this is one case where the comparative ease is
    > deceptive, as creating lots of temporary objects is not something that
    > Objective-C supports well.  Objective-C is a *hybrid* OO language, not a
    > pure OO language.

    This disagrees completely with my experience. ObjC supports creating
    lots of temporary objects very well. It may not be as *fast* at it as
    some languages, but as you say, speed is not a concern in 90% of the
    cases. Typical ObjC code creates a great deal of temporary objects,
    and this usually works just fine.

    >> In my experience, code which goes to great lengths to avoid autoreleased
    >> objects is messy and much
    >> more bug prone than the "normal" way.
    >
    > Yes, if it is autoreleasing you avoid, not object creation in the first
    > place.  The extra autorelease only costs you maybe 30%, the extra object
    > allocation costs you an order of magnitude or more.  So for example, my
    > standard pattern for initialization is something like this:
    >
    > -init {
    > self=[super init];
    > [self setFoo:[Bar bar]];
    > return self;
    > }

    I don't understand what you're trying to show with this example,
    unless it's the fact that you don't avoid autorelease. But then this
    directly contradicts what you said before: "Also:  don't gratuitously
    create and/or autorelease objects if you don't have to." So I'm
    confused.

    >> Thus, yes, you can avoid many
    >> autoreleased objects if you want, but this is a painful micro
    >> optimization, not the standard way to do things.
    >
    > Once again, avoiding temporary object-creation is not a micro-optimization,

    It certainly is. You're improving the constant in your running time,
    not improving the asymptotic algorithmic performance. That's a micro
    optimization in my book.

    > and creating lots of temporary objects is definitely NOT the standard way to
    > do things in Objective-C.

    It certainly has been in all of the ObjC code I've ever created or
    worked with, and most of what I've looked at.

    >>> You might have heard about the 80/20 rule, which is actually more a 90/10
    >>> or
    >>> 95/05 rule:  most of the execution time is spent in a very small portion
    >>> of
    >>> your code.  Being able to go in and *really* optimize those hotspots
    >>> actually gives you the most bang for the buck.  The "typical usage",
    >>> meaning
    >>> the bulk of the program, generally does not matter.
    >>
    >> I did my master's thesis on high performance code and optimization; I
    >> am more than vaguely familiar with these concepts.
    >
    > Glad to hear that!  However, in the sections above you were continually
    > treating ops that differ in cost by an order of magnitude or more with equal
    > weight, which makes me somewhat dubious of your claimed credentials...

    Where did I ever treat anything with equal weight? I don't recall ever
    stating or even implying that two different operations were
    necessarily equally significant.

    >> My point is merely that GC can help you without you needing to change your
    >> code in any
    >> way. This, to me, is more valuable than peppering my code with lots of
    >> painful manual memory management to make it go faster.
    >
    > ...as does this.  Once again:  this is not about randomly "peppering" code
    > with "lots of painful manual memory management", this is about (a) adopting
    > a coding style that is clean, (b) flows with what Objective-C provides and
    > is good at and (c) allows for the highly focused optimizations that actually
    > make an impact, rather than wasting it on the parts of the code that don't
    > matter.

    Well, you're advocating avoiding object creation and avoiding
    autoreleasing objects, which in my experience creates messy,
    overly-long, bug-prone code. Points a, b, and c are all fulfilled by
    using convenience constructors whenever possible and, in most cases,
    not worrying about how many objects you're making.

    >>> This is one of those areas where Objective-C really, really excels:  the
    >>> ability to combine very high-level, very abstracted code with small bits
    >>> of
    >>> highly optimized code to get an optimum balance of expressiveness and
    >>> performance.
    >>
    >> I agree, but I don't agree with your proposed methods.
    >
    > I haven't seen any indication that you know what my proposed methods are.
    > They certainly do not involve peppering code with micro-optimizations or

    Seems to me that you've stated them pretty explicitly. Avoid
    autorelease, avoid creating objects when you can, reuse objects with
    caches when you can't avoid creating them. No?

    >> I've optimized lots of ObjC code in my time, and I've never found it
    >> necessary or
    >> even particularly helpful to perform these refcounting or allocation
    >> tricks you discuss. In my experience, object allocation and
    >> refcounting are never hotspots.
    >
    > I've given some references to back up my claim...the Postscript interpreter
    > I wrote in Objective-C is 20x faster because of object-caching, turning it
    > from laughably slow to competitive with the industry standard.  MPWXmlKit is
    > around 10x faster than other XML scanners, again due in large part to object
    > caching.

    Yep, well, like I said, your experience differs.

    >> Your experience may differ. In fact, I know that it *does* differ,
    >> because we've had that conversation before. But, to be perfectly
    >> frank, your experience is not going to change my mind.
    >
    > "I have my opinion, who cares about facts or evidence"

    That is a gross misrepresentation of what I said. *Your* experience is
    not going to override *my* experience.

    Now you're just being abusive. Please stop it.

    Mike
  • On Jul 11, 2008, at 8:59 , Michael Ash wrote:

    >> The cost of a single refcounting op is negligible compared to the
    >> cost of
    >> object allocation, so these two are quite irrelevant.
    >
    > A quick test of this claim would appear to disprove it. On my Mac Pro,
    > an alloc/init/release cycle of NSObject costs about 290ns. Adding an
    > extra pair of retain/release costs about 480ns. I'm not sure how I can
    > reasonably measure the object allocation by itself, or the cost of
    > just retain or just release. But it seems clear that these refcounting
    > operations are quite significant in cost.

    This is the extra refcount table in action, and why inline reference
    counts can be such a performance win on objects that are frequently
    retained and released.

    Changing the object to an NSString (which has an internal reference
    count) yields the following results on my MacBook Pro:

    retain+release NSString: time per iteration:  67 nanoseconds

    Compare this with the times for NSObject, both retain/release and
    allocation / deallocation:

    retain+release NSObject 2->3 / 3->2 : time per iteration:  223
    nanoseconds
    retain+release NSObject 1->2 / 2->1 : time per iteration:  276
    nanoseconds
    alloc+dealloc NSObject:  time per iteration:  415 nanoseconds

    Cheers,

    Marcel
    [EOT for me]

    [Differences of opinion and experience snipped]
  • On Fri, Jul 11, 2008 at 2:17 PM, Marcel Weiher <marcel.weiher...> wrote:
    >
    > On Jul 11, 2008, at 8:59 , Michael Ash wrote:
    >
    >>> The cost of a single refcounting op is negligible compared to the cost of
    >>> object allocation, so these two are quite irrelevant.
    >>
    >> A quick test of this claim would appear to disprove it. On my Mac Pro,
    >> an alloc/init/release cycle of NSObject costs about 290ns. Adding an
    >> extra pair of retain/release costs about 480ns. I'm not sure how I can
    >> reasonably measure the object allocation by itself, or the cost of
    >> just retain or just release. But it seems clear that these refcounting
    >> operations are quite significant in cost.
    >
    > This is the extra refcount table in action, and why inline reference counts
    > can be such a performance win on objects that are frequently retained and
    > released.
    >
    > Changing the object to an NSString (which has an internal reference count)
    > yields the following results on my MacBook Pro:
    >
    > retain+release NSString: time per iteration:  67 nanoseconds
    >
    > Compare this with the times for NSObject, both retain/release and allocation
    > / deallocation:
    >
    > retain+release NSObject 2->3 / 3->2 : time per iteration:  223 nanoseconds
    > retain+release NSObject 1->2 / 2->1 : time per iteration:  276 nanoseconds
    > alloc+dealloc NSObject:  time per iteration:  415 nanoseconds

    Seems that NSString and NSMutableString are just faster at everything.
    In all cases, the cost of an extra retain/release for them is still
    roughly 50% of the cost of an alloc/init/retain. Here are my raw
    numbers, times in nanoseconds:

    NSObject alloc/init/release                        284.3
    NSObject alloc/init/retain/release/release          495.7
    Extra time taken:                                  74%

    NSString alloc/init/release                        40.2
    NSString alloc/init/retain/release/release             73.4
    Extra time taken:                                  45%

    NSMutableString alloc/init/release                  194.7
    NSMutableString alloc/init/retain/release/release  300.7
    Extra time taken:                                  54%

    I have no explanation as to why NSMutableString is so much slower at
    everything. They both end up creating an instance of NSCFString, so
    this is puzzling. But there you are.

    Mike
  • On 7/7/08 11:25 PM, Hamish Allan said:

    > On Mon, Jul 7, 2008 at 6:33 PM, Sean McBride <sean...>  wrote:
    >
    >> There's always the lower-level:
    >>
    >> objc_collect (OBJC_EXHAUSTIVE_COLLECTION |
    >> OBJC_WAIT_UNTIL_DONE);
    >
    > If this were called from the main thread, would it guarantee that the
    > collector run without interruption, given that user input would be
    > suspended?

    From it's name, I would think so.  But I can't find docs for these
    functions (only 4 google hits).  Perhaps someone on the obj-c list would
    know...

    --
    ____________________________________________________________
    Sean McBride, B. Eng                <sean...>
    Rogue Research                        www.rogue-research.com
    Mac Software Developer              Montréal, Québec, Canada
  • On Jul 11, 2008, at 12:53 , Michael Ash wrote:

    >
    > Seems that NSString and NSMutableString are just faster at everything.
    > In all cases, the cost of an extra retain/release for them is still
    > roughly 50% of the cost of an alloc/init/retain. Here are my raw
    > numbers, times in nanoseconds:
    >
    > [timings snipped]

    >
    > I have no explanation as to why NSMutableString is so much slower at
    > everything.

    I do ;-)

    > They both end up creating an instance of NSCFString, so
    > this is puzzling. But there you are.

    These sorts of tests can be tricky to get right:  of the two, only the
    NSMutableString test is creating strings for you, your NSString test
    is just returning the same empty+constant string over and over (you
    can verify this by printing the object pointer).  You can control for
    this, for example by using -initWithCString: with a buffer whose
    contents change slightly each time.

    These are the numbers I get:

    NSMutableString alloc+init(WithCString:) + release(/dealloc) :  1246
    nanoseconds
    NSMutableString alloc+init(WithCString:) + release(/dealloc) +retain/
    release:  1365 nanoseconds
    NSString alloc+init(WithCString:) + release(/dealloc) :  418
    nanoseconds
    NSString alloc+init(WithCString:) + release(/dealloc) +retain/
    release:  527 nanoseconds

    In both cases, the extra retain/release costs  around 110ns, so
    allocation is 4x to 10x slower.  (I have to admit the 4x surprises me
    a little bit, in my experience that value was higher).

    The reason NSMutableString is slower is that it needs to allocate an
    extra buffer.  NSString is pretty heavily optimized, it takes just as
    long to allocate as does an empty NSObject:

    NSObject alloc+init+release(/dealloc):  417 ns

    But of course the retain/release for NSObject is much more expensive:

    NSObject alloc+init+release(/dealloc) + retain/release:  740 ns
    NSObject alloc+init+release(/dealloc) + 2x retain/release:  1026 ns

    So as I said:  (a) object allocation slowest (b) out-of-band retain
    count slow (c) inline retain count much faster than either.

    Regards,

    Marcel
  • On Sat, Jul 12, 2008 at 2:25 AM, Marcel Weiher <marcel.weiher...> wrote:
    >
    > On Jul 11, 2008, at 12:53 , Michael Ash wrote:
    >
    >>
    >> Seems that NSString and NSMutableString are just faster at everything.
    >> In all cases, the cost of an extra retain/release for them is still
    >> roughly 50% of the cost of an alloc/init/retain. Here are my raw
    >> numbers, times in nanoseconds:
    >>
    >> [timings snipped]
    >
    >>
    >> I have no explanation as to why NSMutableString is so much slower at
    >> everything.
    >
    > I do ;-)
    >
    >> They both end up creating an instance of NSCFString, so
    >> this is puzzling. But there you are.
    >
    > These sorts of tests can be tricky to get right:  of the two, only the
    > NSMutableString test is creating strings for you, your NSString test is just
    > returning the same empty+constant string over and over (you can verify this
    > by printing the object pointer).  You can control for this, for example by
    > using -initWithCString: with a buffer whose contents change slightly each
    > time.
    >
    > These are the numbers I get:
    >
    > NSMutableString alloc+init(WithCString:) + release(/dealloc) :  1246
    > nanoseconds
    > NSMutableString alloc+init(WithCString:) + release(/dealloc)
    > +retain/release:  1365 nanoseconds
    > NSString alloc+init(WithCString:) + release(/dealloc) :  418 nanoseconds
    > NSString alloc+init(WithCString:) + release(/dealloc) +retain/release:  527
    > nanoseconds
    >
    > In both cases, the extra retain/release costs  around 110ns, so allocation
    > is 4x to 10x slower.  (I have to admit the 4x surprises me a little bit, in
    > my experience that value was higher).
    >
    > The reason NSMutableString is slower is that it needs to allocate an extra
    > buffer.  NSString is pretty heavily optimized, it takes just as long to
    > allocate as does an empty NSObject:
    >
    > NSObject alloc+init+release(/dealloc):  417 ns
    >
    > But of course the retain/release for NSObject is much more expensive:
    >
    > NSObject alloc+init+release(/dealloc) + retain/release:  740 ns
    > NSObject alloc+init+release(/dealloc) + 2x retain/release:  1026 ns
    >
    >
    > So as I said:  (a) object allocation slowest (b) out-of-band retain count
    > slow (c) inline retain count much faster than either.

    Well that all makes sense, thanks.

    One further question for you, if you will. I got curious and went off
    hunting for the inline refcount in NSCFString but couldn't find it.
    The closest I got was the '_rc' field in CFRuntimeBase, but it's
    inside an #if __LP64__ clause, so we don't get it in normal code these
    days. The __CFString struct doesn't seem to have any place to store a
    refcount. Am I missing something here, or does it only have an inline
    refcount in 64-bit?

    Mike
  • On Jul 12, 2008, at 8:25 AM, Michael Ash wrote:

    > On Sat, Jul 12, 2008 at 2:25 AM, Marcel Weiher <marcel.weiher...>
    >> wrote:
    >>
    >> So as I said:  (a) object allocation slowest (b) out-of-band
    >> retain count
    >> slow (c) inline retain count much faster than either.
    >
    > Well that all makes sense, thanks.

    You're very welcome :-)

    > One further question for you, if you will. I got curious and went off
    > hunting for the inline refcount in NSCFString but couldn't find it.

    Yeah, NSCFString doesn't actually declare any of "its" instance
    variables, which are actually those of the private CFString structure
    it uses.

    > The closest I got was the '_rc' field in CFRuntimeBase, but it's
    > inside an #if __LP64__ clause, so we don't get it in normal code these
    > days. The __CFString struct doesn't seem to have any place to store a
    > refcount. Am I missing something here, or does it only have an inline
    > refcount in 64-bit?

    No, the inline reference count is available for all CF objects, and
    not limited to 64 bit.

    What version of the structure are you looking at?  For example  http://www.cocoadev.com/index.pl?HowToCreateTollFreeBridgedClass
      shows this version, which matches what I got from opensource.apple.com

    /* All CF "instances" start with this structure.  Never refer to
      * these fields directly -- they are for CF's use and may be added
      * to or removed or change format without warning.  Binary
      * compatibility for uses of this struct is not guaranteed from
      * release to release.
      */
    typedef struct __CFRuntimeBase {
        void *_isa;
    #if defined(__ppc__)
        uint16_t _rc;
        uint16_t _info;
    #elif defined(__i386__)
        uint16_t _info;
        uint16_t _rc;
    #else
    #error unknown architecture
    #endif
    } CFRuntimeBase;

    Cheers,

    Marcel
  • Marcel Weiher wrote:
    >
    > uint16_t _rc;

    Oh, the horror of it all!  Only 65,535 objects can retain a string!!!
    What am I to do when I model all the citizens of the US voting for just
    one presidential candidate in November, and the retain count overflows?!?!

    Okay, that's not a real concern for me, but that's the kind of thing
    where you find hanging chads are the least of your worries.  Didn't
    someone famous once say, "Who needs more than 64 KB in a computer?" ;-)
  • On Sat, Jul 12, 2008 at 1:24 PM, Marcel Weiher <marcel.weiher...> wrote:
    > No, the inline reference count is available for all CF objects, and not
    > limited to 64 bit.
    > What version of the structure are you looking at?  For example
    > http://www.cocoadev.com/index.pl?HowToCreateTollFreeBridgedClass shows this
    > version, which matches what I got from opensource.apple.com

    I was looking at:

    http://www.opensource.apple.com/darwinsource/projects/apsl/CF-476.10/CFRunt
    ime.h


    It defines:

    typedef struct __CFRuntimeBase {
        uintptr_t _cfisa;
        uint8_t _cfinfo[4];
    #if __LP64__
        uint32_t _rc;
    #endif
    } CFRuntimeBase;

    I guess this isn't the right one, then.

    To Gary, about 16-bit refcounts, I'd imagine that there's some logic
    in there where if you hit 0xFFFF, it considers that to be a flag to
    use an external refcount instead, at the cost of some speed.

    Mike
  • It looks like it was expanded in 10.5, then, but 10.4 and prior use the
    16-bit variables.  Regardless of whether it handles overflows or not, my
    little joke was meant to illustrate that too many developers (generally
    speaking) think that resources (generally speaking) are unlimited and
    put no thought into what to do if an error occurs or how to handle
    requirements that are potentially huge.  I'm not saying those on this
    list think this way, but when people say that there's no cost to this or
    that approach, I just want to say, "Get thee a computer science degree!"

    Michael Ash wrote:
    > On Sat, Jul 12, 2008 at 1:24 PM, Marcel Weiher <marcel.weiher...> wrote:
    >> No, the inline reference count is available for all CF objects, and not
    >> limited to 64 bit.
    >> What version of the structure are you looking at?  For example
    >> http://www.cocoadev.com/index.pl?HowToCreateTollFreeBridgedClass shows this
    >> version, which matches what I got from opensource.apple.com
    >
    > I was looking at:
    >
    > http://www.opensource.apple.com/darwinsource/projects/apsl/CF-476.10/CFRunt
    ime.h

    >
    > It defines:
    >
    > typedef struct __CFRuntimeBase {
    > uintptr_t _cfisa;
    > uint8_t _cfinfo[4];
    > #if __LP64__
    > uint32_t _rc;
    > #endif
    > } CFRuntimeBase;
    >
    > I guess this isn't the right one, then.
    >
    > To Gary, about 16-bit refcounts, I'd imagine that there's some logic
    > in there where if you hit 0xFFFF, it considers that to be a flag to
    > use an external refcount instead, at the cost of some speed.
    >
    > Mike
  • On Jul 12, 2008, at 13:42 , Michael Ash wrote:

    >
    > http://www.opensource.apple.com/darwinsource/projects/apsl/CF-476.10/CFRunt
    ime.h

    >
    > typedef struct __CFRuntimeBase {
    > uintptr_t _cfisa;
    > uint8_t _cfinfo[4];
    > #if __LP64__
    > uint32_t _rc;
    > #endif
    > } CFRuntimeBase;
    >
    > I guess this isn't the right one, then.

    If you look at the corresponding CFRuntime.c file, I think you'll find
    that there is logic there for treating part of the _cfinfo as a retain
    count (look for _CFRetain() )

    > To Gary, about 16-bit refcounts, I'd imagine that there's some logic
    > in there where if you hit 0xFFFF, it considers that to be a flag to
    > use an external refcount instead, at the cost of some speed.

    Yep.  Inline reference counts are an optimization, as such they need
    to cater to the common case, not to the outliers (which still have to
    be handled correctly, but don't need to be as fast).

    Cheers,

    Marcel
  • On Sun, Jul 13, 2008 at 12:29 PM, Marcel Weiher <marcel.weiher...> wrote:
    >
    > On Jul 12, 2008, at 13:42 , Michael Ash wrote:
    >
    http://www.opensource.apple.com/darwinsource/projects/apsl/CF-476.10/CFRunt
    ime.h

    >>
    >> typedef struct __CFRuntimeBase {
    >> uintptr_t _cfisa;
    >> uint8_t _cfinfo[4];
    >> #if __LP64__
    >> uint32_t _rc;
    >> #endif
    >> } CFRuntimeBase;
    >>
    >> I guess this isn't the right one, then.
    >
    > If you look at the corresponding CFRuntime.c file, I think you'll find that
    > there is logic there for treating part of the _cfinfo as a retain count
    > (look for _CFRetain() )

    So it does. That'll teach me to take CF structures at face value. Thanks.

    Mike
previous month june 2008 next month
MTWTFSS
            1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30            
Go to today