iphone development controls null after ViewDidLoad - iphone

I am a newbie to both Iphone dev and obj c. I am trying to call a web service when the view is loaded and display a activity indicator during the time it takes to retrieve result from the web service.
My problem is that once the ViewDidLoad is completed, my activityindicator and labels are getting null. Hence after the web service call I am not able to manipulate any of the controls.
I am not able to understand why this is happening; obviously it is my lack of understanding of the basics. I done a bit of homework on this but I have not reached anywhere. It would very helpful if some could please explain why the controls get null once the viewdidload is complete.
Update: thank you for the answers. On viewdidload, I am doing the following, I am animating an activity indicator and assigning some values to my textview. Then I call a web service. Debugging the code, I realized that once viewdidload is complete and -(void) connection: (NSURLConnection *) connection didReceiveResponse:(NSURLResponse *) is called, my text view/activityindicator gets null.
(void)viewDidLoad {
[activityindicator startAnimating];
if(myarray != NULL) {
myuitextview.text = #""; // displaying some values from array
#try{
// call web service
} #catch (NSException *ns) {
}
}
}

Welcome to Objective-C! :) The start can be a bit rough, but once you get used to it, it's great..
It's hard to answer your question without code, cause variables can "turn null" for many reasons. However since you're a newbie, it probably has to do with properties, retaining, etc.
There are obviously many related posts on the subject. Here's one: Objective-C 101 (retain vs assign) NSString
Basically in your .h file you need:
#import <UIKit/UIKit.h>
#interface Car:NSObject{
NSString *name;
}
#property(nonatomic, retain) NSString *name;
And then in your .m file in viewDidLoad:
self.name = [[[NSString alloc] init] autorelease];
// For strings, can use convinient function:
// self.name = [NSString string];
It's very important to use self.name and not just name as the former calls the property setter method and the latter does not (important for retaining the property correctly).
HTH, It's obviously general, but so is your question :)
Oded.

Related

property loses its value (long but easy to understand)

