Archive for the 'Cocoa' Category

Multiple methods found

Friday, March 14th, 2008

I wrote some code like this:

[[QTMovie movieNamed:sound error:NULL] play];

and Xcode gave me three warnings:

  • GrowlApplicationController.m:537: warning: multiple methods named ‘-play’ found

    • /Developer/SDKs/MacOSX10.4u.sdk/System/Library/Frameworks/AppKit.framework/Headers/NSSound.h:47: warning: using ‘-(BOOL)play
    • /Developer/SDKs/MacOSX10.4u.sdk/System/Library/Frameworks/QTKit.framework/Headers/QTMovie.h:340: warning: also found ‘-(void)play

All warnings are bad, but these are even worse: The compiler is telling me that it's picked the wrong method. I'm creating a QTMovie, so I want it to use -[QTMovie play] (the one that returns void), but it picked -[NSSound play] (the one that returns BOOL).

Now, in this case, it won't matter, thanks to Obj-C's dynamism—all it will do is send a play message to the object, and the object will use whatever play method it knows about. In other words, despite what the compiler is telling me, the program will actually call the correct method (the one in QTMovie).

But this could be a lot worse. If I'm expecting, say, a structure type, and the compiler picks a method that returns a scalar type such as int or float (or vice versa), the compiler will write the wrong assembly code for the message. My method will get gibberish back from the method it called, and Bad Things will follow.

The reason for this  problem is that +movieNamed:error: returns an id—not a QTMovie * specifically. For all the compiler knows, that method returns an NSSound *, or even an NSUserDefaultsController *.

Since the compiler doesn't know what type the method returns, it must pick one from all the methods by that name that it knows about. You can't rely on it picking any particular one, and I could only guess as to how it makes its choice. For all I know, it's blind chance.

The ugly and naïve solution would be to tell the compiler what type we're expecting by casting it:

[(QTMovie *)[QTMovie movieNamed:sound error:NULL] play];

Of course, besides being ugly, this specific code still has the problem of not accepting an error object. What I should do, and will do, is accept the error object and stash the returned movie in a variable, so that I can test it and then either report the error or play the movie.

NSError *error = nil;
QTMovie *movie = [QTMovie movieNamed:soundName error:&error];
if (movie)
    [movie play];
else
    [NSApp presentError:error];

This code is longer, but it will always use the correct method and it doesn't ignore the error. (You may also have noticed that I change the previously-misleading name of the name variable from sound to soundName.)

Weirdest compiler warning I’ve ever seen

Saturday, February 9th, 2008

Here's the code:

[[GrowlApplicationController sharedController] setQuitAfterOpen:YES];

And here's the warning:

Core/Source/GrowlApplicationBridgePathway.m:43: warning: ‘GrowlPreferencesController’ may not respond to ‘-setQuitAfterOpen:’

Wait, what? I said GrowlApplicationController, not GrowlPreferencesController!

The problem is that the compiler is recognizing “sharedController” as a class method, and assuming that I'm using the class that it knows has it—which, in this case, is GrowlPreferencesController. GrowlPreferencesController is declared in its header, which is imported by the prefix header; GrowlApplicationController, meanwhile, is declared only with @class (in another header). So I think it's assuming that GrowlApplicationController is a subclass or something.

However, +[GrowlPreferencesController sharedController] is typed as returning a GrowlPreferencesController *, so when I try to call a GrowlApplicationController method on it, it gives me that warning.

The fix is to #import "GrowlApplicationController.h" so that the compiler knows that GrowlApplicationController has a sharedController method of its own.

List process start dates

Friday, February 8th, 2008

I just wrote a test app that prints the name and start-date of every process that the Process Manager will tell it about. Here it is:

File: ListProcessStartDates-r4.tbz

It's a command-line tool, so you'll need to run it from a terminal. Alternatively, you could wrap the executable in a service using ThisService, then use the service to insert the listing into a text document.

The code to convert the start-date of each process is ugly: Process Manager provides those dates as numbers of ticks since system startup, and I currently go through NSDate to convert those to numbers of seconds since 1970-01-01. I'm fairly sure that this answer is imprecise.

I'd prefer to get the startup time as a number of ticks since some epoch (probably 1904-01-01), so that I can do a more precise conversion. If you know of a way, please post a comment.

Why ASL?

