Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

mikezang

macrumors 6502a
Original poster
May 22, 2010
939
41
Tokyo, Japan
I use code as below to download data from Internet, that file is very large about 10MB, I want to know if I have to change 60 to 600 or 6000?

I read document about [NSRunLoop currentRunLoop ] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:], but I am still not sure about it, does anyone explain to me?

Code:
-(void)fetchURL {
  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:self.url]];
  [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
  [[NSRunLoop currentRunLoop ] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:60]];
  [pool release];
}
 
Why do you need the NSRunloop stuff at all? NSURLConnection will download in the background using it's async API all by itself.
 
Why do you need the NSRunloop stuff at all? NSURLConnection will download in the background using it's async API all by itself.
really? My code as below, you mean the line before last line doesn't need?
Code:
-(void) fetchURL {
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:self.url]];
	[[NSURLConnection alloc] initWithRequest:request delegate:self];
	[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:600]];
	[pool release];
}
 
I am not sure so I post here, if you don't want to answer me it is ok!

But don't tell me to check documents on apple site, I have read!

Well, If you are a expert, don't think all people like you.

This is a forum to answer people's question, they want to get answer ASAP, though they might get some info after read all documents, but that will spend a lot of time, so they ask here.
 
You must have had a reason to think you needed that. To write that you'd have had to read quite a bit of documentation on NSRunLoop. Anyway you absolutely don't need it in any way. NSURLConnection attaches itself to the run loop and will call your delegate methods as required.

As for reading the documentation: you need to do so with more care. No where in the NSURLConnection documentation does it even suggest that such code is required. Randomly adding weird code will just cause you issues. Slow down and think more about what you are writing does. If you cannot explain why a line or section of code is there and exactly what it is doing you should not be writing it as you do not know what it's effect will be and the behaviour of your application will not be predictable (to you).
 
OK, I'm only telling you what I use successfully without issue and understand 100% as I've not copied it from somewhere without knowing what the hell I am doing. Why don't you explain more about what's going on. Are you initiating your downloads from a thread other than the main thread? If so I can see that you might need to do something odd to attach your NSURLConnection to the main run loop to make it work. The simpler solution would be to just use the standard NSURLConnection async API on the main thread: it's non-blocking and runs in the background anyway. I would have to have an amazingly good reason to run the download in another thread.
 
You like to use NSObject's performSelectorInBackground message, but this way that you can't communicate with downloading and don't know when download is finished.

I used NSThread, so that I can send results back to the main thread by
Code:
[self performSelectorOnMainThread:@selector(updateContent:)  
withObject:str waitUntilDone:NO];

this is what I need.
 
My point is you don't need anything to be in another thread: the NSURLConnection async API runs in the background and when the download is complete you can call whatever you need. No extra threads. No messing with NSRunLoop. No complication.
 
My point is you don't need anything to be in another thread: the NSURLConnection async API runs in the background and when the download is complete you can call whatever you need. No extra threads. No messing with NSRunLoop. No complication.
Can you show me your method if you don't mind? I mean don't use NSRunLoop and doing NSURLConnection async.

I want compare the two ways.
 
Are you trying to make an async routine synchronous in the main UI thread? Bad idea.

Return/exit from your code (whatever is calling fetchURL). Let the NSURL callback call your code later and let you know that the data is ready. Then continue on.
 
Well, the source like this, how can I do follow your method?
Code:
#import "ImagesViewController.h"
#import "InternetResource.h"

#define SOME_RESOURCE_URL @"http://media.wiley.com/product_data/coverImage/28/04707428/0470742828.jpg"
#define BAD_RESOURCE_URL2 @"http://media.wiley.com/product_data/coverImage/5.jpg"

@implementation ImagesViewController

@synthesize iResources;

- (id)initWithStyle:(UITableViewStyle)style {
  if (self = [super initWithStyle:style]) {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleFinishedLoading:) name:FinishedLoading object:nil];
    self.iResources = [NSArray  arrayWithObjects:
                       [[[InternetResource alloc] initWithTitle:@"First pic" andURL:BAD_RESOURCE_URL2] autorelease],
                       [[[InternetResource alloc] initWithTitle:@"Second pic" andURL:SOME_RESOURCE_URL] autorelease],
                       [[[InternetResource alloc] initWithTitle:@"Third pic" andURL:SOME_RESOURCE_URL] autorelease],
                       [[[InternetResource alloc] initWithTitle:@"Fourth pic" andURL:SOME_RESOURCE_URL] autorelease],
                       [[[InternetResource alloc] initWithTitle:@"Fifth pic" andURL:SOME_RESOURCE_URL] autorelease],  
                       [[[InternetResource alloc] initWithTitle:@"Sixth pic" andURL:SOME_RESOURCE_URL] autorelease],
                       nil];
  }
  return self;
}

-(void)handleFinishedLoading:(NSNotification*)notification{
  [self performSelectorOnMainThread:@selector(reloadTheData:) withObject:notification.object waitUntilDone:NO]; 
}