In my app, I have:
car.h
#interface car : NSObject
{
NSString *model;
NSString *price;
// others atributes
}
#property(nonatomic, retain) NSString *model;
#property(nonatomic, retain) NSString *price;
...
myshop.h
#import "car.h"
#interface myshop : UIViewController...
{
car *mycar;
}
#property(nonatomic, retain) car *mycar;
...
myshop.m
...
-(void) viewDidLoad
{
...
mycar = [[car alloc] init];
}
so, I have a method that shows a popover, where I can select a car from a tableview. This popover callback a method in the myshop.m, using delegate, where I assign a value to mycar.model, and call the method doA above, all of this works fine, and shows the value of mycar.model in Output:
-(void) doA
{
NSLog(#"car = %#", mycar.model );
...
}
But... now it is the problem: I have a buttom in the myshop view. When I press this button, the action shows an alert view (there is the delegate in .h). The return of this alert calls:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
[self doA];
...
}
And the program crash when doA tries to write mycar.model to Output. No error is showing in Output View of Xcode. In the code, it shows: Thread 1: Program received signal: EXEC_BAD_ACCESS.
I can show as many cars I want from popover view, but when press the button, and the AlertView closes, the program crashes.
Just for test, I call [self doA] in other methods that runs from another class via delegate and always the app craches.
Any idea, what is wrong?
Completing the code
Is there any error here? (this is a method of car.m)
- (void) setValues: (NSDictionary *) data
{
model = [data objectForKey:#"model"];
price = [data objectForKey:#"price"];
...
I don't initialize the properties (model, price, ...) anywhere in code.
SOLVED !!!
It was a memory management problem!
I change
model = [data objectForKey:#"model"];
by
model = [[NSString alloc] initWithFormat:#"%#", [data objectForKey:#"model"]];
I hope that I am right now! At least the app seens to work well!
Thanks a lot friends !!!
You have got a memory management bug somewhere in your code. I don't know where the bug is, it almost certainly isn't in the code you posted (it all looks perfect to me).
Unless you post more code, we can't help you fix this one.
I strongly recommend you enable ARC. It's fairly new, but nowadays it's old enough everyone should start using it.
There's a very good chance your bug will simply go away if you turn ARC on.
The code you posted looks fine, but somewhere else you must be over-releasing mycar. Profile it with the "zombies" instrument, and it will tell you where it is being released.

Sharing data/string with a singleton between views

I'm trying to share a string between two views on my iPhone project. It currently works if I use the actual #"something here" for the string, but if I want to use something like label.text, it doesn't even though it is still a string.
I'll show you what I have to make it clearer.
First View: Info_ViewController.h
#import <UIKit/UIKit.h>
#interface Info_ViewController : UIViewController {
IBOutlet UITextField *locationField;
}
#property (nonatomic, retain) NSString *locationString;
+ (id)sharedInfoVC;
#end
First View: Info_ViewController.m
#import "Info_ViewController.h"
static Info_ViewController *sharedInfoVC = nil;
#implementation Info_ViewController
#synthesize locationString;
#pragma mark Singleton Methods
+ (id)sharedInfoVC {
#synchronized(self) {
if (sharedInfoVC == nil)
sharedInfoVC = [[self alloc] init];
}
return sharedInfoVC;
}
- (id)init {
if (self = [super init]) {
locationString = [[NSString alloc] initWithString:locationField.text]; //This is there part I mentioned earlier, when using #"something" instead of locationField.text works.
}
return self;
}
Second View: Confirm_ViewController.m
#import "Confirm_ViewController.h"
#import "Info_ViewController.h"
#implementation Confirm_ViewController
- (IBAction)buttonZ:(id)sender
{
Info_ViewController *infoVCmanager = [Info_ViewController sharedInfoVC];
locationLabel.text = infoVCmanager.locationString;
}
I put it under a button for now, but it will eventually be under viewDidLoad.
If you replace locationField.text with a string (#"blahblahblah") it won't crash and works.
When it crashes I get the error: Program received signal: "SIGABRT"
EDIT: I tried changing
initWithString:locationField.text
to
initWithFormat:#"%#",locationField.text
and now it my label in the second view prints "(NULL)"
Thanks for taking the time to give advice, I really appreciate it.
It is an error to pass nil as the format string to -[NSString initWithString].
So how are you passing nil? You actually have two instances of Info_ViewController. You have the one instance which is the normal part of your app, and then you also have a second instance which is your "singleton" (which really isn't a singleton any more).
So in your "singleton" instance, the UITextField is nil (and will always be nil) and so locationField.text is nil and you are passing that to initWithString:, which is a crash. In fact the "singleton" isn't even fully baked as view controller's go.
If you want a singleton to share data elsewhere in your app, it really should not be a Info_ViewController or any type of view controller. It should be of some other class that you use to manage your data. I would create another class and implement that as a singleton.
Hope that helps you understand what's happening here.
Pre-pend "self." to your location string.
self.locationString = [[NSString alloc] initWithString:locationField.text];
From what I understand of your code, you have got the value for locationString when you from the textfield when you initialize the viewController. At this point of time, your textfield would not be visible. After it becomes visible and you enter something, you don't have the code to store it to locationString.
What you should do is wait for Info_ViewController object to be initialized and displayed. Then on the press of some button or some other event, assign locationLabel.text from the locationString or even directly from locationField.text.
I would provide code, but I have no clue as to how you are structuring this. If you still need help, please provide the details.

objective-c/iphone sdk newcomer question

At present I'm building a basic application to learn Objective-C and the iPhone SDK.
I'm creating NSObject with getters and setters to get to grips with how these works. I've successfully added a property and getters and setters to my main controller, currently I'm trying to create a separate class which I can create a new instance of within my controller but it crashes when I try and use the setter.
Thank you in advance for your time, sorry if this question is as stupid as I'm sure it is.
Here's the header for my class
QuizQuestion.h
#import <Foundation/Foundation.h>
#interface QuizQuestion : NSObject {
NSString *question;
}
#property (retain) NSString* question;
#end
QuizQuestion.m
#import "QuizQuestion.h"
#implementation QuizQuestion
#synthesize question;
- (void) dealloc
{
[question release];
[super dealloc];
}
#end
And here is my controller code (i've cut some out)
#implementation Quiz2ViewController
#class QuizQuestion; // Is this correct?
- (void)viewDidLoad {
QuizQuestion *aQuestion;
//gets here fine, but crashes (the app closes) when I set question.
[aQuestion setQuestion:#"hello world"];
[super viewDidLoad];
}
#end
As well as #class I tried import "QuizQuestion.h" and I get the same issue.
You're not actually allocating an instance of the QuizQuestion class—your aQuestion variable isn’t pointing to anything in particular, so trying to send it a message, like -setQuestion:, is sending that message to... well, there’s no telling where, and sending things messages that aren’t meant for them is a surefire way to crash your app. What you need to do is this:
QuizQuestion *aQuestion = [[QuizQuestion alloc] init];
[aQuestion setQuestion:#"hello world"];
You also need to call [aQuestion release] at some point, or you’ll leak the memory associated with it.
You need to allocate space and initialize the QuizQuestion.
QuizQuestion *aQuestion = [[QuizQuestion alloc] init];
before setting the question.
I think you need to read up on some documentation before you try any more coding. Apple has several intro programming guides that are very good. The problem you are having is addressed in this section:
http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocAllocInit.html
QuizQuestion *aQuestion = [[QuizQuestion alloc] init];
[aQuestion setQuestion:#"hello world"];
[aQuestion release];

iPhone add to NSMutableArray from another viewController

I have a bit of a dilemma and I was wondering if the good folk here could lend me their programming expertise. I'll try to be as simple and precise as I possibly can so here goes:
I'm a new IOS developer and have only been learning for a couple of months. I am developing an iPhone application for my dissertation at university. The app is simply a guide for people who wish to develop for the iPhone themselves, consisting of tutorials. It consists of numerous Table Views but there is one thing that has got me stumped.
What im trying to do:
One feature im trying to include in my app is a bookmarks facility, this will be accessible from a tab bar. I want to be able to click on a button from any nib file (tutorial) which adds a string to an existing NSMutableArray. This string will correspond with the name of the tutorial where the IB-Action was performed and after added to the Array I can load the nib file when selecting the row at index path.
The Problem:
I can add any object to the array from within the implementation file that contains the array but cannot figure out how to add it from a different implementation file. The UITable view populates from the array perfectly but adding a new entry is another story.
I'll show you my code but i'll leave out anything that is unrelated.
BookmarksViewController.h
#interface BookmarksViewController : UITableViewController {
NSMutableArray *bookmarksArray;
}
#property (nonatomic, retain) NSMutableArray *bookmarksArray;
#end
BookmarksViewController.m
-(void)viewDidLoad {
bookmarksArray = [[NSMutableArray alloc] init];
NSLog(#"String Added");
[bookmarksArray addObject:#"String"];
[super viewDidLoad];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// Configure the cell...
cell.textLabel.text = [bookmarksArray objectAtIndex:indexPath.row];
return cell;
}
-(void)dealloc {
[bookmarksArray release];
[super dealloc];
}
NOW FOR THE SECOND VIEW CONTROLLER
Ch01GettingStarted.h
#interface Ch01GettingStarted : UIViewController {
IBOutlet UIScrollView *ScrollView;
}
-(IBAction) pushChap01Bookmark:(id)sender;
#end
Ch01GettingStarted.m
-(IBAction) pushChap01Bookmark:(id)sender{
BookmarksViewController *bookmarksViewController = [[BookmarksViewController alloc]init];
[bookmarksViewController.bookmarksArray addObject:#"NewString"];
NSLog(#"ADD ENTRY");
[bookmarksViewController.tableView reloadData];
NSLog(#"RELOAD TABLE");
[bookmarksViewController release];
NSLog(#"ADD BOOKMARK RELEASE");
}
BTW - the IB-Action was declared in the header file.
Ah, i originally tried doing this as '[BookmarksViewController.bookmarksArray addObject:#"NewString"];' but I came up with an "expected ':' at '.'" error and I read somewhere that I needed to use an instance variable of BookmarksViewController so i declared it just above the addObject method.
Please be gentle with me as I haven't been doing this for long but this is certainly something that's going to be a big part of my professional future.
Any insight anyone could offer to me would be magical.
Thank you, thank you, thank you.
The problem lies in this method:
-(IBAction) pushChap01Bookmark:(id)sender
You are creating a NEW bookmarksViewController here, by doing this:
BookmarksViewController *bookmarksViewController = [[BookmarksViewController alloc]init];
You don't want to do that because you want to update the current view controller. You need to create a link from Ch01GettingStarted.
Assuming you are using Interface Builder, you could create this link using an IBOutlet. In the Ch01GettingStarted interface, add the following line:
IBOutlet BookmarksViewController *bookmarksViewController;
(Between the brackets)
I think you already know how to link this in Interface Builder.
Then just remove this line:
BookmarksViewController *bookmarksViewController = [[BookmarksViewController alloc]init];
And this line:
[bookmarksViewController release];
And it should work.
Why? The 'bookmarksViewController' variable now references the original object (the one you created in Interface Builder) that is actually displayed.
Just to be sure.
Do you have this method in BookmarksViewController.m?
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
[bookmarksArray count];
}
Because after bookmarksViewController is created. ViewDidLoad method not run yet. bookmarksArray has not been created. So bookmarksArray = nil. You can't add object to nil object. In Second Viewcontroller, You should create bookmarksViewController in loadView method. And addobject in pushChap01Bookmark.
I'm not good English. I hope you can understand it. :D

Odd Core Data error caused by over releasing?

Occasional reader and first time question asker, so please be gentle :)
I am creating a Managed Object (Account), that is being passed into a child view controller where its being set in a property that is retained.
Account * account = [[Account alloc] initWithEntity:entity insertIntoManagedObjectContext:context];
AddAccountViewController *childController = [[AddAccountViewController alloc] init];
childController.title = #"Account Details";
childController.anAccount = account;
childController.delegate = self;
[self.navigationController pushViewController:childController animated:YES];
[childController release];
[account release];
The view controller interface:
#interface AddAccountViewController : UIViewController {
}
#property (nonatomic, retain) IBOutlet UITextField * usernameTextField;
#property (nonatomic, retain) IBOutlet UITextField * passwordTextField;
#property (nonatomic, retain) Account * anAccount;
#property (nonatomic, assign) id <AddAccountDelegate> delegate;
- (IBAction)cancel:(id)sender;
- (IBAction)add:(id)sender;
- (IBAction)textFieldDone:(id)sender;
#end
So in code sample 1 I've released the account object because I am no longer interested in it in that method. As it is retained by the AddAccountViewController I have an entry in AddAccountViewController's dealloc that releases it.
However when I go to delete the object from the ManagedObjectContext the app crashes with the following (rather unclear) error:
Detected an attempt to call a symbol in system libraries that is not present on the iPhone:
_Unwind_Resume called from function _PFFaultHandlerLookupRow in image CoreData.
After much debugging & hair pulling I discovered that if I don't release account in AddAccountViewController's dealloc method the app works properly continually and doesn't appear to leak according to Instruments.
Can anyone shed any light as to whats going on? I understand from the docs on properties that those retained need to be released. What have I missed?
Update to answer Kevin's question
The code to delete the object from the ManagedObjectContext is in the RootViewController (that holding the child controller)
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the managed object for the given index path
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
[context deleteObject:[self.fetchedResultsController objectAtIndexPath:indexPath]];
// Save the context.
NSError *error = nil;
if (![context save:&error]) {
/*
Replace this implementation with code to handle the error appropriately.
abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
*/
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
}
Firstly: It sounds like a bug on Apple's part. Core Data is calling _Unwind_Resume, which is (probably) some sort of exception unwind. Exception-unwinding exists on the phone, but (I think) uses the ARM ABI, which uses function names beginning with __cxa_. Are you running on the simulator? Which version of the SDK?
There might be an extra release floating around somewhere which is "balanced" when you remove the call to [account release];.
"Instruments doesn't show any leaks" doesn't mean there aren't any; last I checked it got confused by cycles (i.e. it wouldn't show a leak if you forgot to un-set IBOutlets in dealloc). I tested with NSMutableData * d = [NSMutableData dataWithLength:1<<20]; memcpy(d.mutableBytes, &d, 4);, but an easier test is just [[UIView alloc] initWithFrame:CGRectZero].
If you think it's a retain/release issue, I once debugged these by overriding retain/release/autorelease to call NSLog. I then added breakpoints on all of them, set them to run the command "bt", and clicked the autocontinue. Then run the thing that breaks (in my case I think it was just an extra retain), print out the log output,stick it on a whiteboard, and spend half an hour matching retains and releases.
I had a similar issue ending in a
"Detected an attempt to call a symbol in system libraries that is not present on the iPhone:
_Unwind_Resume called from function _PFFaultHandlerLookupRow in image CoreData."
error message.
My problem was a wrong "cascading" deletion-rule on a relation in the model. With this rule, my top managed object got deleted but still referenced in the code.
After setting the "delete rule" on this relation to "nulify", everything worked as designed.
--> no core data issue...design issue!
Johnny
When ever you delete any managedobject, system will automatically release all reference related to that object. So there is no need to realese object programatically. Once you delete object there you can not access that object in parent class.