Sunday, January 20th, 2008

It occurred to me just a few minutes ago that I should have discussed this first. So, I'm retroactively designating this post as the first in the ASL series. (I'm also forging the timestamp—I actually posted this at about 16:44 PST).


Most of us probably use NSLog when we want to log some information:

void NSLog(NSString *format, ...);

Those of us unlucky enough to not have access to Cocoa (e.g., working on a UNIX utility) probably use printf/fprintf instead:

int printf(const char *restrict format, ...);
int fprintf(FILE *restrict file, const char *restrict format, ...);

Both of these are fine, but they lack a couple of things.

First off, they lack any indication of a message's importance. All messages are just lines in a file (especially with the *printf functions), and they're all on equal footing.

That problem is solved by syslog(3):

void syslog(int priority, const char *format, ...);

syslog lets you specify any of eight different levels of priority for a message:

  • Emergency
  • Alert
  • Critical
  • Error
  • Warning
  • Notice
  • Info
  • Debug

And that's great. syslog and the lesser functions NSLog and printf/fprintf have served all of us well for years.

But, for Mac OS X 10.4, Apple said “we can do better”.

And so they created the Apple System Logger.

ASL redefines a “message” to be more than just text. Now, it's a bundle of key-value pairs—what Cocoa and CF call a dictionary. All the usual properties of a message (including the timestamp, the priority, the process that sent it, and the message itself) are now simply pairs in the dictionary. This is the other thing that NSLog and printf/fprintf lack.

This has two practical benefits:

  1. You can make up your own properties. You're not limited to just the stock properties anymore; you can invent any properties you want.
  2. You can search the log more easily, now that messages are no longer just text. (This was improved in Leopard, which uses a real database for the log storage; Tiger used a text-based log file.)

And all this is very easy to use—barely more complex than using NSLog. Here's a taste:

asl_log(NULL, NULL, LOG_NOTICE, "Hello %s!", "world");

I'll tell more about the task of writing to the log (as well as list all nine of the standard properties) in the next post.

How to set up your custom view to work with services

Friday, January 4th, 2008

If you've used any Cocoa-based text editors at all, especially TextEdit, then you know that NSTextView provides free support for text-based services. Indeed, this is what makes ThisService so great: there are dozens of programs that work with text at the command line, and those programs are just as useful in the Services menu.

But what if you're not working with text? What if you're in, say, an image editor, and you want to insert a screenshot from Grab? (I don't mean to pick on Acorn; that's just the situation that gave me the idea for this post.)

This is one of those rare things that you have to write code for. However, like most things in Cocoa, it takes very little code.

I'm going to give you the test app right up front. You should download that now so you can play along at home. It accepts any image, such as from the Grab services.


The first thing you need to do is tell AppKit that you can handle certain types. You'll do this by implementing this method in one of your NSResponders—probably a custom view:

- (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)returnType

This method will be called a lot, so try not to take too long in this method. Fortunately, your implementation will probably be very simple. Here's the implementation from the test app:

Here's a diagram showing what “send” and “return” mean in the context of a service.
- (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)returnType {
    if ((sendType == nil || [sendType length] == 0U) && [[NSImage imagePasteboardTypes] containsObject:returnType])
        return self;

    return [super validRequestorForSendType:sendType returnType:returnType];
}

The sendType is the pasteboard type of data that you will send the service (its input), or nil* if it doesn't take input. The returnType is the pasteboard type of data that the service will return to you (its output), or nil* if it doesn't give output. In Leopard, they can also be UTIs; you'll be called with both the pasteboard type and the UTI.

If you can handle that combination of send and return types, then return the object that will handle it. That object, whether it's self or another object, must conform to the NSServicesRequests protocol.

If you can't handle that combination of types, call up to super, and return that.

Your return value to this method controls whether the service's menu item will be enabled or disabled. For this reason, if your custom view allows a selection (such as a range of the view's text, or a sub-shape of its image), you may want to consider the selection. In particular, you may or may not want to say you can send the service data if the user hasn't selected anything.

Note: On Leopard, your view must be in the responder chain, or this method will not be called. Thus, you may need to add code for it to become the first responder. If this method doesn't get sent to any object, or if it does but doesn't return an object, you don't get any services.

* In both cases, the documentation says empty, which would be @"", but reality disagrees.


