先说结论吧:
@Bill Cheng、
@麦子龙和
@李铁柱的答案都是片面的。
导致这个 bug 的元凶,在 iOS 6 上是 WebCore,在 OS X 10.8 上是 CoreText。
========
以下是反驳
@Bill Cheng的观点:
这是那个越狱补丁的源码:
https:// github.com/FilippoBiga/ GlyphPatch/blob/master/Tweak.mm很明显是给 WebCore 打的补丁。
代码的注释里提到了 WebCore 的 characterRangeCodePath() 函数会在处理 U+0600 ~ U+109F 的字符(其中 U+0600 ~ U+06FF、U+0750 ~ U+077F 是阿拉伯字母)时当成复杂类型,而它调用的 ComplexTextController::adjustGlyphsAndAdvances() 方法在处理这种类型的文字时却出错了。
以下代码来自 WebKit 源码(
https:// github.com/WebKit/webki t/blob/6d656cf12b0b12cfb5555dd50d5d908d6d2793ff/Source/WebCore/platform/graphics/mac/FontComplexTextMac.cpp#L59):
float Font::getGlyphsAndAdvancesForComplexText(const TextRun& run, int from, int to, GlyphBuffer& glyphBuffer, ForTextEmphasisOrNot forTextEmphasis) const { float initialAdvance; ComplexTextController controller(this, run, false, 0, forTextEmphasis); controller.advance(from); float beforeWidth = controller.runWidthSoFar(); controller.advance(to, &glyphBuffer); if (glyphBuffer.isEmpty()) return 0; float afterWidth = controller.runWidthSoFar(); if (run.rtl()) { initialAdvance = controller.totalWidth() + controller.finalRoundingWidth() - afterWidth; glyphBuffer.reverse(0, glyphBuffer.size()); } else initialAdvance = beforeWidth; return initialAdvance; }
它构造了一个 ComplexTextController 类型的对象,这个对象在初始化时崩溃了。
这个类是 WebCore 框架的,根据
@麦子龙和匿名用户的回答(
http://www. zhihu.com/question/2156 8134/answer/18652888),这里并没有更深的调用栈(也不排除调用了内联函数),基本可以认定是 WebCore 的 bug。我并不同意
@麦子龙说异常栈可能不完整的问题,因为汇编代码都显示出来了,在哪崩溃是很显而易见的。
补丁给出的解决办法是将类型设为自动,也就不会调用上述方法了。但这种解决办法也许会引来其他的 bug(主要是显示方面,对不用阿拉伯语的人应该没影响);只是我对阿拉伯语不了解,也就不妄做判断了。
更直接的依据是
@祝博韬的回答:iOS 6 的 CoreText 是能正常显示这段文字的。
========
但上述结论也并不代表 CoreText 就没问题,我今天在测试时发现某些字符组合甚至可以让 OS X 10.8 的 Terminal 崩溃,而它是不太可能使用 WebCore 的。
此外,这个 bug 并不是在 iOS 7 和 OS X 10.9 上就不存在了,在某些情况下仍能造成 crash。例如用那串文字做文件名,那么 Finder 在显示这个文件时就会崩溃。这也让我觉得 CoreText 很可疑。
以 Mac Safari 的崩溃异常栈为例,它是在 CoreText 里崩溃的;虽然也用到了 WebCore,但崩溃的位置和 iOS 下不一样:
Thread 0 Crashed:: Dispatch queue: com.apple.main-thread 0 libvDSP.dylib 0x00007fff8efdbadb 0x7fff8efbf000 + 117467 1 com.apple.CoreText 0x00007fff8e6c1d5c TRun::TRun(TRun const&, CFRange, TRun::SubrangingStyle) + 850 2 com.apple.CoreText 0x00007fff8e6c19ee CTGlyphRun::CloneRange(CTRun const*, CFRange, TRun::SubrangingStyle) + 142 3 com.apple.CoreText 0x00007fff8e6d0764 TLine::SetLevelRange(CFRange, unsigned char, bool) + 162 4 com.apple.CoreText 0x00007fff8e6d1e2c TLine::SetTrailingWhitespaceLevel(unsigned char) + 70 5 com.apple.CoreText 0x00007fff8e6d1d58 TRunReorder::ReorderRuns(TBidiLevelsProvider const&, TLine&) + 122 6 com.apple.CoreText 0x00007fff8e6d1bfe TTypesetter::FinishLineFill(TLine&, double, double) const + 142 7 com.apple.CoreText 0x00007fff8e6c1775 CTTypesetterCreateLine + 131 8 com.apple.WebCore 0x00007fff8f7f1f1a WebCore::ComplexTextController::collectComplexTextRunsForCharactersCoreText(unsigned short const*, unsigned int, unsigned int, WebCore::SimpleFontData const*) + 1562 9 com.apple.WebCore 0x00007fff8f7f176a WebCore::ComplexTextController::collectComplexTextRuns() + 522
这是 Mac QQ 崩溃的异常栈,受伤的也是 CoreText:
Exception Type: EXC_CRASH (SIGSEGV) Exception Codes: 0x0000000000000000, 0x0000000000000000 Application Specific Information: Performing @selector(paste:) from sender NSMenuItem 0x6b85170 Thread 0:: Dispatch queue: com.apple.main-thread 0 com.apple.CoreText 0x9568e48f TStorageRange::SetStorageSubRange(CFRange) + 307 1 com.apple.CoreText 0x9568cdf7 TRun::TRun(TRun const&, CFRange, TRun::SubrangingStyle) + 853 2 com.apple.CoreText 0x9568ca8e CTGlyphRun::CloneRange(CTRun const*, CFRange, TRun::SubrangingStyle) + 166 3 com.apple.CoreText 0x95699947 TLine::SetLevelRange(CFRange, unsigned char, bool) + 157 4 com.apple.CoreText 0x9569b000 TLine::SetTrailingWhitespaceLevel(unsigned char) + 86 5 com.apple.CoreText 0x9569aee7 TRunReorder::ReorderRuns(TBidiLevelsProvider const&, TLine&) + 107 6 com.apple.CoreText 0x9569ad8c TTypesetter::FinishLineFill(TLine&, double, double) const + 122 7 com.apple.CoreText 0x9568c870 TTypesetter::FillLine(TLine&, CFRange, double, double) const + 90 8 com.apple.CoreText 0x9568c7ff CTTypesetterCreateLine + 185 9 com.apple.AppKit 0x98085a9a -[NSATSLineFragment layoutForStartingGlyphAtIndex:characterIndex:minPosition:maxPosition:lineFragmentRect:] + 802 10 com.apple.AppKit 0x9808452c -[NSATSTypesetter _layoutLineFragmentStartingWithGlyphAtIndex:characterIndex:atPoint:renderingContext:] + 2254 11 com.apple.AppKit 0x980a91cb -[NSATSTypesetter layoutParagraphAtPoint:] + 147 12 com.apple.AppKit 0x98645f21 -[NSTypesetter _layoutGlyphsInLayoutManager:startingAtGlyphIndex:maxNumberOfLineFragments:maxCharacterIndex:nextGlyphIndex:nextCharacterIndex:] + 3403 13 com.apple.AppKit 0x980a813a -[NSTypesetter layoutCharactersInRange:forLayoutManager:maximumNumberOfLineFragments:] + 215 14 com.apple.AppKit 0x980a8011 -[NSATSTypesetter layoutCharactersInRange:forLayoutManager:maximumNumberOfLineFragments:] + 1185 15 com.apple.AppKit 0x980a69c5 -[NSLayoutManager(NSPrivate) _fillLayoutHoleForCharacterRange:desiredNumberOfLines:isSoft:] + 738 16 com.apple.AppKit 0x980d9f1e _NSFastFillAllLayoutHolesForGlyphRange + 227 17 com.apple.AppKit 0x980a4f95 -[NSLayoutManager textContainerForGlyphAtIndex:effectiveRange:] + 198 18 com.apple.AppKit 0x97ee4a80 -[NSTextView(NSPrivate) _ensureLayoutCompleteToEndOfCharacterRange:] + 125 19 com.apple.AppKit 0x97ee442b -[NSTextView(NSSharing) didChangeText] + 159 20 com.apple.AppKit 0x97ee1f71 -[NSTextView insertText:replacementRange:] + 2261 21 com.tencent.qq 0x003ed850 -[TXTypingTextView insertText:replacementRange:] + 163 22 com.apple.AppKit 0x985b57f0 -[NSTextView insertText:] + 319 23 com.tencent.qq 0x003cfd6c -[TXBaseTextView AddAttributedString:] + 69 24 com.tencent.qq 0x003d2a0c -[TXBaseTextView ReadFromPasteborad:] + 392 25 com.tencent.qq 0x003ee97f -[TXTypingTextView paste:] + 98 26 libobjc.A.dylib 0x96dbc5d3 -[NSObject performSelector:withObject:] + 70 27 com.apple.AppKit 0x98110ad2 -[NSApplication sendAction:to:from:] + 436 28 com.apple.AppKit 0x9824d2fc -[NSMenuItem _corePerformAction] + 529 29 com.apple.AppKit 0x9824cf8b -[NSCarbonMenuImpl performActionWithHighlightingForItemAtIndex:] + 163 30 com.apple.AppKit 0x9824c614 -[NSMenu _performActionWithHighlightingForItemAtIndex:sendAccessibilityNotification:] + 79 31 com.apple.AppKit 0x9824c5c0 -[NSMenu _performActionWithHighlightingForItemAtIndex:] + 48 32 com.apple.AppKit 0x9824bac5 -[NSMenu performKeyEquivalent:] + 306 33 com.tencent.qq 0x0053233e -[TXMenu performKeyEquivalent:] + 503 34 com.apple.AppKit 0x9824ae7c -[NSApplication _handleKeyEquivalent:] + 915 35 com.apple.AppKit 0x98100de1 -[NSApplication sendEvent:] + 5512 36 com.apple.AppKit 0x9801a62c -[NSApplication run] + 951 37 com.apple.AppKit 0x97fbd5f6 NSApplicationMain + 1053 38 com.tencent.qq 0x000028b5 start + 53
这就说明在 OS X 10.8 上,CoreText 才是真凶。
因此 iOS 和 OS X 上各有不同的 bug,而不能想当然地认为凶手只有一个。