Archive for the 'Programming' Category

Capture Cursor

Saturday, March 13th, 2010

Most people don't know how to get the cursor that's currently on the screen—a useful ability, especially if you're writing screenshot or screen-recording software. I've written an app that demonstrates the technique, or at least tries to.

It's a bit flaky. The API it uses, IOFramebuffer, doesn't tell me how many frames there are or what format they're in, so the app assumes ARGB in native byte-order and doesn't worry about frames. This gives wrong results more of the time than I like.

I've filed a request for a higher-level API, which would make the task much easier and the app much shorter.

I've posted a build in the repository's downloads area, in case you'd like to see it in action. If you want to build it yourself, you'll need to download SGHotKeysLib and put the source where the Capture Cursor Xcode project expects it to be.

The Green Checkmark of Acceptance

Saturday, March 13th, 2010

Here's how answers to a question on Stack Overflow appear to the questioner:

Every answer has, below the helpful/unhelpful buttons, a hollow checkmark button.

When the questioner clicks on one of those checkmarks, it marks the answer as the accepted answer to that question, and changes the checkmark from a gray stroke to a green fill.

Everybody else reading the question will see, below the questioner's name, an indication of how many of their questions have accepted answers. Today, for example, my questions have:

Peter Hosey
23.1k 2 14 28
67% accept rate

This indicates that I have accepted answers on four-sixths of my questions.

