Better documentation for +[CATransaction flush]

The documentation for +[CATransaction flush] is woefully inadequate. It spends one sentence on providing an incomplete description of the method’s effects, and two subsequent paragraphs describing when the framework automatically calls this method.

Here’s Apple’s entire description of what +flush does:

Delays the commit until any nested explicit transactions have completed.

What commit? You might think it applies to “the commit of whatever transaction inside which this method is called”, but you’d be wrong. It applies to the implicit transaction that is the parent of all transactions created with +begin. And it doesn’t really delay anything; rather, it commits the implicit transaction when the outermost explicit transaction is committed, which is earlier than the automatic commit that happens at the end of the run loop.

The headerdocs are differently bad:

/* Commits any extant implicit transaction. Will delay the actual commit
 * until any nested explicit transactions have completed. */

At least we have a more accurate description of what the method does, but it’s devoid of any reason why you’d want to call it. And because of historical context, in which there’s a lot of code that calls +[CATransaction flush] from within an explicit transaction, it’s still not entirely certain that “the commit” refers to “the commit of the implicit transaction” instead of “the commit of whatever transaction we’re currently inside”.

A much better if still concise description comes courtesy of Apple’s John Harper in a 2008 post to the cocoa-dev mailing list:

what the second sentence is trying to get across is that if you do something like this:

[CATransaction begin];

[CATransaction flush];

[CATransaction commit];

the actual flush to the render tree is delayed until after the outermost commit.

I’ve rewritten the +flush documentation to

a. accurately describe the effects of calling +flush,

b. describe the motivation for calling it directly or refraining to do so,

c. and warn of common misunderstandings about its effects.

Apple, feel free to copy and paste this into your developer documentation. I’ll fax you any piece of paper you want to sign over the copyright on this description. I’ve filed this as rdar://problem/15489526.

+[CATransaction flush]

Commits the current thread’s implicit transaction when all outstanding explicit transactions have been committed. If no explicit transactions are awaiting a +commit, the implicit transaction will be committed immediately. Since all explicit transactions are descendants of the implicit transaction, this method has the effect of applying the effects of all transactions to the presentation tree.

If the current thread has a running run loop, it is recommended that you not call this method directly as it will be called automatically at the end of the run loop. This will lead to better performance and more predictable timing of when transactions are applied to the presentation tree. If the current thread does not have a running run loop, then you must call this method explicitly to apply the effects of any transactions.

Note:

Because the implicit transaction is only committed when the last remaining explicit transaction is committed, calling +flush from within a transaction does not guarantee that the flush will actually occur when that transaction is committed. Since it is impossible for code to know whether it is executing within an explicit transaction, this can lead to unexpected delays in the commit of the implicit transaction.

For example, in the following code snippet, the presentation layer will not be updated until after the outer transaction is committed:

// presume we are not already within an explicit transaction

[CATransaction setDisableActions:YES];

CALayer *myLayer = /* ... */ ;
printf("starting layer opacity: %.1f", [myLayer.presentationLayer opacity]); // will print 1.0

[CATransaction begin];

    [CATransaction begin];
        myLayer.opacity = 0.0f;
        [CATransaction flush];

        printf("inner transaction layer opacity: %.1f", [myLayer.presentationLayer opacity]); // will print 1.0
    [CATransaction commit];

    printf("outer transaction layer opacity: %.1f", [myLayer.presentationLayer opacity]); // will print 1.0

[CATransaction commit];

printf("final layer opacity: %.1f", [myLayer.presentationLayer opacity]); // will print 0.0