If we imagine how we'd use an atomic integer in another language (say, C++) across two types:
// 🛑 WRONG! Producer and Consumer aren't sharing state!
struct Producer {
std::atomic<int> value;
};
struct Consumer {
std::atomic<int> value;
};
We can immediately see we need to share a reference to the same atomic value across a producer/consumer pair, because each std::atomic<int> instance has a unique location in memory.
In Swift, value types do not have unique locations in memory by default. Swift uses ~Copyable to tell the compiler that an instance of a type cannot be copied; as an intentional side effect and with dome some behind-the-scenes attribute magic, the compiler can then reliably store instances of such types at fixed locations in memory (knowing no other component of the system will accidentally make a copy in another location.)
In C++, to resolve this issue, you'd share a single instance of std::atomic<int> between a producer/consumer pair by assigning it a unique location in memory that both the producer and consumer know about: in other words, they'd reference it by pointer (or by reference, but let's ignore that for now):
struct Producer {
std::atomic<int> *value;
};
struct Consumer {
std::atomic<int> *value;
};
std::pair<Producer, Consumer> makePCPair() {
std::atomic<int> *value = ...; // heap-allocate, get from a bucket, whatever
return std::make_pair(Producer(value), Consumer(value));
}
void destroyPCPair(Producer&& producer, Consumer&& consumer) {
assert(producer->value == consumer->value);
delete producer->value; // or however you need to clean it up
producer->value = nullptr;
consumer->value = nullptr;
}
In Swift, you need to do something broadly similar. You could allocate the Atomic<Int> manually using UnsafeMutablePointer:
struct Producer {
nonisolated(unsafe) let value: UnsafePointer<Atomic<Int>>
}
struct Consumer {
nonisolated(unsafe) let value: UnsafePointer<Atomic<Int>>
}
func makePCPair() -> (Producer, Consumer) {
let value: UnsafePointer<Atomic<Int>> = ... // as with C++
return (Producer(value: value), Consumer(value: value))
}
func destroyPCPair(_ producer: consuming Producer, _ consumer: consuming Consumer) {
precondition(producer.value == consumer.value)
producer.value.deinitialize(count: 1)
producer.value.deallocate()
}
This looks suspiciously like the C++ strawman implementation above. Unfortunately, using unsafe pointers is pretty ugly and you end up being responsible for cleaning up any allocated memory manually (as you were in the C++ example.) Instead, you can store the atomic value inside a class (whose instances are refcounted and have fixed locations in memory already. @nkbelov provided a good example already of how to do that. 
So why not just make Atomic a class to guarantee its fixed state and make it refcountable? Because reference types are significantly more expensive in terms of both space and time than a simple atomic value (which can be stored in as little as a register and accessed with direct CPU instructions.) So the primitive type provided by Swift is ~Copyable and callers that need to share them across space/time can opt into the necessary overhead.
I hope that was helpful!