Exchanging data

If you'll be providing input to the service (such as some text to TextEdit's “New Window Containing Selection”), then you need to implement this method:

- (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pboard types:(NSArray *)types

The name is not an accident. The idea is that, if the user can select a portion of the data, this method should consider that selection.

The types array is of pasteboard types (and probably UTIs, on Leopard—but I haven't tested this part) that the service is ready to accept from you. Don't panic if you can't provide all the types; just provide all the types that you can provide efficiently. Then, return YES.

If you can't provide any of the types in the array, return NO.


If you'll be receiving output from the service (such as a screenshot from Grab, or a result from a service such as Script Editor's “Get Result of AppleScript”), then you need to implement this method:

- (BOOL)readSelectionFromPasteboard:(NSPasteboard *)pboard

Replace the currently-selected data with the new data from the pasteboard, then return YES. If there's no data on it that you can use, return NO.

And an answer: What to do if your table view shows the whole array in every row

Sunday, December 9th, 2007

Before writing my previous post, I had asked some people on IRC for help, including David Smith, who is a fellow Adium developer. He remembered a solution, but uncertainly. One thing he said, though, proved relevant: “You have two NSTableColumns; which one is it [NSTableView] creating the content binding from?”

David was referring to the content binding of NSTableView, which I had dismissed as an artifact of an earlier design of NSTableView, or perhaps a simplified path for those people who had only one column in their table view. (I saw the mention in the docs that the table view binds it automatically to the value of one of its columns' value bindings, but didn't make any connections then.)

This latter explanation made the content binding incompatible with my app, since I had two parallel arrays of NSStrings (one array contained keys, and the other contained values). So I continued looking for a solution, and eventually gave up and wrote the post.

After I published it, Scott Anguish posted a comment on it:*

you can't have simple arrays of strings as the content of an array controller. it doesn't work. You need to have an array of objects of some sort that have an attribute, even if they're just NSDictionaries with a single attribute called "string".

I don't believe that NSArrayController has a special case for NSString, but Scott's comment got me thinking. I added a category to NSString that added a -selfString method, then added that name as the model key path in the table columns' value bindings. I was wondering, you see, whether this would be enough to satisfy NSTableColumn and NSArrayController.

It still didn't work. At this point, I had the idea to fire up F-Script Anywhere and have a look around. I did that, and was digging down to the table columns when I noticed this:

That's FSA's object browser, showing the table view. So the table view's content binding was bound to the keys array controller, with a model key path of lastMessageKeys.

That's why the keys were showing up fine—they were in the array that was in the table view's content binding. The values array was a different array with a different array controller, so NSTableView threw up its hands and simply showed the entire array's description.

This is the point that Scott came close to: NSTableView expects you to have one array of model objects—no more. My parallel arrays of NSStrings do not qualify, because only one of the array controllers can be the value of the table view's content binding.

So what role do the columns play? Well, each row in the table view (and by that, I mean *the whole row*) corresponds to one of those model objects. The value in each column is then determined by the model key path in the value binding of the corresponding NSTableColumn.

Thus, all the NSTableColumns' value bindings must be bound to the same array controller and the same controller key, and each column should be bound to a different model key path. Your columns' value bindings should look like this:

Column “Foo”Column “Bar”
Bind to: Your one array controller
Controller key: arrangedObjects (or whatever)
Model key path: fooModel key path: bar

In my case, of course, this required creating a new model class for a key-value pair, to replace the parallel arrays of NSStrings. This is a good thing, since it brings conceptual purity to my design. Remember, Cocoa is designed around MVC; I didn't have enough M in my app, which was the problem.

So, in summary, here's the fix for an NSTableColumn that shows the entire array in every row:

  1. Replace your evil parallel arrays with a model class, and a single array of instances of that class.
  2. Delete all but one of your array controllers, since you now have only one array. Update the remaining controller's model key path to point to your new array (I assume you renamed it).
  3. Bind all of the table columns to your new One True Array Controller, distinguishing the columns only by the model key path, which identifies which property you want the column to show for each model object.

Many thanks to both David and Scott for their help in discovering the root of the problem.

* Scott posted his comment on the previous post with no last name; he identified himself in a comment on this post.

I have a question

Saturday, December 8th, 2007

See also the follow-up: And an answer: What to do if your table view shows the whole array in every row.

Normally, on this blag, I give answers.

This time, I have a question.

Here's my problem:

I have a window containing a table view. The table view has two columns, labeled “Keys” and “Values”.

Each column's value binding is bound to an NSArrayController; each array controller's contentArray binding is bound to a property of my application delegate; each property's value is an NSArray of NSStrings.

The Keys column correctly has one of its strings in each row. The Values column incorrectly has its array in every row. This is the problem that has stumped me.

The object class is NSString in both array controllers. There's no Core Data in this app.

I have 12-tuple-checked the bindings in IB; they are the same between the two array controllers and between the two table columns.

The accessors are automatically generated by my accessor-generator services. Even so (having added some debug logging previously), I double-checked them, and they are the same.

I have logged the contents of both arrays in my setters. Both arrays are arrays of strings, and both contain the same number of strings.

This problem manifests on both Tiger and Leopard.

Here are screenshots of my Bindings Inspectors:

The array controllers' contentArray bindings are bound to the app delegate's lastMessageKeys and lastMessageValues properties. There is no value transformer in use. The options that are turned on are Conditionally Sets Editable and Raises For Not Applicable Keys; the options that are turned off are Always Presents Application Modal Alerts, Deletes Objects On Remove, Handles Content As Compound Value, Selects All When Setting Content, and Validates Immediately.

The table columns' value bindings are bound to the array controllers' arrangedObjects properties. There is no value transformer in use. The options that are turned on are Allows Editing Multiple Values Selection, Conditionally Sets Editable, Creates Sort Descriptor, and Raises For Not Applicable Keys; the options that are turned off are Always Presents Application Modal Alerts, Conditionally Sets Enabled, Continuously Updates Value, and Validates Immediately.

Each screenshot has two Inspectors superimposed on each other, with the upper Inspector set to half opacity; the result is that pixels that are the same look normal, whereas pixels that differ look ghostly. For your ease of reading, I knocked out the parts that differ and staggered them, so you don't have to try to make out ghostly text.

Any suggestions?

A possible solution to the stolen-focus problem

Wednesday, December 5th, 2007

Jeff Atwood posted an article today titled Please Don't Steal My Focus, about dialog boxes coming up while you're typing.

The problem, in a nutshell, is that these dialog boxes steal keyboard focus. Sometimes it's for a text field; sometimes it's just for Full Keyboard Access (i.e., a button gets the focus—usually, these are alert boxes). Regardless of what control gets keyboard focus, it gets it by stealing it from your app, which is a problem if the user is typing when that happens. Most likely, either some text will go to the wrong place, or the dialog box will beep for every keystroke; either result will annoy the user.

We've had the same problem for a long time in Adium: we'll throw up a dialog box, but the user is typing a message, and notices that half his message isn't in the inputline. We believe we've fixed most of these, but we still get complaints about this problem from time to time.

Currently, we solve the problem by simply ordering the dialog front without making it key. This works for most people, but it doesn't help people who really do need Full Keyboard Access, since you have to click on the dialog box to dismiss it (or make it key) if it isn't key. We (by which I mean Mac programmers, not just Adium) need a better solution.

I propose an addition to NSAlert. Here's how it would work:

  1. From the creation of the NSAlert instance, it watches for key events.
  2. When any key goes down, the alert resets the timer (if one is present) and sets a flag.
  3. When all keys are up, the alert creates and starts the timer (with an interval of, say, a couple of seconds).
  4. If you tell the alert to show, it checks the flag. If the flag is clear, it simply performs makeKeyAndOrderFront: like usual. If the flag is set, it only orders front, and it sets a second flag.
  5. When the timer fires, the alert clears the first flag, and if the second flag is set, makes itself key.

From the user's perspective, this results in the alert waiting a couple of seconds for them to pause in their typing before it takes keyboard focus.

Does anybody know of an existing free implementation of this, or plan to write it themselves?

Why you should use spaces before your method arguments

Monday, November 12th, 2007

You can select a rectangle that intersects the spaces. You can't do this with tabs.

Backstory: I had moved the method being called there from a file of NSCalendarDate additions to a file to NSDate additions, so I needed to remove “Calendar”, and wanted to also take out the now-extraneous alignment spacing. One rectangular edit did the job, thanks to using spaces for the alignment.

Of course, you should always use tabs for that initial indent.

An invalid property list

Monday, October 1st, 2007

If you work on an [added: a document-based] app that uses a plist-based format, you should test your plist-reading code to see how it handles an invalid (e.g., eaten by the user's dog) plist. Here's one:

<plist>
<qux></qux>
</plist>

The binary plist parser won't even touch it because it doesn't have the bplist header, the XML parser will choke on it because it contains an element that it doesn't recognize, and the OpenStep parser will choke on it because the first line doesn't end with a semicolon or backslash.

When you feed this to your app, your app should present an error message. Anything else is a bug. (UPDATE 2007-10-02: OK, maybe not. There's at least one circumstance where you can ignore it, as pointed out in the comments. I still think I'm right in most circumstances, just clearly not all.)

Report-an-Apple-Bug Friday! 70

Friday, September 14th, 2007

This bug is Borken implementation of -setNilValueForKey: in Model Object Impl Guide. It was filed on 2007-09-14 at 12:24 PDT.

(more...)

Quickie: Bindings and KVV

Tuesday, September 11th, 2007
If you want Bindings to call your Key-Value Validation methods, you need to check the Validates Immediately checkbox in the binding editor in IB. In my case, this was with an NSTableColumn's value binding in an NSTableView.

Apple Bug Friday! 69

Friday, August 31st, 2007

This bug is Member variable is not an Objective-C term. It was filed on 2007-08-10 at 12:13 PDT.

(more...)

Report-an-Apple-Bug Friday! 68

Friday, August 10th, 2007

This bug is Either you can use a sdef directly, or you cannot. It was filed on 2007-08-10 at 11:51 PDT.

(more...)

Four headers worth reading

Tuesday, August 7th, 2007
  • <Foundation/FoundationErrors.h>
  • <AppKit/AppKitErrors.h>
  • <CoreData/CoreDataErrors.h>
  • <WebKit/WebKitErrors.h>

These four headers define error codes in NSCocoaErrorDomain, which you can use in and expect from NSError instances.

There's also <QuickTime/QuickTimeErrors.h>, but its error codes are OSStatuses (usable with NSOSStatusErrorDomain), which makes them better-known to begin with.

A complete raw list of KVC accessor selector formats

Tuesday, August 7th, 2007

Implementing and using (i.e., calling) these accessors will generate appropriate KVO notifications for free.

Some are for arrays, some are for sets, some are only for mutable properties, and some are for all keys.

  • validate%s:error:
  • %sForKeyPath:
  • _%sForKeyPath:
  • get%s
  • is%s
  • _get%s
  • countOf%s
  • objectIn%sAtIndex:
  • %sAtIndexes:
  • get%s:range:
  • enumeratorOf%s
  • memberOf%s:
  • _is%s
  • set%s:
  • _set%s:
  • getPrimitive%s
  • primitive%s
  • setPrimitive%s:
  • add%sObject:
  • remove%s:
  • remove%sObject:
  • add%s:
  • intersect%s:
  • insertObject:in%sAtIndex:
  • insert%s:atIndexes:
  • removeObjectFrom%sAtIndex:
  • remove%sAtIndexes:
  • replaceObjectIn%sAtIndex:withObject:
  • replace%sAtIndexes:with%s:

Source: strings /S/L/F/Foundation.framework/Foundation.

Note that NSArrayController will not call the finer-grained array methods (e.g., insertObject:in%sAtIndex:) if you provide the basic get and set methods (%s and set%s:).

WWDC 2007 session videos are out

Monday, July 30th, 2007

If you attended WWDC, you can head over to ADC on iTunes and see what you missed.

How to make your app’s Dock tile highlight

Monday, July 23rd, 2007

So, no matter what you do, your Dock tile doesn't highlight when you drag a document onto it. You've hexadecuple-checked your CFBundleDocumentTypes list, and everything looks correct, but Dock is not co-operating and you just want to kick it.

Finally, you decide to remove the LSItemContentTypes key, and it works just fine! How can this be?


Well, since Tiger, LSItemContentTypes shuts out all the other document-type tag keys. Your CFBundleTypeOSTypes, your CFBundleTypeExtensions—all of those are ignored when LSItemContentTypes is present; it looks at nothing but the LSItemContentTypes list. Taking out LSItemContentTypes forces LS to look at your OSTypes and extensions lists instead.

More to the point, your problem is that the set of UTIs you've specified does not contain the UTI of the would-be document that you're dragging.

You could be forgiven for expecting that having com.apple.application in your set of UTIs would allow you to open applications as your documents. This is not true, because a standardly-constructed Mac OS X application is of type com.apple.application-bundle. That type does conform to com.apple.application, but it is not equal to com.apple.application, so the Dock refuses your drag with a dismissive wave of its kerchief bundle and an AIFF file of a sharp “hmph!”.

So how do you find out the true UTI of your document?

The easy way is mdls. Ask for the kMDItemContentType property:

% mdls -name kMDItemContentType Foo.app
Foo.app -------------
kMDItemContentType = "com.apple.application-bundle"

But this only works on a volume with a Spotlight index; for example, it doesn't work on disk images or RAM disks (same thing). Another way is Nicholas Riley's launch, with its -f option:

% launch -f Foo.app
Foo.app: Mac OS X application package 
        type: 'APPL'    creator: …
        architecture: PowerPC, Intel 80x86
        bundle ID: …
        version: …
        kind: Application
        content type ID: com.apple.application-bundle
        contents: 1 item
        created: 2007-07-23 07:12:31
        modified: 2007-07-23 07:12:31
        accessed: 2007-07-23 07:12:41 [only updated by Mac OS X]
        backed up: 1903-12-31 17:00:00

Either way, put that UTI into your list. Then drags will work.

In summary, the Dock checks for equality of the prospective document's UTI to the UTIs listed under LSItemContentTypes, not conformity. You need to list every UTI you support in your Info.plist, including those that conform to the ones you expect. It's either that or give up on UTIs entirely.

(Summary suggested by wootest.)

UPDATE 08:44: ssp suggested mdls instead of launch.

A simple way to make your NSLogs and NSAsserts more informative

Wednesday, June 13th, 2007

OK, so I'm not totally radio-silent. I learned about this in a WWDC session, but since it's already public API in Tiger (actually, it's a GCC extension), I can talk about it.

It's a built-in macro called __PRETTY_FUNCTION__. This is a fully-qualified human-readable signature of the function you're in. The GCC docs don't mention this part, but it even works in Objective-C, in addition to C++ and plain C. Here's a test app, containing this code:

@implementation Blah(blah)
- (void)blah {
    NSLog(@"%s", __PRETTY_FUNCTION__);
}
@end

And the output:

2007-06-14 07:50:37.733 printmethod[1800] -[Blah(blah) blah]

Notice that it includes the class name, category name (if any), and method selector.

Note that that's a C-string, not an NSCFString. Be sure to set up your format string accordingly.

What if we had a language keyword for ownership?

Tuesday, May 29th, 2007

For example:

retained NSImage *myImage;

mutablyCopied NSTextStorage *myTextStorage;
NSLayoutManager *layoutManager; //Owned by myTextStorage
NSTextContainer *textContainer; //Owned by layoutManager

Or instead of retained, we could have a keyword for non-ownership, with retain being the default for instance variables:

NSImage *myImage; //Retained by default

mutablyCopied NSTextStorage *myTextStorage;
borrowed NSLayoutManager *layoutManager; //Owned by myTextStorage
borrowed NSTextContainer *textContainer; //Owned by layoutManager

(Hopefully, this is already common, but using comments rather than language keywords.)

Perhaps these could be implemented using something like Python's decorator syntax:

#define retained @ownership(retain)
#define copied @ownership(copy)
#define mutablyCopied @ownership(mutableCopy)

//What the code looks like after macro-expansion, or without using macros at all:
@ownership(copy) NSString *myTitle;

Either way, the correct retain/copy/mutableCopy/release magic would happen automatically on any assignment. The only manual work still involved would be:

- (void)dealloc {
	[myTimer invalidate];
	myTimer = nil; //Implicitly releases myTimer

	myTextStorage = nil; //Implicitly releases myTS (and on death, it releases its LM, and that releases its TC)

	[super dealloc];
}

This would make it totally impossible to forget to retain something (forgetting to retain or copy things leads to double-releases or zombie objects), and make it much harder to retain when you should copy or vice-versa. It would also make your header much richer in documentation, and the more you can learn from the header, the easier reading the implementation will be.