PDA

View Full Version : [Resolved] Weird Problem. can't grab data from SQL database.




at0m87
Mar 5, 2012, 05:04 PM
My problem here is that 2 data pointers are doing what they supposed to be doing by pointing to data in a SQL database except whenever they are called in the two methods webViewDidStartLoad and webViewDidFinishLoad.

The other 2 data pointer(total 4) which belongs to the same SQL database have no problem functioning properly when called in all methods. Only the username and password pointers are giving me problems by pointing to rubbish code in the two previously mentioned methods..

In ControllerA, when i select a row, it will push me to the WebViewController as the code below shows.

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

if (internetBrower == nil) {
internetBrower = [[WebViewController alloc] initWithNibName:@"WebViewController" bundle:nil];
}

Login *loginObj = [appDelegate.loginArray objectAtIndex:indexPath.row];

//Get the detail view data if it does not exists.
//We only load the data we initially want and keep on loading as we need.
[loginObj hydrateDetailViewData];

//--passing the data to WebViewController
internetBrower.loginObj = loginObj;

//--Push WebViewController
[self.navigationController pushViewController:internetBrower animated:YES];
internetBrower = nil;
}


When i run the program and click on a row in the tableView, it pushes the WebViewController and i will get a EXC_BAD_ACCESS at those 4 points show below.

#import "WebViewController.h"
#import "FYP_TEST_1AppDelegate.h"
#import "MainLoginController.h"
#import "Login.h"

@implementation WebViewController

@synthesize myWebView, loginObj, urlTextField, activity;

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}

-(void) showWebView{

NSLog(@"showWebView desc: %@", loginObj.loginDesc);
NSLog(@"showWebView url: %@", loginObj.loginURL);
NSLog(@"showWebView username: %@", loginObj.loginUserName);
NSLog(@"showWebView password: %@", loginObj.loginPassword);

NSURL *url = [NSURL URLWithString:loginObj.loginURL];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[myWebView loadRequest:request];
}

-(IBAction)openSafari:(id)sender{
[[UIApplication sharedApplication] openURL:myWebView.request.URL];
}

-(void) webViewDidStartLoad:(UIWebView *)webView{

NSLog(@"viewDidStartLoad desc: %@", loginObj.loginDesc);
NSLog(@"viewDidStartLoad url: %@", loginObj.loginURL);
EXC_BAD_ACCESS ->NSLog(@"viewDidStartLoad username: %@", loginObj.loginUserName);
EXC_BAD_ACCESS ->NSLog(@"viewDidStartLoad password: %@", loginObj.loginPassword);

//--illusion that the webpage has already started loading..
NSString *currentURL = myWebView.request.URL.absoluteString;
if (!(currentURL == NULL)) {
urlTextField.text = currentURL;
}

[activity startAnimating];
}

- (void)webViewDidFinishLoad:(UIWebView *)webView {

NSLog(@"webViewDidFinishLoad desc: %@", loginObj.loginDesc);
NSLog(@"webViewDidFinishLoad url: %@", loginObj.loginURL);
EXC_BAD_ACCESS ->NSLog(@"webViewDidFinishLoad username: %@", loginObj.loginUserName);
EXC_BAD_ACCESS ->NSLog(@"webViewDidFinishLoad password: %@", loginObj.loginPassword);

[activity stopAnimating];

NSString *usrAmazon = [NSString stringWithFormat:@"document.getElementById('ap_email').value='tom@tom.com';"];
NSString *passAmazon = [NSString stringWithFormat:@"document.getElementById('ap_password').value='secret';"];
[myWebView stringByEvaluatingJavaScriptFromString:usrAmazon];
[myWebView stringByEvaluatingJavaScriptFromString:passAmazon];

//--extracting title from the HTML and printing it on the UIWebView's Title
NSString *webTitle = [webView stringByEvaluatingJavaScriptFromString:@"document.title"];
self.title = webTitle;
}

- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];

// Release any cached data, images, etc that aren't in use.
}

#pragma mark - View lifecycle

