iOS - Why my NSURL isn't retained in the init method? - iphone

I'm creating a new UIViewController with 2 properties :
#property (retain, nonatomic) NSURL *url;
#property (retain, nonatomic) NSString *title;
and synthetised :
#synthesize url = _url;
#synthesize title = _title;
in my custom init method i'm not using the setter like the Memory Management Guide says but when I need to use the properties in the viewDidLoad, the url seems empty, the title doesn't
- (id)initWithURL:(NSURL *)url andTitle:(NSString *)titleTemp
{
self = [super initWithNibName:#"navigatorViewController" bundle:nil];
if (self) {
_url = url;
_title = titleTemp;
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
[_titreBarButtonItem setTitle:_title];
NSURLRequest *urlRequest = [[NSURLRequest alloc] initWithURL:_url];
[_webView loadRequest:urlRequest];
[urlRequest release];
}
I can see my title but my web view is blank.
If I use self.url in the init, it's working !
Do you have an idea ?
PS : Here's how I call my init :
NSString *urlString = [[[NSBundle mainBundle] pathForResource:#"infos" ofType:#"html"] copy];
NSURL *url = [[NSURL alloc] initFileURLWithPath:urlString];
[urlString release];
navigatorViewController *navigatorVC = [[navigatorViewController alloc] initWithURL:url andTitle:#"Infos"];
[url release];
[self presentViewController:navigatorVC animated:YES completion:nil];
[navigatorVC release];
Thanks a lot

you are setting up the properties as retain, but this only applies if you use the synthesized getters. By setting the value directly on the ivar, you are bypassing this and the value is not being retained
you should be doing (probably not a great way when you already have setters):
_url = [url retain];
or better:
self.url = url;
or even better as matt said in the comments: use ARC

You must retain the URL either using :
self.url = url;
in the init method or call the retain after assigning the url in the init method like this.
_url = url;
[_url retain];

You need to add a reference (for what you hold) using copy or retain:
_url = [url copy];
_title = [titleTemp copy];
or
_url = [url retain];
_title = [titleTemp retain];

Well if you write a custom init method you need to do the retain your self:
- (id)initWithURL:(NSURL *)url andTitle:(NSString *)titleTemp
{
self = [super initWithNibName:#"navigatorViewController" bundle:nil];
if (self) {
_url = [url retain];
_title = [titleTemp retain];
}
return self;
}
That it worked with title is just that the variable is probably released later then the NSURL passes in the init method.

Related

UIWebView Not loading

I am having a problem where UrlDisplay isn't displaying any data. UrlDisplay is located at the bottom of my .M. What I am doing is requesting a website collecting a few urls specific to the users request. Then setting up UiWebViews for them to navigate though is there any way to split a single UiWebView, or is this the best way to do it? (My sample is only collecting one website and trying to set one website to a UIWebView)I am not sure of why this is happening Everything in the .h is connected properly to the storyboard. Thanks for the help in advanced for any feed back!
.H
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#property (weak, nonatomic) IBOutlet UIWebView *UrlDisplay;
#end
.M
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize UrlDisplay;
- (void)viewDidLoad
{
[super viewDidLoad];
NSURL *website = [NSURL URLWithString:#"www.Google.com"];
NSURLRequest *request = [NSURLRequest requestWithURL:website];
NSURLResponse* response = nil;
NSError *error = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
//storing data in a string
NSString *myString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSArray *newString = [myString componentsSeparatedByString:#"\n"];
// Do any additional setup after loading the view, typically from a nib.
NSMutableString *curLineNum;
NSString *curLine;
NSMutableArray *URL =[[NSMutableArray alloc]init];
int i;
for (i = 0; i <[newString count]; i++) {
curLine = [newString objectAtIndex:i];
if ([curLine rangeOfString:#"Start Here"].location != NSNotFound) {
NSScanner *scanner = [NSScanner scannerWithString:curLine];
[scanner scanUpToString:#"Start Here" intoString:NULL];
[scanner setScanLocation:([scanner scanLocation]) + 16];
[scanner scanUpToString:#"End Here" intoString:&curLineNum];
[Url addObject:curLineNum];
}
}
NSURL *preWebsite = [NSURL URLWithString:[Url objectAtIndex:0]];
NSURLRequest *preRequest = [NSURLRequest requestWithURL:preWebsite];
[UrlDisplay loadRequest:preRequest];
//NSLog(#"%#",myString);
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
the method URLWithString always expecting full URL path including URL scheme. In your case URL scheme is missing. Try this.
NSURL *website = [NSURL URLWithString:#"http://www.Google.com"];

Dealloc of custom object

I have this Class in my project :
#interface VideoItem : NSObject <NSCoding> {
NSString *name;
NSString *artist;
int seconds;
}
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSString *imgUrl;
#property (nonatomic, retain) NSString *artist;
#end
And this is how i create this object:
VideoItem *item = [[VideoItem alloc] init];
item.name = name;
item.imgUrl = imgLink;
item.artist = artist;
And this is the dealloc:
- (void)dealloc{
[name release];
[imgUrl release];
[artist release];
[super dealloc];
}
And i want to know if this dealoc is ok with the NON-ARC? did i need to do something else because this NSString are with Property?
Edit
And if the VideoItem object was create with:
VideoItem *item = [[VideoItem alloc] init];
item.name = [NSString alloc]initWithFormat#"%#",name];
item.imgUrl = [NSString alloc]initWithFormat#"%#",imgLink];
item.artist = [NSString alloc]initWithFormat#"%#",artist];
Did in this case the dealloc is still ok? or i need to change something?
Everything looks ok, you are releasing all the #properties of your object. I would probably as well point them to nil, just to make sure, that if one of those properties is called, it will be nilled and not have a garbage value, like so:
- (void)dealloc{
[name release], name = nil;
[imgUrl release], imgUrl = nil;
[artist release], artist = nil;
[super dealloc];
}
Another thing, no related, it would be cleaner, if you would create your own init, so you can pass the properties values, when you actually create the object, like so:
-initWithName:(NSString *)name withImgURL:(NSString *)imgURL withArtist:(NSString *)artist;
Your edit:
item.name = [NSString alloc]initWithFormat#"%#",name];
item.imgUrl = [NSString alloc]initWithFormat#"%#",imgLink];
item.artist = [NSString alloc]initWithFormat#"%#",artist];
Only based on this, it will create a leak, so you should be careful. To fix this:
item.name = [[NSString alloc]initWithFormat#"%#",name] autorelease];
item.imgUrl = [[NSString alloc]initWithFormat#"%#",imgLink] autorelease];
item.artist = [[NSString alloc]initWithFormat#"%#",artist] autorelease];
If you don't have ARC enabled than your destructor is correct. You are releasing all the properties that are retained and calling super, which is all you need.

NSURLConnection and JSON Data

I am stuck with something crazy. I used ASIHTTPRequest to receive my data from a web service and everything worked fine. I switched to using a NSURLConnection and I am receiving the same data and parsing it the same way but my code won't recognize the data with the NSURLConnection.
Here is the data I am receiving (from NSLog)
Did receive data: {"d":"[{\"id\":1.0,\"Category\":1,\"hPlan\":0.0,\"Tip\":\"It takes 3500
calories to gain a pound. If you want to lose a pound per week, reduce your calorie
intake by 250 calories and incorporate daily physical activity that will burn 250
calories.\",\"TipDate\":\"2012-05-12T00:00:00\",\"TimeStamp\":\"AAAAAAAAB9I=\"}]"}
2012-06-06 09:42:11.809 StaticTable[27488:f803] Jsson Array: 0
2012-06-06 09:42:11.809 StaticTable[27488:f803] Jsson Array: (null)
Code:
#import "UYLFirstViewController.h"
#import "MBProgressHUD.h"
#import "JSON.h"
#interface UYLFirstViewController ()
#end
#implementation UYLFirstViewController
#pragma mark -
#pragma mark === UIViewController ===
#pragma mark -
#synthesize MessageField;
#synthesize jsonArray = _jsonArray;
#synthesize TipLabelField;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.title = NSLocalizedString(#"Tickle!", #"Tickle!");
self.tabBarItem.image = [UIImage imageNamed:#"heart_plus"];
[self GetTipOfDay];
}
return self;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
-(BOOL)GetTipOfDay{
NSDate *date = [NSDate date];
NSDateFormatter *dateFormat = [[NSDateFormatter alloc]init];
[dateFormat setDateFormat:#"EEEE MMMM d, YYYY"];
NSString *dateString = [dateFormat stringFromDate:date];
NSString *yourOriginalString = #"Tip of the Day for ";
yourOriginalString = [yourOriginalString stringByAppendingString:dateString];
TipLabelField.text = yourOriginalString;
NSURL *url = [NSURL URLWithString:#"http://www.mysite.com/api/GetHealth.asmx/getTipOfDay"];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
[request setHTTPMethod:#"GET"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[NSURLConnection connectionWithRequest:request delegate:self];
// Clear text field
MessageField.text = #"";
// Start hud
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.labelText = #"Gathering Tip of the Day...";
return TRUE;
}
- (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[MBProgressHUD hideHUDForView:self.view animated:YES];
NSLog(#"Did receive data: %#", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
NSDictionary *responseDict = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] JSONValue];
NSString *jsonResponse = [responseDict objectForKey:#"d"];
self.jsonArray = [jsonResponse JSONValue];
NSLog(#"Jsson Array: %d", [jsonArray count]);
NSLog(#"Jsson Array: %#", jsonArray);
NSEnumerator *myEnumerator;
myEnumerator = [jsonArray objectEnumerator];
int i;
i=0;
id myObject;
while (myObject = [myEnumerator nextObject])
{
NSDictionary *itemAtIndex = (NSDictionary *)[self.jsonArray objectAtIndex:i];
NSLog(#"Checking for games");
NSString *myCheck = [itemAtIndex objectForKey:#"FName"];
if ([myCheck length] != 0)
{
// NSLog(myCheck);
MessageField.text = myCheck;
}
}
}
- (void)viewDidUnload {
[self setMessageField:nil];
[self setTipLabelField:nil];
[super viewDidUnload];
}
#end
#import <UIKit/UIKit.h>
#interface UYLFirstViewController : UIViewController{
NSMutableArray *jsonArray;
}
#property (weak, nonatomic) IBOutlet UILabel *MessageField;
#property (weak, nonatomic) NSMutableArray *jsonArray;
#property (weak, nonatomic) IBOutlet UILabel *TipLabelField;
-(BOOL)GetTipOfDay;
#end
-didRecieveData can be called multiple times as the bytes and chunks come in. You should move your logic to -connectionDidFinishLoading. This will let you know when the connection is completely done and the data is ready to be parsed.
You're only implementing one of the NSURLConnectionDelegate methods. Try adding this
- (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
//set up *receivedMutableString as instance variable in .h
if (!receivedMutableString) {
self.receivedMutableString = [[NSMutableString alloc] initWithData:data encoding:NSUTF8StringEncoding];
} else {
NSString *dataString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
[receivedMutableString appendString:dataString];
}
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
//Now receivedMutableString contains all json data
...continue with your code
}
NSURLConnection is a bit of overkill if you're just doing a simple GET request (and you're developing for an iOS version that supports blocks). You can do this in a dispatch_async block:
- (void) getData
{
dispatch_async(<some_queue>, ^{
NSError * error = nil;
NSString * response = [NSString stringWithContentsOfURL: stringWithContentsOfURL: requestUrl error: &error];
// process JSON
dispatch_async(dispatch_get_main_queue(), ^{
// Update UI on main thread
}
});
}
As you can see from my example code, you can also perform your JSON processing on the background queue (provided the method you're calling is thread safe). Just pass back to the main queue to update the UI.
Seems like the issue had nothing to do with fetching from the webservice. I had to define my array as __strong. Thanks for all the help. I did get some good ideas on how to do things better.

Trouble using a custom NSObject class to simplify repetitive code in iPhone app

I am trying to store a REST connection model in a single object so that I don't keep having to use it over and over again. Here is the model I created:
//RestModel.h
#import "ASIHTTPRequest.h"
#interface RestModel : NSObject{
NSString* _baseUrl;
NSString* _modelUrl;
}
#property (nonatomic, retain) NSString* modelUrl;
- (id)initWithModel:(NSString*)model;
- (NSDictionary*)getById:(NSInteger*)ident;
- (NSDictionary*)getAll;
#end
//RestModel.m
#import "RestModel.h"
#implementation RestModel
#synthesize modelUrl = _modelUrl;
- (id)init
{
self = [super init];
if (self) {
_baseUrl = #"http://myRESTurl.com";
}
return self;
}
- (id)initWithModel:(NSString*)model
{
self = [super init];
if (self) {
_baseUrl = #"http://myRESTurl.com";
self.modelUrl = [NSString stringWithFormat:#"%#/%#/", _baseUrl, model];
}
return self;
}
- (NSDictionary*)HTTPRequest:(NSURL*)url
{
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request startSynchronous];
NSError *error = [request error];
if(!error){
NSData *responseData = [request responseData];
NSString *errorDesc = nil;
NSPropertyListFormat format;
[error release];
[request release];
return (NSDictionary*)[NSPropertyListSerialization propertyListFromData:responseData mutabilityOption:NSPropertyListMutableContainersAndLeaves format:&format errorDescription:&errorDesc];
}else{
NSLog(#"%#", error);
return nil;
}
}
- (NSDictionary*)getById:(NSInteger*)ident
{
NSURL* url = [NSURL URLWithString:[NSString stringWithFormat:#"%#/%#", self.modelUrl, ident]];
return [self HTTPRequest:url];
}
- (NSDictionary*)getAll
{
NSURL* url = [NSURL URLWithString:[NSString stringWithFormat:#"%#", self.modelUrl]];
return [self HTTPRequest:url];
}
- (void)dealloc
{
[_modelUrl release];
// [_responseData release];
// [_responseDict release];
[super dealloc];
}
#end
Edit: Now it isn't crashing, but my local NSDictionary has a count of 0. I'm calling the following in -viewDidLoad in my controller:
RestModel* rm = [[RestModel alloc] initWithModel:#"user"];
self.dict = [rm getAll];
[rm release];
I planted NSLog of [self.dict count] throughout the controller. It is always 0. The RestModel rm is called, the functions are called (again more NSLogs), but no data. Any ideas?
[error release];
[request release];
Those should be auto-released objects, as you got them by convenience methods, so releasing them explicitly will make your application crash.
NSInteger isn't an object so it isn't necessary to pass it using NSInteger * and certainly you do not want to use the %# format specifier. Instead try this:
- (NSDictionary*)getById:(NSInteger)ident
{
NSURL* url = [NSURL URLWithString:[NSString stringWithFormat:#"%#/%d", self.modelUrl, ident]];
return [self HTTPRequest:url];
}

When and where are controller's properties supposed to be set?

This time, I was wondering when I was supposed to set my properties ...
I got a nav bar which I use to push a new controller (controlling a Web View) :
NewsViewController *webViewController = [[NewsViewController alloc] init]; // I create my controller
webViewController.urlText = #"http://www.google.fr"; // I set the property
InfonulAppDelegate *delegate = [[UIApplication sharedApplication] delegate];
[delegate.newsNavController pushViewController:webViewController animated:YES];
[webViewController release];
I don't know why but the code below doesn't work :
- (void)viewDidLoad { //viewDidLoad from my webViewController
[super viewDidLoad];
//Create a URL object.
NSURL *url = [NSURL URLWithString:urlText];
//URL Requst Object
NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];
//Load the request in the UIWebView.
[webView loadRequest:requestObj];
}
I just want to create a UIWebView but I need to give the controller the URL to use !
Any idea where and when my urlText property needs to be set ?!?
Cheers,
Gauthier
Do you use property correctly? Like
#property(nonatomic,retain) NSString *urlText;
If so, try to use a customized init method like this;
-(id)initWithUrl:(NSString *)url
{
if(self = [super init])
{
self.urlText = url;
}
return self;
}
dont forget to release urlText in dealloc. Now use;
NewsViewController *webViewController = [[NewsViewController alloc] initWithUrl:#"someUrl"];