Why, on the second line, do we not have to write *testString = ... in order to access the location where it's actually pointing?
The init method returns a generic pointer to an object -- its return type is id. testString is a pointer to an NSString, which is an object, so you are assigning a pointer to another pointer. Dereferencing the assigned-to pointer would be a type mismatch.
A variable name is a place (a label for a memory address) in which to put something. The type of the variable is the kind of thing that you can put there. In the case of a pointer, the kind of thing that you put in it is also a memory address. In order to get that address, you dereference the pointer. The kind of thing that you can put at that address is different from the kind that you put in the pointer itself.
After the first line, what is *testString and what is testString?
After the first line, *testString, or the thing at which testString points, is garbage (actually undefined). testString is a pointer (4 or 8 bytes depending on your system) to a address in memory, and it is also undefined.
After the second line, *testString is an NSString object. testString is still a pointer to an address, where there is a valid NSString object.