- (void)viewDidLoad{

[super viewDidLoad];

NSLog(@"viewDidLoad desc: %@", loginObj.loginDesc);
NSLog(@"viewDidLoad url: %@", loginObj.loginURL);
NSLog(@"viewDidLoad username: %@", loginObj.loginUserName);
NSLog(@"viewDidLoad password: %@", loginObj.loginPassword);

myWebView.delegate = self;

//--Clear all Cache and Cookies so everytime webview is shown, it will be brand new
//Set Cache
NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:0 diskCapacity:0 diskPath:nil];
[NSURLCache setSharedURLCache:sharedCache];

//Clear All Cookies
for(NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) {

//if([[cookie domain] isEqualToString:someNSStringUrlDomain]) {

[[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:cookie];

}

[self showWebView];

// Do any additional setup after loading the view from its nib.
}

-(void)viewDidUnload{
[self setMyWebView:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}

-(void) viewWillAppear:(BOOL)animated{
NSLog(@"viewWillAppear desc: %@", loginObj.loginDesc);
NSLog(@"viewWillAppear url: %@", loginObj.loginURL);
NSLog(@"viewWillAppear username: %@", loginObj.loginUserName);
NSLog(@"viewWillAppear password: %@", loginObj.loginPassword);
[super viewWillAppear:animated];

//--set title of webview first to create the illusion that it is loading the page already. later on when page finish load, will grab the title from the HTML and replace this.
self.title = loginObj.loginDesc;
urlTextField.text = loginObj.loginURL;
}

- (void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
}

- (void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
}

- (void)viewDidDisappear:(BOOL)animated{
[super viewDidDisappear:animated];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return YES;
}

-(void) dealloc{
[myWebView release];
[activity release];
[urlTextField release];
//[loginObj release];
[super dealloc];
}

@end


The below debugger shows when the first EXC_BAD_ACCESS will appear. if you remove that line with the EXC_BAD_ACCESS error, the next EXC_BAD_ACCESS will occur as shown above in order, respectively.


2012-03-06 06:24:36.527 FYP_TEST_1[208:707] hydrateDetailViewData is called
2012-03-06 06:24:36.529 FYP_TEST_1[208:707] detailview is not HYDRATED
2012-03-06 06:24:36.539 FYP_TEST_1[208:707] self.loginURL = http://www.facebook.com
2012-03-06 06:24:36.542 FYP_TEST_1[208:707] self.loginUserName = tom@tom.com
2012-03-06 06:24:36.546 FYP_TEST_1[208:707] self.loginPassword = secret
2012-03-06 06:24:37.074 FYP_TEST_1[208:707] viewDidLoad desc: Facebook
2012-03-06 06:24:37.076 FYP_TEST_1[208:707] viewDidLoad url: http://www.facebook.com
2012-03-06 06:24:37.078 FYP_TEST_1[208:707] viewDidLoad username: tom@tom.com
2012-03-06 06:24:37.080 FYP_TEST_1[208:707] viewDidLoad password: secret
2012-03-06 06:24:37.095 FYP_TEST_1[208:707] showWebView desc: Facebook
2012-03-06 06:24:37.096 FYP_TEST_1[208:707] showWebView url: http://www.facebook.com
2012-03-06 06:24:37.099 FYP_TEST_1[208:707] showWebView username: tom@tom.com
2012-03-06 06:24:37.101 FYP_TEST_1[208:707] showWebView password: secret
2012-03-06 06:24:37.105 FYP_TEST_1[208:707] viewWillAppear desc: Facebook
2012-03-06 06:24:37.107 FYP_TEST_1[208:707] viewWillAppear url: http://www.facebook.com
2012-03-06 06:24:37.109 FYP_TEST_1[208:707] viewWillAppear username: tom@tom.com
2012-03-06 06:24:37.111 FYP_TEST_1[208:707] viewWillAppear password: secret
[Switching to process 8963 thread 0x2303]
warning: Unable to read symbols for /Users/Tom/Library/Developer/Xcode/iOS DeviceSupport/5.0.1 (9A405)/Symbols/System/Library/Extensions/IMGSGX535GLDriver.bundle/IMGSGX535GLDriver (file not found).
warning: No copy of IMGSGX535GLDriver.bundle/IMGSGX535GLDriver found locally, reading from memory on remote device. This may slow down the debug session.
[Switching to process 8451 thread 0x2103]
2012-03-06 06:24:37.818 FYP_TEST_1[208:707] viewDidStartLoad desc: Facebook
2012-03-06 06:24:37.820 FYP_TEST_1[208:707] viewDidStartLoad url: http://www.facebook.com
[Switching to process 7171 thread 0x1c03]
(gdb)

Thank You for taking the time to read my lengthy code.

Looking forward to your advice!



PhoneyDeveloper
Mar 5, 2012, 07:35 PM
The likely reason for the crash is that the objects you're trying to log are invalid in some way. In most cases this is caused by an under-retain bug. You haven't shown how loginObject or the two properties that are causing the crash are declared, created, or assigned.

at0m87
Mar 6, 2012, 02:23 AM
The likely reason for the crash is that the objects you're trying to log are invalid in some way. In most cases this is caused by an under-retain bug. You haven't shown how loginObject or the two properties that are causing the crash are declared, created, or assigned.

Thank you thank you for reading!

here it is on how it is declare in its Class Login.h

@interface Login : NSObject{

NSInteger loginID;
NSString *loginDesc; //description
NSString *loginURL; //URL
NSString *loginUserName; //Username
NSString *loginPassword; //password

//Intrnal variables to keep track of the state of the object.
BOOL isDirty;
BOOL isDetailViewHydrated;
}

@property (nonatomic, readonly) NSInteger loginID;
@property (nonatomic, copy) NSString *loginDesc;
@property (nonatomic, copy) NSString *loginURL;
@property (nonatomic, copy) NSString *loginUserName;
@property (nonatomic, copy) NSString *loginPassword;


This is how they are assign in the hydrateDetailViewData in Login.m method
- (void) hydrateDetailViewData {
NSLog(@"hydrateDetailViewData is called");

//If the detail view is hydrated then do not get it from the database.
if(isDetailViewHydrated) return;
NSLog(@"detailview is not HYDRATED");

if(detailStmt == nil) {
const char *sql = "Select loginURL, loginUserName, loginPassword from login Where loginID = ?";
if(sqlite3_prepare_v2(database, sql, -1, &detailStmt, NULL) != SQLITE_OK)
NSAssert1(0, @"Error while creating detail view statement. '%s'", sqlite3_errmsg(database));
}

sqlite3_bind_int(detailStmt, 1, loginID);

if(SQLITE_DONE != sqlite3_step(detailStmt)) {

//Get the URL in a temporary variable.
NSString *loginURLString = [NSString stringWithUTF8String:(char *) sqlite3_column_text(detailStmt, 0)];

//Assign the URL. The URL value will be copied, since the property is declared with "copy" attribute.
self.loginURL = loginURLString;

NSLog(@"self.loginURL = %@", self.loginURL);

//Release the temporary variable. Since we created it using alloc, we have own it.
[loginURLString release];

//Get the User Name in a temporary variable.
NSString *loginUserNameString = [NSString stringWithUTF8String:(char *) sqlite3_column_text(detailStmt, 1)];

//Assign the User Name. The User Name value will be copied, since the property is declared with "copy" attribute.
self.loginUserName = loginUserNameString;

NSLog(@"self.loginUserName = %@", self.loginUserName);
//Release the temporary variable. Since we created it using alloc, we have own it.
[loginUserNameString release];

//Get the password in a temporary variable.
NSString *passwordString = [NSString stringWithUTF8String:(char *) sqlite3_column_text(detailStmt, 2)];
//Assign the password. The password value will be copied, since the property is declared with "copy" attribute.
self.loginPassword = passwordString;

NSLog(@"self.loginPassword = %@", self.loginPassword);
//Release the temporary variable. Since we created it using alloc, we have own it.
[passwordString release];
}
else
NSAssert1(0, @"Error while getting the credentials for login. '%s'", sqlite3_errmsg(database));

//Reset the detail statement.
sqlite3_reset(detailStmt);

//Set isDetailViewHydrated as YES, so we do not get it again from the database.
isDetailViewHydrated = YES;
}

-(void)dealloc{

[loginDesc release];
[loginURL release];
[loginUserName release];
[loginPassword release];
[super dealloc];
}


Hope it helps you to understand my code better.

PhoneyDeveloper
Mar 6, 2012, 07:05 AM
The comments next to the [loginURLString release] line says the loginURLString was created with alloc but the code didn't create it with alloc. Same for the other strings.

BTW, for an immutable string most likely a copy is a retain.

jnoxx
Mar 6, 2012, 07:08 AM
Like Phoney said, the NSStrings are assigned convencience methods, which is in other words is not memory managed by yourself, so you're trying to release an object you don't own yourself = crash. (at least, thats one of the bugs..)

at0m87
Mar 6, 2012, 07:20 AM
The comments next to the [loginURLString release] line says the loginURLString was created with alloc but the code didn't create it with alloc. Same for the other strings.

BTW, for an immutable string most likely a copy is a retain.

what do you mean by "for an immutable string most likely a copy is a retain" don't quite get it.

Like Phoney said, the NSStrings are assigned convencience methods, which is in other words is not memory managed by yourself, so you're trying to release an object you don't own yourself = crash. (at least, thats one of the bugs..)

Okay i get what you guys mean by i shouldn't release that code because i don't own it. but seems like only the username and password are giving me problem. the URL part could be pointed correctly that is bothering me.

at0m87
Mar 6, 2012, 07:30 AM
OMG you guys are awesome. i remove the release codes and now it works!!

But why i release the URL but it wasn't affected?

jnoxx
Mar 6, 2012, 08:19 AM
I didn't really checked your code, I immedialty saw release errors.
This is a snippet from your code

//Get the URL in a temporary variable.
NSString *loginURLString = [NSString stringWithUTF8String:(char *) sqlite3_column_text(detailStmt, 0)];

//Assign the URL. The URL value will be copied, since the property is declared with "copy" attribute.
self.loginURL = loginURLString;

in other words, you are using a CONVENCIENCE method, which does the allocation/init for you, means that you do NOT own this pointer.
And you assign a loginUrl = the autoreleased pointer.
This will give issues no doubt.
If you want to take ownership of the loginURL, you need to RETAIN the loginURLString if I'm correct. Then you can release the loginURL when it fits your needs.

It's all about memory management..

at0m87
Mar 6, 2012, 08:37 AM
I didn't really checked your code, I immedialty saw release errors.
This is a snippet from your code

//Get the URL in a temporary variable.
NSString *loginURLString = [NSString stringWithUTF8String:(char *) sqlite3_column_text(detailStmt, 0)];

//Assign the URL. The URL value will be copied, since the property is declared with "copy" attribute.
self.loginURL = loginURLString;

in other words, you are using a CONVENCIENCE method, which does the allocation/init for you, means that you do NOT own this pointer.
And you assign a loginUrl = the autoreleased pointer.
This will give issues no doubt.
If you want to take ownership of the loginURL, you need to RETAIN the loginURLString if I'm correct. Then you can release the loginURL when it fits your needs.

It's all about memory management..

shugs i guess i messed up the basic eh? i will try to take note of that in the future. thanks a lot buddy!

PhoneyDeveloper
Mar 6, 2012, 06:55 PM
what do you mean by "for an immutable string most likely a copy is a retain" don't quite get it.

This is not an important point for you to understand. But, from the comments in the code about temporary variables it appears to me that you think that copying an NSString will create a new object. It doesn't have to. As an optimization the NSString class could do something different. An immutable object, like instances of NSString, cannot be changed. So any number of other objects can retain a single instance of NSString and be guaranteed that it cannot change. If an instance of NSMutableString is shared and modified then the object will not longer have the same value it once did, which might surprise an owner of that object. The NSString copy method might be implemented as a simple retain.

If you don't understand this don't worry about it. Learn and use all the memory management rules and you'll do fine.