0

我想在 NSTableView 中显示字体。如果我使用NSFont(name: fontName, size: size)一切都可以的字体。但在这种情况下,我只能使用系统中安装的字体。所以我做了一个 NSFont 扩展:

public extension NSFont {
    static func read(from path: String, size: CGFloat) throws -> NSFont {
        guard let dataProvider = CGDataProvider(filename: path) else {
            throw NSError(domain: "file not found", code: 77, userInfo: ["fileName" : path])
        }
        guard let fontRef = CGFont ( dataProvider ) else {
            throw NSError(domain: "Not a font file", code: 77, userInfo: ["fileName" : path])
        }
        return CTFontCreateWithGraphicsFont(fontRef, size, nil, nil) as NSFont
    }
}

它似乎有效,以这种方式制作的字体在[NSFont]数组中找到了它们的位置。但是如果尝试将它们绑定到程序中的NSTextFieldCell字体NSTableView会爆炸:

(this goes forever and throws Thread 1: EXC_BAD_ACCESS (code=2, address=0x7ffeef3ffff8))
......
......
#261509 0x00007fff6c023e63 in -[NSCTFont isEqual:] ()
#261510 0x00007fff4539908c in _CFNonObjCEqual ()
#261511 0x00007fff6c023e63 in -[NSCTFont isEqual:] ()
#261512 0x00007fff4539908c in _CFNonObjCEqual ()
#261513 0x00007fff6c023e63 in -[NSCTFont isEqual:] ()
#261514 0x00007fff6bfccf63 in -[NSAttributeDictionary isEqualToDictionary:] ()
#261515 0x00007fff6bfccc11 in attributeDictionaryIsEqual ()
#261516 0x00007fff475c6712 in hashProbe ()
#261517 0x00007fff475c6518 in -[NSConcreteHashTable getItem:] ()
#261518 0x00007fff6bfc5be8 in +[NSAttributeDictionary newWithDictionary:] ()
#261519 0x00007fff6bff7cbe in -[_NSCachedAttributedString initWithString:attributes:] ()
#261520 0x00007fff6bfdac1f in __NSStringDrawingEngine ()
#261521 0x00007fff6bff7380 in _NSStringDrawingCore ()
#261522 0x00007fff42b16477 in _NSDrawTextCell2 ()
#261523 0x00007fff42b15328 in __45-[NSTextFieldCell _drawForegroundOfTextLayer]_block_invoke ()
#261524 0x00007fff42a8c529 in -[NSFocusStack performWithFocusView:inWindow:usingBlock:] ()
#261525 0x00007fff42b14bff in -[NSTextFieldCell _drawForegroundOfTextLayer] ()
#261526 0x00007fff42b1445a in -[NSTextFieldCell updateLayerWithFrame:inView:] ()
#261527 0x00007fff42b14322 in -[NSControl updateLayer] ()
#261528 0x00007fff42afe301 in _NSViewUpdateLayer ()
......
......

我认为 CTFont 中缺少 NSFont 所拥有的东西。但是什么?

4

2 回答 2

1

你没有做错什么。这是 macOS 中的一个错误。

您可以转换为CTFontNSFont因为这些类型是“免费桥接”的。这意味着 aCTFont在内存中的布局符合 Objective-C 实例的要求。其中一个要求是对象的第一个字包含指向对象类的指针(称为“isa”指针)。在 a 的情况下CTFont,该类被命名为NSCTFont,它是 的子类NSFont

NSCTFont(在 UIFoundation 私有框架中定义)覆盖该isEqual:方法。如果您查看该函数的反汇编(并且如果您了解 x86 汇编),您会发现它的定义大致如下:

- (BOOL)isEqual:(NSObject *)other {
    if (other == 0) { return NO; }
    if (other == self) { return YES; }
    return _CFNonObjCEqual(self, other);
}

因此,如果对象没有明显不同(因为other是 nil)并且不明显相同(因为它们是相同的指针),那么这个isEqual:方法调用_CFNonObjCEqual,这是一个私有的 Core Foundation 函数。碰巧这_CFNonObjCEqual是 Core Foundation 开源版本的一部分,所以我们可以看看它的实现

Boolean _CFNonObjCEqual(CFTypeRef cf1, CFTypeRef cf2) {
    //cf1 is guaranteed to be non-NULL and non-ObjC, cf2 is unknown
    if (cf1 == cf2) return true;
    if (NULL == cf2) { CRSetCrashLogMessage("*** CFEqual() called with NULL second argument ***"); HALT; }
    CFTYPE_OBJC_FUNCDISPATCH1(Boolean, cf2, isEqual:, cf1);
    CFTYPE_SWIFT_FUNCDISPATCH1(Boolean, cf2, NSObject.isEqual, (CFSwiftRef)cf1);
    __CFGenericAssertIsCF(cf1);
    __CFGenericAssertIsCF(cf2);
    if (__CFGenericTypeID_inline(cf1) != __CFGenericTypeID_inline(cf2)) return false;
    if (NULL != __CFRuntimeClassTable[__CFGenericTypeID_inline(cf1)]->equal) {
        return __CFRuntimeClassTable[__CFGenericTypeID_inline(cf1)]->equal(cf1, cf2);
    }
    return false;
}

注释告诉我们什么是预期的:cf1参数必须知道是不是原生 Objective-C 实例的 Core Foundation 类型,但cf2参数可能是原生 Objective-C 实例。

重要的是这一行:

    CFTYPE_OBJC_FUNCDISPATCH1(Boolean, cf2, isEqual:, cf1);

这是一个 C 宏,我们看不到它的真正定义,因为真正的定义已从开源版本中删除。但我们可以猜测它可能会扩展为这样的东西:

if ([cf2 respondsToSelector:@selector(isEqual:)]) {
    return [cf2 isEqual:cf1];
}

如果cf2也是一个,那将是一个问题NSCTFont,因为它会递归调用-[NSCTFont isEqual:](交换了参数),这将回调_CFNonObjCEqual到)。

您可以在https://feedbackassistant.apple.com/上提交错误报告,或者根据需要使用反馈助手应用程序。

于 2019-07-10T21:27:23.613 回答
0

我没有找到答案。但是因为当我尝试使用 NSPredicate 时无限循环开始了,所以在比较NSCTFont isEqual:时,我为字体制作了一个控制器,并使用它们通过 NSPredicate 进行比较。没有无限循环了。

于 2019-07-10T20:35:21.027 回答