My app has a navigation controller and two views, firstController and secondController.
firstController has a webView that displays an html page with links, and clicking any link will take the user to secondController. This is where the program stops by stepping through the debugger.
See code below.
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
if (navigationType == UIWebViewNavigationTypeLinkClicked) {
secondController *nextController = [[secondController alloc] init];
[self.navigationController pushViewController:nextController animated:YES];
[nextController release];
return NO;
}
return YES;
}
This works fine except for when I navigate from firstController to secondController by clicking any link on firstController the third time, the application just exits.(firstController link click, secondController back button, firstController link click, secondController back button, firstController link click and the application crashes)
Terminating app due to uncaught
exception
'NSInvalidArgumentException', reason:
'*** -[NSCFSet length]: unrecognized
selector sent to instance 0x251f100'
This is so strange. I've tried everything but still couldn't figure out what went wrong.
You have a memory problem, where some object is sent the length message, but that object is long gone and has it's memory occupied by a NSCFSet object. There's the explanation for the error. Now for the bug.
You might want to try not to release nextController so quickly, but wait a little longer; use autorelease so nextController stays alive at least until the moment your app is returning to some idle mode. So:
secondController *nextController = [[[secondController alloc] init] autorelease];
Otherwise, delve into the inner workings of secondController.
Use NSSet count if you want to know how many elements your set has
Related
I've used this method on several apps that are for sale on the app store, but for some reason the current app I am working on is driving me nuts... I must be overlooking something.
The app's main viewController .h file:
#import "MainMenuController.h"
#import "GamePlay.h"
#interface ProjectNameiPhoneViewController : UIViewController <MenuDelegate, GameDelegate> {
UIViewController *currentPageController;
}
The app starts up and loads the MainMenu viewController:
UIViewController *nextController = [[MainMenuController alloc] initWithNibName:#"MainMenuController" bundle:nil];
[nextController performSelector:#selector(setDelegate:) withObject:self];
[self.view addSubview:nextController.view];
currentPageController = nextController;
[currentPageController retain];
[nextController release];
From MainMenuController.m, the user chooses to start the game:
[delegate startGameplay:self];
Back in the app's main viewController:
- (void)startGameplay:(MainMenuController *)sender {
UIViewController *nextController = [[GamePlay alloc] initWithNibName:#"GamePlay" bundle:nil];
[nextController performSelector:#selector(setDelegate:) withObject:self];
[currentPageController.view removeFromSuperview];
[self.view addSubview:nextController.view];
[currentPageController release];
currentPageController = nextController;
[currentPageController retain];
[nextController release];
}
From the gameplay screen, user hits the back button to return to the main menu:
- (IBAction)backTapped {
[delegate backToMenu:self];
}
Back in the app's main viewController:
- (void)backToMenu:(GamePlay *)sender {
UIViewController *nextController = [[MainMenuController alloc] initWithNibName:#"MainMenuController" bundle:nil];
[nextController performSelector:#selector(setDelegate:) withObject:self];
[currentPageController.view removeFromSuperview];
[self.view addSubview:nextController.view];
[currentPageController release];
currentPageController = nextController;
[currentPageController retain];
[nextController release];
}
I once again choose to start the game from the main menu.
The GamePlay class/Nib loads, and I once again click the back button to return to the main menu.
At this point the app crashes, with no information printed to the console.
Any ideas would be GREATLY appreciated - I've commented out almost everything else in my code to the point where this switching between viewControllers is practically the only code being run and I'm at a loss as to why it is crashing...
Thanks so much in advance for your help!
It's likely that you want to have the MainMenuController as the rootViewController of your application's keyWindow.
Additionally, UIViewController's views should not be added to other UIViewController's views as this breaks the responder chain and in iOS 5.0, will throw an exception. If you choose to only implement it as an iOS 5.0 application, then I recommend taking a look at [UIViewController -addChildViewController:] and [UIViewController -removeChildViewController:]
In saying this, a better solution if doable, would be to have the MainMenuController as the rootViewController as stated above, and then to call [UIViewController -presentModalViewController:animated:] in your startGameplay method and use the delegate to dismiss the modal view controller
This doesn't seem like a true resolution but the app only crashes when I'm running it through Xcode's "build and run." If I open the app by clicking the icon on the device itself, the app runs perfectly. I can play it for long periods of time and mash the buttons to switch between view controllers over and over again very quickly, and it does not crash. So I'm going to submit it for the App Store even though it crashes every time without an error message when running through Xcode - it absolutely does not crash on the device itself.
The strangest part about the crash when running through Xcode is this:
The app crashes on the device, exiting out to the home screen. However, Xcode still shows the app as "Running" and I have the option to click "Stop" - it is not grayed out. As I said, no error message is printed to the console.
I am getting this error, while I am trying to load another view
* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-
[__NSCFType new1:]: unrecognized selector sent to instance 0x5c27950'
Here new1 is a button, which when pressed loads a view.
And here is the code inside the new1
-(IBAction) new1:(id) sender
{
viewController = [[iTViewController alloc] initWithNibName:#"iTViewController" bundle:[NSBundle mainBundle]];
[self.view addSubview:viewController.view];
}
Note: When I launch the App from fresh and press the new1 button, it works flawlessly, but when I press other button which loads other view and when I return back to this view and press new1, then it crashes
The error * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '- [__NSCFType new1:]: unrecognized selector sent to instance 0x5c27950' may not be coming from inside the - (IBAction)new1:(id)sender method. What that error is saying is that you are trying to call a non-existent method on whatever object is at the address 0x5c27950. Here are a few possible solutions:
Set NSZombieEnabled, malloc stack logging, and guard malloc in the debugger. Then, when your App crashes, type this in the gdb comsole:
(gdb) info malloc-history 0x5c27950
Replace 0x5c27950 with the address of the object that the stack trace says caused the crash,and it will give you a much more useful stack trace and it should highlight the exact line that is causing the problem.
Check out this article for more info on NSZombieEnabled.
This one for MallocStackLogging info
More info on guard malloc here
Also, have you tried pushing the view controller (assuming your using a navigation controller):
- (IBAction)new1:(id)sender {
iTViewController *viewController = [[iTViewController alloc] initWithNibName:#"iTViewController" bundle:nil];
[self.navigationController pushViewController:viewController animated:YES];
[viewController release];
}
Or, if you are not using a nav controller, you can present it modally:
- (IBAction)new1:(id)sender {
iTViewController *viewController = [[iTViewController alloc] initWithNibName:#"iTViewController" bundle:nil];
[self presentModalViewController:viewController animated:YES];
[viewController release];
}
Another possibility is that you are calling [self new1:someButton]; but the method is not declared in your header file like so:
#interface MyViewController: UIViewController {
........
}
- (IBAction)new1:(id)sender;
#end
I have an app where I have a TTTableView Controller inside a Navigation Controller that is Insider a TabBar.
I want it so that if a user selects an item it will push another TTTableView with the items under that category.
The code I have is
-(void)didSelectObject:(id)object atIndexPath:(NSIndexPath *)indexPath {
if ([object isKindOfClass:[TTTableMoreButton class]]) {
[super didSelectObject:object atIndexPath:indexPath];
} else {
CategoryViewController *viewController = [[CategoryViewController alloc] initWithNibName:#"CategoryViewController" bundle:nil];
[self.navigationController pushViewController:viewController animated:YES];
[viewController release];
}
}
The CategoryViewController is setup as
#interface CategoryViewController : TTTableViewController
and the CategoryViewController.xib file has the datasource & delegate set to the files owner and the view set to the tableview and the tableview class is set to TTTableView.
When I run it I get the following error when selecting a row
2011-10-17 16:18:23.819 Biz Insider[34067:f803] -[CategoryViewController tableView:numberOfRowsInSection:]: unrecognized selector sent to instance 0x6c93d30
2011-10-17 16:18:23.820 Biz Insider[34067:f803] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[CategoryViewController tableView:numberOfRowsInSection:]: unrecognized selector sent to instance 0x6c93d30'
*** First throw call stack:
(0x1893052 0x157ed0a 0x1894ced 0x17f9f00 0x17f9ce2 0xb4cf2b 0xb4f722 0x9ff7c7 0x9ff2c1 0x9b61e 0xa0228c 0xa06783 0x9bb48 0x9b1301 0x1894e72 0x89192d 0x89b827 0x821fa7 0x823ea6 0x823580 0x18679ce 0x17fe670 0x17ca4f6 0x17c9db4 0x17c9ccb 0x1f88879 0x1f8893e 0x972a9b 0x273d 0x26b5)
terminate called throwing an exceptionCurrent language: auto; currently objective-c
If I try and push another view (i have one with a webview on it) then it works fine, or if I go into interface builder and link the File Owner's "tableView" to the TTTableView object it will work fine and push the controller except the "Pull down to refresh" function wont work so I am assuming that the deletage isn't correct when doing it that way.
Any help would be greatly appreciated.
EDIT:
I have a feeling that it has something to do with the following
-(id<UITableViewDelegate>)createDelegate {
return [[[TTTableViewDragRefreshDelegate alloc] initWithController:self] autorelease];
}
This sets the delegate to TTTableViewDragRefreshDelegate which implements the numberOfRowsInSection and all that junk. Is there another way to do this?
Cheers,
Dean
The log says that in your custom class CategoryViewController method tableView:numberOfRowsInSection: is not implemented and so it couldn't call it.
Check it and check types of its parameters.
Well this is an odd one, but I changed it from
CategoryViewController *viewController = [[CategoryViewController alloc] initWithNibName:#"CategoryViewController" bundle:nil];
to
CategoryViewController *viewController = [[CategoryViewController alloc] init];
and it magically works now...dont ask me how or why.
There must have been something in the NIB that was messing it up, but I dont know how the whole layout is working now with no NIB.
I don't know what wrong with this code but everytime when I run the app, after the Menu is shown, the app crash.
NSString * path = [[NSBundle mainBundle] pathForResource:#"tung" ofType:#"doc"];
UIDocumentInteractionController *docController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:path]];
docController.delegate = self;
//[docController presentPreviewAnimated:YES];
CGRect rect = CGRectMake(0, 0, 300, 300);
[docController presentOptionsMenuFromRect:rect inView:self.view animated:YES];
Error I got:
*** Terminating app due to uncaught exception 'NSGenericException',
reason: '-[UIPopoverController
dealloc] reached while popover is
still visible.'
What should I do now ?
To preview a document via a "throwaway" UIDocumentInteractionController you should retain it after interactionControllerWithURL and autorelease it in the UIDocumentInteractionControllerDelegate method documentInteractionControllerDidDismissOptionsMenu. As remarked by David Liu, releasing it will crash. But autoreleasing works. I have checked that dealloc is actually called.
The following code should work:
- (void)previewDocumentWithURL:(NSURL*)url
{
UIDocumentInteractionController* preview = [UIDocumentInteractionController interactionControllerWithURL:url];
preview.delegate = self;
[preview presentPreviewAnimated:YES];
[preview retain];
}
- (void)documentInteractionControllerDidEndPreview:(UIDocumentInteractionController *)controller
{
[controller autorelease];
}
It's basically the old memory management problem.
[UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:path]] returns an autoreleased object, so it'll get autoreleased soon after your code block finishes. I'm guessing this is unlike presentModalViewController which will retain a copy for you, but that's a side point.
Basically, you need to retain it before your code block ends. The more annoying part is keeping track of what the docController is doing so you don't leak memory. You'll have to check the result from
[docController presentOptionsMenuFromRect:rect inView:self.view animated:YES];
If it returns NO, that means the menu never showed up, and so you should do a release on it right away (if you already did the retain).
However, if it returns YES, then you'll need to implement the delegate methods for docController, and release it when the menu is dismissed (in this case, it would be when:
- (void)documentInteractionControllerDidDismissOptionsMenu:(UIDocumentInteractionController *)controller
gets called.
EDIT:
I want to make a correction here:
The previous answer will crash if the popup menu is dismissed. Essentially there's really not any good way to create a throwaway DocController. Instead, I think it's best to just create one for every file you need in the viewcontroller, and deallocate when you're completely done. Otherwise you'll run into a myriad of possible cases where the DocController will get released too early and crash.
This error is caused (as others have mentioned) by the UIDocumentInteractionController being released while the presented view controller is still depending upon it. Thats a simple error and creating a strong reference to that view controller, in an reference counted environment, will solve the problem. The object can be released when it's no longer necessary by responding to delegate methods.
The reason this is confusing is that some other tools in Cocoa similar in appearance do not need to be retained the same way. For example UIImagePickerController or UIActivityViewController could be created and presented within a method without problem.
The difference between these other examples and UIDocumentInteractionController is that the other components are all subclasses of UIViewController. When they are pushed onto a navigation stack or presented they are retained by the navigation stack or the presenting view controller. When they are dismissed, that reference is removed andy they are released. UIDocumentInteractionController is not a UIViewController. Instead it provides view controllers which can display the relevant interface, but importantly do not (for good reason as it would cause a retain cycle) retain the document interaction controller. Because of that, whoever is creating the document controller must also maintain strong reference to it as long as it's needed by the presented interface.
This example is essentially the same as the accepted answer, but using ARC friendly style of retaining an object.
#interface MYViewController : UIViewController <UIDocumentInteractionControllerDelegate>
#property (nonatomic, strong) UIDocumentInteractionController *documentInteractionController;
#end
#implementation MYViewController
- (void)presentDocumentWithURL:(NSURL*)url {
self.documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:url];
self.documentInteractionController.delegate = self;
[self.documentInteractionController presentPreviewAnimated:YES];
}
- (void)documentInteractionControllerDidEndPreview:(UIDocumentInteractionController *)controller {
self.documentInteractionController = nil;
}
#end
SWIFT 3
Controller variable:
var documentIteratorController : UIDocumentInteractionController?
Call method:
documentIteratorController = UIDocumentInteractionController(url: reportURL)
documentIteratorController?.delegate = self
documentIteratorController?.presentOptionsMenu(from: self.printButton.frame, in: self.view, animated: true)
With Christian's technique...
Should you decide to launch different PDFs from different buttons in the view rather than from the navigation bar, don't use:
[controller autorelease];
Because it will remove the controller, so further instances won't work after the first use.
But if you are using it you may want to say
[self.controller autorelease];
I solved this problem by creating a property and then using this code.
[_DocController dismissMenuAnimated:NO];
_DocController = [UIDocumentInteractionController interactionControllerWithURL:url];
//docController.delegate = self;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
{
[_DocController presentOptionsMenuFromRect:((UIView*)control).bounds inView:control animated:YES];
}
else
{
[_DocController presentOptionsMenuFromBarButtonItem:control animated:YES];
}
The dismissMenuAnimated is important to prevent the UIPopover Dealloc error. The most common occurance of the error was when the popOver was still showing, and you pressed the button again to display the popover.
I have a UITabBarConroller that I use to switch between 3 different views. This all works perfectly. On one of my tabs, I added a button at the to called "Add", I have added an outlet to this, as well as an IBAction method which looks like the following:
// Method used to load up view where we can add a new ride
- (IBAction)showAddNewRideView {
MyRidesViewController *controller = [[MyRidesViewController alloc] initWithNibName:#"AddNewRide" bundle:nil];
controller.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:controller animated:YES];
[controller release];
}//end showAddNewRideView
This currently works fine, and loads up my AddNewRide nib file. But, once that view loads, I have a cancel button, which, when clicked, I want to return to the previous view. So, I figured I would just do the reverse of the above, using the following method which would load back my previous nib:
- (IBAction)cancelAddingNewRide {
MyRidesViewController *controller = [[MyRidesViewController alloc] initWithNibName:#"MainWindow" bundle:nil];
controller.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:controller animated:YES];
[controller release];
}//end cancelAddingNewRide
But, which trying to load the MainWindow nib, the program crashes, and I get the following error:
2010-05-05 20:24:37.211 Ride[6032:207] *** -[MyRidesViewController cancelAddingNewRide]: unrecognized selector sent to instance 0x501e450
2010-05-05 20:24:37.213 Ride[6032:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[MyRidesViewController cancelAddingNewRide]: unrecognized selector sent to instance 0x501e450'
So, I am a little lost as to why it would work one way, but not the other.
First, I wanted to address part of the error: Think of your views as a stack. When you "push" a modal controller, you are adding that view to a stack. The old view is still there underneath. So you need to "pop" off the modal view to return to the old view. If you push a new view on, you now have 3 views on the stack which are all taking up memory, where you really only need one.
So, inside cancelAddingNewRide just try:
[super dismissModalViewControllerAnimated:true];
You may have other issues that are causing the crash, but this should generally get things working.
Typically when I have used presentModalViewController the presented viewController tells the calling viewController to dismiss it using dismissModalViewControllerAnimated:YES;
So in other words in the cacncelAddingNewRide you simply call the class that hass showAddnewRideView in it and have it pass itself to the method.
It's hard to explain but I'll show you an example:
cancelAddingNewRide class:
- (IBACtion)home:(id)sender {
if (self.delegate respondsToSelctor:#selector(dismiss:)]) {
[self.delegate dismiss:self];
}
}
and then in the showAddNewRideView class
-(void) dismiss:(cancelAddingNewRide_class *) controller {
[self dismissModalViewControllerAnimated:Yes];
}
Hope that makes sense and soz for typos
Edit: oh and make the controller's delegate self
controller.delegate = self;
Actually thinking about it more there is a bit more to this. You have to define the called viewController as a Delegate. Have a look at Stanford universities iPhone lectures, lecture 11 deals with this and is available from iTunesU.