简体   繁体   中英

Why is NSFontManager.availableMembers(ofFontFamily:) crashing in Xcode 8 GM?

I wrote this in Xcode 8 Beta 6 (8S201h):

guard let faceMembers = NSFontManager.shared().availableMembers(ofFontFamily: familyName ?? fontName) else { return nil }

And it worked just fine. Now that I've upgraded to Xcode 8 GM Seed (8A218a) Xcode 8 (8A218a), it crashes ( EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0) ).

Using the debugger to narrow it down, I found that something in NSFontManager.availableMembers(ofFontFamily:) really hates this, since it crashes no matter what I put in there, even with common (definitely installed!) fonts like Helvetica Neue.

(lldb) po NSFontManager.shared()
<NSFontManager: 0x6100000a24c0>

(lldb) po familyName
▿ Optional<String>
  - some : "Helvetica Neue"


(lldb) po fontName
"HelveticaNeue"


(lldb) po NSFontManager.shared().availableMembers(ofFontFamily: familyName ?? fontName)
error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0).
The process has been returned to the state before expression evaluation.
(lldb) po NSFontManager.shared().availableMembers(ofFontFamily: familyName!)
error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0).
The process has been returned to the state before expression evaluation.
(lldb) po NSFontManager.shared().availableMembers(ofFontFamily: "Not a real font?!")
nil

So when I pass it a valid font family name, it crashes... but when I pass it a fake one, it returns nil .

Is this a problem I can solve, or just an issue with Xcode 8 GM Seed Xcode 8 which will be solved in an SDK update?


After looking through the crash log, I saw this suspiciousness:

Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0   libswiftFoundation.dylib        0x0000000107cbb249 _TZFE10FoundationSa26_forceBridgeFromObjectiveCfTCSo7NSArray6resultRGSqGSax___T_ + 153
1   libswiftCore.dylib              0x00000001079031f3 swift_dynamicCast + 1635
2   libswiftCore.dylib              0x000000010790448b _dynamicCastFromExistential(swift::OpaqueValue*, swift::OpaqueValue*, swift::TargetExistentialTypeMetadata<swift::InProcess> const*, swift::TargetMetadata<swift::InProcess> const*, swift::DynamicCastFlags) + 91
3   libswiftCore.dylib              0x0000000107903919 swift_dynamicCast + 3465
4   libswiftFoundation.dylib        0x0000000107d6a348 _TPA__TFFs15_arrayForceCastu0_rFGSax_GSaq__U_FQ_Q0_ + 56
5   libswiftFoundation.dylib        0x0000000107cbbc45 _TFEsPs10Collection3mapurfzFzWx8Iterator7Element_qd__GSaqd___ + 885
6   libswiftFoundation.dylib        0x0000000107cbb4c3 _TFs15_arrayForceCastu0_rFGSax_GSaq__ + 227
7   libswiftFoundation.dylib        0x0000000107cbb7a5 _TZFE10FoundationSa36_unconditionallyBridgeFromObjectiveCfGSqCSo7NSArray_GSax_ + 197

So it seems like it's crashing within Swift-Foundation, in some function called _forceBridgeFromObjectiveC ... not sure if that helps anyone but it does confirm it's within the SDK/runtime.

The only way that I could figure out how to work around this in the meantime was to create a static method in an objective-c class. I then imported the header into my bridging header and called the static method from Swift 3, where it worked fine.

Hope this helps you get through these difficult times!

#import <Foundation/Foundation.h>
#import <Cocoa/Cocoa.h>

@interface WorkAround : NSObject

+ (NSArray *)typefacesForFontFamily:(NSString *)family;

@end


#import "WorkAround.h"

@implementation WorkAround

/// Returns an array of arrays, or nil, that contain information about
/// each typeface found for the specified font family.
+ (NSArray *)typefacesForFontFamily:(nonnull NSString *)family {
  NSFontManager *fontManager = [NSFontManager sharedFontManager];
  return [fontManager availableMembersOfFontFamily:family];
}
@end

Joseph E.'s answer is a good starting point. However to get it to work in Swift 3, Xcode 8 (8A218a) I had to approach differently...

  1. Subclass NSFontManager (in Objective C), and create the bridging header if you don't already have one. 在此输入图像描述 在此输入图像描述

Make sure to change the language to (Objective C). Its important that you do it this way, as it appears you cannot directly create an objective cm files? , even though there is an option to do so.

  1. Implementation

FontManager.h

#import <Cocoa/Cocoa.h>

@interface FontManager : NSFontManager
    NS_ASSUME_NONNULL_BEGIN

    + (NSArray *)typefacesForFontFamily:(NSString *)family;


    NS_ASSUME_NONNULL_END
@end

FontManager.m

#import "FontManager.h"

@implementation FontManager
    NS_ASSUME_NONNULL_BEGIN

    + (NSArray *)typefacesForFontFamily:(nonnull NSString *)family {
        NSFontManager *fontManager = [self sharedFontManager];
        return [fontManager availableMembersOfFontFamily:family];
    }

    NS_ASSUME_NONNULL_END
@end

bridging-header.h

//
//  Use this file to import your target's public headers that you would like to expose to Swift.
//

#import "FontManager.h"
  1. Usage in your Swift 3 project

     if let fontMembers = FontManager.typefaces(forFontFamily: "Arial") as? [[Any]] { } 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM