First of all, __block is a storage classifier for variables, and it is equally suitable for all types of variables. Is this a variable of primitive type, such as a pointer-object, etc. (By the way, a variable cannot have an "object type"); irrelevant. Variables of all types are usually on the stack if they are local variables of the function. Referring to a local variable of type "pointer-object" after its scope came out equally in undefined behavior.
About your question, the answer is that __block variables __block not only stored on the stack. They can also be stored in a heap. How it works is an implementation detail, but it is guaranteed by the specification for use in all the blocks that fix it.
If you want to know how this is implemented, you can read the Clang Block Implementation Specification (although I think there are some typos). Essentially, when you have a __block variable, you actually have a data structure where the variable is a field. And the data structure also has a pointer to track where the current version is. Access to a variable is implicitly translated by the compiler to access it through these levels of indirection.
Like blocks, this data structure starts on the stack for optimization, but can be βmovedβ to the heap (it is moved for the first time when any block that captures it is moved to the heap, because when the life time may require an excess of volume, in which it was created). When it "moves" to the heap, the pointer that says where it is is updated to let people know to use the new copy. This data structure, when on the heap, is memory managed by a reference counting system.
Blocks in which __block variables are fixed have a copy of the "current version" pointer, which is updated when the data structure is moved, so they know where to access the variable both before and after the move.
About your tests, you should be very careful in choosing the address of the __block variable, as the location of the variable moves throughout life! (which usually does not happen in C).
When you first take the spam address, it is still on the stack, so you have the address of its stack version. spam is fixed in a block, which is then assigned to the myProperty property, whose setter copies the block, moving it to the heap, and also moving the spam to the heap. addrOfSpam still points to the stack version of the variable, which is no longer the used version; using it to change an integer changes the wrong version of a variable, because now everyone uses a copy of the heap.