I have a simple free iOS arcade game not functioning correctly, after installation from the App Store, for owners of an iPhone with iOS 7.1 - 7.1.2. The game is build with Sprite Kit & Objective-C (no Swift what-so-ever) but is very simple in nature & doesn't have any fancy code or complexity to it. It also works wonderfully for anyone with iOS 8.x.x installed. After downloading iOS 7.1 Simulator for Xcode 6, I was able to reproduce the problem: iPhone's 4S, 5 or 5S running iOS version 7.1 - 7.1.2 all crash - So the launch image appears, but when it needs to load up the game from SKScene class called MyScene, it just doesn't open. In crash logs it says the following:
+[SKLabelNode labelNodeWithText:]: unrecognized selector sent to class 0x1022503a0
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[SKLabelNode labelNodeWithText:]: unrecognized selector sent to class 0x1022503a0'
*** First throw call stack:
(
0 CoreFoundation 0x00000001029a6495 __exceptionPreprocess + 165
1 libobjc.A.dylib 0x000000010270199e objc_exception_throw + 43
2 CoreFoundation 0x0000000102a3755d +[NSObject(NSObject) doesNotRecognizeSelector:] + 205
Upon crashing, Xcode brings me to some Apple code page about "dispatch_once" & highlights line 68:
dispatch_once(predicate, block); Thread 1: signal SIGABRT
Basically, inside MyScene, there's -(id)initWithSize:(CGSize)size method. Inside it, I created
static dispatch_once_t onceMS;
dispatch_once(&onceMS, ^{
Inside dispatch_once, I have 4 things: 1. An instance of audioController is created (a class responsible for playing looped background music). 2. SKSpriteNode spriteNodeWithImageNamed: 3. SKLabelNode labelNodeWithText: 4. SKLabelNode labelNodeWithText:
All of these 4 things are meant to show up once, in the beginning of game startup: they are visual instructions for how to play. The background music is self explanatory.
I tried commenting all of this out but it still displayed the same crash logs as before. I proceeded commenting out the entire static dispatch_once & still the game crash. Can someone please give out some wisdom? I don't know what to do due to my lack of experience.
According to the documentation +[SKLabelNode labelNodeWithText:] is simply not available on iOS7
I have literally no experience with SpriteKit, but you should be able to replace all instances of
SKLabelNode *node = [SKLabelNode labelNodeWithText:#"your text"];
with:
SKLabelNode *node = [SKLabelNode labelNodeWithFontNamed:#"HelveticaNeue-UltraLight"];
node.fontSize = 32;
node.text = #"your text";
You can create your own category on SKLabelNode as well:
#interface SKLabelNode (iOS7Compatibility)
+ (instancetype)mba_labelNodeWithText:(NSString *)text;
#end
#implementation SKLabelNode (iOS7Compatibility)
+ (instancetype)mba_labelNodeWithText:(NSString *)text {
SKLabelNode *node = [self labelNodeWithFontNamed:#"HelveticaNeue-UltraLight"];
node.fontSize = 32;
node.text = text;
return node;
}
#end
which allows you to use:
SKLabelNode *node1 = [SKLabelNode mba_labelNodeWithText:#"your text"];
You must not name the category method labelNodeWithText:, because you don't want to overwrite methods in categories.
Related
I'm suddenly beset by Core Data problems in the form of managedObjectContexts that are nil two-thirds of the time: the onset more or less coincides with upgrading to Xcode 8. The new NSPersistentContainer class seemed to offer an opportunity to solve my problems and get rid of some ugly code. Unfortunately, I cannot make it work.
In AppDelegate.h
#property (readonly, strong, nonatomic) NSPersistentContainer *persistentContainer;
In AppDelegate.m:
#synthesize persistentContainer = _persistentContainer;
- (NSPersistentContainer *)persistentContainer
{
if (!_persistentContainer) {
_persistentContainer = [NSPersistentContainer persistentContainerWithName:#"I_WILL_PERSIST"];
__block BOOL storesWereLoaded = NO;
[_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *psd, NSError *error) {
if (error) {
NSLog(#"loadPersistentStoresWithCompletionHandler: persistent container could not load store %#. Error: %#",
psd, error.debugDescription);
} else {
storesWereLoaded = YES;
}
}];
if (storesWereLoaded) {
NSLog(#"Stores were loaded");
} else {
NSLog(#"Stores were not loaded");
}
}
return _persistentContainer;
}
The app is a little test app, with an arrayController bound (in IB) to App Delegate.persistentContainer.viewContext. App Delegate is connected and not nil. The persistentContainer getter is called repeatedly but the NSLog's inside the block never fire and the console shows:
Cannot perform operation without a managed object context 2016-09-24
19:34:39.207 I_WILL_PERSIST[5588:180829] ( 0 CoreFoundation
0x00007fff8da994f2 __exceptionPreprocess + 178 1 libobjc.A.dylib
0x00007fff8ed6173c objc_exception_throw + 48 2 CoreFoundation
0x00007fff8db004bd +[NSException raise:format:] + 205 3 AppKit
0x00007fff85d411c4 -[_NSManagedProxy _managedObjectContext] + 66
Does the problem lie in my shaky block-programming skills? Is it a signing / permissions issue (the app is not Sandboxed, code signing automatic)? What's with Core Data all of a sudden?
====EDIT====
After upgrading to Mac OS 10.12 (Sierra), NSPersistentContainer fails with error:
2016-09-28 20:55:53.256588 osPersist[1936:41151] [error] error:
-addPersistentStoreWithType:SQLite configuration:(null) URL:file:///Users/user/Library/Application%20Support/osPersist/osPersist.sqlite options:{
NSInferMappingModelAutomaticallyOption = 1;
NSMigratePersistentStoresAutomaticallyOption = 1; } ... returned error Error Domain=NSCocoaErrorDomain Code=512 "The file couldn’t be saved." UserInfo={reason=Failed to create file; code = 2} with userInfo dictionary {
reason = "Failed to create file; code = 2"; } 2016-09-28 20:55:53.256747 osPersist[1936:41151] Unresolved error Error Domain=NSCocoaErrorDomain Code=512 "The file couldn’t be saved." UserInfo={reason=Failed to create file; code = 2}, {
reason = "Failed to create file; code = 2"; }
Why the database creation fails is not clear: it happens both with and without code-signing. I've made a bug report and I've opened a support request.
As soon as I upgraded to Sierra (MacOS 10.12) NSPersistentContainer started failing with errors. So, you can implement NSPersistentContainer in Xcode 8 running on MacOS 10.11 and your project will compile and run, but NSPersistentContainer won't do squat. I've filed a bug report and I hope that Apple will add preprocessor directives to the class, but they don't seem very interested.
After updating to 10.10.3 the WebView component started to crash after dealloc
- (void)dealloc {
[self.webView.windowScriptObject setValue:nil forKey:#"CocoaApp"];
[[self.webView mainFrame] stopLoading];
[self.webView setUIDelegate:nil];
[self.webView setEditingDelegate:nil];
[self.webView setFrameLoadDelegate:nil];
[self.webView setPolicyDelegate:nil];
[self.webView removeFromSuperview];
}
The crash happens somewhere deep in WebView
EXC_BAD_ACCESS
1 0x7fff910bae9e WebDocumentLoaderMac::detachFromFrame()
2 0x7fff920288c0 WebCore::FrameLoader::detachFromParent()
3 0x7fff910d0e55 -[WebView(WebPrivate) _close]
4 0x7fff910d0c49 -[WebView dealloc]
5 0x7fff8b1cf89c objc_object::sidetable_release(bool)
6 0x7fff8b1b5e8f (anonymous namespace)::AutoreleasePoolPage::pop(void*)
7 0x7fff912b26f2 _CFAutoreleasePoolPop
8 0x7fff8830e762 -[NSAutoreleasePool drain]
9 0x7fff8e3f0cc1 -[NSApplication run]
10 0x7fff8e36d354 NSApplicationMain
11 0x1000ebb12 main
12 0x7fff8c81e5c9 start
13 0x3
Any ideas? Is this a Apple bug? It started AFTER 10.10.3?
It doesn't crash when NSZombie is enabled!
I noticed you're using your own policy delegate:
[self.webView setPolicyDelegate:nil];
There's a known bug related to policy delegates in WebKit (only very recently fixed):
https://bugs.webkit.org/show_bug.cgi?id=144975
The short version is that you're probably hitting this assertion (which crashes the process with an intentional segfault):
https://github.com/WebKit/webkit/blob/24b1ae89efc10a4e6a6057b429c8e1d8d138a32f/Source/WebCore/loader/DocumentLoader.cpp#L935
because your policy handler (i.e. decidePolicyForMIMEType:request:frame:decisionListener:) is failing to make a policy decision (i.e not use, ignore, or download). The decision hangs around unmade, and when the loader eventually detaches it asserts that there are no pending policy decisions, which fails since the view is still waiting for a decision.
The fix i made, is not to release the webview, but hold a static reference into it (this is far from solving it and i contacted Apple regarding this issue)
#warning HOTFIX
{
//this is because of http://stackoverflow.com/questions/29746074/osx-10-10-3-crashes-webview-on-dealloc
static NSMutableArray * LIVE_FOR_EVER_WEBVIEW;
if (LIVE_FOR_EVER_WEBVIEW == nil) {
LIVE_FOR_EVER_WEBVIEW = [NSMutableArray new];
}
if (self.webView) {
[LIVE_FOR_EVER_WEBVIEW addObject:self.webView];
}
}
I am trying to create a dangling pointer by using the following code:
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(#"I am touched!");
// bar is an instance variable defined by NSString *bar; in the interface
if (!bar) {
NSLog(#"bar is being init");
bar = [NSString stringWithFormat:#"Hello I am %i", arc4random() % 1000];
NSLog(#"bar is now %#", bar);
} else {
NSLog(#"bar is %#", bar);
}
}
So I can successfully crash the program on the second touch on an iPad 2 device (not Simulator), but then when I tried adding a [bar retain]; after the bar = assignment line to see that there will be no dangling pointer, the app cannot start and just keep on showing:
Couldn't register com.mycompany.TryIOSAppLeak with the bootstrap
server. Error: unknown error code.
This generally means that another instance of this process was already
running or is hung in the debugger.(lldb)
Is there a way to make it start again? (besides rebooting the iPad and the iMac or restarting Xcode).
Ahh, this happens to me a lot. Try to force kill the app once it has a crash before you restart, the app is hung on the device. But once it happens delete the app on the device/simulator, restart the device/simulator and restart XCode. Very annoying right! Alternatively you could edit your scheme and change debugger to GDB. It has nothing to do with your code.
This question follows on from my other question on why my app isn't being brought down by exceptions.
The Problem
When an exception is thrown on the main thread via an Action, the app still doesn't crash.
As per Dave's answer to my original question, I've implemented the reportException category on NSApplication and set the uncaught exception handler.
Code
I've got the following in my app delegate, which I've hooked up to a button in my UI to test.
-(IBAction)crashOnMainThread:(id)sender {
[self performSelectorOnMainThread:#selector(crash) withObject:nil waitUntilDone:YES];
}
-(void)crash {
// To test out the exception handling
[NSException raise:NSInternalInconsistencyException format:#"This should crash the app."];
}
When I press the button, my app doesn't crash. When I look at the console log, I see this:
06/09/2010 14:12:25 EHTest1[26384] HIToolbox: ignoring exception 'This should crash the app.' that raised inside Carbon event dispatch
(
0 CoreFoundation 0x00007fff80ab4cc4 __exceptionPreprocess + 180
1 libobjc.A.dylib 0x00007fff819560f3 objc_exception_throw + 45
2 CoreFoundation 0x00007fff80ab4ae7 +[NSException raise:format:arguments:] + 103
3 CoreFoundation 0x00007fff80ab4a74 +[NSException raise:format:] + 148
4 EHTest1 0x00000001000010e3 -[EHTest1_AppDelegate crashLapsus] + 63
5 Foundation 0x00007fff88957c25 -[NSObject(NSThreadPerformAdditions) performSelector:onThread:withObject:waitUntilDone:modes:] + 234
6 Foundation 0x00007fff8896ad48 -[NSObject(NSThreadPerformAdditions) performSelectorOnMainThread:withObject:waitUntilDone:] + 143
7 EHTest1 0x0000000100001030 -[EHTest1_AppDelegate crashOnMainThread:] + 60
8 AppKit 0x00007fff85c7e152 -[NSApplication sendAction:to:from:] + 95
9 AppKit 0x00007fff85ca26be -[NSMenuItem _corePerformAction] + 365
** Snip **
It looks like Carbon is catching the exception, which is really annoying.
This suggests that for any action code, you need to immediately run it in a background thread so any exceptions are registered as uncaught. Huh? I've not seen any code that's structured like this.
What I've tried
Crashing the app with a delay so it's not connected to a UI element. It crashes fine.
I've tried installing a custom NSExceptionHandler using this in my app delegate:
-(BOOL)exceptionHandler:(NSExceptionHandler *)sender
shouldHandleException:(NSException *)exception
mask:(unsigned int)aMask {
abort();
return YES;
}
-(void)applicationWillFinishLaunching:(NSNotification *)aNotification {
NSExceptionHandler *handler = [NSExceptionHandler defaultExceptionHandler];
[handler setExceptionHandlingMask:NSLogAndHandleEveryExceptionMask];
[handler setDelegate:self];
}
the problem here is it crashes on every exception, whether it's caught or not.
If I try and check the mask and don't crash on a caught exception, I'm back to square 1 as it seems that HIToolbox catches the exception in exactly the same way as a try/catch block would.
Questions
How can I stop HIToolbox catching the exceptions so that my app uses the uncaught exception handler and crashes?
Is it OK to be running code that's in the same call stack as an action? Surely this is OK?
If it's not OK, what's the alternative?
This is driving me up the wall, so any help would be much appreciated.
I answered your last question on this subject, and ran into the same problem with Carbon's HIToolbox catching exceptions thrown by IBActions.
First, undo everything I mentioned in my previous answer. It doesn't work with IBActions for some reason. My hunch is that HIToolbox lives lower on the exception-handling-chain, and gets any IBAction/GUI exceptions before NSApplication has the opportunity to. Any custom exception-handling function you can register with NSSetUncaughtExceptionHandler() lives (I believe) at the top of the chain.
You're on the right track with NSExceptionHandling:
Add the ExceptionHandling.framework to your Xcode Project
#import "ExceptionHandlerDelegate.h" into your AppDelegate.m (or a custom Singleton exception class)
Inside AppDelegate.m:
// Very first message sent to class
+ (void)initialize
{
NSExceptionHandler *exceptionHandler = [NSExceptionHandler defaultExceptionHandler];
unsigned int handlingMask = NSLogAndHandleEveryExceptionMask;
[exceptionHandler setExceptionHandlingMask:handlingMask];
[exceptionHandler setDelegate:self];
// ...
}
#pragma mark -
#pragma mark NSExceptionHandler Delegate Methods
// Called 1st: Should NSExceptionHandler log the exception?
- (BOOL)exceptionHandler:(NSExceptionHandler *)sender shouldLogException:(NSException *)exception mask:(unsigned int)aMask
{
// ...log NSException if desired...
return NO; // Changes to YES if NSExceptionHandler should handle logging
}
// Called 2nd: Should NSExceptionHandler handle the exception?
- (BOOL)exceptionHandler:(NSExceptionHandler *)sender shouldHandleException:(NSException *)exception mask:(unsigned int)aMask
{
// ...crash your app here (e.g. [NSApp terminate:self]; )
// ...or handle the NSException and return YES/NO accordingly
return NO; // If app crashed, never gets returned - should crash before that
}
The NSLogAndHandleEveryExceptionMask flag tells NSExceptionHandler to capture every exception it can (for your app only, I believe), regardless of where on the exception chain it exists.
This means that #catch/#try/#finally blocks won't work, because the NSHandleOtherExceptionMask flag tells NSExceptionHandler to capture "everything below it" on the exception-handler chain. You can remove that flag, but then HIToolKit will probably get any IBAction exceptions again, since it appears to be lower on said chain.
Apple's docs have info about the flags: NSExceptionHandler docs
So when an NSException is raised (anywhere in your app AFAIK), and NSLogAndHandleEveryExceptionMask is set, these are called in the delegate in-order:
-exceptionHandler:shouldLogException:mask: is called first.
-exceptionHandler:shouldHandleException:mask: is called second.
Just put your "crash code" inside the second delegate method and you should be OK.
Helpful article: Understanding Exceptions and Handlers in Cocoa
The reason I think you were having trouble getting NSExceptionHandler's delegate to work is because it's NOT compatible with a custom method set with NSSetUncaughtExceptionHandler(), which was part of the answer in my previous question. Per Apple:
The NSExceptionHandler class provides
facilities for monitoring and
debugging exceptional conditions in
Objective-C programs. It works by
installing a special uncaught
exception handler via the
NSSetUncaughtExceptionHandler
function. Consequently, to use the
services of NSExceptionHandler, you
must not install your own custom
uncaught exception handler.
It also probably doesn't work well when you override NSApplication's -reportException: method.
Lastly, there doesn't appear to be a need to use #catch/#try/#finally (also part of my previous answer). Configuring NSExceptionHandler inside +initialize appears to "kick in" immediately, unlike overriding NSApplication's -reportException: method.
You can’t reliably. Throwing exceptions across API boundaries is not supported unless explicitly documented (and I can’t think of any cases that are explicitly documented).
Hy everybody,
I have a screensaver made with obj-c and cocoa. Everything works fine under OsX 10.6.2 except the following.
Within my screensaver I have a WebView with some application running. When I try to call my objective-c app (the screensaver) via javascript, I get an error and the screensaver and the system preferences panel crash.
System Preferences[86666]
*** Terminating app due to uncaught exception 'NSInvalidArgumentException'
reason: '-[NSCFArray drain]: unrecognized selector sent to instance 0x20049b1e0'
*** Call stack at first throw:(
0 CoreFoundation 0x00007fff8123a444 __exceptionPreprocess + 180
1 libobjc.A.dylib 0x00007fff81f130f3 objc_exception_throw + 45
2 CoreFoundation 0x00007fff812931c0 +[NSObject(NSObject) doesNotRecognizeSelector:] + 0
3 CoreFoundation 0x00007fff8120d08f forwarding + 751
4 CoreFoundation 0x00007fff812091d8 _CF_forwarding_prep_0 + 232
5 WebCore 0x00007fff847adee0 _ZN3JSC8Bindings12ObjcInstance10virtualEndEv + 48
6 WebCore 0x00007fff8470d71d _ZN3JSC16RuntimeObjectImp18getOwnPropertySlotEPNS_9ExecStateERKNS_10IdentifierERNS_12PropertySlotE + 397
7 JavaScriptCore 0x00007fff80862b66 NK3JSC7JSValue3getEPNS_9ExecStateERKNS_10IdentifierERNS_12PropertySlotE + 486
)
I know this looks like some memory leak, but as you will see in the code, I really have nearly no objects allocated.
This only happens, when I start the screensaver with the "Test" button from the screensaver system prefs.
When I start the screensaver via terminal or if it starts automatically, the same action (calling obj-c from javascript) works fine.
Maybe someone has any idea, where the error could come from. Here is some code from the implementation:
#implementation ScreensaverView
- (id)initWithFrame:(NSRect)frame isPreview:(BOOL)isPreview {
self = [super initWithFrame:frame isPreview:isPreview];
if (self) {
[self setAnimationTimeInterval:-1];
[self setAutoresizesSubviews:YES];
// ::::::::::::::::::::::: Init stuff ::::::::::::::::::
// init
quitFlag = false;
previewMode = isPreview;
// find out the path the screensaver bundle
pMainBundle = [NSBundle bundleForClass:[self class]];
pBundlePath = [pMainBundle bundlePath];
// read Info.plist
infoDict = [pMainBundle infoDictionary];
}
return self;
}
- (void)startAnimation
{
[super startAnimation];
// combine: bundle path + filename for screensaver file
NSString *pathToScreensaver = [NSString stringWithString:pBundlePath];
NSString *valueScreensaverFile;
if(!previewMode)
{
valueScreensaverFile = [infoDict objectForKey:#"ScreensaverFile"];
}
else
{
valueScreensaverFile = [infoDict objectForKey:#"PreviewFile"];
}
// add filename to bundle path
pathToScreensaver = [pathToScreensaver stringByAppendingString:valueScreensaverFile];
// complete NSURL to the screensaver file
NSURL *screensaverUrl = [NSURL fileURLWithPath: pathToScreensaver];
webView = [WebView alloc];
[webView initWithFrame:[self frame]];
[webView setDrawsBackground:NO];
// delegation policy for interactive mode
[webView setPolicyDelegate: self];
[webView setUIDelegate:self];
// load screensaver
[[webView mainFrame] loadRequest:[NSURLRequest requestWithURL:screensaverUrl]];
scriptObject = [webView windowScriptObject];
[scriptObject setValue:self forKey:#"screensaver"];
[self addSubview:webView];
}
- (void)stopAnimation
{
[[webView mainFrame] stopLoading];
[webView removeFromSuperview];
[webView release];
[super stopAnimation];
}
+ (BOOL)isSelectorExcludedFromWebScript:(SEL)selector
{
if (selector == #selector(quitScreenSaver)) {
return NO;
}
if(selector == #selector(gotoUrl:) ){
return NO;
}
return YES;
}
+(NSString *)webScriptNameForSelector:(SEL)selector
{
if(selector == #selector(quitScreenSaver))
{
return #"quitNoOpen";
}
if(selector == #selector(gotoUrl:))
{
return #"openAndQuit";
}
return nil;
}
- (void) quitScreenSaver
{
quitFlag = true;
[super stopAnimation];
}
- (void) gotoUrl:(NSString *) destinationURL
{
if(destinationURL == NULL)
{
return;
}
NSString * path = destinationURL;
NSURL * fileURL = [NSURL URLWithString:path];
[[ NSWorkspace sharedWorkspace ] openURL:fileURL];
[self quitScreenSaver];
}
#end
I hope that's enough code for you to see some problems / solutions.
I would really appreciaty any answers.
Somehow an NSCFArray (NSMutableArray) is being sent a "drain" message that's meant for an NSAutoreleasePool.
You might be able to get a bit more info on what the array is by implementing the drain method for NSMutableArray, so you can trap the now-recognized selector and print out the contents of the array object. Try adding this somewhere in your code:
#interface NSMutableArray (drain)
- (void) drain;
#end
#implementation NSMutableArray (drain)
- (void) drain
{
NSLog(#"drain message received by object: %#", self);
}
#end
If you don't see any messages show up in the Console, try changing the "NSMutableArray" in the above code to "NSObject".
One thing to be aware of is that when you start the screensaver via the "Test" button in System Prefs, you actually have 2 instances of your screensaver view running in the same process' address space on different threads. One (with isPreview==YES) is the little preview in the SysPrefs window (which continues running even when the full-screen version is started), and the other one is the full-screen version. They are both running in the SysPrefs.app process. So, you have to be careful to check all Notifications/etc. to see if they are coming from the view instance you expect.
I don't see any obvious problems from a quick glance at the code you posted, but it may be somewhere else. Do you use Notifications anywhere?
I put a similar webview-in-a-screensaver project on github at http://github.com/kelan/WikiWalker, where I initially had some similar problems (though I wasn't using any javascript stuff). It's not perfect code, but might help. I also did some tricks to forward notifications to the main thread (for drawing) in a . See the "Threaded Notification Support" parts of WWScreenSaverView.{h,m}.
Something to try:
Open up a terminal window and enter the following line to run System Preferences with NSZombieEnabled:
env NSZombieEnabled=YES "/Applications/System Preferences.app/Contents/MacOS/System Preferences"
Perform the steps that lead to the crash.
Run the Console app, set the filter in the upper right to "System Preferences", and look for NSZombie messages.
Hope this helps!
Just to troubleshoot, did you try not releasing the WebView?
Also, maybe set the WebView's delegates to nil before releasing it first?