Why would delaying a thread response fix view corruption? - iphone

6 times out of 10 my very simple iPhone app is getting a corrupted display on launch or crashes randomly. But it behaves fine in the simulator. The display corruption looks like mis-colored fonts, out of place font text, wrong background colors, etc.
I've found a strange work-around.. when my thread delays by 2 seconds before calling the "done" notification, everything works swimmingly. The thread reads a web page and the "done" notification loads up a PickerView with strings. So what gives? Can I not safely initiate a threaded task from viewDidLoad?
- (void) loadWebPage:(NSString *)urlAddition {
NSAutoreleasePool *subPool = [[NSAutoreleasePool alloc] init];
NSString *pageSource;
NSError *err;
NSString *urlString = [NSString stringWithFormat:#"http://server/%#",
urlAddition];
pageSource = [NSString stringWithContentsOfURL:[NSURL URLWithString: urlString]
encoding:NSUTF8StringEncoding
error:&err];
[NSThread sleepForTimeInterval:2.0]; // THIS STOPS THE DISPLAY CORRUPTION
[[NSNotificationCenter defaultCenter] postNotificationName:#"webDoneNotification"
object:nil];
[subPool drain];
}
- (void) webDoneNotification: (NSNotification *)pNotification {
[mediaArray release];
mediaArray = [[NSArray arrayWithObjects:
[NSString stringWithString:#"new pickerview text"],
nil] retain];
[mediaPickerView reloadAllComponents];
[mediaPickerView selectRow:0
inComponent:0
animated:NO];
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
mediaArray = [[NSArray arrayWithObjects:
[NSString stringWithString:#"init pickerview text"],
nil] retain];
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
myWebThread = [[WebThread alloc] initWithDelegate:self];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(webDoneNotification:)
name:#"webDoneNotification"
object:nil];
[myWebThread performSelectorInBackground:#selector(loadWebPage:) withObject:#""];
}
Thanks!
Update: Even a delay of 0.1 seconds is enough to completely fix the problem.

You're updating the view from a background thread. This is what's causing your problems; UIKit views are not thread-safe. When you need to alter the view, do it in the main thread.

Related

App doesn't save before going to background

I want to save some texts in textfields whenever the user goes to the background i think i wrote everything correctly since i followed many question/answers. However when I close my app my app doesn't save the text or even create a plist so when i reopen it, the textfields are empty. Here is the code:
RootViewController.h:
#interface RootViewController: UIViewController <UITextFieldDelegate> {
UITextField *textField1;
UITextField *textField2;
UITextField *textField3;
UITextField *textField4;
}
#property (nonatomic, retain) UITextField *textField1, *textField2, *textField3, *textField4;
#end
RootViewController.m:
#import "RootViewController.h"
#implementation RootViewController
#synthesize textField1, textField2, textField3, textField4;
...
- (UILabel*)addNewLabel:(NSString*)_text
{
//Initializing TextViews
}
....
- (NSString *) saveFilePathB
{
NSString *path = #"/Applications/AppDissassembler.app/Cache/savefileb.plist";
return path;
}
- (void)loadView {
self.view = [[[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] autorelease];
self.view.backgroundColor = [UIColor whiteColor];
//creating textfields
self.textField1 = [self addNewTextfield:60:80:175:true:#"Binary Name Here":1];
self.textField2 = [self addNewTextfield:60:145:200:true:#"Offset Here (0xOffset)":2];
self.textField3 = [self addNewTextfield:75:210:175:false:nil:3];
self.textField4 = [self addNewTextfield:75:310:175:false:nil:4];
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
NSArray *values = [[NSArray alloc] initWithObjects:self.textField1.text,self.textField2.text,self.textField3.text,self.textField4.text,nil];
NSFileHandle *fout1;
[[NSFileManager defaultManager] createFileAtPath:[self saveFilePathB] contents:nil attributes:nil];
//open output file for writing
fout1 = [NSFileHandle fileHandleForWritingAtPath:[self saveFilePathB]];
[values writeToFile:[self saveFilePathB] atomically:YES];
[values release];
}
- (void)viewDidLoad
{
NSString *myPath = [self saveFilePathB];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:myPath];
if (fileExists)
{
NSArray *values = [[NSArray alloc] initWithContentsOfFile:myPath];
self.textField1.text = [values objectAtIndex:0];
self.textField2.text = [values objectAtIndex:1];
self.textField3.text = [values objectAtIndex:2];
self.textField4.text = [values objectAtIndex:3];
[values release];
}
UIApplication *myApp = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(applicationDidEnterBackground:) name:#"UIApplicationDidEnterBackgroundNotification" object:myApp];
[super viewDidLoad];
}
- (void)dealloc {
[textField1 release];
[textField2 release];
[textField3 release];
[textField4 release];
[super dealloc];
}
#end
If I change this:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(applicationDidEnterBackground:) name:#"UIApplicationDidEnterBackgroundNotification" object:myApp];
to this:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(applicationDidEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:myApp];
I get an error which says that UIApplicationDidEnterBackgroundNotification wasn't declared.
It wont run in method you try, so in UIApplicationDidEnterBackground delegate. All you have to do is handle your process while going into background with beginBackgroundTaskWithExpirationHandler. See this topic: topic
The second version you tried, where UIApplicationDidEnterBackgroundNotification is an identifier not a literal string, is the correct way to use this feature. It should be defined in UIApplication.h.
You say you are compiling on the iPhone itself, so it sounds like the development environment you have there is lacking this definition. I haven't tried that myself, so can't be sure.
Once you have got past the compilation error I would recommend to use NSLog to see where your code has got to, because this is easier than looking for the file to be created.

dealloc vs ViewDidDissapear for memory management

I have a few views which parse xml downloaded from the internet.
The instruments leaks tool tells me I have leaks when I release data members inside the dealloc method but not when i put the [objectname release]; inside viewDidDissapear.
Is this a cardinal sin?
Coming from a c/c++ background I find obj-c memory management very confusing!
EDIT: Here is the code:
#import "SuggestedFriendList.h"
#implementation SuggestedFriendList
#synthesize maincell,nationalityimageview, subjectimageview, accommodationimageview;
- (void)parser:(NSXMLParser *)parser
didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qualifiedName
attributes:(NSDictionary *)attributeDict
{
currentElement = [[elementName copy] autorelease];
if ([elementName isEqualToString:#"shared"])
{
tshared = [[NSMutableString alloc] init];
}
if ([elementName isEqualToString:#"fullname"])
{
tfullname = [[NSMutableString alloc] init];
}
if ([elementName isEqualToString:#"nationality"])
{
tnationality = [[NSMutableString alloc] init];
}
if ([elementName isEqualToString:#"subject"])
{
tsubject = [[NSMutableString alloc] init];
}
if ([elementName isEqualToString:#"accommodation"])
{
taccommodation = [[NSMutableString alloc] init];
}
if ([elementName isEqualToString:#"memberid"])
{
tmemberid = [[NSMutableString alloc] init];
}
if ([elementName isEqualToString:#"count"])
{
tcount = [[NSMutableString alloc] init];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if ([currentElement isEqualToString:#"shared"])
{
[tshared appendString:string];
}
if ([currentElement isEqualToString:#"fullname"])
{
[tfullname appendString:string];
}
if ([currentElement isEqualToString:#"nationality"])
{
[tnationality appendString:string];
}
if ([currentElement isEqualToString:#"subject"])
{
[tsubject appendString:string];
}
if ([currentElement isEqualToString:#"accommodation"])
{
[taccommodation appendString:string];
}
if ([currentElement isEqualToString:#"memberid"])
{
[tmemberid appendString:string];
}
if ([currentElement isEqualToString:#"count"])
{
[tcount appendString:string];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementName isEqualToString:#"shared"])
{
[lshared addObject:tshared];
[tshared release];
}
if ([elementName isEqualToString:#"fullname"])
{
[lfullname addObject:tfullname];
[tfullname release];
}
if ([elementName isEqualToString:#"nationality"])
{
[tnationality appendString:#".png"];
[lnationality addObject:tnationality];
[tnationality release];
}
if ([elementName isEqualToString:#"subject"])
{
[lsubject addObject:tsubject];
[tsubject release];
}
if ([elementName isEqualToString:#"accommodation"])
{
[laccommodation addObject:taccommodation];
[taccommodation release];
}
if ([elementName isEqualToString:#"memberid"])
{
[lmemberid addObject:tmemberid];
[tmemberid release];
}
if ([elementName isEqualToString:#"count"])
{
count = [[NSMutableString alloc] init];
count = tcount;
[tcount release];
}
}
-(void)fetchsuggestions
{
MyManager *sharedManager = [MyManager sharedManager];
suggestiondetailxml = [[NSMutableData alloc] init];
NSString *urlString = [NSString stringWithFormat:#"http://secreturl.com/a.php?username=%#&password=%#",sharedManager.user,sharedManager.passw];
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *req = [NSURLRequest requestWithURL:url];
connection = [[NSURLConnection alloc] initWithRequest:req delegate:self startImmediately:YES];
}
-(void) connection:(NSURLConnection *)conn didReceiveData:(NSData *)data
{
[suggestiondetailxml appendData:data];
}
-(void) connectionDidFinishLoading:(NSURLConnection *)conn
{
NSString *xmlcheck = [[[NSString alloc] initWithData:suggestiondetailxml encoding:NSUTF8StringEncoding] autorelease];
NSLog(#"%#",xmlcheck);
lshared = [[NSMutableArray alloc] init];
lfullname = [[NSMutableArray alloc] init];
lnationality = [[NSMutableArray alloc] init];
lsubject = [[NSMutableArray alloc] init];
laccommodation = [[NSMutableArray alloc] init];
lmemberid = [[NSMutableArray alloc] init];
NSXMLParser *parser = [[NSXMLParser alloc] initWithData: suggestiondetailxml];
[parser setDelegate:self];
[parser parse];
[parser release];
//[xmlcheck release]; //causes crash
[connection release];
connection = nil;
[suggestiondetailxml release];
[[self tableView] reloadData];
NSLog(#"%#",lmemberid);
}
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (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];
[[self navigationItem] setTitle:#"My Culture"];
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self fetchsuggestions];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[lshared release];
[lfullname release];
[lnationality release];
[lsubject release];
[laccommodation release];
[lmemberid release];
//[count release];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [lfullname count];
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 93.0;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:nil];
if (cell==nil)
{
[[NSBundle mainBundle] loadNibNamed:#"SuggestedFriendCell" owner:self options:nil];
cell = maincell;
//self.detailcell = nil;
}
UILabel *name;
name = (UILabel *)[cell viewWithTag:4];
name.text=[lfullname objectAtIndex:indexPath.row];
if(([[lshared objectAtIndex:indexPath.row]isEqualToString:#"all"])||([[lshared objectAtIndex:indexPath.row]isEqualToString:#"nationalityandaccommodation"])||([[lshared objectAtIndex:indexPath.row]isEqualToString:#"nationalityandsubject"])||([[lshared objectAtIndex:indexPath.row]isEqualToString:#"nationality"]))
{
UIImageView *nationality;
nationality = (UIImageView *)[cell viewWithTag:1];
nationality.image=[UIImage imageNamed:[lnationality objectAtIndex:indexPath.row]];
}
if(([[lshared objectAtIndex:indexPath.row]isEqualToString:#"all"])||([[lshared objectAtIndex:indexPath.row]isEqualToString:#"nationalityandaccommodation"])||([[lshared objectAtIndex:indexPath.row]isEqualToString:#"subjectandaccommodation"])||([[lshared objectAtIndex:indexPath.row]isEqualToString:#"accommodation"]))
{
UIImageView *accommodation;
accommodation = (UIImageView *)[cell viewWithTag:2];
accommodation.image=[UIImage imageNamed:#"accommodation.jpeg"];
}
if(([[lshared objectAtIndex:indexPath.row]isEqualToString:#"all"])||([[lshared objectAtIndex:indexPath.row]isEqualToString:#"nationalityandsubject"])||([[lshared objectAtIndex:indexPath.row]isEqualToString:#"subjectandaccommodation"])||([[lshared objectAtIndex:indexPath.row]isEqualToString:#"subject"]))
{
UIImageView *subject;
subject = (UIImageView *)[cell viewWithTag:3];
subject.image=[UIImage imageNamed:#"degree.jpg"];
}
return cell;
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Navigation logic may go here. Create and push another view controller.
/*
<#DetailViewController#> *detailViewController = [[<#DetailViewController#> alloc] initWithNibName:#"<#Nib name#>" bundle:nil];
// ...
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];
*/
MyManager *sharedManager = [MyManager sharedManager];
int row = indexPath.row;
sharedManager.interrogatedmemberid=[lmemberid objectAtIndex:row];
ViewFriendProfile *frienddetail_vc = [[[ViewFriendProfile alloc] init] autorelease];
[[self navigationController] pushViewController:frienddetail_vc animated:YES];
}
-(void)dealloc
{
[super dealloc];
}
#end
When I uncomment [tcount release] it causes crashes: why is this.
PS: I am SO sorry about the formatting: How can you copy and paste code so it appears in the code blocks?
Thanks
To understand what's going on you need to understand how viewWillAppear and viewWillDisappear work.
Both methods are invoked multiple times in a view controller's life cycle depending on whether you're pushing/popping a viewcontroller, or if you're displaying / dismissing a modal from a view controller.
For example:
Push viewController A: viewDidLoad and viewWillAppear called on viewController A
Push viewController B from viewController A: viewWillDisappear called on viewController A, viewDidLoad and viewWillAppear called on viewController B
Pop viewController B to go back to viewController A: viewWillDisappear called on viewController B, viewDidUnload called on viewController B, dealloc called on viewController B, viewWillAppear called on viewController A
Display a modal from viewController A: viewWillDisappear called on viewController A
Dismiss the modal: viewWillAppear called on viewController A
Pop viewController A: viewWillDisappear called on viewController A, viewDidUnload called on viewController A, dealloc called on viewController A
Sounds like you're allocating an object in viewWillAppear, which is called multiple times, but deallocating it in the dealloc method, which is called only once, hence the memory leak.
Releasing your object in viewWillDisappear is balancing out the allocs in viewWillAppear hence there is no leak.
All I can say at this point is, unless you have a very good reason to allocate something in viewWillAppear (i.e., you know what you're doing), don't do it.
There's not much more I can tell you... posting more code for what you're trying to do might help you get a more elaborate answer :)
Hope this helped though.
EDIT after the code was posted.
First of all, do you mean 'when I uncomment [count release]'? Could you post the console message for that crash? It's crashing because parserDidEndElement is where it's allocated, but it is getting released in viewWillDisappear. If these don't balance out, you'll get an EXC_BAD_ACCESS
Coming to your memory situation. I see now why releasing in viewWillDisappear fixes the leaks. You're calling fetchsuggestions on viewWillAppear which in turn creates and fires off the URL request. This will lead to connectionDidFinishLoading being called on a successful connection. Here is where you're allocating the various ivars. Since I mentioned earlier that viewWillAppear is called multiple times, this means that you're firing off the connection multiple times which leads to connectionDidFinishLoading being called multiple times.
That is why releasing in viewWillDisappear fixes the leaks because for every allocate on a viewWillAppear, you balance it by releasing in viewWillDisappear.
What you missed though is that your program will crash when there's no network available. This is because connectionDidFinishLoading will not get called when there's no network. Now, when you try releasing in viewWillDisappear, you will get an EXC_BAD_ACCESS as you're trying to release previously deallocated instances which were never allocated again in viewWillAppear.
I don't think I see a need to call fetchsuggestions multiple times on viewWillAppear, unless you're doing it intentionally.
This is the most glaring thing I see right now... the issues could be (and probably are) multiple, but how about we start off by restructuring the code keeping in mind that multiple unbalanced allocs and releases inside viewWillAppear/Disappear might not be a good idea...? :)
It's kind of strange to release anything in viewWillDisappear, but if you allocate it in viewWillAppear, that's the right thing to do. I think you should be allocating your objects in viewDidLoad, and releasing them in viewDidUnload. In some cases that would avoid unnecessarily recreating them.
You should also release retained ivars in dealloc. Just make sure that when you release them in other methods, you set them to nil, so if they get released again, it's just sending release to nil, which is a no-op.
One tool that I find helpful in finding potential memory leaks is Build > Analyse in Xcode.
It tells you which line did the allocation and where might it leak later down the code. It also tells you where you have made incorrect [object release] decrement count.
There is a possibilty that you are not completely releasing the view and dealloc is never being called.

Can we thread the web-service and insert in in custom table cell?

The thing is i have to make Custom cell of UITable, i have to call large webservice with many data which will take longer tym to load it once. So i want to apply threading in it. now my que is. can we at run -time insert value one by one in custom cell , as user will see data comming one by one at run time..?? if yes how we can do that.
yes you can ... try this
- (void)viewDidLoad
{
myApp = (myAppDelegate *) [[UIApplication sharedApplication] delegate];
[self performSelectorInBackground:#selector(startParsing) withObject:nil];
[super viewDidLoad];
}
- (void) startParsing
{
[self performSelectorOnMainThread:#selector(startIndicator) withObject:nil waitUntilDone:NO];
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSURL *url = [[NSURL alloc] initWithString:#"http://abc.com"];
NSData *data = [NSData dataWithContentsOfURL:url];
// 2 -- parsing
parser = [[VolMyParser alloc] init];
[parser parseXML:data];
[data release];
//[parser print];
[self performSelectorOnMainThread:#selector(updateTable) withObject:nil waitUntilDone:NO];
[pool release];
}
- (void) startIndicator
{
av.hidesWhenStopped = YES;
[av startAnimating];
}
- (void) updateTable
{
[av stopAnimating];
[myTable reloadData];
}
Hope its gives you an Idea...

AsyncSocket and Notifications - memory leak

I have a memory leak in the following scenario. I read data at every 30 seconds, use SBJSONParser to transform it to a dictionary, add a notification and after that use the data to bind it to a tableview:
// Read data and send notification
-(void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
NSString *content = [[NSString alloc] initWithData:[data subDataWithRange:NSMakeRange(0, [data length] - 2)] encoding: NSUTF8StringEncoding];
// Line where leaks appear
NSMutableDictionary* dict = [[NSMutableDictionary alloc] initWithDictionary:[content JSONValue]];
[content release];
// Post notification
[[NSNotificationCenter defaultCenter] postNotificationName:#"BindData" object:nil userInfo:dict];
[dict release];
}
On a CustomViewController I have the observer:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(bindData) name:#"BindData" object:nil];
and the bindData method:
-(void)bindData:(NSNotification*)notification
{
NSAutoreleasePool* pool = [[NSAutoReleasePool alloc] init];
NSMutableArray* customers = [notification.userInfo objectForKey:#"Customers"];
for (NSDictionary* customer in customers)
{
Company* company = [[Company alloc] init];
company.name = [customer objectForKey:#"CompanyName"];
NSLog(#"Company name = %#", company.name);
[company release];
}
[pool drain];
}
The problem is: when I set company.name = something from that dictionary, I get a memory leak on the line: NSMutableDictionary* dict = [[NSMutableDictionary alloc] initWithDictionary:[content JSONValue]]; which keeps increasing since I'm reading at every 30 seconds.
I appreciate any help you can give. Thanks.
dict is leaking because you are using alloc and init (thus increasing its retain count by 1), but never releasing it. Since the dictionary will no longer be needed after the notification has been posted, you can safely release it on the following line, like so:
// Post notification
[[NSNotificationCenter defaultCenter] postNotificationName:#"BindData" object:nil userInfo:dict]
[dict release];
See the Memory Management Programming Guide for more details.

Memory allocation, release and NSURLConnection in iPhone app

I'm hoping someone can help me with this. I'm struggling to find an answer to what should be an easy question. By the way, this is my first major obj-c project after years of using c and c# so feel free to point out things I'm failing on.
I have an iPhone app designed to load an album of photos into a UIScrollView. It also has a random image function which uses the same process but only displays a single image. It works like so:
Read an external XML feed (stored on ruby-on-rails website) which contains a path to a random url of photo once parsed.
Content of URL is downloaded using NSURLConnection to NSData.
ScrollView is created and pushed
Subclassed UIView allocs an UIImageView, allocs a UIImage with the NSData which, inits the UIImageView with the UIimage and finally adds the imageview to the UIView.
The parent view then adds the UIView to the UIScrollView which is then pushed to the front.
This process occurs again when when the next random image is required. It also uses the same process when displaying an entire album of images except several UIViews are added to the UIScrollView.
The problem is, despite using release and delloc where appropriate, the activity tool indicates that the memory used by NSURLConnection and UIImage isn't being released from memory when the next image is requested. This is further proven by building the app to the iPhone. Requesting several images in a row causes the app the crash presumably from memory consumption.
Below is some of the code as I'm unable to post the entire project due to contractual agreements.
URLDownload class (uses DataDownload)
#implementation URLDownload
-(NSData *)GetURL:(NSURL *)strURL
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
DataDownload *request = [DataDownload alloc];
request.strURL = strURL;
[request init];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
while (request.isLoading && [runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
[pool release];
return [request urlData];
[request release];
}
DataDownload class
#implementation DataDownload
#synthesize urlData, connection, strURL, isLoading;
- (id)init
{
if(self = [super init])
{
self.isLoading = YES;
NSURLRequest *dataRequest = [NSURLRequest requestWithURL:self.strURL
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:60];
/* establish the connection */
self.connection = [[NSURLConnection alloc]
initWithRequest:dataRequest
delegate:self
];
if (connection == nil) {
self.urlData = nil;
} else {
self.urlData = [NSMutableData data];
}
}
return self;
}
- (void)dealloc {
[connection cancel];
[connection release];
[urlData release];
[strURL release];
[super dealloc];
}
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse {
return nil;
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse*)response
{
[urlData setLength:0];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData*)incrementalData
{
[self.urlData appendData:incrementalData];
}
-(void)connectionDidFinishLoading:(NSURLConnection*)connection
{
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
self.isLoading = NO;
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
self.isLoading = NO;
}
#end
ImageView subclass
#implementation ImageView
#synthesize strURL, imvImageView, image, currentlyRotated, imgOptionsView, startDate;
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.imvImageView = [UIImageView alloc];
if (self.strURL != nil){
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
NSData *datImageData = [NSData dataWithContentsOfURL: [NSURL URLWithString:self.strURL]];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
self.image = [UIImage imageWithData: datImageData];
CGSize imgSize = image.size;
CGFloat fltWidth = imgSize.width;
CGFloat fltHeight = imgSize.height;
[imvImageView initWithImage:image];
// If landscape rotate image
if (fltWidth > fltHeight){
imvImageView.frame = CGRectMake(-80.0, 80.0, 480.0, 320.0);
CGAffineTransform rotate = CGAffineTransformMakeRotation(-1.57079633);
[imvImageView setTransform:rotate];
self.currentlyRotated = YES;
}else{
imvImageView.frame = CGRectMake(0.0, 0.0, 320.0, 480.0);
self.currentlyRotated = NO;
}
[image release];
}else{
self.image = [UIImage alloc];
[imvImageView initWithImage:image];
[image release];
}
[self addSubview:imvImageView];
}
[imvImageView release];
return self;
}
- (void)drawRect:(CGRect)rect {
// Drawing code
}
- (void)dealloc {
[strURL release];
[imvImageView release];
[image release];
[imgOptionsView release];
[startDate release];
[super dealloc];
}
Sample code of how images are being displayed
- (void)displayImage:(NSString *)strURL {
NSString *strFullURL = #"http://www.marklatham.co.uk";
strFullURL = [strFullURL stringByAppendingString:strURL];
self.scroller = [[UIScrollView alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 480.0)];
[scroller setContentSize:CGSizeMake(320.0, 540.0)];
[self.view addSubview:scroller];
CGRect rect = CGRectMake(0.0, 0.0, 320.0, 480.0);
self.imageView = [ImageView alloc];
imageView.strURL = strFullURL;
[imageView initWithFrame:rect];
[scroller addSubview:imageView];
[scroller release];
[imageView release];
}
The following images show all allocations to illustrate what's happening. 1 shows alloc on startup, 2 shows after first image is loaded and 3 after after second image.
http://www.gretanova.com/misc/iphone/1.png & 2.png & 3.png
Thanks everyone,
Lee
A couple of things stick out. For example within the URLDownload class the call to [request release] will never be reached as its placed after the return statement
return [request urlData];
[request release];