Resolved Weird Problem. can't grab data from SQL database.

Discussion in 'iOS Programming' started by at0m87, Mar 5, 2012.

  1. at0m87, Mar 5, 2012
    Last edited: Mar 10, 2012

    macrumors newbie

    Joined:
    Mar 2, 2012
    #1
    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.

    Code:
    - (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.

    Code:
    #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);
        [B]EXC_BAD_ACCESS ->[/B]NSLog(@"viewDidStartLoad username: %@", loginObj.loginUserName);
        [B]EXC_BAD_ACCESS ->[/B]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);
        [B]EXC_BAD_ACCESS ->[/B]NSLog(@"webViewDidFinishLoad username: %@", loginObj.loginUserName);
        [B]EXC_BAD_ACCESS ->[/B]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.

    Code:
    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!
     
  2. macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #2
    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.
     
  3. at0m87, Mar 6, 2012
    Last edited: Mar 6, 2012

    thread starter macrumors newbie

    Joined:
    Mar 2, 2012
    #3
    Thank you thank you for reading!

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

    Code:
    @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
    Code:
    - (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.
     
  4. PhoneyDeveloper, Mar 6, 2012
    Last edited: Mar 6, 2012

    macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #4
    The comments next to the
    Code:
    [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.
     
  5. macrumors 65816

    jnoxx

    Joined:
    Dec 29, 2010
    Location:
    Aartselaar // Antwerp // Belgium
    #5
    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..)
     
  6. thread starter macrumors newbie

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

    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.
     
  7. thread starter macrumors newbie

    Joined:
    Mar 2, 2012
    #7
    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?
     
  8. macrumors 65816

    jnoxx

    Joined:
    Dec 29, 2010
    Location:
    Aartselaar // Antwerp // Belgium
    #8
    I didn't really checked your code, I immedialty saw release errors.
    This is a snippet from your code
    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..
     
  9. thread starter macrumors newbie

    Joined:
    Mar 2, 2012
    #9
    shugs i guess i messed up the basic eh? i will try to take note of that in the future. thanks a lot buddy!
     
  10. macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #10
    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.
     

Share This Page