你应该考虑使用NSOperation; NSInvocationOperation,如果你有一个特定的对象进行渲染,或者NSBlockOperation,如果渲染足够简单,可以放入单个函数中。
如果您可以在实际到达视图的drawRect: 之前开始渲染,那么就这样做(也许您的应用委托在启动时就开始了该过程)。否则,检查drawRect: 内容是否可用;如果没有,则开始操作并继续绘制另一幅图。当渲染对象完成它的工作时,它可能会发布一个通知,或者,如果你给它一个对视图的引用,调用setNeedsDisplay:
根据您最后一句话,您还可以考虑您的渲染对象能够返回部分渲染的图像。我不确定您的渲染的性质,但可能会在某些点(每 n 个循环的结尾,或每 n 行像素)获得结果,将其放入单独的 NSImage 的与最终图像大小相同(必要时在末尾填充),并使该部分图像可用于视图进行绘制。
更新:NSImage 不“依赖”setNeedsDisplay: 或具有视图引用,因为它不代表屏幕的一部分。它所做的只是包含图像的数据;它只能在内部一个视图中绘制自己,然后“显示”——实际上是在屏幕上绘制。当您使用initByReferencingURL: 时,它会存储 URL,然后当另一个对象(例如包含图像并需要显示的视图)向它询问其内容时,它会执行您使用 @ 时会执行的操作987654332@,即打开文件并将其内容读入内存。不过,它不会绘制。它只会将 绘制到需要它的视图中, 该视图正在绘制。
子类化NSImage 来实现自己的延迟加载或延迟渲染可能并不容易;它使用了我认为是class cluster 一部分的辅助类,这就是为什么我建议使用一个包含并返回NSImage 的“渲染器”对象。
更多:
自定义视图的drawRect:
- (void)drawRect:(NSRect)dirtyRect {
NSLog(@"Entered: %@", NSStringFromSelector(_cmd));
// Use a nice big image of the Milky Way -- this is about 5MB
NSImage * lazyImage = [[[NSImage alloc] initByReferencingURL:
[NSURL URLWithString:@"http://www.eso.org/public/archives/images/original/milkyway.jpg"]]
autorelease];
NSLog(@"Image instantiated.");
[lazyImage drawInRect:[self bounds] fromRect:NSZeroRect operation:NSCompositeCopy fraction:1.0];
NSLog(@"Image drawn"); // 2 minutes later; sometimes 3 in my testing
[[NSColor yellowColor] set];
[[NSBezierPath bezierPathWithRect:NSInsetRect([self bounds], 4, 4)] stroke];
NSLog(@"Bezier path drawn; exiting drawRect.");
}
由此产生的日志输出;请注意,实例化非常快,但加载和绘制需要两分钟,在此期间什么都没有绘制,应用程序什么也不做(旋转的沙滩球):
2011-04-27 21:33:00.899 SetNeedsDisplay[80162:a0b] 输入:drawRect:
2011-04-27 21:33:00.901 SetNeedsDisplay[80162:a0b] 图像实例化。
2011-04-27 21:34:57.911 SetNeedsDisplay[80162:a0b] 绘制的图像。
2011-04-27 21:34:57.912 SetNeedsDisplay[80162:a0b] 绘制的贝塞尔路径;退出drawRect。