Conceptually, BJ is correct, but the generated code is slightly different. It goes something like this:
NSString *string = [[NSString alloc] init];
// Oh, we're changing what `string` points to. Gotta release the old value.
NSString *tmpString = string;
string = [[NSString alloc] init];
[tmpString release];
[string release]; // string goes out of scope at this point in your code
This order of operation is usually not that critical (and if you care too much about it, you are probably coding incorrectly). But understanding it explains why the objects are destroyed exactly when they are.