Horrible crash after trying to use MBProgressHud - objective-c

I'm using MBPogressHUD in my project and I have all the libraries, imports, etc. set up correctly. However, I'm getting a crash that seems like it has to do with the selector methods. I have a method called "getInfo" that basically connects to a server. The HUD is triggered by pressing a button. After doing some research on the crash, people said to put the initialization in viewWillAppear because some of the init time takes up the time it takes to actually do the task and show the HUD.
-(void)viewWillAppear:(BOOL)animated {
connectionHUD = [[MBProgressHUD alloc]initWithView:self.view];
[self.view addSubview:connectionHUD];
connectionHUD.labelText = #"Connecting";
connectionHUD.mode = MBProgressHUDModeAnnularDeterminate;
}
-(IBAction)connectButton:(id)sender {
[connectionHUD showWhileExecuting:#selector(getInfo) onTarget:self withObject:nil animated:YES];
}
Crash:
Tried to obtain the web lock from a thread other than the main thread or the web thread. This may be a result of calling to UIKit from a secondary thread. Crashing now...
-(void)getInfo {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
UserPi *newPi = [[UserPi alloc]init];
newPi.passWord = self.passTextField.text;
[defaults setObject:self.passTextField.text forKey:#"password"];
newPi.userName = self.userTextField.text;
[defaults setObject:self.userTextField.text forKey:#"username"];
newPi.ipAddress = self.ipTextField.text;
[defaults setObject:self.ipTextField.text forKey:#"ip"];
[newPi connectToServer];
NSString* newAddress = [newPi returnIP];
self.connected = newPi.connected;
[self.delegate sendIP:newAddress];
[self.delegate isConnected:self.connected];
[defaults synchronize];
[self.navigationController popToRootViewControllerAnimated:YES];
}
full error:
bool _WebTryThreadLock(bool), 0x7327740: Tried to obtain the web lock from a thread other than the main thread or the web thread. This may be a result of calling to UIKit from a secondary thread. Crashing now...
1 0x3a1ffe9 WebThreadLock
2 0x4ec8ff -[UITextRangeImpl isEmpty]
3 0x4ec4db -[UITextRange(UITextInputAdditions) _isCaret]
4 0x48e7b6 -[UITextSelectionView setCaretBlinks:]
5 0x328f79 -[UIKeyboardImpl setCaretBlinks:]
6 0x3185bc -[UIKeyboardImpl setDelegate:force:]
7 0x3184ae -[UIKeyboardImpl setDelegate:]
8 0x53ff65 -[UIPeripheralHost(UIKitInternal) _reloadInputViewsForResponder:]
9 0x29215b -[UINavigationController navigationTransitionView:didStartTransition:]
10 0x418961 -[UINavigationTransitionView transition:fromView:toView:]
11 0x418658 -[UINavigationTransitionView transition:toView:]
12 0x294651 -[UINavigationController _startTransition:fromViewController:toViewController:]
13 0x29489b -[UINavigationController _startDeferredTransitionIfNeeded:]
14 0x295dc6 _popViewControllerNormal
15 0x296065 -[UINavigationController _popViewControllerWithTransition:allowPoppingLast:]
16 0xe6124b0 -[UINavigationControllerAccessibility(SafeCategory) _popViewControllerWithTransition:allowPoppingLast:]
17 0x2961a8 -[UINavigationController popViewControllerWithTransition:]
18 0x2965b9 -[UINavigationController popToViewController:transition:]
19 0x296257 -[UINavigationController popToRootViewControllerWithTransition:]
20 0x2961de -[UINavigationController popToRootViewControllerAnimated:]
21 0x4868 -[ConnectionViewController getInfo]
22 0x126a6b0 -[NSObject performSelector:withObject:]
23 0x8657 -[MBProgressHUD launchExecution]
24 0xca1805 -[NSThread main]
25 0xca1764 __NSThread__main__
26 0x95108ed9 _pthread_start
27 0x9510c6de thread_start

Most UIKit framework methods are not thread safe and must always be called on the main thread. In your case, getInfo is calling UIKit APIs from a background thread notably, -[UINavigationController popToRootViewControllerAnimated:]
MBProgressHUD causes your getInfo to be called on a background thread. See the method showWhileExecuting:onTarget:withObject:animated:.
Try using GCD to dispatch the UIKit methods on the main thread:
-(void)getInfo {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
UserPi *newPi = [[UserPi alloc]init];
newPi.passWord = self.passTextField.text;
[defaults setObject:self.passTextField.text forKey:#"password"];
newPi.userName = self.userTextField.text;
[defaults setObject:self.userTextField.text forKey:#"username"];
newPi.ipAddress = self.ipTextField.text;
[defaults setObject:self.ipTextField.text forKey:#"ip"];
[newPi connectToServer];
NSString* newAddress = [newPi returnIP];
self.connected = newPi.connected;
// **NOTE**
// PS: self.delegate methods should not call UIKit methods
// if they do, then move them into the main thread callback block
[self.delegate sendIP:newAddress];
[self.delegate isConnected:self.connected];
[defaults synchronize];
// Do UI work on main thread.
dispatch_async(dispatch_get_main_queue(), ^{
[self.navigationController popToRootViewControllerAnimated:YES];
});
}

Are you calling -getInfo on the main thread? [self.navigationController popToRootViewControllerAnimated:YES]; must be called on the main thread.

The problem is that your getInfo method makes calls into the UI, like this:
[self.navigationController popToRootViewControllerAnimated:YES];
… but you're running it on a background thread.
The whole point of -showWhileExecuting: onTarget: withObject: animated: is that it automatically runs your code in a background thread.
So, you need to protect things the same way as you do when manually running in a background thread.
So, any UI code in your method needs to use performSelectorOnMainThread: and friends, or dispatch_async or other means of doing the same thing.
In this particular case, you want to dispatch a method that takes a primitive (BOOL) argument, which means you can't just use -performSelectorOnMainThread: withObject:. But you also presumably want to wait until it's done, which means you can't just use dispatch_async.
You can just write a wrapper method that takes no arguments:
- (void)popNavigationControllerToRoot {
[self.navigationController popToRootViewControllerAnimated:YES];
}
… and then:
[self performSelectorOnMainThread:#selector(popNavigationControllerToRoot)
waitUntilDone:YES];
Or you can use wrappers around NSInvocation, like the ones here:
[[self.navigationController dd_invokeOnMainThreadAndWaitUntilDone:YES]
popToRootViewControllerAnimated:YES];

Related

OSX 10.10.3 crashes WebView on dealloc

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];
}
}

handle Object interdependency in a mulithreaded ios 6 app

I have an ios 6 app which instantiates 3 singletons in the App Delegate as below:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
Constants *constSingleton = [Constants getSingleton];
EntryContainerSingleton * eSingle = [EntryContainerSingleton getSingleton];
LocationMgrSingleton *loc = [LocationMgrSingleton getSingleton];
return YES;
}
However the problem happens that all three calls are executing in different threads simultaneously. EntryContainerSingleton depends on Constants to do some tasks. But Constants is not instantiated completely when it is executing those tasks.
How can I handle this situation?
I was googling around and in previous versions of iOS people have used the NSOperation queue to do this.
However, Im not sure whether this is a good approach in iOS 6. And even if it is I haven't used NSOperation queue before and all the samples on the web are from previous versions and are instantiated in some class which is not an APP Delegate.
If someone can give me some sample code on how to do this in App Delegate to get me started Id really appreciate it
EDIT
Entries controller singleton
-(id)init
{
self = [super init];
NSLog(#"%s %d", __PRETTY_FUNCTION__, __LINE__);
[self getEntriesFromServer];
..............
.............
constSingleton = [Constants getSingleton];
[self addSelfAsObserverToNotifications];
return self;
}
Inside entriescontrollersingleton
-(void)getEntriesFromServer
{
NSLog(#"%s %d", __PRETTY_FUNCTION__, __LINE__);
if(!constSingleton)
{
NSLog(#"constSingleton is null");
}
__block NSString *dateString1 = [constSingleton getCurrentDateTime];
NSLog(#"Sending notification: Async Call started successfully at time %#", dateString1);
[[NSNotificationCenter defaultCenter] postNotificationName:#"didStartAsyncProcess"
object:self];
.......
}
Console output
[96894:c07] -[AppDelegate application:didFinishLaunchingWithOptions:] 21
[96894:c07] +[Constants getSingleton] 39
[96894:c07] -[Constants init] 65
[96894:c07] -[EntryContainerSingleton init] 75
[96894:c07] -[EntryContainerSingleton getEntriesFromServer] 154
[96894:c07] constSingleton is null
[96894:c07] Sending notification: Async Call started successfully at time (null)
[96894:c07] -[Constants incrementNumBackgroundNetworkTasksBy1:] 87
If the Entries singleton needs access to the Constants singleton, it should call [Constants getSingleton] to obtain it. Make sure you implement your singleton methods correctly (see Objective-C Singleton implementation, am i doing it right?)
Every time you need access to a singleton object, you should call [ClassName getSingleton]. There shouldn't be any reason to store a singleton as an instance variable in your application delegate.

NSUndoManager + endUndoGrouping called with no matching begin

I have a Cocoa document-based app and running into some problems with NSUndoManager even though I'm having Core Data take care of all of it.
So every time a new persistent document is created I create a root context and a child context. The root context is responsible for writing to the persistent document and the child context is the context that I modify via create/edit/delete NSManagedObjects. I set the root context as the parent context. I also set the undo manager of the child context to the original context that was created with the NSPersistentDocument. Here's some code to hopefully make it a bit clearer:
// create root context
NSManagedObjectContext *rootContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
// startContext is the context created with the document
[rootContext setPersistentStoreCoordinator:[startContext persistentStoreCoordinator]];
NSManagedObjectContext *childContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
// set root as parent
[childContext setParentContext:rootContext];
// set undo
[childContext setUndoManager:[startContext undoManager]];
The reason I do all of this is because I ran into a similar problem as described here:
Enable saving of document NSManagedObjectContext immediately?
So I bring this up because this is the only code in my application where I even touch the NSUndoManager. I am testing my app by simply inserting NSManagedObjects and then Undo-ing the insertion. Sometimes after two undos, or maybe five, or maybe even ten I receive the following error:
_endUndoGroupRemovingIfEmpty:: NSUndoManager 0x100159f30 is in invalid state, endUndoGrouping called with no matching begin
2013-01-29 21:31:23.375 TestApplication[30125:303] (
0 CoreFoundation 0x00007fff8a54f0a6 __exceptionPreprocess + 198
1 libobjc.A.dylib 0x00007fff8215f3f0 objc_exception_throw + 43
2 CoreFoundation 0x00007fff8a54ee7c +[NSException raise:format:] + 204
3 Foundation 0x00007fff80ea021f -[NSUndoManager _endUndoGroupRemovingIfEmpty:] + 195
4 Foundation 0x00007fff80ea0154 -[NSUndoManager endUndoGrouping] + 42
5 Foundation 0x00007fff80ed79da +[NSUndoManager(NSPrivate) _endTopLevelGroupings] + 447
6 AppKit 0x00007fff8253632d -[NSApplication run] + 687
7 AppKit 0x00007fff824dacb6 NSApplicationMain + 869
8 TestApplication 0x0000000100001512 main + 34
9 TestApplication 0x00000001000014e4 start + 52
So if I'm reading the debugging information correctly then I need to call [[context undoManager] beginUndoGrouping] however the problem with this is that no where in my program do i use the `[[context undoManager] endUndoGrouping]'. Has anyone experience this before?
Any help is appreciated.
Each context in OSX creates an undoManager by default (nil in iOS). The reason for the grouping error was that your child context undoManager was trying to nest changes inside the rootContext's undoManager which was nested inside the startContext undoManager (which was the same as the child context's undoManager).
// create root context
NSManagedObjectContext *rootContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
// startContext is the context created with the document
[rootContext setPersistentStoreCoordinator:[startContext persistentStoreCoordinator]];
[rootContext setUndoManager:startContext.undoManager];
NSManagedObjectContext *childContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
// set root as parent
[childContext setParentContext:rootContext];
For me, it seems to be caused by sending begin/endGrouping message during undo/redo handling.
I disable undo registration temporarily to remove this error message.
[[NSNotificationCenter defaultCenter] addObserverForName:NSUndoManagerDidUndoChangeNotification object:undoManager queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
[undoManager disableUndoRegistration];
// Some code for undo containing undo grouping
[undoManager enableUndoRegistration];
}
I did some more digging and found this.
In my project some sorting operation is done in a child managed object context running on a background thread and I tried to undo the whole process including the sorting one in the main thread (top level context). I could resolve this by moving the sorting operation to the background thread as it was during undo/redo.

NSTimer crashing my application

So I have an application which uses an NSTimer. The problem is that when the NSTimer runs, my application will crash with EXC_BAD_ACCESS. I only just started with objective-c so I don't know how to properly debug it. If I thought call the -(void)logIn{} with [self logIn]; the application will work.
My code:
.h
#interface DJ_WAppDelegate : NSObject {
NSTimer *loginTimer;
}
-(void)logIn;
#end
.m
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
[self logIn]; // this works
loginTimer = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:#selector(logIn) userInfo:nil repeats:YES]; // this fails
}
- (void)logIn {
NSLog(#"Logging in...");
// if I comment out these 2 lines it works with the NSTimer!?... (I've would have more code below)
NSURL *loginConn = [NSURL URLWithString:[NSString stringWithFormat:#"some-website.com"]];
NSInteger loginReturn = [[NSString stringWithContentsOfURL:loginConn encoding:NSASCIIStringEncoding error:nil] intValue];
// when "loginReturn" return "OK" the timer (loginTimer) will be stopped with: [loginTimer invalidate];
// more code would be below... (which works!)
}
So the problem is with the NSURL it think.
Thanks for helping.
EDIT 1:
Here's the crash stack:
EException Type: EXC_BAD_ACCESS (SIGBUS)
Exception Codes: KERN_PROTECTION_FAILURE at 0x0000000000000020
Crashed Thread: 0 Dispatch queue: com.apple.main-thread
Application Specific Information:
objc_msgSend() selector name: respondsToSelector:
Thread 0 Crashed: Dispatch queue: com.apple.main-thread
0 libobjc.A.dylib 0x9603bed7 objc_msgSend + 23
1 com.apple.CoreFoundation 0x922ed5f2 _CFStringAppendFormatAndArgumentsAux + 3138
2 com.apple.CoreFoundation 0x922ec979 _CFStringCreateWithFormatAndArgumentsAux + 105
3 com.apple.Foundation 0x9656ebfb -[NSPlaceholderString initWithFormat:locale:arguments:] + 163
4 com.apple.Foundation 0x9656eaae +[NSString stringWithFormat:] + 88
5 com.who.DJ-W 0x00001d56 -[DJ_WAppDelegate logIn] + 116 (DJ_WAppDelegate.m:108)
6 com.apple.Foundation 0x965b08d4 __NSFireTimer + 141
7 com.apple.CoreFoundation 0x922ffadb __CFRunLoopRun + 8059
8 com.apple.CoreFoundation 0x922fd464 CFRunLoopRunSpecific + 452
9 com.apple.CoreFoundation 0x922fd291 CFRunLoopRunInMode + 97
10 com.apple.HIToolbox 0x91646e04 RunCurrentEventLoopInMode + 392
11 com.apple.HIToolbox 0x91646bb9 ReceiveNextEventCommon + 354
12 com.apple.HIToolbox 0x91646a3e BlockUntilNextEventMatchingListInMode + 81
13 com.apple.AppKit 0x9265378d _DPSNextEvent + 847
14 com.apple.AppKit 0x92652fce -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] + 156
15 com.apple.AppKit 0x92615247 -[NSApplication run] + 821
16 com.apple.AppKit 0x9260d2d9 NSApplicationMain + 574
17 com.who.DJ-W 0x00001c92 start + 54
hope this helps... to find the error
EDIT 2:
With the Zombie Mode on I get this: *** -[CFString respondsToSelector:]: message sent to deallocated instance 0x46d550 I hope this helps.
EDIT 3:
To Ball: Here is the original URL (took away the domain here) /DJW/work.php?user=iBlackbirdi&udid=00000000-0000-1000-80005&key=660e5744e&cmd=slocau&type=get
There are several problems here:
djwLog is a class method so you should call it on the class not the instance
like this: [[self class] djwLog:#"foo bar"]
The URLFromString: method needs the string to contain a valid URL as specified by RFC 1808. Something along the lines of [NSURL URLFromString:#"http://example.com/foo"]
stringWithContentsOfURL:url… is a syncornous method. Unless you have this code running in a separate thread you should not use this. Look at NSURLConnection for a class to asynchronously load data from a URL. Using synchronized calls for this is a bad idea. Always.
intValue returns a signed integer. To get a NSInteger use integerValue. Also if you use stringWithContentsOfURL make sure to check if it's result is nil before calling
integerValue otherwise you might get a result of 0 if the URL call failed or did not return data. A real parser for the API you are calling would be a good idea in any case.
You're calling a class method as if it was an instance method:
[self djwLog:#"Logging in..."];
Since there is no instance-method named djwLog the application crashes. You should either call NSLog directly, make it an instance-method - or preferably make a macro for logging:
#ifdef DEBUG
# define DLog(...) NSLog(#"%s %#", __PRETTY_FUNCTION__, [NSString stringWithFormat:__VA_ARGS__])
#else
# define DLog(...) do { } while (0)
#endif
If you instead of NSLog write DLog - it will only be logging when DEBUG-flag has been defined. In addition to this, it will log the origin of the log message - so you can see which class/method the log entry came from.

Mac Screensaver with WebView crashes

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?