我正在创建一个应用程序,我想绘制很多形状 - 圆圈,方框,线条等。 数以百万计。
为了测试这个的表现,我把这个简单的UIView汇总在一起。请注意,信用到期 - 我受到了this project的启发。
import UIKit
let qkeyString = "label" as NSString
var QKEY = qkeyString.UTF8String
let qvalString = "com.hanssjunnesson.Draw" as NSString
var QVAL = qvalString.UTF8String
public class RenderImageView: UIView {
var bitmapContext: CGContext?
let drawQueue: dispatch_queue_attr_t = {
let q = dispatch_queue_create(QVAL, nil)
dispatch_queue_set_specific(q, QKEY, &QVAL, nil)
return q
}()
public override init() {
super.init()
render()
}
required public init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
render()
}
override required public init(frame: CGRect) {
super.init(frame: frame)
render()
}
public override func drawRect(rect: CGRect) {
if let bitmapContext = self.bitmapContext {
let context = UIGraphicsGetCurrentContext()
let image = CGBitmapContextCreateImage(bitmapContext)
CGContextDrawImage(context, self.bounds, image)
}
}
private func render() {
dispatch_async(drawQueue) {
let startDate = NSDate()
let bounds = self.bounds
UIGraphicsBeginImageContextWithOptions(bounds.size, false, 0.0)
let context = UIGraphicsGetCurrentContext()
self.bitmapContext = context
CGContextSetFillColorWithColor(context, UIColor.whiteColor().CGColor)
CGContextFillRect(context, bounds)
CGContextSetFillColorWithColor(context, UIColor(red: 0.15, green: 0.4, blue: 0.8, alpha: 1.0).CGColor)
for i in 1...1000000 {
CGContextFillEllipseInRect(context, bounds)
}
UIGraphicsEndImageContext()
self.setNeedsDisplay()
let benchmark = startDate.timeIntervalSinceNow
println("Rendering took: \(-benchmark*1000) Ms")
}
}
}
这很好用。在我的iOS模拟器上,每隔一百万个圆圈就需要花一点多时间。
我想加快速度,所以我尝试从多个线程绘制到位图上下文。
let group = dispatch_group_create()
for i in 1...100 {
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0)) {
dispatch_group_enter(group)
CGContextFillEllipseInRect(context, bounds)
dispatch_group_leave(group)
}
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER)
但是,这没有用。在致电EXC_BAD_ACCESS
时,我会收到CGContextFillEllipseInRect(context, bounds)
。
在后台线程中绘制到CGContext
似乎没问题,只要它与创建它的线程相同。
任何人都知道让这个工作的方法吗?
答案 0 :(得分:4)
1)您实际上并没有等待您创建的组完成 - 在执行任何块之前,将在该代码中调用dispatch_group_wait
,因此在其中进入/离开调用不会有任何影响。请改用dispatch_group_async
(见下文)。
2)您无法同时从两个不同的线程绘制到CGContext
- 如果在绘图循环中添加println,则可以看到这一点。它会工作几次,结果各不相同,但最终你最终会出错。
let group = dispatch_group_create()
for i in 1...10 {
dispatch_group_async(group, dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0)) {
for j in 1...100 {
println("i:\(i), j:\(j)")
CGContextFillEllipseInRect(context, bounds)
}
}
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER)
示例输出:
iiii::::4123,,,, jjjj::::1111
ii:1, j:2
:5, j:1
i:6, j:1
EXC_BAD_ACCESS
唯一的解决办法就是跳回单个线程进行绘图,但这会以任何方式击败你试图做的事情。如果你必须做很多计算来决定绘制什么,那可能会在不同的线程上发生,但是绘制到CGContext
本身并不是线程安全的。