Releasing "referential" variables in method scope - objective-c

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

Related

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

TableView: Based on rowName setting variables

Im gonna do a TableView-layout but depending on the chose I would like to set variables to specific numbers.
Examples:
Choosing "row1" -> Alpha = 1.00
Choosing "row2" -> Alpha = 2.00 & Beta = 3.00
and so on...
As far as I concerns tapping on a row just forward to next view, I want all my rows to be linked to the same View but with different/custom Alpha and Beta.
Tried to search but finding it hard to formulate this in xcode word.
So you are going to a new viewController, when tapping on a row? Than look for your code in
tableView: didSelectRowAtIndexPath:
Inside that code you will find smth. like that:
ViewControllerClass* viewController = [[ViewControllerClass alloc] init];
[self.navigationController pushViewController: viewController animated: YES];
[viewController release];
Just add your custom settings, before pushing:
ViewControllerClass* viewController = [[ViewControllerClass alloc] init];
if(indexPath.row == 0)
{
viewController.alpha = ...;
viewController.beta = ...;
}
else if(indexPath.row == 1)
{
viewController.alpha = ...;
viewController.beta = ...;
}
else
{
viewController.alpha = ...;
viewController.beta = ...;
}
[self.navigationController pushViewController: viewController animated: YES];
[viewController release];

How to add associated object to uitapgesture?

Currently I have in my code
tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(addCompoundToLabel:)];
My code would be cleaner if it was something like
tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(addCompoundToLabel:) withObject:data];
I read that associated objects allow you to pass objects in a gesture recognizer, but after reading the docs, I don't quite understand how to implement it into my code. An example implementation of associated objects would be appreciated. Thanks
EDIT:
this is what the beginning of addCompound looks like
- (void)addCompoundToLabel:(UIGestureRecognizer *)recognizer {
if( [recognizer state] == UIGestureRecognizerStateEnded ) {
FormulaLabel* label = (FormulaLabel*)[recognizer view];
Using associated objects is a messy solution. Instead, you could create a false target object, give it your UIView and your Data, and have access to both of them in the selector method. It is a lot more readable, and your code expresses your intent a lot better.
Here is a quick and dirty example:
#interface FalseTarget : NSObject {
MyViewController *_viewCtrl;
MyData *_data;
}
-(id)initWithViewCtrl:(MyViewController*)viewCtrl andData:(MyData*)data;
-(void)tap:(id)sender;
#end
#implementation
-(id)initWithViewCtrl:(MyViewController*)viewCtrl andData:(MyData*)data {
self = [super init];
if (self) {
_viewCtrl = viewCtrl;
_data = data;
}
return self;
}
-(void)tap:(id)sender {
[_viewCtrl processTapFromSender:sender withData:_data];
}
#end
Add processTapFromSender:withData: method to your controller:
-(void)processTapFromSender:(id)sender withData:(MyData*)data {
// Your action
}
Now you can create your tap recognizer like this:
FalseTarget *target = [[FalseTarget alloc] initWithViewCtrl:self andData:data];
tap = [[UITapGestureRecognizer alloc] initWithTarget:target action:#selector(tap:)];

EXC_BAD_ACCESS although objects are not freed?

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.