With one difference: the reference count is usually stored inside the Object's pointer (taking advantage of the fact that allocations are 16-byte aligned so the lowest 4 bits are otherwise idle). This means that there's no storage overhead on the reference count.
Weak references don't primarily exist to deal with cycles, and cycles generally often involve necessarily strong references even in systems that support weak references. (That is, e.g., cycles where A needs to be reachable if B is reachable and vice versa, such that a weak reference that allowed B to be disposed while A was reachable or vice versa would not be acceptable. A system that can detect cyclic garbage will still be able to collect both A and B -- connected by strong references -- when neither can be reached from the rest of the program.)
The standard use case for weak references in Objective-C is a child-to-parent reference. The parent holds a strong reference to the child and children hold weak references to their parents, avoiding potential cycles.
In fact, part of good memory management in a reference counted system is that you should always have a hierarchy to your data structures so it never makes sense to have an actual strong reference loop.
So then you play pass-the-strong-reference in a destructor - i.e. if A goes out of scope the A->B ref is weakened and the B->A ref is strengthened, then vice versa if required.
We did this for DBIx::Class in perl, thereby keeping full liveness but still getting timely destruction for connection objects. It was a trifle insane to get right, but it works extremely well.
Sort of, CPython has both reference counting and a normal Garbage Collection. You can disable the latter and still avoid memory leaks as long as you don't have cycles