One solution to this kind of thing is to wrap your objects in a weak proxy object. Here is an example implementation of such an object:
https://github.com/j-h-a/jalib-core/blob/master/JALibCore/Helpers/JAWeakProxy.h
https://github.com/j-h-a/jalib-core/blob/master/JALibCore/Helpers/JAWeakProxy.m
Then you can do this:
NSMutableArray* a = [NSMutableArray array];
NSMutableArray* b = [NSMutableArray array];
[a addObject:[JAWeakProxy weakProxyWithTarget:b]];
[b addObject:[JAWeakProxy weakProxyWithTarget:a]];
Now both a and b are weakly referenced within the array. The good thing about using weak proxy objects is that you don't need to 'unbox' the real object inside them - you can just send messages (call methods) directly on the weak proxy object as if it were the target and it will pass the message along. For example:
[a[0] addObject:@(0)];
Notice how a[0] actually returns the weak proxy object holding b as its target, but I can sill send addObject directly to this weak proxy representing b (which is an NSMutableArray) and the implementation of the weak proxy will ensure that the message is forwarded to b.
However, you do lose some compile-time type-checking, so this technique is best used to help with the internal implementation of some class, which will have well-typed methods to access and enumerate the contents.
When I use such things I usually put in some auto-clean-up code into the array enumerators. That is I hide the arrays inside a utility class and provide block-based enumeration methods and I also keep a toRemove array. When iterating I skip any objects that the target has auto-zeroed to nil and add them to the toRemove array (the weak proxy object still exists even though its target is gone), then afterwards I iterate through the toRemove array and remove the proxy objects. You can also check if the target is nil and add it to the toRemove array in any accessor helpers.