lea*_*lin 10 cocoa objective-c nslayoutmanager nsattributedstring nstextstorage
在AppKit中有没有办法快速测量大量NSString对象(比如一百万)的宽度?我尝试了3种不同的方法:
Count\Mechanism sizeWithAttributes NSAttributedString NSLayoutManager
1000 0.057 0.031 0.007
10000 0.329 0.325 0.064
100000 3.06 3.14 0.689
1000000 29.5 31.3 7.06
NSLayoutManager显然是要走的路,但问题在于
如果你想玩,这是github项目.
这里有一些我还没有尝试过的想法。
\n\n直接使用核心文本。其他 API 均构建在其之上。
并行化。所有现代 Mac(甚至所有现代 iOS 设备)都具有多个内核。将字符串数组分成几个子数组。对于每个子数组,将一个块提交到全局 GCD 队列。在块中,创建必要的核心文本或NSLayoutManager对象并测量子数组中的字符串。通过这种方式可以安全地使用这两个 API。(核心文本) ( NSLayoutManager)
关于\xe2\x80\x9c高内存占用\xe2\x80\x9d:使用本地自动释放池块来减少峰值内存占用。
关于 \xe2\x80\x9c 所有花费的时间都是在创建上述字符串期间,这本身就是一个破坏者\xe2\x80\x9d:你是说所有时间都花在这些行上:
\n\ndouble random = (double)arc4random_uniform(1000) / 1000;\nNSString *randomNumber = [NSString stringWithFormat:@"%f", random];\n格式化浮点数的成本很高。这是您的真实用例吗?如果您只想为 0 \xe2\x89\xa4 n < 1000 格式化 n/1000 形式的随机有理数,有更快的方法。此外,在许多字体中,所有数字都具有相同的宽度,因此可以轻松排版数字列。如果您选择这样的字体,您可以首先避免测量字符串。
这是我使用 Core Text 编写出的最快的代码。调度版本几乎是我的 Core i7 MacBook Pro 上单线程版本的两倍。我的项目的分支在这里。
\n\nstatic CGFloat maxWidthOfStringsUsingCTFramesetter(\n        NSArray *strings, NSRange range) {\n    NSString *bigString =\n        [[strings subarrayWithRange:range] componentsJoinedByString:@"\\n"];\n    NSAttributedString *richText =\n        [[NSAttributedString alloc]\n            initWithString:bigString\n            attributes:@{ NSFontAttributeName: (__bridge NSFont *)font }];\n    CGPathRef path =\n        CGPathCreateWithRect(CGRectMake(0, 0, CGFLOAT_MAX, CGFLOAT_MAX), NULL);\n    CGFloat width = 0.0;\n    CTFramesetterRef setter =\n        CTFramesetterCreateWithAttributedString(\n            (__bridge CFAttributedStringRef)richText);\n    CTFrameRef frame =\n        CTFramesetterCreateFrame(\n            setter, CFRangeMake(0, bigString.length), path, NULL);\n    NSArray *lines = (__bridge NSArray *)CTFrameGetLines(frame);\n    for (id item in lines) {\n        CTLineRef line = (__bridge CTLineRef)item;\n        width = MAX(width, CTLineGetTypographicBounds(line, NULL, NULL, NULL));\n    }\n    CFRelease(frame);\n    CFRelease(setter);\n    CFRelease(path);\n    return (CGFloat)width;\n}\n\nstatic void test_CTFramesetter() {\n    runTest(__func__, ^{\n        return maxWidthOfStringsUsingCTFramesetter(\n            testStrings, NSMakeRange(0, testStrings.count));\n    });\n}\n\nstatic void test_CTFramesetter_dispatched() {\n    runTest(__func__, ^{\n        dispatch_queue_t gatherQueue = dispatch_queue_create(\n            "test_CTFramesetter_dispatched result-gathering queue", nil);\n        dispatch_queue_t runQueue =\n            dispatch_get_global_queue(QOS_CLASS_UTILITY, 0);\n        dispatch_group_t group = dispatch_group_create();\n\n        __block CGFloat gatheredWidth = 0.0;\n\n        const size_t Parallelism = 16;\n        const size_t totalCount = testStrings.count;\n        // Force unsigned long to get 64-bit math to avoid overflow for\n        // large totalCounts.\n        for (unsigned long i = 0; i < Parallelism; ++i) {\n            NSUInteger start = (totalCount * i) / Parallelism;\n            NSUInteger end = (totalCount * (i + 1)) / Parallelism;\n            NSRange range = NSMakeRange(start, end - start);\n            dispatch_group_async(group, runQueue, ^{\n                double width =\n                    maxWidthOfStringsUsingCTFramesetter(testStrings, range);\n                dispatch_sync(gatherQueue, ^{\n                    gatheredWidth = MAX(gatheredWidth, width);\n                });\n            });\n        }\n\n        dispatch_group_wait(group, DISPATCH_TIME_FOREVER);\n\n        return gatheredWidth;\n    });\n}\n| 归档时间: | 
 | 
| 查看次数: | 763 次 | 
| 最近记录: |