You're missing a key concept of application programming. I'll try to explain briefly.
Applications are driven by what is commonly called an event loop. The application sits and waits until a mouse, keyboard, or some other event happens, and then processes and reacts to that event. Once it's done processing the event, it starts waiting again for another.
You're probably familiar with the beach ball, or spinning pizza of death, right? Well, what's happening there is the program is "hanging" or stuck processing some event and doesn't return to the "waiting for another event" state, so the program is unable to respond to any further events.
In Cocoa, drawing is triggered by a special kind of event. It happens whenever a part of a view "invalidates" itself, or says it needs to be redisplayed. When this event occurs, the application calls -drawRect: on the views that need updating, and they draw themselves. However, the drawing doesn't immediately appear on screen. After all the views in a window draw themselves, all the drawing for that window is "flushed" to the screen and becomes visible. (This is done for performance's sake.) Then, the application starts waiting for the next event again.
Attempting to draw "Hello World" a million times will definitely "block" the application from returning to the "waiting for next event" state, as well as the "flush" to the screen. That's why you don't see anything until the drawing is done.
If you want to see one "Hello World" drawn at a time over a period of time, you might want a timer that, when fired, causes an event during which you can add more point to draw Hello World at to your view, and tell the view to redraw itself (setNeedsDisplay:YES).
If you want to just experiment, go ahead and take a look at -[NSGraphicsContext flushGraphics], which should flush all pending drawing. (You may need -[NSWindow flushWindow] too.) Be aware though, this is not something that can reasonably be done in a real application. You'll need to use another technique.