使用 CGImage / CGLayer 在另一个线程中绘图
Posted
技术标签:
【中文标题】使用 CGImage / CGLayer 在另一个线程中绘图【英文标题】:Drawing in another thread with CGImage / CGLayer 【发布时间】:2013-05-14 13:48:23 【问题描述】:我有自定义 UICollectionViewCell 子类,我在其中使用剪切、描边和透明度进行绘制。它在 Simulator 和 iPhone 5 上运行良好,但在旧设备上存在明显的性能问题。
所以我想把耗时的绘图移到后台线程。由于 -drawRect 方法总是在主线程上调用,我最终将绘制的上下文保存到 CGImage(原始问题包含使用 CGLayer 的代码,但正如 Matt Long 指出的那样,它有点过时了)。
这是我在这个类中的 drawRect 方法的实现:
-(void)drawRect:(CGRect)rect
CGContextRef ctx = UIGraphicsGetCurrentContext();
if (self.renderedSymbol != nil)
CGContextDrawImage(ctx, self.bounds, self.renderedSymbol);
定义此 renderSymbol 属性的渲染方法:
- (void) renderCurrentSymbol
[self.queue addOperationWithBlock:^
// creating custom context to draw there (contexts are not thread safe)
CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
CGContextRef ctx = CGBitmapContextCreate(nil, self.bounds.size.width, self.bounds.size.height, 8, self.bounds.size.width * (CGColorSpaceGetNumberOfComponents(space) + 1), space, kCGImageAlphaPremultipliedLast);
CGColorSpaceRelease(space);
// custom drawing goes here using 'ctx' context
// then saving context as CGImageRef to property that will be used in drawRect
self.renderedSymbol = CGBitmapContextCreateImage(ctx);
// asking main thread to update UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^
[self setNeedsDisplayInRect:self.bounds];
];
CGContextRelease(ctx);
];
这个设置在主线程上完美运行,但是当我用 NSOperationQueue 或 GCD 包装它时,我得到了很多不同的“invalid context 0x0”错误。应用程序本身不会崩溃,但不会发生绘图。我想发布自定义创建的 CGContextRef 有问题,但我不知道该怎么办。
这是我的财产声明。 (我尝试使用原子版本,但没有帮助)
@property (nonatomic) CGImageRef renderedSymbol;
@property (nonatomic, strong) NSOperationQueue *queue;
@property (nonatomic, strong) NSString *symbol; // used in custom drawing
属性的自定义设置器/获取器:
-(NSOperationQueue *)queue
if (!_queue)
_queue = [[NSOperationQueue alloc] init];
_queue.name = @"Background Rendering";
return _queue;
-(void)setSymbol:(NSString *)symbol
_symbol = symbol;
self.renderedSymbol = nil;
[self setNeedsDisplayInRect:self.bounds];
-(CGImageRef) renderedSymbol
if (_renderedSymbol == nil)
[self renderCurrentSymbol];
return _renderedSymbol;
我能做什么?
【问题讨论】:
您的第一个drawInRect
最终会绘制一个 NULL-CGLayer
,因为您在 renderedSymbol
中异步调用 renderCurrentSymbol
但会立即返回 _renderedSymbol
。
您是否尝试过在释放 CGLayer 之前不执行 CGContextRelease(ctx)(大概在 dealloc 中?)
@MikePollard 释放上下文是正确的,如果你不释放上下文就会有内存泄漏。
是的,我不是建议不释放它,而是在dealloc中释放它,这大概是释放CGLayer的地方,否则此刻正在泄漏。
invalid context 0x0 通常意味着您的 CGContext
为 nil 或其大小为零。你应该为这两件事调试你的程序。
【参考方案1】:
您是否注意到您引用的 CGLayer 上的文档自 2006 年以来一直没有更新?您认为 CGLayer 是正确解决方案的假设是不正确的。 Apple 几乎放弃了这项技术,您可能也应该这样做:http://iosptl.com/posts/cglayer-no-longer-recommended/ 使用 Core Animation。
【讨论】:
哇。谢谢!在 CGLayer 之前,我尝试通过将上下文保存到 CGImageRef 来做同样的事情(就像在Stack Overflow 上的这个问题中解释的那样),但它也不起作用。仍然收到此 0x0 无效上下文错误 我不会说 CGLayer 不是正确的解决方案,它“可能”不是,但它可以。 @RinatKhanov 我建议你从更高的层面提出你的问题,看看人们认为什么是最好的方法。从您的问题来看,在我看来,您可以只合成多个 Core Animation 层并让它为您完成所有艰苦的工作。我创建了许多自定义集合视图单元格,它们使用了诸如剪切描边和不透明度之类的东西。您可能想查看 CALayer 的 shouldRasterize 属性。它将提高绘图性能。您甚至可以考虑使用图像而不是以编程方式绘图,因为这也可以提高性能。 @MattLong 在集合视图中,我需要上千个带有文本符号或图像的圆角矩形(带有描边和透明角)。我在应用程序的其他部分需要该符号作为文本表示,因此使用图像不是我特定情况的解决方案。我试图关闭那个圆角矩形(用作背景),但令人惊讶的是应用程序在旧设备上滚动时感觉很慢。这就是为什么我尝试处理大量渲染到后台线程的原因。我去看看CALayer,谢谢。【参考方案2】:通过 Mind Snacks 使用令人惊叹的第三方库解决了问题 — MSCachedAsyncViewDrawing。
【讨论】:
以上是关于使用 CGImage / CGLayer 在另一个线程中绘图的主要内容,如果未能解决你的问题,请参考以下文章