Sometimes, I see a comment like this semi-fictional example (written by me, based on several real examples I've seen) on a question whose author has a low or zero acceptance rate:

0% accept rate? You really should accept answers on your questions, or people may not answer any further questions from you.

This is a bad reason to accept answers.

The real reason to accept an answer is that you believe it's the correct answer.

Sometimes questioners choose bad answers (deprecated APIs, hacky solution, etc.). When that happens, it's a problem because it may lead future readers astray—they may think that this is the correct answer (because the questioner said so), without reading the other answers or the comments and finding out that this way sucks and/or there is a better one.

The same problem happens when a questioner accepts an answer because they think they have to, out of some sort of social obligation, rather than because they truly believe it is the correct answer. They may not have the correct answer yet, or there may not be a correct answer yet, but they feel like they have to accept something, so they accept the best answer they have, however good or bad it is, solely to raise that all-important number.

That sucks.

Questioners: About a day after asking a question, you should return to it, read all the answers, try them in descending order by votes, and accept the one that works and is the least hacky, for the benefit of other people who have the same question you asked. Take comments into account—something may not look hacky, but a comment may point out the hackiness.

And if there is no good answer, you don't need to accept anything. For the same reason (the benefit of future readers), you should leave the question open.

It's OK to have an acceptance rate that is below 100% or even low, as long as you are accepting answers that you find work and are non-hacky, on as many of your questions as you can. As long as you're making that effort, you're doing it right.

People who post comments like the one above: Why are you so desperate for karma? It's not like it's scarce or valuable. Net scores on answers are meaningful (usually), but your personal total, like mine, is next to meaningless. It's a reward, yes, but an empty one, so I don't see why you get all hurt when you perceive a risk that someone may not give it to you.

In summary: Don't worry about it. Accept correct answers, write correct answers, and don't worry about your acceptance rate or anyone else's.

Voices That Matter iPhone Developers Conference 2010

Monday, February 22nd, 2010

I'm still Mac-only, so I still won't be there, but here's the information for this year:

  • The conference is in Seattle, Washington on April 24th and 25th.
  • The speakers, including Aaron Hillegass, Jonathan “Wolf” Rentzsch, Erica Sadun, and Eric Buck, will cover many different topics, including Core Data, Core Location and MapKit, in-app purchasing, UI design, and OpenGL ES.
  • Early bird pricing is $200 off until March 12th.
  • If you use the coupon code “PHBLOGS”, you'll get another $100 off.

(I do still wonder what the “PH” stands for…)

If this sounds good to you, register for the conference.

See also Brent Simmons's post about it. He wants to play pinball with you.

Easy main-thread performing category

Saturday, February 13th, 2010

Anyone who's ever had to perform a message on the main thread knows how ugly the code can be:

[obj performSelectorOnMainThread:@selector(receiveAnswer:)
                      withObject:[NSNumber numberWithUnsignedInteger:42]
                   waitUntilDone:NO];

I've written a category that changes the above code to something much simpler:

[[obj performOnMainThread_PRH] receiveAnswer:42];

Notice that not only is the code much easier to read and to write, but you no longer need to box and unbox primitive values.

You can get the code, under a BSD license, from the higher-order main-thread perform repository.

Back when I originally mentioned my creation of the category on Twitter, Dave Dribin replied that he had already written one of his own. His is part of his DDFoundation framework, and requires DDInvocationGrabber (a fork of Jonathan Wight's CInvocationGrabber), also part of that framework.

My category requires nothing but Cocoa and is not part of a larger framework. That said, it's interesting to read in his code how the functionality of the Invocation Grabber makes his implementation nothing but a thin wrapper around it. (Also, his version lets you say waitUntilDone:YES.)

An introduction to Cocoa and Cocoa Touch

Wednesday, February 3rd, 2010

If you know someone who's recently taken up programming the Mac or the iPhone (or both), please give them one (or both) of these links:

As a frequent answerer of questions on Stack Overflow, I see certain patterns—not just frequently-asked questions, but also frequently-unasked questions: Things that are biting the questioner that they don't know about.

Sometimes they're thinking about things the wrong way (e.g., using property list objects where they should have a model). Sometimes there is a route to a solution (“my app is slow” “run Instruments”), but they don't know about that route. Often, they'll use wrong terms that are right on another platform (like “member variable”), like a speaker using their native language's pronunciation rules when speaking a foreign one.

To help people over these speed bumps, I've composed an introduction for new Cocoa and Cocoa Touch programmers. (Both of the above links redirect to it.)

If any part of it doesn't help them (i.e., is unclear or wrong), I'd appreciate it if you'd let me know, either here or by email.

Reunification

Tuesday, February 2nd, 2010

A common question from people new to Objective-C is “why do I have to separately alloc and init? Why can't I just call one thing and get a fully initialized object?”.

It's certainly not how other environments do it. Usually, you call one function or class method, and you get an object. Cocoa makes you send two messages: one to the class, then another to the fresh, uninitialized instance you got back. What does Cocoa know that those other environments don't?

There are two reasons why alloc and init are separate:

  • “NSResponder”* says that sometimes, NeXT wanted to initialize the same object multiple times.

    This is interesting, but horrifying to modern readers. Practically all init methods I've seen do not handle the case of initializing the already-initialized; they blindly assign newly created/retained objects to the ivars without checking for existing objects there.

    A bigger problem is when the init method releases the receiver and returns some other object; in the second message, the “other” object may in fact be the receiver (returned from the first message), so [self release] this time would be killing off an object that should stay alive. On the other hand, retaining the object before re-initializing it will create a leak if the object doesn't release itself. Unpredictable behavior is bad.

  • On the same Stack Overflow question, Darren points out that alloc is shorthand for allocWithZone:; if allocation and initialization were merged in Cocoa, then, to maintain the same functionality, every initializer would need to take a zone parameter, and optionally come in a flavor without one. You can imagine how a proliferation of initializers would ensue.

    This is essentially what the documentation says: “Separating allocation from initialization gives you individual control over each step so that each can be modified independently of the other.” I.e., both steps can be customized, so they must remain separate so that you can customize either or both.

So, there are two practical reasons why Cocoa's designers separated allocation from initialization.

But let's revisit this with modern eyes:

  • As I mentioned, in modern Cocoa, initializing an instance again is risky at best. In practice, I don't think anybody does it, and if anybody does, they probably feel guilty about it and mean to refactor that code “someday”.
  • Nobody uses zones anymore.

That blows those two reasons away.

So, let's imagine what life would be like if they had never existed in the first place, and allocation and initialization had remained one task:

+ (id) newWithFramistan:(Framistan *)framistan {
    id obj = [super new];
    if (obj) {
        obj->framistan = framistan;
    }
    return obj;
}

Right away, you can see several advantages:

  • We can name our local variable “framistan”, not “newFramistan” or anything like that.
  • No more assignment in condition (if ((self = …))).
  • No more assignment to self (which should make Wil Shipley happy).
  • Eliminates the problem of [super init] returning a different object, whereupon you would have to release the receiver. Since the receiver is the class, there is nothing for the upstream-initialized object to be different from. If the superclass wants to return an existing object, it can just do that.
  • Related to the first point, we no longer have a name conflict between instance variables and local (especially argument) variables. In init methods, we resolve this conflict with style rules, such as the aforementioned newFramistan for locals or m_framistan for instance variables; in this newWithFramistan: method, the conflict doesn't exist in the first place.
  • There is never an uninitialized object that the caller could leave lying around. We've all seen (or written) code like Foo *foo = [Foo alloc]; [foo init];; in this alternate universe, such faux pas are impossible.

There are trade-offs:

  • It's one line longer. (Depending on your employers' level of cluefulness, this may be a small advantage.)
  • Since you're not in an instance, you have to prefix every instance-variable access with obj->. I can see how this could get tedious; on the other hand, this is what prevents ivar-vs.-local name conflicts.
  • If you use object controllers that automatically prepare their content, they'll use alloc and init, bypassing this initializer. I've never used the automatic-content-preparation feature, so this doesn't affect me.
  • It'll be less than familiar to anybody who hasn't read this post, and it's certainly a change from currently-typical Cocoa code.
  • Doing this in a subclass of a class that has an unusual designated initializer, such as NSView with its initWithFrame:, could get tricky.

My opinion is mixed. On the one hand, real problems with the init way of doing things are rare for anyone who knows the ropes. On the other hand, it sure is more straightforward, isn't it?

What do you think?

* Presumably the same one most of us recognize as John C. Randolph, although there's no direct evidence of this on his SO profile.

Blog posts vs. web pages

Monday, January 25th, 2010

Steve Smith says “Stop Blogging”:

I mean it. All of you people are writing fantastic, useful articles about code, methods, and technologies, but you’re putting them in blog posts — a date-based format that encourages us to leave things as they were, historically.

This got me to thinking about the difference between two of the tutorials I've published.

The pointers tutorial is a single web page. There's a date stamp, but it's way down at the bottom. The ASL series is nine blog posts.

In the three years since the previous version of the pointers tutorial, dozens of people emailed me to tell me about its major errors.

In the two years since I published the last of the ASL series (ignoring approximately a week afterward), nobody has told me of an inaccuracy in any of the posts.

There are a number of possible explanations for the ASL series receiving fewer (that is, no) corrections:

  • That its audience is narrower: Anyone who programs C has to deal with pointers. Only a very few Mac OS X programmers will ever touch ASL.
  • That it is less visible: One of these is linked from my home page and plenty of CS course reading lists (exhibits A, B, C, and D), and was linked for a while from the Wikipedia article on the C programming language; the other is practically unknown to anyone who wasn't subscribed to my blog at the time.
  • That I'm just that good. (Ha!)
  • That ASL hasn't changed at all since Leopard. (Ha!)

Smith writes from the perspective of the author and publisher, who must maintain a web page; he says that the author and publisher finds no (or not much of) such obligation for a blog post. I think the difference in my supply of corrections hints at a reader side to this, although, as shown above, my two examples are hardly comparable.

I have been meaning to move the ASL tutorial into a pointers-style web page at some point, although I don't know when. I may start receiving corrections then, which means I'll have to spend time to fix them. The flip side to that is that if I leave it as blog posts, I'll have that time for other things, but the posts will be consigned to periodically-increasing inaccuracy.

I expect to think more about Smith's suggestion.

There's also the merit of the word “blog”, which is wearing thin for me.

The trilogy is now complete

Saturday, January 23rd, 2010

The third video of last month's meeting is now up. Here are three easy-to-remember links:

  1. http://bit.ly/CHLF2009Leaks1
  2. http://bit.ly/CHLF2009Leaks2
  3. http://bit.ly/CHLF2009WebInsp

You'll notice that the third video is not CHLF2009Leaks3.

Unlike the other two, this one features Scott Ellsworth, our organizer, demonstrating the Web Inspector in WebKit. He starts off in Safari, then switches over to Chrome. He also talks about a profiling tool that's part of Google Web Toolkit called Speed Tracer, although he wasn't able to finish a demo of it in time for the meeting, so he talks about one of the examples instead.

I don't think I'll link these here anymore. If you want to find out about future CocoaHeads Lake Forest videos, subscribe to the CocoaHeads Lake Forest channel on Vimeo.

CocoaHeads Lake Forest videos

Monday, January 18th, 2010

It's been in editing for over a month, but now, it's finally done. I've just uploaded the second part of last month's meeting of CocoaHeads Lake Forest. There's one more to go from last month, and then I can start posting the video from this month.

This one's a lot more random than the first one. Portions of my own presentation are only part of what you'll see in this video.

More long-term, I've created a channel on Vimeo for all past and future CocoaHeads LF videos.

The myth of Carbon’s 64-bit unavailability

Monday, January 18th, 2010

There's a recurring myth going around, which goes something like this:

In fact, using Carbon locks you out of 64 bit.

No, it doesn't.

The truth is that some APIs that are part of the Carbon sub-frameworks are not available in 64-bit. Merely linking against Carbon does not mean your app suddenly stops working in the 64-bit world.

Carbon itself is just an umbrella framework. It doesn't have any APIs that it provides immediately; everything comes from either another framework or one of its sub-frameworks.

Examples of the first category include Core Foundation, Core Services (home of Launch Services), and Application Services (home of Core Graphics). Even these are not immune to the myth, as demonstrated by the comment I linked to above: It's on an answer suggesting a Core Foundation function. The answerer referred to it as a “Carbon function” (true in a sense), and the commenter pounced.

Many things that were once explicitly considered part of Carbon, such as the File Manager and Resource Manager, are now part of Core Services. Note that they haven't even been consigned to the Legacy Library!

In the other category, even some APIs that remain strongly identified with Carbon, by being in a direct sub-framework of Carbon, are still at least partially around. One of these is the Carbon Event Manager, which even has the word Carbon in its name. Try registering a hot-key with it in a 64-bit-only app—it'll work. (That one is in the Legacy Library. I should probably file a bug on that.)

What is deprecated and not available in 64-bit, as you can see in the Carbon Event Manager Reference, is anything that you would need to build your application on Carbon. That's what's deprecated: the concept of a Carbon application. Only Cocoa apps remain.

But some parts of “Carbon” are useful for things other than building a Carbon application. You can use them from a Cocoa application, or from a command-line tool.

Those APIs are still around. They are not deprecated, they are still useful, and they are still available in 64-bit. Use them freely.

(Oh, and before anybody mentions it: No, those NSEvent class methods introduced in Snow Leopard are not direct replacements for Carbon Event Manager hot-keys. You'd have to sift through the events yourself, whereas Carbon Event Manager filters them for you and only calls your function when your key is pressed. The correct analogue would be CGEventTap.)

Pointers tutorial 1.3

Saturday, January 16th, 2010

At long last, a new version of Everything you need to know about pointers.

The most significant changes are long-overdue corrections regarding declarations of const pointers and the difference between arrays and pointers. You can—and, if you learned how to work with pointers from this tutorial, should—read all of the changes in the delta between 1.2 and 1.3.

Ship-It Saturday: IconGrabber 2.0.1

Sunday, January 3rd, 2010

The last time I released a version of IconGrabber was only a week after Valve released Half-Life 2—way back in 2004. That game wasn't even on my radar then, since I couldn't run it on my PowerPC-based Mac!

Just over five years later, I've played all of the Half-Life 2 games and love them, and IconGrabber returns with some bug fixes and support for the new bigger icon sizes introduced in Tiger and Leopard. Version 2.0.1 is available from the IconGrabber home page.

Ship-It Saturday: Translate Text

Sunday, December 27th, 2009

Real artists ship.

Steve Jobs

With the idea that an application that's 95% finished and in active use is better than an application waiting for 100% in the seclusion of my hard drive, Ship-It Saturday is where I dust off a program that I have 95% finished, call it done, and just ship it already. I hope to make this a regular feature, although I have no idea how frequently I'll do it.

Today's winner is Translate Text, an app I wrote to make handling Adium feedback emails easier. Just select some text, then choose the service corresponding to the language it's in (or the auto-detect-language-and-then-translate service). A window will open with the original, a couple of language pop-up menus, and the translation.

Screenshot of myself invoking the French-to-English service through a contextual menu.

More information, both screenshots, and the downloads at the Translate Text web page.

Warnings I turn on, and why

Saturday, November 7th, 2009

I've started turning on most of Xcode's warning options and one warning-related build setting in all of my personal projects. I suggest you do the same.

There are some warnings I don't turn on, for any of several reasons:

  • They're inapplicable. For example, “‘Effective C++’ Violations” doesn't apply to me, because I don't write C++ code.
  • They don't help anything. An example is “Four Character Literals”, which is about 'abcd' literals for four-byte-code types such as OSType. These sacrifice something convenient for no benefit, so I leave them off.
  • They are impossible for me to fix. An example is “Multiple Definition Types for Selector”. Cocoa raises that one all over its headers, and I can't do anything about Cocoa.

The rest of the warnings, I turn on because either they make something clearer or they tell me about either real or potential (i.e., future real) bugs. They are:

  • Check Switch Statements

    Warn whenever a switch statement has an index of enumeral type and lacks a case for one or more of the named codes of that enumeration. The presence of a default label prevents this warning.

    Leave no case unhandled.

    Consider whether a default: label is appropriate for your enumeration. If your switch statement handles all possible values, cut out the default and assert that the value is one of the possible ones instead. An easy way to do this, if the enumeration values are serial and the enumeration is not part of an API you expose, is to have one extra name defined as the number of possible values:

    enum Foo {
    	kFooFoo,
    	kFooBar,
    	kFooBaz,
    	kFooNumberOfValidFooValues
    };
    

    Then, in your assertion macro, compare the value against that name:

    #define MyParameterAssertValidFoo(foo) \
    	NSAssert1((foo) < kFooNumberOfValidFooValues, @"Invalid Foo value: %d", (foo));

    When you add kFooQux, insert it above kFooNumberOfValidFooValues, and the value of kFooNumberOfValidFooValues will increase by one to fit it.

    The result is that your switch statement covers all known values for the enumeration (or you get a warning because it doesn't), and your method throws an exception (from the assertion) whenever anyone passes an unknown value.

  • Hidden Local Variables

    Warn whenever a local variable shadows another local variable, parameter or global variable or whenever a built-in function is shadowed.

    One common way to get this warning is to name a variable index, because there is a function by that name in the standard C library. That's not as much of a false positive as you may think: If you fail to declare your index variable, all your references to it will actually refer to the index function. You can see how it would be bad to send a message such as [myArray objectAtIndex:index] with this bug.

    The solution is simple: Never, ever name a variable index.

  • Implicit Conversion to 32 Bit Type

    Warn if a value is implicitly converted from a 64 bit type to a 32 bit type.

    This is most useful when converting old code to work correctly in a 64-bit architecture. Storing a pointer into an int variable (such as a reference constant) when targeting an LP64 architecture is a good way to get this warning, and rightly so.

  • Initializer Not Fully Bracketed

    Example, Here initializer for a is not fully bracketed, but that for b is fully bracketed.

    	int a[2][2] = { 0, 1, 2, 3 };
    	int b[2][2] = { { 0, 1 }, { 2, 3 } };

    This is a cleanliness warning. It also applies to structures, such as NSRect:

    NSRect warns = { 0.0f, 0.0f, 640.0f, 480.0f };
    NSRect doesNotWarn = { { 0.0f, 0.0f }, { 640.0f, 480.0f } };

    (In real code, I'm more likely to use NSZeroPoint instead of the { 0.0f, 0.0f } element above. It's harder to spell that wrong and get away with it than it is to get away with typing 9.9f, 1.1f, or 2.2f instead of 0.0f.)

  • Mismatched Return Type

    Causes warnings to be emitted when a function with a defined return type (not void) contains a return statement without a return-value. Also emits a warning when a function is defined without specifying a return type.

  • Missing Braces and Parentheses

    Warn if parentheses are omitted in certain contexts, such as when there is an assignment in a context where a truth value is expected, or when operators are nested whose precedence people often get confused about.

    Also warn about constructions where there may be confusion to which if statement an else branch belongs. Here is an example of such a case:

    	if (a)
    		if (b)
    			foo ();
    	else
    		bar ();

    In C, every else branch belongs to the innermost possible if statement, which in this example is if (b). This is often not what the programmer expected, as illustrated in the above example by indentation the programmer chose.

    This may appear to be just a cleanliness warning, but as you can see from the example, it can also warn you about code that may not flow the way you expect it to.

  • Missing Fields in Structure Initializers

    Warn if a structure's initializer has some fields missing. For example, the following code would cause such a warning, because "x.h" is implicitly zero:

        struct s { int f, g, h; };
        struct s x = { 3, 4 };

    This option does not warn about designated initializers, so the following modification would not trigger a warning:

        struct s { int f, g, h; };
        struct s x = { .f = 3, .g = 4 };

    I'm not sure why it warns about the former and not the latter, since all the members get initialized in both code examples (C99 §6.7.8 ¶21). If nothing else, this warning is good motivation for you to switch to designated initializers, which make your code more explicit about which members it's initializing.

  • Missing Newline At End Of File

    Another cleanliness warning—this one, about the cleanliness of diffs.

  • Sign Comparison

    Warn when a comparison between signed and unsigned values could produce an incorrect result when the signed value is converted to unsigned.

  • Strict Selector Matching

    Warn if multiple methods with differing argument and/or return types are found for a given selector when attempting to send a message using this selector to a receiver of type "id" or "Class". When this setting is disabled, the compiler will omit such warnings if any differences found are confined to types which share the same size and alignment.

    I don't turn this one on, because it's unnecessary. When the multiple declarations differ significantly (e.g., one method returns an object and the other returns a float), the compiler will raise the warning whether it's turned on or not. When the declarations don't differ significantly (e.g., both methods return an object), the difference won't cause a problem, so you don't need to worry about it.

    So, you should leave this one off.

  • Typecheck Calls to printf/scanf

    Check calls to printf and scanf , etc, to make sure that the arguments supplied have types appropriate to the format string specified, and that the conversions specified in the format string make sense.

    The biggest reason to turn this on is that it checks your use of methods that take a nil-terminated list of arguments:

    NSArray *array = [NSArray arrayWithObjects:@"foo", @"bar"];

    That message should have a nil after the last argument. With this warning turned on, the compiler will point out that I don't.

    The ostensible main reason to turn this on is to have the compiler check your uses of printf and scanf formats. I don't use printf often (and I never use scanf), so that's not so important for me, but when I do, this could come in handy.

    Sadly, it doesn't work on NSLog calls.

  • Undeclared Selector

    Warn if a "@selector(...)" expression referring to an undeclared selector is found. A selector is considered undeclared if no method with that name has been declared before the "@selector(...)" expression, either explicitly in an @interface or @protocol declaration, or implicitly in an @implementation section.

    Another benefit of this warning is that you can use it to get a warning when you pass a wrong key to a KVC, KVO, KVV, or Bindings method. Uli Kusterer has a macro for that.

  • Unused Functions

    Warn whenever a static function is declared but not defined or a non-inline static function is unused.

    Works best with a policy of declaring any function as static that you don't need to be visible elsewhere in your program.

  • Unused Labels

  • Unused Values

  • Unused Variables

    These follow the general rule of “code you don't have is code you don't have to debug”. If you're not using a label, expression statement, or variable, you don't need it, and you will find your code clearer without it.

    You may notice that I don't turn on Unused Parameters. Most times when I trip that warning, it's a callback function or method, so I can't get rid of the argument. Rather than litter my code with bright yellow #pragma unused(foo) directives, I prefer to just turn this one off. (See my rule above about less code being better.)

Once I have turned on all of these warnings and then eradicated them from my code, I turn on two more build settings:

  • Treat Warnings as Errors

    I call this “hardass mode”.

    Remember what I said above: Almost all of these warnings represent real or potential (i.e., future) bugs in my program. Rather than tolerate them, I turn this setting on so that any time I write such a bug, I break the build.

    I haven't been able to turn this on yet in Adium or Growl, although I have turned it on in Adium's Spotlight-importer project. I do, however, turn it on in all of my solo projects.

  • Run Static Analyzer

    Activating this setting will cause Xcode to run the Clang static analysis tool on qualifying source files.

    The Clang Static Analyzer is the find-your-bugs-for-you tool you've heard so much about. This setting makes Xcode run it whenever you build. Thus, every build, you get all those warnings errors and your analysis results.

    Whenever possible, I leave this on; if there's a source file that it takes a long time to analyze (e.g., GrowlPluginController.m), then I turn it off, but only then.

UPDATE 2009-11-22: Jonathan “Wolf” Rentzsch wrote a script to turn on all of these settings in all of the projects you have open.

UPDATE 2009-11-28: Updated the entry on “Typecheck Calls to printf/scanf” after seeing that Jeremy W. Sherman pointed out a much better benefit of it in a comment on a Stack Overflow answer.

UPDATE 2009-12-05: Corrected the discussion of the index problem. You can't use index, or any other function, as a C-array subscript, so the problem only affects higher-level arrays, such as NSArray.

The peril of index(3)

Thursday, November 5th, 2009

This is mainly for Andy Finnell on Twitter, who wonders why some of us avoid naming variables index.

I pointed out that there is a function in standard C named index, and this causes one of two problems: If you declare a variable named index, you have shadowed the function and should get a warning for that; if you fail to declare the variable, you pass the pointer to the index function as your array index, which is probably not what you intended.

I say “should” there because, as he noted in his response, the shadowed-name warning is off by default. You should turn it on, because it catches bugs. In fact, the index bug is one that it can prevent.

Suppose you do name a variable index, and either you don't have the shadowed-name warning turned on or you ignore it. You initialize the variable with an index, but don't otherwise assign to it. Then, you attempt to access an object in an array by this index.

All well and good so far. index is a variable, so everything works as intended.

But then, one of several things happens:

  1. You comment out both the declaration and the usage of index, for whatever reason, but then you uncomment the usage but forget to uncomment the declaration.
  2. You update and/or merge in your version-control system, or otherwise apply one or more diffs. Usually, this works, but today isn't your lucky day: The merge breaks your source code. Perhaps it introduces conflicts, and you resolve them incorrectly. Or maybe it breaks the code silently (e.g., by merging in another branch's division of this function into two).
  3. You move the code to another location, but you forget to move half of it, or you move one half and delete another, forgetting that the declaration of index was in the code you deleted.

You had a variable named index, but now you don't—but the index function is always there*. Since there is something named index, your code compiles. It's the wrong type, so you'll get a warning, but maybe you don't notice it.

Then you run the code and it crashes. Why? Because you passed a function as the index into an array.

In the worst possible case, it was #2 and you weren't aware that this code was affected. Maybe you'd been working on something else. Anyway, since you hadn't been working on the now-broken code, you aren't testing it**, so you don't know that it's now broken.

So you ship it. You ship this index-way-out-of-range crasher. And then your user runs the code and gets the crash.

This isn't theoretical. I've had this happen more than once (fortunately, not in the hands of a user). It's one reason why I turn on the shadowed-name warning and “Treat Warnings as Errors”, and it's the reason why I never use index as a variable name.

UPDATE 2009-12-05: To clarify, this problem does not affect C arrays, as C does not allow you to use a pointer in an array subscript. It mainly affects higher-level array interfaces, such as Cocoa's NSArray.

* Assuming that, directly or indirectly, you've included string.h. If you're using Cocoa, you have (Core Foundation includes it).

** Unless, of course, you have automated test cases covering this code, and you run those.

How not to use UTIs

Tuesday, September 22nd, 2009

John C. Welch has written an article telling you to use UTIs to replace creator codes.

It is wrong.

Daring Fireball, rosscarter.com, and (the most sober of the bunch), TidBITS have all gotten on the "Apple has abandoned creator codes, now we're screwed, damn you Apple" bandwagon. They're all right about one thing: Apple has dumped creator codes in Snow.

So before you panic, there's a replacement mechanism, the UTI, or Universal Type Identifier.

No. Uniform Type Identifiers are a replacement for type codes (and MIME media types, and filename extensions), not for creator codes. There is no replacement for creator codes.

Uniform Type Identifiers are just that: They identify types of data. Using them for anything else is misusing them.

Ideally, Coda should assign an application-specific UTI for CSS files it creates.

No, it should not.

As it turns out, Coda is a UTI-aware application, and according to Coda's info.plist file, that identifier is com.panic.coda.css.

And that identifier is wrong, because CSS is not Panic's format.

I use CSSEdit. Currently, it doesn't declare a UTI, but if they should follow Coda's example, they should make one up of their own, such as com.macrabbit.cssedit.css. Now we have one type with two names. Which is right?

Suppose I write an application that accepts CSS data. Which UTI do I look for: com.panic.coda.css or com.macrabbit.cssedit.css? Should I look for both of them? What if I find both? What if a third developer releases a CSS editor? Now I have to keep up with the entire population of Mac CSS editors just to accept all the many names for one type.

The right type identifier would be something like org.w3.css or org.w3.cascading-style-sheets, following the rule that the type should be named after whoever controls it. The W3C controls CSS, so its UTI should be named after them. Some other formats, such as PNG, aren't controlled by anybody, so they go in public.

You might point out public.html and public.xml, as both of those are also controlled by the W3C. Obviously, I disagree that these should be in public.*. But it's too late to change them now, so we have to put up with them.

Better examples include com.adobe.pdf and com.microsoft.word.doc. In each of these cases, some third party controls the format, so they get the format's UTI named for them. Notice that Preview does not invent a com.apple.preview.pdf UTI, and TextEdit does not invent a com.apple.textedit.word.doc UTI; they use the UTIs named for the creators and maintainers of those types.

So now we see a problem. All the text-ish files have the same type identifier: com.apple.traditional-mac-plain-text.

Because they are all the same type. And that type is named after Apple because Apple controls that format (in this case, a text encoding).

The Photoshop PNG file is typed as public.png.

There's no such thing as a “Photoshop PNG” file. PNG is an open format controlled by no-one (or, arguably, the W3C, in which case the type should have been named for them). What you have is a PNG file that you created in Photoshop. That latter detail is irrelevant to its type, which is why HFS had the creator code separate from the type code in the first place.

If there is such a thing as a “Photoshop PNG” file (that is, if Adobe incompatibly extends the PNG format in some way), then they should assign that format its own name (say, com.adobe.png), because it's a different format.

If you take away the creator code, you're screwed, because all you have are the extensions.

Exactly why John Gruber, Ross Carter, and Matt Neuberg don't like the change.

Coda CSS file props:

displayed name:"test.css",
creator type:"TStu",
type identifier:"com.apple.traditional-mac-plain-text",

I'm surprised you didn't notice this. Why isn't this file's type identifier com.panic.coda.css?

… why aren't any of these applications doing the right things? Pages '09 does. It sets the type for .pages files to "com.apple.iwork.pages.pages".

Because it's Apple's own format specifically for Pages. Again, see also com.microsoft.word.doc.

Unsurprisingly, it doesn't even have a creator code.

Yeah. Cocoa makes it difficult to apply a creator code to a file and doesn't do it automatically, so expect most Cocoa apps to not do it.

Even thought the default for the .html extension is BBEdit, the UTI overrode that. When I changed the handler for public.html to BBEdit, then the file opened in BBEdit. That's exactly the behavior I'd expect, and it matches what you got with Creator Codes.

It matches what you got when you assigned the default handler of a file type, as well. That's what you did the newer equivalent of.

There never was a way to set which application opened files that had a given creator code. That was what the creator code was for: Determining which application would opened the file.

Yes, Apple did abruptly kill off Creator Codes, however, they've been warning about that for some time now.

Sort of. They've been warning that they'd kill off the combination of file type codes and creator codes. UTIs are the replacement for file type codes; as I said above, there is none for creator codes.

Now, I know that 'simple' is a big fat lie when it comes to application development, however, at some point during the 10.5 lifecycle, all these applications should have been updated to not only support UTIs, but to use them. Instead, they all relied overmuch on FIle Types/Creator Codes …

Actually, it was more common to use filename extensions. See what I said above about the difficulty of assigning file types and creator codes from a Cocoa app. I think that was actually quite rare, simply because filename extensions were so much easier.

… and now that those are gone, well, the OS does what it can with the information it has available.

No, it doesn't. The creator code information is still available, and the OS ignores it. The OS now only uses type information to determine how to open a given file, except for files that the user has assigned a specific application to.

Oh, and what about when you change a file association in the Finder? Well, unsurprisingly, the Finder cheats. It adds a resource fork to the file with the path to the desired application coded within. … [M]aybe the Finder could start doing the right thing too?

Agreed. I say it should set the file's creator code.

Incidentally, I tested in Tiger, Leopard, and Snow Leopard, and Finder did this resource trick in all three versions. If the Info window ever did set the file's creator code, it hasn't done that for many years.

If the only way to have a unique UTI associated to a file is for that file to use a completely unique file extension, then WHAT THE FUCK GOOD ARE UTIS? Fucking seriously, why the FUCK bother? BBEdit creates TEXT FILES. Does Apple seriously expect that Bare Bones, to properly use UTIs, is going to now create a .bbedittext extension, a .bbedithtml extension, a .bbeditohmyfuckinggodwhatthefuckisgoingon extension, then make you EXPORT to 'standard' extensions, just so you can know a file created in BBEdit will open in it?

And now you know why smashing type information and creator/opener information into a single value is a bad thing.

If this is what Apple is pushing, then everyone who signed off on non-settable UTIs is a fucking idiot…

There is no reason why the average user should be able to change the type of a file. If they created an HTML file, it's an HTML file, and there's no reason for a user to be able to set it to anything different unless they really know what they're doing. (I've done it, and I'm assuming you have, too. We're exceptions.)

What you want to do is to change which application opens the HTML file, and that is completely separate from the fact that the file contains HTML.

The other way to install the Mac Reference Library

Wednesday, September 2nd, 2009

Starting with Xcode 3.2, the Xcode installer package no longer includes a local copy of any developer documentation. Instead, you have to either go to the website (which is what the redesigned Documentation Viewer window does) or download the documentation within Xcode.

Both solutions have their own problems. Reading the docs on the website can be frustrating if you're streaming or downloading something in the background. And downloading within Xcode is dubious if your internet connection is not super-fast or is flaky, because Xcode cannot resume downloads.

There is a third solution.

  1. In Xcode's Preferences, click on an info button at the right edge of the list of docsets, or right-click on the docset and choose “Get Info”.
  2. Select the row labeled “Feed URL”, and copy it.
  3. In a text editor or text field, paste the text, then edit it (it's the whole row, not just the value) down to the URL alone. There's a period (full stop) at the end of the text; it's not part of the URL, so make sure you delete that.
  4. Open the URL in a feed reader.
  5. On the most recent entry, copy the URL for the enclosure.
  6. Paste it into your download manager or browser Downloads window.

Assuming your download manager or browser supports resuming downloads, you now have a way to pause the download for any reason that may arise, and resume it from that point.

Of course, then you have to install what you downloaded. You're probably not used to seeing xar files (although you've seen more than you think—flat packages are based on them), so you may not know what to do with them.

  1. Pop open a terminal. cd over to where you downloaded the xar archive.
  2. pushd /Developer/Documentation/DocSets
  3. sudo xar -xf ~+1/filename.xar
  4. Once that finishes, delete (or back up) the xar file.

Xcode's Documentation Viewer window (or your browser) should suddenly be much quicker.

Git fetch weirdness explained

Sunday, August 30th, 2009

In another tussle with Git, I performed the following sequence of commands:

  1. cd mach_star-rentzsch
  2. git fetch (from GitHub)
  3. cd ../mach_star-boredzo
  4. git fetch rentzsch (in this context, my git-remote alias for ../mach_star-rentzsch)
  5. git merge rentzsch/master

Step 5 failed with “Already up-to-date”.

What? I just fetched! I should have new commits to merge in!

Nope. For one thing, this output from step 4:

From ../mach_star-rentzsch
 * [new branch]      master     -> rentzsch/master

seems to mean “OK, here's a new name for the master branch of that other repository, but we didn't actually bring in any commits”.

The reason it didn't bring in any commits is because git fetch apparently only fetches commits that are ancestors of the source repository's current HEAD. In English: git fetch cares what you have checked out in the source repository.

It's because I had fetched in step 2, and thereby not changed my HEAD, that step 4 did not see anything new to fetch. I don't know why it works that way, or why they consider it useful.

Anyway, the “correct” sequence of steps is not much different: git pull, not fetch, in step 2 above. Or use Mercurial, which I've found makes a lot more sense in general.

It's entirely possible that I've figured this out the wrong way, so take this entire explanation with a grain of salt.

Variables for clarification

Friday, August 14th, 2009

Before:

if (![newTrackURL isEqualToString:trackURL] || ([newTrackURL hasPrefix:@"http://"] && !([lastPostedDescription isEqualToString:displayString]))) { // this is different from previous notification, or it's a stream and the playing track has changed

After:

BOOL URLChanged = ![trackURL isEqualToString:newTrackURL];
BOOL isStream = [newTrackURL hasPrefix:@"http://"];
BOOL descriptionChanged = ![lastPostedDescription isEqualToString:displayString];
if (URLChanged || (isStream && descriptionChanged)) {

Why remove the comment? Well, I had just changed the code to what you see in the Before example, but I had not changed the comment, and I thought I was ready to commit. I almost committed a stale comment. It's that easy. Luckily, I caught it in reading the patch before I qfinished it, so I was able to change the comment to what you see above.

Now I have no comment at all, but the code is clear, so I don't need one. This is an improvement.

Here, have a coupon

Sunday, July 26th, 2009

Voices That Matter: iPhone Developers Conference
October 17th and 18th.

This October 17th and 18th, there's a conference for iPhone developers in Boston, which the organizers asked me to attend.

The conference is called Voices That Matter: iPhone Developers Conference, and the titular voices include Aaron Hillegass (famous for his book), Erica Sadun (famous and infamous for her jailbreak work and advocacy), Bill Dudney (famous for his book), Stephen Kochan (somewhat famous for his book), Jonathan “Wolf” Rentzsch, Fraser Speirs, and Daniel Jalkut.

As most of you know, I'm not an iPhone developer—I write for the Mac only. So I declined that part of the offer.

The other part was a coupon for you, my readers. I know many of you probably are iPhone developers, so I asked whether they would still like me to give you that coupon code. They agreed, so here it is:

PHBLOG

(Those of you who read Jesper's weblog may recognize it.)

Without the coupon, the registration fee is $495 until September 12th, $695 thereafter. The Word document they sent me says that that the coupon will knock $100 off.

Enjoy the conference!