...旧例子已编辑出来,见下面的代码......
如果我将那些中间str = "Blah \(odat.count)" + str
类型的行更改为str = str + "Blah \(odat.count)"
,则UI会停止并且我会获得色轮。 NSTextField确实到达第一个self.display.string...
,但随后冻结。
我是一名多线程新手,请随时纠正我的方法。希望它清楚我想要的东西。
我不得不承认,工作版本也有点陈词滥调但从未实际冻结过。 典型值为n = 70,var3 = 7.
编辑:
这是一个完整的例子。只需链接文本视图,进度条和按钮即可。尝试在主要功能之间切换。
//
// Controllers.swift
//
//
import Cocoa
class MainController: NSObject {
@IBOutlet var display: NSTextView!
@IBOutlet weak var prog: NSProgressIndicator!
@IBAction func go1(sender: AnyObject) {
theRoutine(70)
}
@IBAction func go2(sender: AnyObject) {
theRoutine(50)
}
class SomeClass {
var x: Int
var y: Int
var p: Double
init?(size: Int, pro: Double) {
x = size
y = size
p = pro
}
}
func theRoutine(n: Int) {
prog.hidden = false
prog.doubleValue = 0
prog.maxValue = 7 * 40
let priority = DISPATCH_QUEUE_PRIORITY_HIGH
dispatch_async(dispatch_get_global_queue(priority, 0)) {
self.theFunc(n, var1: 0.06, var2: 0.06, var3: 7)
self.theFunc(n, var1: 0.1*log(Double(n))/Double(n), var2: 0.3*log(Double(n))/Double(n), var3: 7)
dispatch_async(dispatch_get_main_queue()) {
self.prog.hidden = true
self.appOut("done!")
}
}
}
//This doesn't
// func theFunc(n: Int, var1: Double, var2: Double, var3: Int) {
// var m: AnEnum
// var gra: SomeClass
// var p = var1
// for _ in 0...(var3 - 1) {
// var str = "blah \(p)\n"
// for _ in 1...20 {
// gra = SomeClass(size: n, pro: p)!
// m = self.doSomething(gra)
// switch m {
// case .First(let dat):
// str = str + "Blah:\n\(self.arrayF(dat, transform: {"blah\($0)blah\($1)=blah"}))" + "\n\n" + str
// case .Second(let odat):
// str = str + "Blah\(odat.count) blah\(self.arrayF(odat, transform: {"bl\($1)"}))" + "\n\n" + str
// }
// dispatch_async(dispatch_get_main_queue()) {
// self.prog.incrementBy(1)
// }
// }
// dispatch_async(dispatch_get_main_queue()) {
// // update some UI
// self.display.string = str + "\n" + (self.display.string ?? "")
// }
// p += var2
// }
// }
//This works
func theFunc(n: Int, var1: Double, var2: Double, var3: Int) {
var m: AnEnum
var gra: SomeClass
var p = var1
for _ in 0...(var3 - 1) {
var str = "blah \(p)\n"
for _ in 1...20 {
gra = SomeClass(size: n, pro: p)!
m = self.doSomething(gra)
switch m {
case .First(let dat):
str = "Blah:\n\(self.arrayF(dat, transform: {"blah\($0)blah\($1)=blah"}))" + "\n\n" + str
case .Second(let odat):
str = "Blah\(odat.count) blah\(self.arrayF(odat, transform: {"bl\($1)"}))" + "\n\n" + str
}
dispatch_async(dispatch_get_main_queue()) {
self.prog.incrementBy(1)
}
}
dispatch_async(dispatch_get_main_queue()) {
// update some UI
self.display.string = str + "\n" + (self.display.string ?? "")
}
p += var2
}
}
func doSomething(G: SomeClass) -> AnEnum {
usleep(30000)
if drand48() <= G.p {
return AnEnum.First([0, 0])
} else {
return AnEnum.Second([1, 1, 1])
}
}
enum AnEnum {
case First([Int])
case Second([Int])
}
func appOut(out: String?) {
if out != nil {
display.string = out! + "\n\n" + (display.string ?? "")
}
}
func arrayF(array: [Int], transform: (index: Int, value: Int) -> String) -> String {
let arr = Array(0...(array.count - 1))
return "[\(arr.map{transform(index: $0, value: array[$0])}.joinWithSeparator(", "))]"
}
}
答案 0 :(得分:3)
因为除了
之外你并没有真正提出问题你能相信吗?
我会告诉你,我当然可以,但严重的是,有很多情况下,预先添加某些东西可能比追加更慢/更快。以链接列表为例。如果您没有引用列表的最后一个元素,则Prepend为O(1),Append为O(N)。
我把那个特定问题的主题放在一起,并且在5-6次运行中它似乎没有显着差异,但是我的机器上的前置仍然慢了10%。
有趣的是,在你的情况下,你基本上有4种方式在Swift中连接字符串:
str = newstr + str
str = str + newstr
str.append(newstr)
a = []; a.append(x); str = a.joined(separator: " ")
在我的机器上,他们似乎几乎同时偏离了这样的典型时机:
prepend
real 0m0.082s
user 0m0.060s
sys 0m0.018s
append
real 0m0.070s
user 0m0.049s
sys 0m0.018s
append mutate
real 0m0.075s
user 0m0.054s
sys 0m0.019s
join
real 0m0.086s
user 0m0.064s
sys 0m0.020s
追加最快。
您可以在我的要点中查看所有四种情况的代码 https://gist.github.com/ojosdegris/df72a94327d12a67fe65e5989f9dcc53
如果您在Github上查看Swift源代码,您会看到:
@effects(readonly)
@_semantics("string.concat")
public static func + (lhs: String, rhs: String) -> String {
if lhs.isEmpty {
return rhs
}
var lhs = lhs
lhs._core.append(rhs._core)
return lhs
}
因此,随着累加器字符串的增长,可能会发生这种情况,复制它的成本会更高。
答案 1 :(得分:1)
Vittore的回答是正确的。查看Swift的String(stdlib/public/core/String.swift)源代码,我们可以看到:
虽然Swift中的字符串具有值语义,但字符串使用写时复制 将数据存储在缓冲区中的策略。然后可以共享此缓冲区 通过不同的字符串副本。字符串的数据只是懒惰地复制, 在变异时,当多个字符串实例使用相同的字符串实例时 缓冲。因此,任何变异操作序列中的第一个都可以 花费O( n )的时间和空间。
当字符串的连续存储填满时,必须分配新的缓冲区 并且必须将数据移动到新存储。字符串缓冲区使用 指数增长策略,使字符串附加一个常量 许多追加操作的平均时间操作。
每个维基百科的Copy-on-write:
如果资源重复但未修改,则无需创建新资源;资源可以在副本和原始文件之间共享。
考虑到这一点,在执行str = str + "abc"
时,编译器正在执行str
的浅表副本并将"abc"
附加到其连续内存中。另一方面,str = "abc" + str
创建一个具有自己唯一数据副本的独立实例,因为它不再使用连续内存。