If you want to do that, you are probably better off writing a small app and using your own classes to test it with. Overriding the behavior of a public API like NSString won't work the way you think, because you don't control the code. It doesn't help that NSString never returns an NSString when you create one.
As for how autorelease pools work, think of each thread having a 'stack'. When you allocate a pool, it pushes that pool onto this stack. When you release it, it pops it off the stack. This may not be 100% accurate, but it is what I have seen in terms of behavior so far (EDIT: This is actually accurate, I checked the docs, and it does use a stack the way I describe).
When you autorelease an object, it adds it to the pool currently on the top of this stack. Then, when you release the pool, it sends a release message to every object in the pool. If you autorelease an object multiple times, then the pool will send multiple release messages. That is really all it does.
The idea being that if you alloc & init an object, it has a retain count of 1. Then if you want to return this object as a return value you autorelease it. This ensures the object continues to live long enough that the method that called you can use the object, but that it will get a release message later on to balance out your init. If the method that called you wants to keep the object, it should retain it, and then release it later when it is done with it.