iPhone MultiView App Issue [duplicate] - iphone

This question already has an answer here:
Closed 11 years ago.
Possible Duplicate:
iPhone App Dev - Loading View From View Controller
I have a root view controller with a tool bar which has a button. When the root view controller loads I want it to load a subview underneath the toolbar:
//assigns JSON to question objects
-(void) setQuestions {
questionArray = [[NSMutableArray alloc] init];
for (NSDictionary *q in self.questions) {
/* Create our Question object and populate it */
QuestionViewController *question = [[QuestionViewController alloc]init];
[question setQuestionId:[q objectForKey:#"questionId"] withTitle:[q objectForKey:#"question"] number:[q objectForKey:#"questionNumber"] section:[q objectForKey:#"sectionId"]];
/* Add it to our question (mutable) array */
[questionArray addObject:question];
[question release];
}
}
-(void) startQuestionnaire {
currQ = [questionArray objectAtIndex:0];
[self.view insertSubview:currQ.view atIndex:0];
[currQ release];
}
I use the startQuestionnaire to load the viewcontroller from questionArray which contains a load of QuestionViewController objects...When I click on the slider in the view that is loaded the program crashes...Do I have have to hand control over to the subview or something?The program doesnt crash when I emove the code within startQuestionnaire

While it's tough to say without seeing the rest of your code, it looks like your problem is somewhere in the way you're instantiating / referencing objects.
If I were you, I'd approach this slightly differently in the interest of simplifying things.
First off, try to use #property and #synthesize, and get used to referencing ivars with 'self'. Retain counts & memory management issues are often the cause of crashes, and you can clean things up quite a bit by being more formal with your ivars. Here's some reading: http://www.optictheory.com/iphone-dev/2010/02/objective-c-primer-part-3-property-and-synthesize/
Second, I would refrain from creating your QuestionViewController until you're ready to add it to your root ViewController (ie. inside startQuestionnaire). You can pass your JSON data into it with a custom init function. This way, you can ensure that there aren't other QuestionViewController instances lying around that might be causing complications, and taking up memory when they're not being used.
As for the slider issue itself, it's tough to say without seeing the actual error, but hopefully this helps a bit.
Best of luck

Related

iOS: How to Preload Data before Display

In an IPhone app I am making, I have an initialViewController
(1) with a button. Once the button is clicked, it segues to another View Controller
(2), which at that point loads data from a CoreData File and displays it to the user.
My issue is that there is a small delay between the loading of (2) and the actual Display of the data. That is of course because the data takes a little moment to be loaded. I am doing it asynchronously, and my goal is to never show a spinning wheel or a loading screen (more user friendly).
What I want to do is to "preload" the data at (1), not at (2), that way data should have already been loaded by the time (2) loads and should be displayed immediately. I know how to load the data at (1), but I have no idea how to easily pass it along to (2). I can't do it with a segue because my app is actually a bit more complicated than the description above and it's a hassle to do it via segue.
I have heard it is possible to use the "AppDelegate" but as I am new to programming I have no idea how to use it effectively. The online classes I've been following do not give very clear insight on how to use it, so I'm a bit lost.
After seeing your comment on Paras's post, I have a better solution for you:
Create a subclass of NSObject called fileLoader
//in fileLoader.h
#interface fileLoader : NSObject {
}
+(fileLoader *)sharedInstance;
+(void)createSharedInstance;
//add functions and variables to load, store data for use in another class
#end
Then,
//in fileLoader.m
#implementation fileLoader
static id _instance;
+(void)createSharedInstance{
_instance = [[fileManager alloc] init];
}
+(fileManager *)sharedInstance{
return _instance;
}
//other functions for storing, retrieving, loading, standard init function, etc.
#end
Now you can call [fileManager createSharedInstance] to instantiate a file manager that you can use from anywhere by calling functions on [fileManager sharedInstance].
You could use some kind of DataManager object in which you store your data after loading it for easy retrieval anywhere in your app. If you only have to load the data once you could do it when the app starts.
The trick is to make this a singleton object so no matter where in your app you refer to it it will that same instance that already pre-loaded the data. I use these a lot in my project to manage any and all data that I need inside the app.
There are countless examples out there
The AppDelegate would be better suited for passing data from (2) to (1). For loading data into (2) from (1), you can use the same function that you would use to load in (2) because (1) sees (2) as an instance of a viewController. It is simply like this:
//In OneViewController.m
-(void)viewDidLoad:(BOOL)animated{
[super viewDidLoad:animated];
TwoViewController *VCTwo = [[TwoViewController alloc] initWithNibName:#"TwoViewController" bundle:nil];
//note that I am only instantiating VCTwo, I am not displaying it.
}
-(void)loadVCTwoFromVCOne{
[TwoViewController preloadInformation];
//simply call the function to load the data on the instance of the viewController before displaying it
}
-(IBAction)presentVCTwo{
[self presentViewController:VCTwo animated:YES completion:nil];
//when you are ready, you can display at any time
}
Property synthesize the NSMutableArray with you data like this.. in yourNextViewController.h file
#property(nonatomic, retain) NSMutableArray *nextViewArray;
and in yourNextViewController.m file just Synthesize like bellow..
#synthesize nextViewArray;
and then just pass out like bellow..
yourNextViewController *objNextView = [[yourNextViewController alloc]initWithNibName:#"yourNextViewController" bundle:nil];
objNextView.nextViewArray = [[NSMutableArray alloc]init];
objNextView.nextViewArray = yourDataArray;
[objNextView.nextViewArray retain];
[self.navigationController pushViewController:objNextView animated:YES];
[objNextView release];
Also you can pass string or Dictionary instead of Array..

Improvements on a beginner iOS application

I recently started out with iOS programming (with the Stanford CS193P lectures from iTunes U). I got stuck with the homeworks, that dreaded calculator is a bit too hard for me at the moment, so I started writing little, extremely simple applications to get used to the syntax and data structures in Objective-C.
I wrote a small application which has two buttons.. One 'initializes' an array and fills two UILabels with a) the first object (objectAtIndex:0) and b) the count. I then have a button which allows me to cycle through the array.. showing the next object in there when I click, and of course going back to the very first when I reach the end of the array.
This is easy. I know. But alas, I don't get any feedback like a real Stanford student would, and reading books don't really make me feel okay either. So I would like to ask for some feedback and possible code-improvements, best practices, etc. here.
I created a model with one method, which creates a list of items. I then send retain to it, because I was getting EXC_BAD_ACCESS problems without it; so here my first question: is this okay? :) I didn't think I had to retain it at first, and I still don't exactly understand why it works when I do in fact retain it. Or would it be better to somehow return this and 'catch' it in the controller? I really am not sure what the 'good' way is here.
-(void)populateItemsList
{
itemsList = [NSArray arrayWithObjects:#"Test", #"Test2", #"test3", nil];
[itemsList retain];
}
On to the next piece of code in my Controller:
I lazily instantiate my model here, because I learnt how to do this from the Stanford Calculator assignment. I suppose this is okay to do? I understand that this way, the memory for the model doesn't come in use until it's actually required?
- (DisplayerModel *)model
{
if(!displayerModel)
{
displayerModel = [[DisplayerModel alloc] init];
}
return displayerModel;
}
I then have a method to 'create' the array and populate my outlets (two UILabels, as mentioned before).
- (IBAction)createArray:(UIButton *)sender
{
[[self model] populateItemsList];
arrayCount = [[[self model] itemsList] count];
stepper = 0;
NSString *firstObject = [[[self model] itemsList] objectAtIndex:stepper];
countDisplay.text = [NSString stringWithFormat:#"%d", arrayCount];
display.text = [NSString stringWithFormat:#"%#", firstObject];
}
I'm honestly not sure about this code. It might just be me though, because I'm not really used to writing code in Objective-C; are there any improvements I could make here? I'm especially concerned about the whole stepper idea. I was looking for something like currentIndex or similar, but I couldn't seem to find it in the documentation.. that's why I created the stepper variable to keep track of where I am in the array.
And then finally, the method that makes me cycle through:
- (IBAction)showNextValue:(UIButton *)sender
{
if (stepper == arrayCount - 1) {
stepper = 0;
}
else {
stepper ++;
}
display.text = [NSString stringWithFormat:#"%#", [[[self model] itemsList] objectAtIndex:stepper]];
}
I'm not putting my dealloc or viewDidUnload overrides here because well.. I tested this application with Leaks and it doesn't seem to leak any memory. Are there other ways to test this? Build and analyze doesn't report any problems either. Are there other pitfalls I have to look out for?
Thanks to anyone who's willing to look through my code and provide me with some advice, etc. Still learning!
By convention, class methods using the class name at the beginning return an autoreleased object. Whereas, alloc/init or new return an object with a single retain count.
Thus:
foo = [[NSArray array] retain];
foo = [[NSArray alloc] init];
foo = [NSArray new];
are all equivalent and create an object (an empty array) with a retain count of 1
Lazy creation of needed objects is generally a good practice. It's also good to release those things in a variety of situations, viewDidUnload and memory warnings are two common places for that. If you do that, you might want to save the state in some way - often NSUserDefaults.
The stepper variable is perfectly fine. You could encapsulate it within your model if you wanted to. But in the controller is an acceptable location as well.
Looks pretty good. NSArray doesn't have anything like a currentIndex. An array just contains elements. The same array can be referenced by many objects. If two objects x and y use an array a, a can't keep track of currentIndex for users x and y separately. It's the responsibility of x and y to have its own stepper, as you did.

iphone / ObjC - Method to remove/add views based on arguments - possible?

I'm new - i'm sorry - but I'm experimenting with multiview iphone apps, and wondered whether the below idea was a) possible and b) sensible.
I want to create a method that can remove and add views based on some parameters - the outgoing view, the incoming view and the incoming class.
- (void)switchViews:(Class)inView:(Class)outView:(Class)inClass{
inClass *tempView = [[inClass alloc]
initWithNibName:#"inView" bundle:nil];
tempView.burgerViewController = self;
self.inView = tempView;
[tempView release];
[outView.view removeFromSuperview];
[self.view insertSubview:tempView.view atIndex:0];
}
This would be called by:
[burgerViewController switchViews:viewMainMenu:viewOptions:ViewMainMenu];
Any help is much appreciated - I have a lot to learn.
Mike.
Your code is wrong, in that (it appears that) you've misunderstood how method names work in Objective-C.
For example, as your method currently stands, it is named:
switchViews:::
That's probably not what you're looking for.
A better name might be:
replaceView:forProperty:withViewOfClass:
Declared, that would look like:
- (void) replaceView:(UIView *)outView forProperty:(NSString *)propertyName withViewOfClass:(Class)inClass;
And you would use it like this:
Class viewOptions = ...;
NSString *viewMainMenu = #"...";
[burgerViewController replaceView:viewMainMenu forProperty:viewMainMenu withViewOfClass:viewOptions];
For more on Objective-C method names and interleaved arguments, check out the Objective-C Programming Language Reference.
Well, your first problem is that you release tempView and then attempt to insert it into the view. Don't release tempView at all, just keep it as-is for insertion into the main view.

Return object crashing iPhone

Im fairly new to the whole world of iPhone dev so forgive me if this is very easy. I have an object Card that holds 6 Question objects on it. when I say [card getQuestion:#"Art"] I am currently returning a Question object like so
- (Question*) getQuestion: (NSString*) questionType {
Question *q = [questions objectForKey:questionType];
return [q autorelease];
}
Question has a property of text (type NSString) which allows me to see what the text for the question is. So I want to use this text to update a UILabel in the viewController
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSLog(#"%#", [[self.card getQuestion:#"Art"] qText]);
self.myQuestion.text = [[self.card getQuestion:#"Art"] qText];
}
This crashes the iPhone, whereas if I change the function in object Card to this
- (NSString*) getQuestion: (NSString*) questionType {
return [[questions objectForKey:questionType] qText];
}
and my call in the viewController to
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSLog(#"%#", [self.card getQuestion:#"Art"]);
self.myQuestion.text = [self.card getQuestion:#"Art"];
}
This works fine.. can anyone explain what I am doing wrong, in both cases the call to NSLog returns me the relevant text. In both cases the display loads but in the first instance it crashes shortly after, whereas the other way it stays stable.
Any help appreciated.
- (Question*) getQuestion: (NSString*) questionType {
Question *q = [questions objectForKey:questionType];
return [q autorelease];
}
According to the memory management rules, you should not be autoreleasing that object, and this is almost certainly what is causing your crash. You're relinquishing ownership of an object you don't own, which is causing it to get deallocate prematurely. This means that when another object which is supposed to own the Question tries to access it, the Question is gone and you crash with an EXC_BAD_ACCESS error.
Additionally, the method should probably be called questionForType: or questionOfType:. Using the get prefix implies that the object will be returned via an out-parameter, which it's not.
Since you are new at all of this - you should definitely make sure you run your app looking for zombies and also run it with the instruments application to make sure you are not leaking anywhere.
You should read a very short explanation of memory mangement in Objective - C every few days - one day it will all sink in and become second nature.
http://macdevelopertips.com/objective-c/objective-c-memory-management.html
I can see two errors,
1: You are releasing something you don't own (Question 'q') - If you do either alloc, copy or retain, you should release it. If not, you shouldn't.
2: The setter-line should be written like this:
Question *q = (Question *)[questions objectForKey:questionType];
That way you avoid getting warnings or errors.

Unloading ViewControllers from Apple's PageControl Example + UINavigationController's Relationship to its RootViewControllers

So I modified Apple's PageControl example to dynamically load various navigation controllers (along with their root view controllers) into the scroll view. I also added a technique that attempts to unload a navigation controller when it's no longer needed. I've only been at ObjC for a little over a month, so I'm not sure if I'm doing the unloading correctly. Please see my code below, followed by my questions.
First I create a mutable array and fill it with nulls, just like Apple does:
// Create dummy array for viewControllers array, fill it with nulls, and assign to viewControllers
NSMutableArray *array = [[NSMutableArray alloc] init];
for (unsigned i = 0; i <= kNumberOfPages; i++)
{
[array addObject:[NSNull null]];
}
self.viewControllers = array;
[array release];
...Later, I fill the array with UINavigationController objects like so (this is just partial code, please excuse the missing parts...the main idea is that I alloc a couple of things, assign them and then release):
id controller = [[classForViewController alloc] initWithNibName:NSStringFromClass(classForViewController) bundle:nil];
navController = [[UINavigationController alloc] initWithRootViewController:controller];
[controller release];
[self.viewControllers replaceObjectAtIndex:page withObject:navController];
[navController release];
...Finally, if a page doesn't need to be loaded anymore I do this:
[self.viewControllers replaceObjectAtIndex:i withObject:[NSNull null]];
Questions:
My understanding is that once I replace the navigation controller in my viewControllers array with null, the array releases the navigation controller. Thus the navigation controller's retain count hits zero and it no longer takes up memory. Is this correct?
What about the root view controller inside the navigation controller? Do I need to do anything with it or does it get released automatically once the navigation controller's retain count hit zero?
Thanks!
Yes. Any object put into a collection is sent a retain message. Likewise any object removed from a collection is sent a release message, the cause of the removal is irrelevant.
Yes, all objects will release all the objects it owns when they are released.
This all boils down to the simple principle of ownership that Cocoa defines:
You own the object if you received it as return value by calling a method that:
Is named alloc or new.
Contains the word copy, such as copy and mutableCopy.
You own the object if you call retain.
You may only call release and autorelease on objects you own.
You must release all owned objects in your dealloc methods.
There is just one exception; delegates are never owned. This is to avoid circular references and the memory leaks they cause.
As a side effect this also means that when you yourself are implementing a method, you must return an auto released object unless you are implementing new, or a method with copy in it's name. Objects returned as out arguments are always autoreleased.
Follow this strictly and Objective-C can be treated as if it is garbage collected 95% of the time.
Presumably yes, once your retain count reaches zero (however or whenever that happens) your object will receive the dealloc message. You can put a breakpoint to ensure that is happening. Instruments comes with a Leaks utility that should help you find memory problems, it's a great tool and I suggest using it frequently.
I'm not quite sure what you mean by "do anything to it". I presume you mean release it. The general pattern is that if you alloc or retain, you release. You can roughly guess if there is going to be a problem if your allocs and retains outnumber your releases (or vice versa, you don't want to double release).