I'm loading images with this class. I need help on how to stop them from loading when dealloc is called. This class is extending UIImageView. Also any other advice for designing the class is appreciated, i'll want for example to dispatch the loaded bytes.
#implementation RCPhoto
- (id)initWithFrame:(CGRect)frame delegate:(id)d {
if ((self = [super initWithFrame:frame])) {
// Initialization code
delegate = d;
self.contentMode = UIViewContentModeScaleAspectFit;
}
return self;
}
- (void)dealloc {
[super dealloc];
}
#pragma mark Load photo
- (void)loadImage:(NSString *)path {
// Create a new thread for loading the image
[NSThread detachNewThreadSelector:#selector(load:)
toTarget:self
withObject:path];
}
- (void)load:(NSString *)path {
// You must create a autorelease pool for all secondary threads.
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//NSLog(#"RCPhoto loadImage into a new thread: %#", path);
NSURL *url = [NSURL URLWithString: path];
NSData *data = [NSData dataWithContentsOfURL: url];
UIImage *image = [[UIImage alloc] initWithData: data];
[self performSelectorOnMainThread: #selector(performCompleteEvent:)
withObject: image
waitUntilDone: NO];
[pool release];
}
- (void)performCompleteEvent:(UIImage *)image {
//NSLog(#"RCPhoto finish loading");
[self setImage:image];
self.opaque = YES;
if ([delegate respondsToSelector:#selector(onPhotoComplete)]) {
[delegate performSelector:#selector(onPhotoComplete)];
}
}
#end
dataWithContentsOfURL: is designed to block until it times out or finishes receiving a complete response. You seem interested in interrupting the background thread when its associated view is deallocated. These facts are fundamentally at odds.
If you want to terminate the request as quickly as possible when your object is going away, this can be done by making an asynchronous request with NSURLConnection and canceling it in your dealloc method. If it isn't canceled before the response is received, you'll get a callback to connectionDidFinishLoading: on your delegate, at which point you can reconstitute your UIImage from the received data.
Final code:
- (void)loadImage:(NSString *)path {
imageData = [[NSMutableData data] retain];
NSURL *url = [NSURL URLWithString: path];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)conn {
UIImage *image = [[UIImage alloc] initWithData:imageData];
[self setImage:image];
self.opaque = YES;// explicitly opaque for performance
[image release];
}
Related
I receive Image from URL by threading .
but memory leaking in NSData.
Why? How do I fix it?
Leaking in Iphone devie not in simulator. help me!!
//viewcontroller
-(void)viewDidLoad
{
[self performSelectorInBackground:#selector(loadImageScreenshot:) withObject:args];
}
// loding image
-(void) loadImageScreenshot:(NSDictionary *) args
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
UIImage * screenshotImage=[UIImage imageWithStringURL:url];
NSDictionary *args2=[NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:num], #"screenNum",
screenshotImage,#"image",
nil];
[self performSelectorOnMainThread:#selector(assignImageToScreenshotImageView:) withObject:args2 waitUntilDone:YES];
[pool release];
}
//image add
- (void) assignImageToScreenshotImageView:(NSDictionary *)arg
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
UIImage * image= [arg objectForKey:#"image"];
UIImageView *imageview=[UIImageView alloc]init];
.
.
imageview.image=image;
[self.mScreenshotSpace addSubview:imageview];
[imageview release];
[pool release];
}
//image from url
+(UIImage *)imageWithStringURL:(NSString *)strURL
{
NSURL *url =[NSURL URLWithString:strURL];
NSData * data=[[NSData alloc]initWithContentsOfURL:url options:NSDataReadingUncached error:&error];
UIImage * image=[UIImage imageWithData:data ];
[data release];
return image;
}
How do you know it is leaking? You are creating an autorelease pool in - (void) assignImageToScreenshotImageView:(NSDictionary *)arg that is never drained, that is a leak.
Otherwise the code seems fine.
I have parsed my xml and i got some images and their corresponding urls of pdf from server.so whenever i click on image i have their corresponding url of pdf.I am giving an alertView on click of images and when user select the download button of alertView it should download the pdf from url into my iphone device
CODE:-
#implementation SecondViewController
#synthesize scrollView,receivedData;
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[receivedData appendData:data];
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
[myIndicator setActivityIndicatorViewStyle:UIActivityIndicatorViewStyleWhiteLarge];
myIndicator.hidesWhenStopped = YES;
[myIndicator startAnimating];
UIColor *background = [[UIColor alloc] initWithPatternImage:[UIImage imageNamed:#"iphone_landscape.png"]];
self.view.backgroundColor = background;
[background release];
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://litofinter.es.milfoil.arvixe.com/displayxml1.aspx"] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:150.0];
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection) {
receivedData = [[NSMutableData data] retain];
}
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
int x=20,y=50;
appDelegate = (AppDelegate_iPhone *)[[UIApplication sharedApplication] delegate];
scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 45,320, 480)];
scrollView.contentSize = CGSizeMake(320,5000);
scrollView.showsVerticalScrollIndicator = YES;
for (Litofinter *lito in appDelegate.bookArray) {
if([appDelegate.currentButtonPressed isEqualToString:lito.cName])
{
NSLog(#"Count == %d ===",[lito.productsArray count]);
for (Products *prod in lito.productsArray) {
NSString * urlString = [prod.thumbnail stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
NSURL * imageURL = [NSURL URLWithString:urlString];
NSData * imageData = [NSData dataWithContentsOfURL:imageURL];
UIImage * image = [UIImage imageWithData:imageData];
[myIndicator stopAnimating];
[myIndicator removeFromSuperview];
UIButton *imageButton = [[UIButton buttonWithType:UIButtonTypeCustom]retain];
[imageButton setFrame:CGRectMake(x, y, 150, 200)];
[imageButton setImage:image forState:UIControlStateNormal];
[imageButton setTitle:prod.pdf forState:UIControlStateNormal];
[imageButton addTarget:self action:#selector(onTapBook:) forControlEvents:UIControlEventTouchUpInside];
[scrollView addSubview:imageButton];
x = x + 150;
if(x >300)
{
y = y +250;
x = 20;
}
}
}
}
[self.view addSubview:scrollView];
[connection release];
[receivedData release];
}
-(void)onTapBook:(id)sender{
UIButton *button = (UIButton *) sender;
appDelegate.currentBookPressed = [button currentTitle];
// viewController2 = [[PdfShowViewController alloc]initWithNibName:#"PdfShowViewController" bundle:nil];
// [self presentModalViewController:viewController2 animated:YES];
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Ver Catalogo!" message:#"" delegate:self cancelButtonTitle:#"Cancelar" otherButtonTitles:#"Ver on-line",#"Descargar",nil];
[alert show];
/*[NSString stringWithFormat:#"%#",appDelegate.currentBookPressed] */
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
NSString *title = [alertView buttonTitleAtIndex:buttonIndex];
if([title isEqualToString:#"Ver on-line"])
{
// i will show the pdf online here
}
else if([title isEqualToString:#"Descargar"])
{
// what to write to download the pdf
}
}
-(IBAction)onTapBack{
[self dismissModalViewControllerAnimated:YES];
}
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)viewDidUnload {
[super viewDidUnload];
}
- (void)dealloc {
[super dealloc];
[scrollView release];
}
#end
I would do it with NSURLConnection and then I would reuse same code above, because you have it already declared properly.
Save data to NSData and then with writeToFile save it to main bundle.
So here is some more explanation how I would do it.
There are several ways to do it.
Here is how to do it with NSData
NSData *myFile = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"your_url"]]; [myFile writeToFile:[NSString stringWithFormat:#"%#/%#", [[NSBundle mainBundle] resourcePath], #"yourfilename.pdf"] atomically:YES];
Also you can use ASIHTTPRequest library which has been discontinued by author, but still works like it should.
ASIHTTPRequest *myDownloadRequest = [ASIHTTPRequest requestWithURL:fileUrl];
[request setDownloadDestinationPath:[NSString stringWithFormat:#"%#/%#", [[NSBundle mainBundle] resourcePath], #"yourfilename.pdf"]];
But maybe easiest way of all because as I can see you have displayed pdf already, so it's contents are in receivedData is just to call
[receivedData writeToFile:[NSString stringWithFormat:#"%#/%#", [[NSBundle mainBundle] resourcePath], #"yourfilename.pdf"] atomically:YES];
So actually you can reuse code that you have already wrote in viewDidLoad, replace url if necessary and after connection is closed save file to disk.
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://litofinter.es.milfoil.arvixe.com/displayxml1.aspx"] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:150.0];
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection) {
receivedData = [[NSMutableData data] retain];
}
Is there any way of achieving the following that avoids using "initWithData" ? (Just in case you are curious, initWithData is getting my app flagged by Apple as using an illegal API sigh).
NSData * imageData = [NSData dataWithContentsOfURL : [NSURL URLWithString : [details image]]];
picture = [[UIImage alloc] initWithData:imageData];
Many thanks,
Martin
if you want to get the image data,then initialize a UIImage using that data:
NSData * imageData = [[NSData alloc] initWithContentsOfURL: [NSURL URLWithString: #"http://Serverurl/pic007.jpg"]];
cell.image = [UIImage imageWithData: imageData];
[imageData release];
First of all, you should do this asynchronously so that your thread won't block. Here is the code for the class:
#implementation AsyncImageView
+ (void)initialize {
[NSURLCache setSharedURLCache:[[SDURLCache alloc] initWithMemoryCapacity:0
diskCapacity:10*1024*1024
diskPath:[SDURLCache defaultCachePath]]];
}
- (void)setImageFromURL:(NSURL *)url{
/* Put activity indicator */
if(!activityIndicator) {
activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
CGRect frame = [activityIndicator frame];
frame.origin.x = (self.frame.size.width - frame.size.width)/2;
frame.origin.y = (self.frame.size.height - frame.size.height)/2;
activityIndicator.tag = 9999;
activityIndicator.frame = frame;
[self addSubview:activityIndicator];
[activityIndicator startAnimating];
}
/* Cancel previous request */
if(fetchImageConnection) {
[fetchImageConnection cancel];
}
[imageData release];
/* Start new request */
NSURLRequest *req = [NSURLRequest requestWithURL:url
cachePolicy:NSURLRequestReturnCacheDataElseLoad
timeoutInterval:30];
imageData = [NSMutableData new];
fetchImageConnection = [NSURLConnection connectionWithRequest:req
delegate:self];
[fetchImageConnection retain];
}
- (void)setImageFromDisk:(UIImage *)img {
self.image = img;
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[imageData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
if(connection == fetchImageConnection) {
self.image = [UIImage imageWithData:imageData];
[[NSNotificationCenter defaultCenter] postNotificationName:#"imageDownloaded" object:self];
[activityIndicator removeFromSuperview];
[imageData release];
[activityIndicator release];
activityIndicator = nil;
imageData = nil;
fetchImageConnection = nil;
}
[connection release];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
[connection release];
NSLog(#"error: %#", error);
}
#end
Try this code:
NSString *imgString = #"https://www.lockated.com/system/attachfiles/documents/000/002/489/original/ZPMHaJUSjAGnUrVuOmbqoExRMryvcySVOIkJQMivnAntvpmpYd.jpg?1501833649";
NSData *imageData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:imgString]];
accountImageView.image = [UIImage imageWithData: imageData]; // accountImageView is imageView
NSData *receivedData = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://Serverurl/pic007.jpg"]];
self.image=nil;
UIImage *img = [[UIImage alloc] initWithData:receivedData ];
self.image = img;
[img release];
I hope this code will help you!!
I have a UIViewController presented by a navigation controller. This UIViewController loads an image asynchronously as follows :
[self performSelectorInBackground:#selector(downloadData) withObject:nil];
- (void)downloadData {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
LocationDetails * details = [[LondonDatabase database] locationDetails : UniqueID];
NSData * imageData = [NSData dataWithContentsOfURL : [NSURL URLWithString : [details image]]];
picture = [[UIImage alloc ]initWithData:imageData];
[self performSelectorOnMainThread : #selector(updateUI) withObject : nil waitUntilDone : NO];
[pool release];
}
The problem is that if this view controller is popped off the stack while the above method is executing the app crashes with the error :
bool _WebTryThreadLock(bool), 0x61b3950: Tried to obtain the web lock
from a thread other than the main thread or the web thread. This may
be a result of calling to UIKit from a secondary thread. Crashing
now...
Can anyone help ?
Thanks,
Martin
Use an NSThread and keep a handle to that in the view controller object. When the view controller dealloc's, cancel the thread (assuming it has not completed). In downloadData, check that the thread isn't cancelled before trying to access elements of the view controller. That is to say, if the thread has been cancelled, just return. I had a similar issue and this is how I solved it.
Here's the code (which was loading an image into a custom table cell):
-(void)displayImage:(id)context
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
UIImage *image = (UIImage *)context;
[spinner stopAnimating];
brandImage.image = image;
[brandImage setNeedsDisplay];
[pool release];
}
-(void)fetchImage:(id)context
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *url = (NSString *)context;
if ([[NSThread currentThread] isCancelled])
return;
UIImage *image = [ArtCache imageForURL:url];
if (![[NSThread currentThread] isCancelled]) {
[self performSelectorOnMainThread:#selector(displayImage:) withObject:image waitUntilDone:NO];
}
[pool release];
}
-(void)showBrandImageURL:(NSString *)url
{
[spinner startAnimating];
brandImage.image = nil;
if (imageLoadThread) {
[imageLoadThread cancel];
[imageLoadThread release];
}
imageLoadThread = [[NSThread alloc] initWithTarget:self selector:#selector(fetchImage:) object:url];
[imageLoadThread setThreadPriority:0.8];
[imageLoadThread start];
}
These were 3 methods in the custom table cell class. The last one was the one called from cellForRowAtIndexPath:. The other two support the one that is called.
How to call main thread ??? i can parse but i cant display data
- (void)viewDidLoad {
//self.navigationItem.rightBarButtonItem = self.editButtonItem;
self.parentViewController.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"10.png"]];
[super viewDidLoad];
[NSThread detachNewThreadSelector:#selector(startTheBackgroundJob) toTarget:self withObject:nil];
}
- (void)startTheBackgroundJob {
NSUserDefaults *getida = [NSUserDefaults standardUserDefaults];
myIDa = [getida stringForKey:#"AppID"];
NSLog(#"#BOOK MARK ");
NSString *ubook = [[NSString alloc] initWithFormat:#"http://www.wapp=%#&action=show",myIDa];
NSLog(#" bookmark %#",ubook);
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
//NSString *outputString = [[NSString stringWithString:usearch] stringByAppendingString: UserText];
ubook = [ubook stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSLog(#"My string is now = %#", ubook);
NSURL *url = [[[NSURL alloc] initWithString:ubook]autorelease];
//NSURL *url= [NSURL URLWithString:outputString];
NSLog(#" bookmark URL IS %#",url);
NSXMLParser *xmlParser = [[[NSXMLParser alloc] initWithContentsOfURL:url] autorelease];
//Initialize the delegate.
XMLParserbookm *parser = [[[XMLParserbookm alloc] initXMLParser]autorelease];
//Set delegate
[xmlParser setDelegate:parser];
//Start parsing the XML file.
BOOL success = [xmlParser parse];
if(success)
{
NSLog(#" xml parsed suucess");
//[super viewDidLoad];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
//[self searchTableView];
//mytimer4=[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(wipe) userInfo:nil repeats:NO];
}
else{
NSLog(#"eeror");
}
[NSThread sleepForTimeInterval:3];
[self performSelectorOnMainThread:#selector(makeMyProgressBarMoving) withObject:nil waitUntilDone:NO]; // HOW TO CALL MAIN THREAD
[pool release]
}
You can try with
viewDidAppear:, this method is called after you go to a new view. Then at least, you can switch to new view, you should make sure that there is something on the screen in waiting for the xml parsing
Using Thread: You put parsing into another thread and then callback your main thread after you finish, then there will be no block at all