-(void)reloadTheData:(InternetResource*)_resource{
  [self.tableView reloadData];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
  return self.iResources.count;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
  return 140;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  static NSString *CellIdentifier = @"Cell";
  UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
  if (cell == nil) {
    cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
  }
  InternetResource  *iResource = [self.iResources objectAtIndex:indexPath.row];
  cell.textLabel.text = iResource.title;
  cell.imageView.image = nil;
  @synchronized(iResource){
    switch (iResource.status) {
      case NEW:
        cell.imageView.image = [UIImage imageNamed:@"loading.png"];
        [iResource start];
        break;
      case COMPLETE:
        cell.imageView.image = iResource.image;
        break;
      case FAILED:
        cell.imageView.image = [UIImage imageNamed:@"failed.png"];
        break;
      case FETCHING:
        cell.imageView.image = [UIImage imageNamed:@"loading.png"];
        break;
      default:
        cell.imageView.image = nil;
        break;
    }
  }
  return cell;
}

- (void)dealloc {
  [[NSNotificationCenter defaultCenter] removeObserver:self];
  self.iResources = nil;
  [super dealloc];
}
@end

Code:
#import "InternetResource.h"

@implementation InternetResource

@synthesize url, title, image, status;

-(id)initWithTitle:(NSString*)_title andURL:(NSString*)_url{
  if(self = [super init]){
    self.title = _title;
    self.url = _url;
    self.status = NEW;
  }
  return self;
}

-(void)start{
  self.status = FETCHING;
  receivedData = [[NSMutableData data] retain];
  [NSThread detachNewThreadSelector:@selector(fetchURL) toTarget:self withObject:nil];
}

-(void)fetchURL {
  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:self.url]];
  [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
  [[NSRunLoop currentRunLoop ] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:60]];
  [pool release];
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
  int statusCode = ((NSHTTPURLResponse*) response).statusCode;
  if(statusCode != 200){
    @synchronized(self){
      self.status = FAILED;
    }
  }
  [receivedData setLength:0];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
  [receivedData appendData:data];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
  [connection release];
  if(receivedData){
    [receivedData release];
    receivedData = nil;
  }
  @synchronized(self){
    self.status = FAILED;
  }
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
  @synchronized(self){
    if(self.status != FAILED){
      self.status = COMPLETE;
      self.image = [UIImage imageWithData:receivedData];    
      [receivedData release];
      receivedData = nil;
    }
  }
  [[NSNotificationCenter defaultCenter] postNotificationName:FinishedLoading object:self];
  [connection release];  
}          

-(void)dealloc{
  if(receivedData){
    [receivedData release];
  }
  self.title = nil;
  self.url = nil;
  self.image = nil;
  [super dealloc];
}
@end
 
Maybe just modify to code as below is ok

old:
Code:
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:600]];

new:
Code:
[[NSRunLoop currentRunLoop] run];
 
Well, the source like this, how can I do follow your method?
Code:
...

You will need to break things up at a higher level so that you don't depend on your table view being loaded until the data actually arrives. Don't wait to finish the entire data array and table initialization in the init. Let the table view get loaded and refreshed over time (lazy loading, etc.) That way the user isn't stuck with a frozen UI waiting for table items that aren't even on the display.

For example, the NPR News iPhone app spins little activity indicators in each unloaded table item as it loads each story, one at a time. But the UI isn't blocked waiting, and the user can jump into the first story without waiting the 5 minutes it takes to load the entire story list.
 
You will need to break things up at a higher level so that you don't depend on your table view being loaded until the data actually arrives. Don't wait to finish the entire data array and table initialization in the init. Let the table view get loaded and refreshed over time (lazy loading, etc.) That way the user isn't stuck with a frozen UI waiting for table items that aren't even on the display.

For example, the NPR News iPhone app spins little activity indicators in each unloaded table item as it loads each story, one at a time. But the UI isn't blocked waiting, and the user can jump into the first story without waiting the 5 minutes it takes to load the entire story list.
If you can provide a skeleton code, that will be helpful...
 
Your talking seems like not right.

I commented that line, but download is failed. The download is ok after back to original code.

By the way, this code is copied from 17.4 Multithreaded Downloads in "iPhone SDK 3 Programming(Advanced Mobile Development for Apple iPhone and iPod touch", try this code at http://code.google.com/p/iphone3/downloads/detail?name=Chapter17.tar.gz&can=2&q=

Fair enough...you got the code from the book. All I can say is to anybody reading this, don't buy that book, if that's the sort of code its recommending you use, as it's rubbish.

Some general points on networking:

1) Always prefer asynchronous APIs over threading if one is available
2) Even if you want to wrap up your tasks as a discrete operation, don't use threads. Use NSOperation (you can use it with a synchronous API, or in concurrent mode with asynchronous APIs. Read this article).
3) If you think you need to use threads, I repeat, once again, consider using NSOperation or GrandCentralDispatch if it is available on your targted platform as they are almost always the better choice.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.