Let's merge managed object models!
userpic
[info]chanson
There was a question recently on Stack Overflow asking how to handle cross-model relationships in managed object models. Now, the poster wasn't asking about how to handle relationships across persistent stores — he was asking how to handle splitting a model up into pieces such that the pieces could be recombined.

It turns out that this is somewhat straightforward to do using Core Data. Let's say you have a simple model with Song and Artist entities. I'll write it out here in a pseudo-modeling language for ease of reading:
MusicModel = {
    Song = {
        attribute title : string;
        attribute duration : float;
        to-one-relationship artist : Artist,
            inverse : songs,
            delete-rule : nullify;
        userInfo = { };
    };

    Artist = {
        attribute name : string;
        to-many-relationship songs : Song,
            inverse : artist,
            delete-rule : cascade;
        userInfo = { };
    };
};
Now let's say you want to split this up into two models, where Song is in one and Artist is in the other. You could just try and create two xcdatamodel files in Xcode, one with each entity, and wire the relationships together after loading them and merging them with +[NSManagedObjectModel modelByMergingModels:]. Except that won't work: Relationships with no destination entity won't be compiled by the model compiler.

What else might you try? You could try just putting dummy entities in for relationships to point to. However, merging models will fail then, because NSManagedObjetModel won't merge models that have entity name collisions.

It turns out, though, that you can merge models very easily by hand, by taking advantage of the way Core Data's model-description objects handle the NSCopying protocol. All you have to do is create your destination model, loop through every entity in each of your source models, and copy every entity that you haven't tagged as a stand-in using a special key in their userInfo dictionary.

Why does this work? The trick is that before you tell a persistent store coordinator to use a model, that model is mutable and references relationship destination entities and inverse relationships by name. So you can have only a minimal representation of Artist in one model, and a minimal representation of Song in another model:
SongModel = {
    Song = {
        attribute title : string;
        attribute duration : float;
        to-one-relationship artist : Artist,
            inverse : songs,
            delete-rule : nullify;
        userInfo = { };
    };

    Artist = {
        /* Note no attributes. */
        to-many-relationship songs : Song,
            inverse : artist,
            delete-rule : cascade;
        userInfo = { IsPlaceholder = YES; };
    };
};

ArtistModel = {
    Song = {
        /* Note no attributes. */
        to-one-relationship artist : Artist,
            inverse : songs,
            delete-rule : nullify;
        userInfo = { IsPlaceholder = YES; };
    };

    Artist = {
        attribute name : string;
        to-many-relationship songs : Song,
            inverse : artist,
            delete-rule : cascade;
        userInfo = { };
    };
};
Then, when you write some code to combine them, the merged model will wind up with the full definition of Song and the full definition of Artist. Here's an example of the code you might write to do this:
- (NSManagedObjectModel *)mergeModelsReplacingDuplicates:(NSArray *)models {
    NSManagedObjectModel *mergedModel = [[[NSManagedObjectModel alloc] init] autorelease];

    // General strategy:  For each model, copy its non-placeholder entities
    // and add them to the merged model. Placeholder entities are identified
    // by a MyRealEntity key in their userInfo (which names their real entity,
    // though their mere existence is sufficient for the merging).

    NSMutableArray *mergedModelEntities = [NSMutableArray arrayWithCapacity:0];

    for (NSManagedObjectModel *model in models) {
        for (NSEntityDescription *entity in [model entities]) {
            if ([[[entity userInfo] objectForKey:@"IsPlaceholder"] boolValue]) {
                // Ignore placeholder.
            } else {
                NSEntityDescription *newEntity = [entity copy];
                [mergedModelEntities addObject:newEntity];
                [newEntity release];
            }
        }
    }

    [mergedModel setEntities:mergedModelEntities];

    return mergedModel;
}
This may seem like a bit of overhead for this simple example. The critical thing to see above is that only that which is necessary for model consistency is in the placeholder entities. Thus you only need the inverse relationship from Song to Artist in ArtistModel. Say you wanted to add a Picture entity related to the Artist entity — you don't have to add that to both models, only to ArtistModel. The benefit of this method for merging models should then be pretty apparent: It gives you the ability to make your model separable, just like your code.

Not it!
userpic
[info]chanson
I didn't write Carrie's Dots — but I did download it!

