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...
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 c m files?, even though there is an option to do so.
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"
Usage in your Swift 3 project
if let fontMembers = FontManager.typefaces(forFontFamily: "Arial") as? [[Any]] { }
Related
We are developing an iPad application starting from map view with annotations.
By using storyboard when We switch to another view which has an opengl based charting solution (shinobi).
On return to the view with map it has no problem until a touch on the map to move it.
As we try to move map it crashes with exc_bad_access exception at [EAGLContext setCurrentContext]
Any ideas?
Here is the part of the crash log:
OS Version: iOS 6.0 (10A403)
Report Version: 104
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x0000000c
Crashed Thread: 0
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 OpenGLES 0x39974b12 +[EAGLContext setCurrentContext:] + 74
1 VectorKit 0x32c64f0c -[VGLGPU setPaused:] + 120
2 VectorKit 0x32c54db8 -[VKMainLoop updateLinkState] + 492
3 VectorKit 0x32c54950 -[VKScreenCanvas _updateDisplayStatus:] + 104
4 VectorKit 0x32ccea9a -[VKScreenCanvas setGesturing:] + 254
5 MapKit 0x34defc3c -[MKMapView _willStartUserInteraction] + 48
6 MapKit 0x34de891a -[MKMapGestureController beginGesturing] + 50
7 MapKit 0x34de8c6c -[MKMapGestureController handlePan:] + 252
8 UIKit 0x379ead2c _UIGestureRecognizerSendActions + 124
9 UIKit 0x379b23d8 -[UIGestureRecognizer _updateGestureWithEvent:] + 388
10 UIKit 0x37b9f474 ...
I work for Shinobi and we've been investigating this - it's partly due to Apple's map code keeping hold of our GL-Context. As a temporary workaround, you can create a subclass of a ShinobiChart and nil-out the GL context in the chart's dealloc method, like so:
- (void) dealloc {
[super dealloc];
[EAGLContext setCurrentContext:nil]; // must be after dealloc
}
or if you're using ARC, (since sending dealloc is not allowed):
#import <ShinobiCharts/SChartCanvas.h>
#interface ShinobiChartGl : ShinobiChart
#end
#implementation ShinobiChartGl
- (void) dealloc
{
[self.canvas.glView removeFromSuperview];
self.canvas.glView = nil; // force glView dealloc
[EAGLContext setCurrentContext:nil];
}
#end
Hope this helps, but do contact us directly - we'll have a full fix out in our next release.
for those who did not work even tried [EAGLContext setCurrentContext:nil]; on dealloc, try this
dispatch_async(dispatch_get_main_queue(), ^{
[EAGLContext setCurrentContext:nil];
});
EAGLContext should be set on main thread.
I am using QTMovieView and , sometimes, I get the following log and follow by an unknown selector exception. The program has options for users to set to show and hide controller of the QTMovieView. The SDK that the program is linking against is 10.7
"[StdMovieUISliderCell sliderType]: unrecognized selector sent to instance"
thanks for any help
This looks like a bug that was introduced in OS X Mountain Lion 10.8 (Edit: there are also reports on OS X 10.7, see comments below) . I guess that QTMovieView will get deprecated in one of the next major OS X releases. The best solution is to move to AV Foundation (AVPlayer and the corresponding AVPlayerLayer class). Apple has some documentation about playing back assets using this framework.
That said, if you can’t update to AV Foundation or you can’t turn off Auto Layout, you still can fix this issue by adding the missing methods dynamically during runtime to the StdMovieUISliderCell class. Make sure to add the Objective C runtime header file and to add the methods as early as possible (e.g. + (void)load in your application delegate). For App Store static analyzer rejection foo reasons, it’s also safe to add some simple encoding to the class name like rot13.
// Make sure that we have the right headers.
#import <objc/runtime.h>
// The selectors should be recognized by class_addMethod().
#interface NSObject (SliderCellBugFix)
- (NSSliderType)sliderType;
- (NSInteger)numberOfTickMarks;
#end
// Add C implementations of missing methods that we’ll add
// to the StdMovieUISliderCell class later.
static NSSliderType SliderType(id self, SEL _cmd)
{
return NSLinearSlider;
}
static NSInteger NumberOfTickMarks(id self, SEL _cmd)
{
return 0;
}
// rot13, just to be extra safe.
static NSString *ResolveName(NSString *aName)
{
const char *_string = [aName cStringUsingEncoding:NSASCIIStringEncoding];
NSUInteger stringLength = [aName length];
char newString[stringLength+1];
NSUInteger x;
for(x = 0; x < stringLength; x++)
{
unsigned int aCharacter = _string[x];
if( 0x40 < aCharacter && aCharacter < 0x5B ) // A - Z
newString[x] = (((aCharacter - 0x41) + 0x0D) % 0x1A) + 0x41;
else if( 0x60 < aCharacter && aCharacter < 0x7B ) // a-z
newString[x] = (((aCharacter - 0x61) + 0x0D) % 0x1A) + 0x61;
else // Not an alpha character
newString[x] = aCharacter;
}
newString[x] = '\0';
return [NSString stringWithCString:newString encoding:NSASCIIStringEncoding];
}
// Add both methods if they aren’t already there. This should makes this
// code safe, even if Apple decides to implement the methods later on.
+ (void)load
{
Class MovieSliderCell = NSClassFromString(ResolveName(#"FgqZbivrHVFyvqrePryy"));
if (!class_getInstanceMethod(MovieSliderCell, #selector(sliderType)))
{
const char *types = [[NSString stringWithFormat:#"%s%s%s",
#encode(NSSliderType), #encode(id), #encode(SEL)] UTF8String];
class_addMethod(MovieSliderCell, #selector(sliderType),
(IMP)SliderType, types);
}
if (!class_getInstanceMethod(MovieSliderCell, #selector(numberOfTickMarks)))
{
const char *types = [[NSString stringWithFormat: #"%s%s%s",
#encode(NSInteger), #encode(id), #encode(SEL)] UTF8String];
class_addMethod(MovieSliderCell, #selector(numberOfTickMarks),
(IMP)NumberOfTickMarks, types);
}
}
I made two assumptions while implementing both methods:
A movie view can only have a linear slider, not a circular one.
A movie view won’t have tick marks.
The latter could be a problem if your movie has chapters, but I don’t know how they are handled, because I don’t need or use them.
I had the same problem if I tried to use setMovie: with AutoLayout on. An update to Xcode 4.4.1 fixed the problem.
i know it's a fairly old post, but for others out there, i had the same problem, i've just deactivated the cocoa autolayout for the xib file containing the QTMovieView and it worked like it should have.
i'm currently working with xcode 4.5.2 under OSX 10.7.4
Fixed this issue by adding empty updateConstraintsForSubtreeIfNeeded method in QtMovieView subclass.
I have the following method in a NSDictionary category, to do a deep copy, which works fine.
I just upgraded from Xcode 4.1 to 4.2, and the Analyze function gives two analyzer warnings for this code, as indicated:
- (id)deepCopy;
{
id dict = [[NSMutableDictionary alloc] init];
id copy;
for (id key in self)
{
id object = [self objectForKey:key];
if ([object respondsToSelector:#selector(deepCopy)])
copy = [object deepCopy];
else
copy = [object copy];
[dict setObject:copy forKey:key];
// Both -deepCopy and -copy retain the object, and so does -setObject:forKey:, so need to -release:
[copy release]; // Xcode 4.2's Analyze says this is an incorrect decrement of the reference count?!
}
return dict; // Xcode 4.2's Analyze says this is a potential leak
}
Are these bugs in Xcode's analyzer, or are there changes I can make to avoid these warnings?
I'm not using ARC yet, though I am interested if there are additional changes needed to support ARC for this method.
Presumably, it is because deepCopy does not begin with the prefix copy.
So you may want to change to something like copyWithDeepCopiedValues (or something like that), and then see if the analyzer flags that.
Update
As Alexsander noted, you can use attributes to denote reference counting intent. This should (IMO) be the exception to the rule, and used rarely, if ever. Personally, I will not use attributes for objc methods because it is fragile.
The only attribute I have used so far has been consume, and every time I use these attributes has been in statically typed contexts (e.g. C functions and C++ functions and methods).
The reasons you should avoid attributes when possible:
1) Stick with conventions for the programmers' sake. The code is clearer and you do not need to refer to the documentation.
2) The approach is fragile. You can still introduce reference count imbalances, and attributes can be used to introduce build errors due to conflicts in attributes.
The following cases are all built with ARC enabled:
Case #1
#import <Foundation/Foundation.h>
#interface MONType : NSObject
- (NSString *)string __attribute__((objc_method_family(copy)));
#end
#implementation MONType
- (NSString *)string
{
NSMutableString * ret = [NSMutableString new];
[ret appendString:#"MONType"];
return ret;
}
#end
int main (int argc, const char * argv[])
{
#autoreleasepool {
id obj = nil;
if (random() % 2U) {
obj = [[NSAttributedString alloc] initWithString:#"NSAttributedString"];
}
else {
obj = [MONType new];
}
NSLog(#"Result: %#, %#", obj, [obj string]);
}
/* this tool's name is ARC, dump the leaks: */
system("leaks ARC");
return 0;
}
This program produces the following error: error: multiple methods named 'string' found with mismatched result, parameter type or attributes.
Great, the compiler's doing what it can to prevent these issues. What that means is that conflicts in attributes can introduce errors based on the translation. This is bad because when nontrivial codebases are combined and attributes conflict, you will have errors to correct and programs to update. This also means that simply including other libraries in translation units can break existing programs when attributes are used.
Case #2
Header.h
extern id NewObject(void);
Header.m
#import <Foundation/Foundation.h>
#import "Header.h"
#interface MONType : NSObject
- (NSString *)string __attribute__((objc_method_family(copy)));
#end
#implementation MONType
- (NSString *)string
{
NSMutableString * ret = [NSMutableString new];
[ret appendString:#"-[MONType string]"];
return ret;
}
#end
id NewObject(void) {
id obj = nil;
if (random() % 2U) {
obj = [[NSAttributedString alloc] initWithString:#"NSAttributedString"];
}
else {
obj = [MONType new];
}
return obj;
}
main.m
#import <Foundation/Foundation.h>
#import "Header.h"
int main (int argc, const char * argv[])
{
#autoreleasepool {
for (size_t idx = 0; idx < 8; ++idx) {
id obj = NewObject();
NSLog(#"Result: %#, %#", obj, [obj string]);
}
}
/* this tool's name is ARC, dump the leaks: */
system("leaks ARC");
return 0;
}
Ok. This is just bad. We've introduced leaks because the necessary information was not available in the translation unit. Here's the leaks report:
leaks Report Version: 2.0
Process 7778: 1230 nodes malloced for 210 KB
Process 7778: 4 leaks for 192 total leaked bytes.
Leak: 0x1005001f0 size=64 zone: DefaultMallocZone_0x100003000 __NSCFString ObjC CoreFoundation mutable non-inline: "-[MONType string]"
Leak: 0x100500320 size=64 zone: DefaultMallocZone_0x100003000 __NSCFString ObjC CoreFoundation mutable non-inline: "-[MONType string]"
Leak: 0x100500230 size=32 zone: DefaultMallocZone_0x100003000 has-length-byte: "-[MONType string]"
Leak: 0x100500390 size=32 zone: DefaultMallocZone_0x100003000 has-length-byte: "-[MONType string]"
note: the count may differ because we used random()
This means that because MONType is not visible to main(), the compiler bound the ARC properties to methods which were visible to the current TU (that is, string from declarations in Foundation, all of which follow convention). As a result, the compiler got it wrong and we were able to introduce leaks into our program.
Case 3
Using a similar approach, I was also able to introduce negative reference count imbalances (premature releases, or a messaged zombie).
note: Code not provided because Case #2 already illustrates how one can accomplish a reference count imbalance.
Conclusion
You can avoid all these problems and improve readability and maintainability by sticking with convention, rather than using attributes.
Bringing the conversation back to non-ARC code: Using attributes makes manual memory management more difficult for programmers' readability, and for the tools which are there to help you (e.g. compiler, static analysis). If the program is suitably complex such that the tools can't detect such errors, then you should reconsider your design, because it will be equally complex for you or somebody else to debug these issues.
Adding onto #Justin's answer, you can tell the compiler that -deepCopy returns a retained object by appending the NS_RETURNS_RETAINED attribute to the method's declaration like so:
- (id) deepCopy NS_RETURNED_RETAINED;
Alternatively, you can use explicitly control the method's "family" using the objc_method_family attribute like so:
- (id) deepCopy __attribute__((objc_method_family(copy)));
If you do this, the compiler will know that this method is in the copy family and returns a copied value.
My app is crashing on Lion when it awakes from sleep.
The problem seems to be with a background thread that is looking for weather info.
I'm not sure but I think the crash log is telling me that the autorelease pool is popping objects that are no longer there, can someone help me confirm this?
Here is the relevant details for the Crash log:
Process: myApp [14187] Identifier: myApp Version:
??? (???) Code Type: X86-64 (Native) Parent Process: launchd
[224]
Date/Time: 2011-08-24 18:58:00.581 -0400 OS Version: Mac OS
X 10.7.1 (11B26) Report Version: 9
Crashed Thread: 7
Exception Type: EXC_BAD_ACCESS (SIGSEGV) Exception Codes:
KERN_INVALID_ADDRESS at 0x0000000000000010
Application Specific Information: objc[14187]: garbage collection is
OFF
Thread 7 Crashed: 0 libobjc.A.dylib
0x00007fff9321700b (anonymous
namespace)::AutoreleasePoolPage::pop(void*) + 385 1
com.apple.CoreFoundation 0x00007fff961306a5
CFAutoreleasePoolPop + 37 2 com.apple.Foundation
0x00007fff969350d7 -[NSAutoreleasePool drain] + 154 3
com.piso13.opusDomini 0x00000001000acb91 -[Weather
internalStart] + 417 4 com.apple.Foundation
0x00007fff9698b1ea -[NSThread main] + 68 5 com.apple.Foundation
0x00007fff9698b162 NSThread_main + 1575 6 libsystem_c.dylib
0x00007fff90b068bf _pthread_start + 335 7 libsystem_c.dylib
0x00007fff90b09b75 thread_start + 13
Here is my code for Weather Internal Start:
-(void)internalStart{
pool = [[NSAutoreleasePool alloc] init];
forecast = FALSE;
liveweather = FALSE;
NSString *query = [self generateQuery];
if (query == nil) {
[pool drain];
return;
}
XmlWrapper * xmlWrapper = [[XmlWrapper alloc] initWithQuery:query delegate:self name:#"liveweather"];
[xmlWrapper release];
query = [self generateForecastQuery];
xmlWrapper = [[XmlWrapper alloc] initWithQuery:query delegate:self name:#"forecast"];
[xmlWrapper release];
[pool drain];
}
Should I even be calling [pool drain] ?
create your autorelease pools with bound lifetimes and explicit scopes.
in this case, you store your autorelease pool in an ivar (presumed).
just make it local to the method, like so:
- (void)internalStart
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
//...
[pool drain], pool = nil;
}
the problems that are typically introduced are:
1) autorelease pools are stack based (pools are pushed and popped). by holding onto it, you can easily mess up the stack order.
2) if this class operates in a multithreaded context, you can easily leak pools or destroy the stack order when you push and pop pools from multiple threads.
3) you could also leak pools in multithreaded contexts
Unfortunately, autoreleasepool crashes are some of the hardest to debug. Start with the static analyzer, which can find some things.
Then turn on NSZombies.
Your XmlWrapper object is a bit strange. Why do you immediately release it as soon as you create it? Is this a wrapper around NSURLConnection? You should still hold onto the object so that you can cancel it or clear its delegate when this object is released.
Make sure you're using accessors for all your ivars rather than accessing them directly. Direct access to ivars outside of init and dealloc is the #1 cause of these kinds of crashes in my experience.
I've got the common-seeming error for beginning thread programmers, ".. does not implement selector.." except that it lists a CLASS method, not an instance method. Which makes perfect sense it's defined as an instance method.. Code:
main app delegate header:
#interface LSSampleAppDelegate : NSObject <NSApplicationDelegate>
{
NSWindow *window;
LSDataObject labelOptions;
}
-(void) doPrintDisc: (LSDataObject*) labelOptions;
#property (assign) IBOutlet NSWindow *window;
//-(void) userDidClickStop:(id)sender;
#end
thread function first line (in delegate object):
-(void) doPrintDisc: (LSDataObject*) labelOptions {
thread launch code:
[NSThread detachNewThreadSelector: #selector(doPrintDisc:)
toTarget: [self class]
withObject: labelOptions];
The error:
*** -[NSThread initWithTarget:selector:object:]: target does not implement selector (*** +[LSSampleAppDelegate doPrintDisc:])
I know, the printDisc method should probably go in the labelOptions object and not the delegate - but I want to get this working before I make another modification.. I've had enough problem today with a malloc error of some kind that seems to show up, only to go away with no apparent reason (it says it's out of memory, but I seriously doubt that it really is unless the lightscribe library itself has a limit on its memory zone) - I assume that the library may run out of memory and then perhaps reset and then the error goes away for a while.
The really odd thing is - earlier today I think I had the thread code actually working..
Try:
[NSThread detachNewThreadSelector: #selector(doPrintDisc:)
toTarget: self
withObject: labelOptions];
instead.