Вы не делаете ничего плохого. Это ошибка в macOS.
Вы можете разыграть CTFont
до NSFont
, потому что эти типы являются «бесплатными мостовыми соединениями». Это означает, что CTFont
размещается в памяти таким образом, чтобы соответствовать требованиям экземпляров Objective-C. Одно из этих требований заключается в том, что первое слово объекта содержит указатель (называемый указателем «isa») на класс объекта. В случае 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
равен нулю) и не являются явно одинаковыми (потому что они имеют одинаковый указатель), то этот метод isEqual:
вызывает _CFNonObjCEqual
, который является частным Основная функция основания. Случилось так, что _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
должен быть известен как базовый тип Foundation, который не является собственным экземпляром Objective-C, но аргумент 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/, или, если хотите, с помощью приложения Feedback Assistant.