It was written by Dr. Chris Hanson, a Chris Hanson who's evidently still in the mid-South. Maybe the next time I get a chance to visit [info]realgreendragon in Mississippi, we'll get to meet up!

Always use notification name globals, not literal strings!
userpic
[info]chanson
What's wrong with this code?
- (void)registerForNotificationsFromTask:(NSTask *)task ( {
    [[NSNotificationCenter defaultCenter]
        addObserver:self
           selector:@selector(taskDidTerminateNotification:)
               name:@"NSTaskDidTerminateNotification"
             object:task];
}
If you didn't notice anything wrong, look again.

What's bad about this is that it's passing a string literal instead of a global variable for the notification name. The code should really look like this:
- (void)registerForNotificationsFromTask:(NSTask *)task ( {
    [[NSNotificationCenter defaultCenter]
        addObserver:self
           selector:@selector(taskDidTerminateNotification:)
               name:NSTaskDidTerminateNotification
             object:task];
}
Isn't that better? (Among other things, Xcode will offer to complete the NSTaskDidTerminateNotification global variable for you — unlike the contents of a string literal.)

This is a bug that often results from copying & pasting from documentation into code. "I need this notification, it needs to be a string, so I'll just put @"" around it." The type of a notification name is, in fact, NSString but you don't have to pass a string literal for that. Instead, pass the global variable that exists for each notification name and you're guaranteed that the right thing will happen.

If you're creating and using your own notifications, be sure to follow the Cocoa pattern and create your own global variables containing the notification name. Otherwise you're at the mercy of typos within string literals.

Update: Sanjay Samani helpfully pointed out that by constant string I meant string literal. Thanks, Sanjay! I've updated my post with this correction. (Not sure where my memory was…)

No NSCoder night for me tonight!
userpic
[info]chanson
I'd like to make it to NSCoder Night tonight, but I just have too much else to do today — again.

I won't be seeing anyone at NSCoder Night next week, either, as I'll be in San Francisco for the Apple Worldwide Developers Conference! If you're attending, be sure to find me and say hi!

No NSCoder night for me tonight!
userpic
[info]chanson
I'd like to make it to NSCoder Night tonight, but I just have too much else to do today. Hopefully I'll see everyone there next week!

CocoaHeads Silicon Valley at Apple on Thursday, May 15, 2008
userpic
[info]chanson
The next CocoaHeads Silicon Valley meeting will be on Thursday, May 15, 2008 — that's tonight! — at 7:30 in the Garage 1 meeting room at Infinite Loop 4 on Apple's main campus. That's inside and upstairs at Apple's Infinite Loop campus in Cupertino. See the web site for directions.

This month's main presentation is on the Best of Both Worlds — an introduction to Cocoa development by Scott Stevenson.
This talk is a combination of an introduction to Cocoa, as well as a series of advanced tips and tricks that even relatively experienced Mac programmers may not know about.

The idea here is that we want to give all of the people who are new to Mac and iPhone development a chance to get started, but we also want to do something special for our advanced programmers. So rather than choosing one, we're just going to go ahead and do both.
Joel Norvell will also be presenting on how to edit PDF forms using Cocoa — he's done a lot of work with PDFKit and Cocoa, and I'm looking forward to learning from him.

Thanks a ton to Scott Stevenson, Steve Zyszkiewicz, Michael Jurewitz and Joar Wingfors for organizing!

In general, at a CocoaHeads meeting we do some introductions, have a presentation including Q&A time with the presenter, and then have an open Q&A and demo-your-cool-app period. After the meeting there's more independent mingling and discussion until it's time to go at 9:30. Often a subset of the meeting moves to BJ's Brewhouse in Cupertino, which is right in front of the Apple Infinite Loop campus on De Anza Boulevard.

No NSCoder Night for me tonight!
userpic
[info]chanson
I'd like to make it to NSCoder Night tonight, but I won't be able to as I'll be looking at a new place. Wish me luck, and have fun everybody!

Copyright canonical form
userpic
[info]chanson
One thing that's nagged at me lately has been the series of applications I've seen lately with copyright statements that appear to be from the Bizarro universe. I don't mean that they have weird license restrictions; rather, they have a copyright statement in their standard About panel that's formatted strangely. It's a minor pet peeve to be sure, but it's a simple thing to get right and getting it wrong looks silly.

Note that the following is not legal advice on asserting or protecting your copyright — you'll have to go to a lawyer for that — it's just a suggestion on how to concisely format your statement that your work is covered under copyright.

In a Cocoa application, the standard About panel will show the copyright statement specified under the NSHumanReadableCopyright of its Info.plist file. This should generally be of the form
Copyright © «YEARS» «HOLDERS». All rights reserved.
where «YEARS» represents the individual year, set of years, or range of years during which the application was authored and «HOLDERS» represent the authors of the application.

Thus if I were to start writing an application in 2007 and finish it in 2008, I would put
Copyright © 2007–2008 Chris Hanson. All rights reserved.
in the NSHumanReadableCopyright key of its Info.plist file. (Yes, that's an en-dash between the years, option-hyphen gets you one.) It wouldn't have the year at the end, or random commas after things, or random abbreviations. Just one simple statement.

Someday I'll figure out how to add
Copyright © 2002–2008 Chris Hanson. All rights reserved.
to the bottom of my weblog, too. Hopefully in such a way that I can actually update it easily when the year rolls over…

CocoaHeads Silicon Valley at Apple on Thursday, April 17, 2008
userpic
[info]chanson
The next CocoaHeads Silicon Valley meeting will be on Thursday, April 17, 2008 — that's tonight! — at 7:30 in the De Anza 3 auditorium at Apple. That's just inside the south side of De Anza 3, right across Mariani Avenue from Apple's Infinite Loop campus in Cupertino. See the web site for directions.

This month's presentation is all about designing and implementing your human interface. User experience and human interface design are critical for Mac OS X software to get right. To that end, there's even going to be a UI makeover as Scott describes in his post on the meeting!

Thanks a ton to Scott Stevenson, Steve Zyszkiewicz, Michael Jurewitz and Joar Wingfors for organizing!

In general, at a CocoaHeads meeting we do some introductions, have a presentation including Q&A time with the presenter, and then have an open Q&A and demo-your-cool-app period. After the meeting there's more independent mingling and discussion until it's time to go at 9:30. Often a subset of the meeting moves to BJ's Brewhouse in Cupertino, which is right in front of the Apple Infinite Loop campus on De Anza Boulevard.

Xcode unit testing articles updated
userpic
[info]chanson
I've updated my posts on creating and debugging unit tests for for Cocoa applications and frameworks for Xcode 3.0. If you have any additional questions about how unit testing works in Xcode, don't hesitate to ask!

Here are pointers to the updated articles:

The most significant change is in debugging Cocoa application unit tests. There's an extra environment variable you'll need to add to your executable, DYLD_FALLBACK_FRAMEWORK_PATH, before you'll be able to debug it with Xcode 3.0. This is necessary because Xcode takes advantage of a new linker feature — runpath-relative install names — in order to support moving and renaming your Developer folder and having multiple versions of Xcode installed.

Dr. Kiki teaches us to say "Theobroma Cacao"
userpic
[info]chanson
Dr. Kiki Sanford teaches us on her latest Food Science podcast about chocolate how to definitively pronounce Theobroma Cacao.

CocoaHeads Silicon Valley at Apple on Thursday, February 7, 2008
userpic
[info]chanson
The next CocoaHeads Silicon Valley meeting will be on Thursday, February 7, 2008 — that's tonight! — at 7:30 in the De Anza 3 auditorium at Apple. That's just inside the south side of De Anza 3, right across Mariani Avenue from Apple's Infinite Loop campus in Cupertino. See the web site for directions.

This month's presentation is by Scott Stevenson about Core Animation, one of the great new technologies available to Cocoa developers in Mac OS X 10.5 Leopard.

Thanks a ton to Scott Stevenson and Steve Zyszkiewicz for doing the organizing!

In general, at a CocoaHeads meeting we do some introductions, have a presentation including Q&A time with the presenter, and then have an open Q&A and demo-your-cool-app period. After the meeting there's more independent mingling and discussion until it's time to go at 9:30. Often a subset of the meeting moves to BJ's Brewhouse in Cupertino, which is right in front of the Apple Infinite Loop campus on De Anza Boulevard.

launchd: Better than sliced bread!
userpic
[info]chanson
A while back, I mentioned that launchd was pretty cool. But what’s so great about it?

Simply put, launchd is what makes it easy to get tasks launched on-demand on Mac OS X 10.4 and later. It obviates lots of different archaic Unix infrastructure — init.d, cron, inetd — in favor of a single self-consistent and easy to use mechanism. Dave Zarzycki’s post Where to begin? describes the launchd design philosophy in some depth.

Processes launched at startup on Mac OS X are managed by launchd. There’s no careful balancing of init.d or SystemStarter scripts on modern releases of the operating system. Instead, launchd jobs have property list entries in the LaunchAgents and LaunchDaemons directories in the system and local domains. Some specify that launchd should keep them alive indefinitely, others simply provide conditions under which they should be launched.

For instance, the standard finger daemon indicates that it should be run whenever something connects to the socket corresponding to the finger service; it doesn’t run all the time, nor does it rely upon a separate “network service daemon” to launch. Similarly, the Bluetooth daemon is only run only when a particular Mach port is connected to; it doesn’t need to be running all the time, and a framework interacting with it can easily just connect to it and start using it, without worrying about the mechanics of launching it, ensuring only one instance is running, and so on.

Where launchd really shines is in the level of integration it lets developers offer between frameworks and daemons or agents when it comes to managing shared state.

Let’s say you’re creating a framework that manages access to a device (or any other shared resource). Only one process should actually actually interact with the device at the IOKit level with the device itself at a time. And besides, you want to provide a nice high-level Cocoa API anyway. So you need to create some sort of background process — a daemon — to actually talk to the device, and a framework that can talk to the daemon on behalf of any number of applications.

The most straightforward way to do this is to put your daemon executable within your framework, and install your framework in /Library/Frameworks. Then you can install a property list describing when launchd should launch the daemon, and in what type of environment to launch it. Since everything is going to be local to a single machine, you can use a Unix-domain socket — which, if you don’t already know, is represented via a path in the filesystem — by describing it in the Sockets key in the property list. That way, any time the framework wants to communicate with the daemon, it can just connect to the socket at that path and it will have a channel.

(You can even use the SecureSocketWithKey option to have launchd generate the path for you, and pass it to all processes via the specified environment variable. See the ssh-agent property list for an example; it's how ssh-agent is launched on-demand in Leopard.)

Your framework and daemon can then use Distributed Objects over that socket (thanks to NSSocketPort) to communicate by sending standard Objective-C messages back and forth. Just be sure to support NSCoding for any objects they exchange, and to annotate your methods with oneway, bycopy, byref and so on as appropriate to keep communication efficient. The daemon can even distinguish between clients by keeping track of which socket it’s accepted their connection on.

Wait, though — how does the daemon actually get that Unix-domain socket if launchd is what’s actually listening on it? There’s a process that the daemon needs to go through when it first starts up called launchd check-in. This process lets launchd pass the daemon information from the property list that was used to start the daemon. You just have to use the launch_msg() API described in <launch.h> to send a LAUNCH_KEY_CHECKIN string:
- (void)checkInWithLaunchd {
    launch_data_t checkinRequest = launch_data_new_string(LAUNCH_KEY_CHECKIN);
    launch_data_t checkinResponse = launch_msg(checkinRequest);
    
    switch (launch_data_get_type(checkinResponse)) {
        // launchd will return an errno if an error occurs
        case LAUNCH_DATA_ERRNO: {
            int error = launch_data_get_errno(checkinResponse);

            // handle it
        }
        break;

        // launchd will return your job's dictionary for successful checkin
        case LAUNCH_DATA_DICTIONARY: {
            // The dictionary isn't an exact copy of your plist.
            // It has file descriptors substituted for socket descriptions,
            // Mach ports substituted for Mach port descriptions, and so on.
        }
        break;

        // launchd returned some other bad response
        default: {
            // handle it
        }
        break;
    }
    
    launch_data_free(checkinRequest);
    launch_data_free(checkinResponse);
}
The trick is that the dictionary given back by launchd upon check-in is not simply a literal copy of what’s specified in the job’s property list! Instead, where you had specified in the property list how you wanted launchd to listen on a socket, upon check-in launchd will actually pass the socket itself as a file descriptor. You can retrieve it easily using the launch_data_get_fd() function, accept the connection (on another socket), and continue listening.

For a bunch more detailed information on how to leverage launchd in your own designs, see the launchd(8) and launchd.plist(5) manual pages, as well as Tech Note TN2083: Daemons and Agents and the System Startup Programming Topics guide. There’s an example daemon called SampleD which illustrates how launchd check-in works, and of course you can browse the source code to launchd — it’s under the Apache License, Version 2.0 — at the launchd site on Mac OS forge.

CocoaHeads Silicon Valley at Apple on Thursday, January 10, 2008
userpic
[info]chanson
The next CocoaHeads Silicon Valley meeting will be on Thursday, January 10, 2008 — that's tonight! — at 7:30 in the Garage 1 conference room at Apple. That's just upstairs from the Town Hall auditorium in Infinite Loop 4, right around the loop at Apple's campus in Cupertino. See the web site for directions.

This month's presentation is by Ted Bonkenburg of Google, who is going to be talking about the MacFUSE project for creating user-space filesystems for Mac OS X!

Thanks a ton to Scott Stevenson and Steve Zyszkiewicz for doing the organizing!

In general, at a CocoaHeads meeting we do some introductions, have a presentation including Q&A time with the presenter, and then have an open Q&A and demo-your-cool-app period. After the meeting there's more independent mingling and discussion — especially at BJ's Brewhouse, which is right along De Anza Blvd. in front of Apple's Infinite Loop 1 headquarters building.

Reminder: NSCoder Night tonight!
userpic
[info]chanson
This is just a gentle reminder that NSCoder Night is tonight, 7PM to 9PM!

NSCoder Night Silicon Valley is at Orchard Valley Coffee in Campbell, CA.

NSCoder Night Washington DC is at Panera Bread in Tyson's Corner.

And finally, cocoaCodingNight in St. Louis, Missouri is tonight at the Kaldi's Coffee in Kirkwood, MO from 4:30PM through 10PM. It's tonight rather than Thursday due to the Thanksgiving holiday.

If you're interested in hanging out, but can't make it in person, there's always IRC. Get the latest build of Colloquy and join channel #nscodernight on FreeNode.

Reminder: NSCoder Night tonight!
userpic
[info]chanson
This is just a gentle reminder that NSCoder Night is tonight, 7PM to 9PM!

NSCoder Night Silicon Valley is at Orchard Valley Coffee in Campbell, CA.

NSCoder Night Washington DC is at Panera Bread in Tyson's Corner.

If you're interested in hanging out, but can't make it in person, there's always IRC. Get the latest build of Colloquy and join channel #nscodernight on FreeNode.

CocoaHeads Silicon Valley at Apple on Thursday, November 8, 2007
userpic
[info]chanson
The next CocoaHeads Silicon Valley meeting will be on Thursday, November 8, 2007 — that's tonight! — at 7:30 in the Town Hall auditorium at Apple. That's just inside Infinite Loop 4, right around the loop at Apple's campus in Cupertino. See the web site for directions.

This month's presentation is by Deric Horn, Application Frameworks Evangelist at Apple. He's going to take us on a tour through what's new in Cocoa in Leopard!

Thanks a ton to Scott Stevenson and Steve Zyszkiewicz for doing the organizing!

In general, at a CocoaHeads meeting we do some introductions, have a presentation including Q&A time with the presenter, and then have an open Q&A and demo-your-cool-app period. After the meeting there's more independent mingling and discussion.

When we haven't had a presentation or two lined up, we've also had some great "unmeetings" (in the spirit of "unconferences") where we came up with an agenda for the core of the meeting on the fly by writing down topics and questions on our room's whiteboard and talking about each one of them for a few minutes. It worked really well.

Update: Every CocoaHeads gets bigger. Tonight's meeting has 59 people so far, which is nearly a 20% increase over last month's meeting!

Reminder: NSCoder Night tonight!
userpic
[info]chanson
This is just a gentle reminder that NSCoder Night is tonight, 7PM to 9PM!

NSCoder Night Silicon Valley is at Orchard Valley Coffee in Campbell, CA.

NSCoder Night Washington DC is at Panera Bread in Tyson's Corner.

If you're interested in hanging out, but can't make it in person, there's always IRC. Get the latest build of Colloquy and join channel #nscodernight on FreeNode.

Reminder: NSCoder Night is tonight!
userpic
[info]chanson
This is just a gentle reminder that NSCoder Night starts tonight, 7PM to 9PM!

NSCoder Night Silicon Valley is at Orchard Valley Coffee in Campbell, CA.

NSCoder Night Washington DC is at Panera Bread in Tyson's Corner.

And, thanks to the magic of time zones, NSCoder Night Washington DC will actually be the first NSCoder Night get-together!

If you're interested in hanging out, but can't make it in person, there's always IRC. Get the latest build of Colloquy and join channel #nscodernight on FreeNode. (And also consider joining some of the other channels there, such as #macdev and #macsb, where you can meet other fellow Mac developers!)

New in Leopard: Objective-C Garbage Collection!
userpic
[info]chanson
That's right, Objective-C has garbage collection now in Leopard! And it's not just a conservative collector like the Boehm collector GNUstep supports, either; it's a full-fledged multi-threaded, generational garbage collector! (Though due to the limitations imposed by C semantics, it isn't a copying collector.)

Practically speaking, this means that you don't have to worry about memory management in Cocoa applications the way you used to, if you can require Leopard. You're not entirely freed from worrying about memory management, of course: For one thing, you need to be careful about multithreading, as any -finalize methods you implement will be run in a collector thread. For another, you can still easily over-root objects that should be collectable; this is a common cause of memory leaks in garbage-collected applications, and should be familiar to Java developers.

To address the over-rooting problem, zeroing weak references have been introduced to Objective-C. By prefixing an instance variable declaration with __weak, you tell the garbage collector that if it's the only reference to an object that the object should be considered collectable. And when the object is collected, the instance variable will be set to nil for you!

This works for any number of weak references to a particular collectable object. So long as there are no strong references to an object — which are the regular kind of non-weak references you're used to — an object is eligible for collection no matter how many weak references there are to it. Thus I can create an object, assign it to a thousand different __weak instance variables, and then not assign it to any regular instance variables, and it will eventually be collected and all of those __weak instance variables will be "simultaneously" changed to nil.

Because objects are commonly stored in collections — for example, in static NSMutableDictionary instances that are used as caches — the Foundation framework also has some additional classes that can support weak references. There's NSPointerArray, NSHashTable, and NSHashMap:
  • NSPointerArray is a lot like NSMutableArray, but it can contain arbitrary pointers (not just objects) and also supports nil elements and can hold zeroing weak references.
  • NSHashTable is a lot like NSMutableSet, but it can hold zeroing weak references.
  • NSHashMap is a lot like NSMutableDictionary, but it can hold zeroing weak references for either its keys or its values, and doesn't have to copy its keys.
And most importantly, the behavior of an instance of any of these classes can be customized by creating an instance of the NSPointerFunctions class: That's how all of the above collection classes implement their equality comparison, hashing, and weak-reference behavior. See the header or documentation for details.

One other thing you can do is create hybrid applications and frameworks. As Scott Stevenson shows in his Objective-C 2.0 tutorial, you can build an application or framework such that garbage collection is either supported or required. For frameworks this is particularly useful; after all, you may want to use a framework in both a GC and in a non-GC application. By building it as GC-supported rather than GC-required, you can create a single framework binary that can be used in either.

How does this work? It's simple: When running under GC, the Objective-C runtime will "eat" all -retain, -release and -autorelease messages such that objects never receive them, and objects will never be sent a -dealloc. However, you can still write your code such that it has all of the proper non-GC memory management too. Then when you build it GC-supported, if it's loaded into a process that is using the collector it will use GC, but if it's loaded into a process using reference counting it will behave correctly there too.

There are a couple of things that you can't do with GC. One is that you can't use a non-GC framework in a GC application; an entire process has to be either GC or non-GC. Thus if you use third-party frameworks that haven't been made hybrid, GC-supporting frameworks, you'll need to stick with reference counting until the frameworks are updated.

The other thing you can't do with GC is write code that makes use of GC but that runs on pre-Leopard operating systems. Code built for GC uses certain runtime calls that are not present in earlier versions of the Objective-C runtime, and unfortunately this means that code built for GC requires the Leopard version of the runtime to execute. Leopard contains so many other compelling developer and end-user features, and other Objective-C 2.0 features also require Leopard, so in practice this probably won't be such a huge deterrent.

Update: At jcr's request, I've expanded the discussion of weak references to make it clear that the number of weak references to an object doesn't matter; as long as there are no strong references to it (whether in instance variables, reachable via globals, or on the stack or in registers), it will be eligible for collection.