The difference stems from the fact that an object comes with its own methods and behaviour, which could cause changes to the object outside of its setter and getter.
Atomic keyword provides thread synchronisation only between the setter and getter that the compiler synthesizes.
This is generally sufficient to provide thread safety to a primitive property, as its underlying ivar is only accessed via its setter and getter.
However, for an object property this would only provide thread safety for the pointer to the object. The setter and getter would be synchronized themselves, but some other method of that object may be modifying it from a different thread and the property will not be thread safe overall. If you want to make it safe you'll have to implement synchronisation with all methods that have the potential to modify the object.
For example, the following immutable string should be thread safe because it is only modified via accessors
@property (atomic, strong) NSString * immutableString;
While the following mutable one will not be thread safe
@property (atomic, strong) NSMutabeString * mutableString;