EXC_BAD_ACCESS although objects are not freed? - objective-c

I have the following problem in my code:
UITableViewController *controller = nil;
switch (indexPath.row) {
case 0:
controller = self.kundenTableViewController;
break;
case 1:
controller = self.projekteTableViewController;
break;
case 2:
controller = self.leistungenTableViewController;
break;
case 3:
controller = self.zeitenTableViewController;
break;
}
[self.navigationController pushViewController:controller animated:YES];
All those four view controllers are properly defined in the .h-file and are synthesized manually (and yes, all are exactly the same, I double checked):
- (LeistungenTableViewController*)leistungenTableViewController {
if (leistungenTableViewController == nil) {
// Neu erzeugen
leistungenTableViewController = [[LeistungenTableViewController alloc] initWithNibName:#"LeistungenListeView" bundle:nil];
}
return leistungenTableViewController;
}
Now, something strange happens: if the case 0: is called, controller becomes self.kundenTableViewController. Then I get an EXC_BAD_ACCESS at the last line, where the view controller is pushed onto the stack. This does only happen with this particular controller, not with the other ones.
I tried NSZombies and checked via NSLog whether the controller gets initialized properly, but everything seems fine. Any ideas?
Update: here's the code for the four controllers:
- (KundenTableViewController*)kundenTableViewController {
if (kundenTableViewController == nil) {
// Neu erzeugen
kundenTableViewController = [[KundenTableViewController alloc] initWithNibName:#"KundenListeView" bundle:nil];
}
return kundenTableViewController;
}
- (LeistungenTableViewController*)leistungenTableViewController {
if (leistungenTableViewController == nil) {
// Neu erzeugen
leistungenTableViewController = [[LeistungenTableViewController alloc] initWithNibName:#"LeistungenListeView" bundle:nil];
}
return leistungenTableViewController;
}
- (ProjekteTableViewController*)projekteTableViewController {
if (projekteTableViewController == nil) {
// Neu erzeugen
projekteTableViewController = [[ProjekteTableViewController alloc] initWithNibName:#"ProjekteListeView" bundle:nil];
}
return projekteTableViewController;
}
- (ZeitenTableViewController*)zeitenTableViewController {
if (zeitenTableViewController == nil) {
// Neu erzeugen
zeitenTableViewController = [[ZeitenTableViewController alloc] initWithNibName:#"ZeitenListeView" bundle:nil];
}
return zeitenTableViewController;
}
I just can't figure out why it only happens with the first one.

Sounds like something goes wrong with intialization of the nib file, in particular once the -loadView method which is called (which happens just before the view controller is displayed and is responsible for hooking up the IBOutlets with the proxy objects). Are you sure every IBOutlet is properly connected and the view is connected as well? You might want to check your nibs.

Related

UIActivity custom activityViewController crash on iPad

I have a custom UIActivity that I use in order to create a Contact to the device's AddressBook. In this UIActivity, I create an ABNewPersonViewController, put it inside a UINavigationController and return it in UIActivity's
- (UIViewController *)activityViewController
The problem is that on the iPad I get a crash due to a reference to a released UINavigationController. Messages are of type:
*** -[UINavigationController _viewControllerForSupportedInterfaceOrientations]: message sent to deallocated instance 0xa6f1660
Also after my controller is dismissed, the iPad does not autorotate the view when the interface changes orientation.
The code I use is the following:
In UIActivity
- (void)prepareWithActivityItems:(NSArray *)activityItems
{
// Prepare the AB View Controller
ABRecordRef aContact = ABPersonCreate();
CFErrorRef error = NULL;
ABRecordSetValue(aContact, kABPersonKindProperty, kABPersonKindOrganization, &error);
ABRecordSetValue(aContact, kABPersonOrganizationProperty, #"Apple Inc.", &error);
ABMultiValueRef phone = ABMultiValueCreateMutable(kABMultiStringPropertyType);
ABMultiValueAddValueAndLabel(phone, #"+1 2345 784513", kABWorkLabel, NULL);
ABRecordSetValue(aContact, kABPersonPhoneProperty, phone, &error);
CFRelease(phone);
self.newContactVC.title = #"New company";
self.newContactVC.displayedPerson = aContact;
[self.navigation setViewControllers:[NSArray arrayWithObject:self.newContactVC]];
CFRelease(aContact);
}
- (UIViewController *)activityViewController
{
return self.navigation;
}
// Dismisses the new-person view controller.
- (void)newPersonViewController:(ABNewPersonViewController *)newPersonViewController didCompleteWithNewPerson:(ABRecordRef)person
{
[self activityDidFinish:YES];
}
In the ViewController that calls this UIActivity
- (IBAction)showActionsSheet:(id)sender {
AddToAddressBookActivity *abActivity = [[AddToAddressBookActivity alloc] init];
UIActivityViewController *activityVC = [[UIActivityViewController alloc] initWithActivityItems:nil
applicationActivities:[NSArray arrayWithObject:abActivity]];
activityVC.excludedActivityTypes = #[UIActivityTypeAssignToContact, UIActivityTypeSaveToCameraRoll];
if (!self.popover || ![self.popover isPopoverVisible]) {
self.popover = [[UIPopoverController alloc] initWithContentViewController:activityVC];
[self.popover setDelegate:self];
self.popover.passthroughViews = nil;
[self.popover presentPopoverFromRect:self.showASBtn.frame
inView:self.view
permittedArrowDirections:UIPopoverArrowDirectionAny
animated:YES];
}
}
Any help would be greatly appreciated.
Link to a demo project:
http://ge.tt/23MeOYq/v/0?c
I had a similar problem, except the crash occurred on rotation after the activity view controller was dismissed. In my case, I had the custom view controller in a storyboard setup to present as "fullscreen", and on changing it to display as "form sheet" the crash went away. I don't know why that made a difference though.
Regarding your autorotation not working after the custom view controller dismisses, try adding a completion handler to your activity view controller to nil your popover view controller ivar. I noticed that if I didn't do this the custom activity and the custom view controller don't get dealloc'd, and autorotation doesn't happen. e.g.:
avc.completionHandler = ^(NSString* activityType, BOOL completed){
if ( UIUserInterfaceIdiomPad == [UIDevice currentDevice].userInterfaceIdiom )
self.popover = nil;
};
Also, check out the post at: "activityviewcontroller not dismissing", which I found helpful.

How to keep a class alive like in Tabbed apps

I currently have an app like this that has the drawer interface but when I go into another view everything resets in the current view and never stays the same ?
This is how my code looks like when I switch views.
[self.slidingViewController anchorTopViewOffScreenTo:ECRight animations:nil onComplete:^{
CGRect frame = self.slidingViewController.topViewController.view.frame;
self.slidingViewController.topViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"homeChannel"];
self.slidingViewController.topViewController.view.frame = frame;
[self.slidingViewController resetTopView];
[tableView deselectRowAtIndexPath:indexPath animated:YES]; }];
So my question is "How would I keep the view alive while going into another view such as the tabbed apps "
Such as my tableView, when I go to another view and back to the original one it resets.
Create a NSMutableArray that will keep a pointer to your views. Save the view when you leave it, fetch it when you need it back. Beware! This will consume a lot of memory, but that's what you asked for.
- (void)initArray
{
myViews = [[NSMutableArray alloc] init];
for (int i=0; i<NUMBER_OF_VIEWS; i++)
{
[myViews addObject:[NSNull null]];
}
}
- (void)saveView:(UIView*)view atIndex:(int)index
{
myViews[index] = view;
}
- (UIView*)fetchViewAtIndex:(int)index
{
UIView *view = myViews[index];
if (view == [NSNull null])
return nil;
return view;
}

ipad objective c using removeFromSuperview to remove UICollectionViewController throws an error

So I'm customizing this control I found since I think it works very well except with this issue of mine:
http://www.cocoacontrols.com/controls/fsverticaltabbarcontroller
I wanted to load a UICOllectionViewCOntroller instead of a regular ViewController whenever an item is tapped on the sidebar. So I did this modification when selecting an item:
- (void)setSelectedIndex:(NSUInteger)selectedIndex
{
NSLog(#"selected Index is %#", [NSNumber numberWithInt:selectedIndex]);
NSLog(#"_selected Index is %#", [NSNumber numberWithInt:_selectedIndex]);
NSLog(#"vc counts is %i", [self.viewControllers count]);
if (selectedIndex != _selectedIndex && selectedIndex < [self.viewControllers count])
{
// add new view controller to hierarchy
UIViewController *selectedViewController = [self getSelectedVCWithSelectedIndex:selectedIndex];
[self addChildViewController:selectedViewController];
selectedViewController.view.frame = CGRectMake(self.tabBarWidth,
0,
self.view.bounds.size.width-self.tabBarWidth,
self.view.bounds.size.height);
selectedViewController.view.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
[self.view addSubview:selectedViewController.view];
// remove previously selected view controller (if any)
if (_selectedIndex != NSNotFound)
{
UIViewController *previousViewController = [self.viewControllers objectAtIndex:_selectedIndex];
NSLog(#"ERROR HERE: remove previous: previousVC = %#", previousViewController);
[previousViewController.view removeFromSuperview];
[previousViewController removeFromParentViewController];
}
// set new selected index
_selectedIndex = selectedIndex;
// update tab bar
if (selectedIndex < [self.tabBar.items count])
{
self.tabBar.selectedItem = [self.tabBar.items objectAtIndex:selectedIndex];
}
// inform delegate
if ([self.delegate respondsToSelector:#selector(tabBarController:didSelectViewController:)])
{
[self.delegate tabBarController:self didSelectViewController:selectedViewController];
}
}
}
So what I did is since it already handles the index number of the items on teh sidebar, I just made sure it instantiates the type of controller it needs to load using this line, I have 3 regular VC and 1 collection VC:
UIViewController *previousViewController = [self.viewControllers objectAtIndex:_selectedIndex];
This is how it looks like:
-(UIViewController *)getSelectedVCWithSelectedIndex:(NSUInteger)selectedIndex{
UIViewController *selectedVC = [[UIViewController alloc]init];
// do a switch case on this.
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
// need to instantiate each and every custom uinav
switch(selectedIndex){
case 1:
selectedVC = [storyboard instantiateViewControllerWithIdentifier:#"UINavAdminCategoryIndexViewControllerID"];
break;
case 2:
selectedVC = [storyboard instantiateViewControllerWithIdentifier:#"UINavAdminParticipantIndexViewControllerID"];
break;
case 3:
selectedVC = [storyboard instantiateViewControllerWithIdentifier:#"UINavAdminTablesIndexCollectionViewControllerID"];
break;
case 4:
selectedVC = [self.viewControllers objectAtIndex:selectedIndex];
break;
default:
break;
}
return selectedVC;
}
Now everything would load smoothly, but whenever I would go to the Collection VC tab and then move away from it by going to another tab it would throw this error:
* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'UICollectionView must be
initialized with a non-nil layout parameter'
The application bombs on this part when I remove it from the superview:
[previousViewController.view removeFromSuperview];
Was wondering why it would instantiate the UIView again when all I'm doing is removing it from the stack (is that the right term?)
EDIT: Added some more codes
So I finally figured it out and hopefully someone else can find this useful. When generating a UiCollectionView you need to initiate the Layout for some reason. Idk why, I will try to find out. But this is what led me to the solution:
http://www.rqna.net/qna/ikvmhu-uicollectionview-must-be-initialized-with-a-non-nil-layout-parameter.html
Before when I instantiated the CollectionViewController on the main ViewController before the FSVerticalTabbar is called I just used the class connected to the ViewController on the storyboard eg. AdminMainController, AdminEventsCollectionController, etc.
I basically just added the Layout and used that for the UICollectionViewController when being initiated. It now removes the VC without any errors.
UICollectionViewFlowLayout *aFlowLayout = [[UICollectionViewFlowLayout alloc] init];
[aFlowLayout setItemSize:CGSizeMake(200, 140)];
[aFlowLayout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
myCollectionViewController = [[MyCollectionViewController alloc]initWithCollectionViewLayout:flowLayout];

How can I add TTTableViewDragRefreshDelegate only for one page in UITabBarController?

Hi I have a tabbar controller which includes two pages A and B. Now I just want to add TTTableViewDragRefreshDelegate to page A.
If I put the code:
- (id<TTTableViewDelegate>) createDelegate {
TTTableViewDragRefreshDelegate *delegate = [[TTTableViewDragRefreshDelegate alloc] initWithController:self];
return [delegate autorelease];
}
Into UITabBarController then both A and B got refresh, so I put above code into A's view controller and call A's view controller in UITabBarController like this:
if (_page == APage) {
AViewController *_aViewController = [[[AViewController alloc] init] autorelease];
self.dataSource = _aViewController.dataSource;
...
}
But seems not work, page A still not get drag and refresh. So how can I get TTTableViewDragRefreshDelegate only for page A in a UITabBarController?
This is AViewController init code:
- (id)init {
if (self = [super init]) {
self.variableHeightRows = YES;
id<TTTableViewDataSource> ds = [APageDataSource dataSourceWithItems:nil];
ds.model = CreateTabModelWithCurrentSettings();
self.dataSource = ds;
}
return self;
}
How to let make page A get all AViewController's properties? Like AViewController's model, dataSource, something like that?
AViewController should be a subclass of TTTableViewController

Releasing "referential" variables in method scope

In objective-c (cocoa touch) I have a series of UIViewControllers that I am switching between.
- (void)switchViews:(id)sender
{
UIButton *button = (UIButton *)sender;
UIViewController *nextViewController;
int tag = button.tag;
switch (tag)
{
// -- has already been created
case kFinancialButton:
nextViewController = financials;
break;
case kSocialButton:
if (social == nil)
{
SocialViewController *socialViewController = [[SocialViewController alloc] initWithNibName:#"SocialViewController" bundle:nil];
social = socialViewController;
[socialViewController release];
}
nextViewController = social;
break;
case kTicketingButton:
if (ticketing == nil)
{
TicketingViewController *ticketingViewController = [[TicketingViewController alloc] initWithNibName:#"TicketingViewController" bundle:nil];
ticketing = ticketingViewController;
[ticketingViewController release];
}
nextViewController = ticketing;
break;
}
///////
------> // -- [button/nextViewController release]????
///////
[self setActiveButton:button];
}
As you can see, I'm assigning one of the view controllers to "nextViewController". What I'm wondering is if I need to release this "local" variable or if it's ok to leave alone since it's just pointing to one of my view controllers (which I release in dealloc). I don't think "tag" needs to be released since it's a "primitive", correct? How about button? I don't quite understand what should and shouldn't be released explicitly so perhaps I'm being overcautious. Thanks in advance.
In general you only have to release a variable that you've retain'd init'd or copy'd.
Edit:
After reading your code a little bit more, it seems like you'd have other issues with bad values. The below code makes a little more sense to me. This assumes that financials, social, and ticketing are all #synthesized ivars.
- (void)switchViews:(id)sender
{
UIButton *button = (UIButton *)sender;
UIViewController *nextViewController;
int tag = button.tag;
switch (tag)
{
// -- has already been created
case kFinancialButton:
nextViewController = self.financials;
break;
case kSocialButton:
if (!social) {
self.social = [[[SocialViewController alloc] initWithNibName:#"SocialViewController" bundle:nil] autorelease];
}
nextViewController = self.social;
break;
case kTicketingButton:
if (!ticketing) {
self.ticketing = [[[TicketingViewController alloc] initWithNibName:#"TicketingViewController" bundle:nil] autorelease];
}
nextViewController = self.ticketing;
break;
}
// Do something with nextViewController I'd assume
[self setActiveButton:button];
}