The difference between those two is that the second includes an ivar named foo
in your class, while the first doesn't.
If your implementation includes @synthesize foo;
and you are using a sufficiently modern compiler, for the first case the compiler will automatically add an appropriate ivar to the class for you. You can even access this implicit ivar later on in the same implementation file as if it were declared as in the second case.
This ivar synthesis is made possible by another feature of modern Objective-C: the fix of the fragile base class problem. In most compiled languages, an ivar is accessed as "object address + ivar offset"; the subclass's ivars necessarily come after the ivars for the base class, so the subclass must know the size of the base class in order to know where its own ivars start. Changing the size of base class (e.g. by adding or removing ivars) therefore requires recompiling all subclasses. Objective-C solves this by storing the size of the class's ivar area in the class metadata and loading it at runtime, and ivar accesses are along the lines of "object address + base class size + ivar offset".
The very-low-level details of how ivar synthesis works are up to the compiler, but it boils down to the compiler arranging for the class's ivar area to be sufficiently large to include space for the synthesized ivars.