A retain count can be 0 or higher but never less. When an object is allocated heap memory (alloc init) the retain count is set to 1. You can then increase the retain count by calling retain on it (as far as I'm aware an unlimited number of times but I could be wrong).
Calling release simply decreased the retain count by 1. The system then periodically checks the retain counts of objects and deallocates any with a count of 0.
Calling release on an already deallocated object is the same as calling any method on a NULL object and should simply return NULL or void. However, if you are explicilty managing heap memory then you should be VERY aware of what you're doing.
Some interesting points:
Why can a retain count be more that 1?
This is so that an object isn't released whilst it is still required by something else. E.g. say you have pet owner and vet. A pet instance is owned by an owner instance. The owner goes to a vet instance and the vet takes ownership of the pet also. For a period of time pet has two owners and therefore (if retain has been called) has a retain count of 2. Lets say that the owner is then released before the vet has finished with the pet; if everything has been done properly the pet wont be deallocated it will simply have its reatain count decreased to 1 by the call to release from owner. The vet can then finish with the pet, call release and the pet will be deallocated.
ARC
As I'm sure you're aware this has all been replaced by Automatic Reference Counting. As developers we now have to simple be aware of the type of relationship an object has with another.
Therefore if you create an object now it will be deallocated when it fall out of scope unless it has a strong relationship (is owned) by another object. You can still get reatain cycles where two objects have strong relationships to each other and therefore never qualify for deallocation.
Appologies for the very long winded answer but memory management is a core part of application programming and is very interesting.