When I use static data there is no problem but I use dynamic data with Web services there is problem (table view scrolling cause crash program) why? If I comment these lines add static data it works;
//tempCs is NSDictionary
tempDc = [arrHaberler objectAtIndex:indexPath.row];
cell.textLabel.text = [tempDc valueForKey:#"short_header"];
NSData *imgData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:[tempDc valueForKey:#"iphone_src"]]];
UIImage *myImage = [[[UIImage alloc] initWithData:imgData] autorelease];
cell.imageView.image = myImage;
You don't release the imgData. You'd want to do that after creating the UIImage.
Other than that, from your description, maybe the numbersOfRowsInSection method has an error?
EDIT (after discussion):
(crash due to unrecognized selector (ie method from NSArray) sent to instance of NSString)
There are many ways you can come to this state, including accessing some memory that was released and reused (ie missing a retain), or overwriting an array with a string due to some parsing yielding a wrong result.
I try something with made an comment another lines and problem is on this line:
tempDc = [arrHaberler objectAtIndex:indexPath.row];
I change "indexPath.row" to "0" still cause crash... Problem about when is assign data to NSDictionary
There is no null-checking (setting NSData *imgData and setting UIImage *myImage) and there in a synchronous call to server. fmpov, problem is out there.
#VNevzatR i am just asking you that Are you are calling plenty no of images from somewhere....because this is no the problem you UITableView as you said working fine in static data,the problem is some where else, so if you are calling plenty of images at a time....Try doing this way...release that pool.
-(void) parseImages
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//Fetch your images here
[self performSelectorOnMainThread:#selector(Done) withObject:nil waitUntilDone:YES];
[pool release];
}
-(void) Done {
[self.tableView reloadData];
}
Hope it will resolve your problem...Good Luck
Related
I am developing an application in which I have a display a lot of images in my table view.These images has to come from server, so I have create another thread in which the image get processed and then set on table view cell to make our table view scrolls smoothly and properly.
My problem is when I scrolls my table view to a number of times, the app get freezes and after some time the xcode shows the message shown in below image:-
My table view cell code :-
id object = [imageDictFunctionApp objectForKey:[NSString stringWithFormat:#"%d",functionAppButton.tag]];
if(!object){
NSArray *catdictObject=[NSArray arrayWithObjects:[NSNumber numberWithInt:functionAppButton.tag],indexPath,[NSNumber numberWithInt:i],nil];
NSArray *catdictKey=[NSArray arrayWithObjects:#"btn",#"indexPath",#"no",nil];
NSDictionary *catPassdict=[NSDictionary dictionaryWithObjects:catdictObject forKeys:catdictKey];
[NSThread detachNewThreadSelector:#selector(displayingSmallImageForFunctionsApps:) toTarget:self withObject:catPassdict];
}
else
{
if(![object isKindOfClass:[NSNull class]]){
UIImage *img = (UIImage *)object;
[functionAppButton setImage:img forState:UIControlStateNormal];
}
-(void)displayingSmallImageForFunctionsApps:(NSDictionary *)dict
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSIndexPath *path=[dict objectForKey:#"indexPath"];
NSArray *catArray=[self.functionDataDictionary objectForKey:[self.functionsKeysArray objectAtIndex:path.row]];
int vlaue=[[dict objectForKey:#"no"] intValue];
NSDictionary *dict1=[catArray objectAtIndex:vlaue];
NSString *urlString=[dict1 objectForKey:#"artwork_url_large"];
NSURL *url = [NSURL URLWithString:urlString];
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];
if(image){
[imageDictFunctionApp setObject:image forKey:[NSString stringWithFormat:#"%d",[[dict objectForKey:#"btn"] intValue]]];
NSMutableDictionary *dict2=[NSMutableDictionary dictionaryWithCapacity:4];
[dict2 setObject:image forKey:#"imageValue"];
[dict2 setObject:[dict objectForKey:#"btn"] forKey:#"tag"];
[dict2 setObject:[dict objectForKey:#"indexPath"] forKey:#"indexPath"];
[self performSelectorOnMainThread:#selector(setImageInCellDictCategroryTable:) withObject:dict2 waitUntilDone:NO];
}
else{
[imageDictFunctionApp setObject:[NSNull null] forKey:[NSString stringWithFormat:#"%d",[[dict objectForKey:#"btn"] intValue]]];
}
[pool drain];
}
- (void)setImageInCellDictCategroryTable:(NSDictionary *)dict{
UITableViewCell *myCell = (UITableViewCell *)[categoriesAndFunctionsTableView cellForRowAtIndexPath:[dict objectForKey:#"indexPath"]];
UIButton *myBtn = (CustomUIButton *)[myCell viewWithTag:[[dict objectForKey:#"tag"] intValue]];
if ([dict objectForKey:#"imageValue"]) {
[myBtn setImage:[dict objectForKey:#"imageValue"] forState:UIControlStateNormal];
}
}
I have posted all my code that might be linked with this error.Please anyone suggest me how to solve this issue.
Thanks in advance!
I would suggest not to use threads and move you code to GCD, looks like what you want to use is a serial queue.
So what I would guess is happening is that you are running out of Mach ports. It looks to me like you are starting a thread for every single cell in your table and then they are all trying to schedule tasks to run on the main runloop when they are done. This is going to stress your system.
I would create an NSOperation for each image and schedule them all on the same NSOperationQueue. The runtime will use a pool of threads tuned to the specific system to run all of the operations.
For a simple thing like this, you can also use GCD as Oscar says, but I recently read on the Apple list that NSOperationQueue is preferred because it is higher level. It gives you more options for controlling what happens to your background tasks.
#implementation SlideShowViewController
- (id)init
{
NSString *temp = [NSString alloc];
[temp stringwithString:#"http://www.inetwallpaper.com/homescreenhero/sunsets/wall009.jpg"];
temp=[(NSString *)CFURLCreateStringByAddingPercentEscapes(
nil,
(CFStringRef)temp,
NULL,
NULL,
kCFStringEncodingUTF8)
autorelease];
NSData *dato = [NSData alloc];
dato=[NSData dataWithContentsOfURL:[NSURL URLWithString:temp]];
if (self = [super initWithNibName:nil bundle:nil])
{
NSArray * images = [NSArray arrayWithObjects:[UIImage imageWithData:dato],[UIImage imageWithData:dato], [UIImage imageWithData:dato], [UIImage imageWithData:dato], [UIImage imageWithData:dato], nil];
self.view = [[[SlideShowView alloc] initWithImages:images] autorelease];
}
return self;
}
I used the following code to load images from the server and view it as that of a photo album
But when the code is run it gets crashed
the error message in console is as follows
2011-06-24 23:54:01.837
SlideShow[13654:207] *
-[NSPlaceholderString stringwithString:]: unrecognized
selector sent to instance 0x49117e0
2011-06-24 23:54:01.839
SlideShow[13654:207] Terminating
app due to uncaught exception
'NSInvalidArgumentException', reason:
'** -[NSPlaceholderString
stringwithString:]: unrecognized
selector sent to instance 0x49117e0'
2011-06-24 23:54:01.840
SlideShow[13654:207] Stack: (
42178640,
43336492,
42187355,
41649782,
41646578,
12567,
7791,
2906510,
2910543,
2936126,
2917623,
2949592,
51171708,
41457820,
41453736,
2908705 ) terminate called after throwing an instance of 'NSException'
it works if the URLS are replaced by the images
could any one help me
I'm a beginner so its hard for me to find it out
thanks
You are trying to call an NSString class method with an instance (an incorrectly created instance at that) here
NSString *temp = [NSString alloc];
[temp stringwithString:#"http://www.inetwallpaper.com/homescreenhero/sunsets/wall009.jpg"];
Change to
NSString *temp = #"http://www.inetwallpaper.com/homescreenhero/sunsets/wall009.jpg";
EDIT:
You are doing several things wrong like calling alloc on things and then setting them to something else. (*temp and *data) when you alloc something it should always be followed with a call to init or initXXXX. Next you do not even need those alloc calls because you are setting the pointer to something else on the line right beneath it which causes a memory leak.
This is all you need
NSData *dato = [NSData dataWithContentsOfURL:[NSURL URLWithString:temp]];
Then you are creating a bunch of images with the same data object. You are also blocking the calling thread while you are downloading the image which should done later probably around time of viewDidLoad asynchronously.
The init function of the view controller is not the place for setting the view. Implement loadView and the system will call it when it is needed to minimize the applications memory footprint.
I've just started using threads and I'm trying to get better performance from my TableView in my app. Each tableViewCell has an imageView and the image is loaded from disk when the tableViewCell is created. I want to load the Image on a differant thread, then set the UIImageView on the main thread. My question is, can a method that is being ran on another thread return a value to the main thread? Is there a better approach for doin this?
Thanks for any help in advance.
maybe something like this, assuming your icons are in the document's directory:
#define DOCUMENTS_DIRECTORY [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]
//inside - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
NSDictionary *d = [NSDictionary dictionaryWithObjectsAndKeys:indexPath, #"indexPath", #"image1.png", #"imageName", nil];
[NSThread detachNewThreadSelector:#selector(loadIcon:) toTarget:self withObject:d];
//
- (void)iconLoaded:(NSDictionary*)dict {
[icons replaceObjectAtIndex:[[dict objectForKey:#"index"] intValue] withObject:[UIImage imageWithData:[dict objectForKey:#"imageData"]]];
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[dict objectForKey:#"indexPath"]]];
}
- (void)loadIcon:(NSDictionary*)dict {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *filePath = [DOCUMENTS_DIRECTORY stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.jpg", [dict objectForKey:#"imageName"]]];
NSData *imageData = [NSData dataWithContentsOfFile:filePath];
[self performSelectorOnMainThread:#selector(iconLoaded:) withObject:[NSDictionary dictionaryWithObjectsAndKeys:[dict objectForKey:#"indexPath"], "indexPath", imageData, #"imageData", nil] waitUntilDone:YES];
[pool drain];
}
you will need to keep track of which cells you are loading an image for, so you dont try to load one while it is already loading it. there may be some small syntax errors as i did not compile this, just wrote it freehand.
icons is an NSMutableArray holding a UIImage for each cell
Try this:
https://github.com/foursquare/fully-loaded
Yes, you can pass data to the main thread. See the following method in NSObject API docs:
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait
You get to pass a single object reference in the arg parameter.
I've got a thread (specifically an NSOperation) that runs to load some images for me for a scroll view when my main view asks for them. Any number of these NSOperations can be queued at once. So it goes with the filepath I give it and loads the image from the disk (as UIImages) and then sends the object back to my mainview by using performSelectorOnMainThread: and passing my mainview an NSDictionary of the object, and an image ID value. My main view is then supposed to insert the image object and the image ID string into an NSMutableDictionary that it has for the mainview to be able to use. I've verified that the NSMutableDictionary is allocated and initialized fine, but when the method the NSOperation calls tries to add the objects to the dictionary nothing happens. I've verified that the object and string i get from the dictionary the thread sent me are not null or anything but yet it doesn't work. Am I not doing something right or using a bad technique? What would anyone suggest to do in a situation like this where I need to add UIImages to an NSMutableDictionary from a thread? Thanks so much!
Here's the NSOperation code I use:
- (void)main {
NSString *filePath = [applicaitonAPI getFilePathForCachedImageWithID:imageID andSize:imageSize];
UIImage *returnImage = [[UIImage alloc] initWithContentsOfFile:filePath];
if (returnImage) {
NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithCapacity:2];
[dict setObject:returnImage forKey:#"IMAGE"];
[dict setValue:[NSString stringWithFormat:#"%d", imageID] forKey:#"IMAGE_ID"];
NSDictionary *returnDict = [[NSDictionary alloc] initWithDictionary:dict];
[dict release];
[mainViewController performSelectorOnMainThread:#selector(imageLoaderLoadedImage:) withObject:returnDict waitUntilDone:NO];
[returnDict release];
}
}
And here's the method on the main thread:
- (void)imageLoaderLoadedImage:(NSDictionary *)dict {
UIImage *loadedImage = [dict objectForKey:#"IMAGE"];
NSString *loadedImage = [dict valueForKey:#"IMAGE_ID"];
[imagesInMemoryDictionary setObject:loadedImage forKey:loadedImageID];
[self drawItemsToScrollView];
}
[mainViewController performSelectorOnMainThread:#selector(imageLoaderLoadedImage:) withObject:nil waitUntilDone:NO];
You're not passing returnDict as the parameter to the method. You're passing nil.
A couple of other thoughts:
you don't need to create returnDict. You can just use dict as the method parameter.
you're leaking returnImage.
edit
Since you apparently are passing returnDict as the parameter to the method, my other guess would be that mainViewController is nil. Other than that, your code looks functional.
I have a strange issue, when it comes to parsing XML with NSXMLParser on the iPhone. When starting the app, I want to preload 4 table-views, that are populated by RSS-Feeds in the background.
When I init the table-views one-by-one, than loading, parsing and displaying all works like a charm. But when I try to init all view at once (at the same time), than it seems, that the XML-parser-instances are disturbing each other. Somehow data from one XML-Feed are "broadcasted" into other xml-parser instances, where they do not belong. Example: there is a "teammember" item, with "This is my name". When this bug occurs, there is a string from another xml-feed added, i.e. resulting in: "This is my name58", where 58 is the chart-position of something from the other view. "58" seems to miss then on the other instance.
It looks to me, that this bug occurs because of the NSXMLParser-delegate method:
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if (!currentStringValue) {
currentStringValue = [[NSMutableString alloc] initWithCapacity:50];
}
[currentStringValue appendString:string];
}
In this case "by coincidence" bytes are appended to strings, where they do not belong to.
The strange thing is, that every instance of NSXMLParser is unique, got its own unique delegates, that are attached to their own ViewController. Every parsing-requests spawns it own background-task, with its own (also also unique named) Autorelease-pool.
I am calling the NSXMLParser like this in the ViewController:
// prepare XML saving and parsing
currentStringValue = [[[NSMutableString alloc] initWithCapacity:50] retain];
charts = [[NSMutableArray alloc] init];
NSURL *url = [[NSURL alloc] initWithString:#"http://(SOME XML URL)"];
xmlParser = [[[NSXMLParser alloc] initWithContentsOfURL:url] retain];
//Set delegate
[xmlParser setDelegate:self];
//loading indicator
progressWheel = [[[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(150.0,170.0,20.0,20.0)] autorelease];
progressWheel.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray;
[self.view addSubview:progressWheel];
[progressWheel startAnimating];
// start loading and parsing the xml-feed in the background
//[self performSelectorInBackground:#selector(parse:) withObject:xmlParser]; -> I also tried this
[NSThread detachNewThreadSelector:#selector(parse:) toTarget:self withObject:xmlParser];
And this is one of the background-tasks, parsing the feed:
-(void)parse:(NSXMLParser*)myParser {
NSAutoreleasePool *schedulePool = [[NSAutoreleasePool alloc] init];
BOOL success = [myParser parse];
if(success) {
NSLog(#"No Errors. xmlParser got: %#", myParser);
(POST-PROCESSING DETAILS OF THE DATA RETURNED)
[self.tableView reloadData];
} else {
NSLog(#"Couldn't initalize XMLparser");
}
[progressWheel stopAnimating];
[schedulePool drain];
[myParser release];
}
What could cause this issue? Am I calling the background-task in the right way? Why is this bug approaching, since every XML-Parser got its own, unique instance?
You should not be updating UI elements (like progressWheel) from inside a background thread. UI updates should be done on the main thread.
Use -performSelectorOnMainThread:withObject:waitUntilDone: to update UI elements from within a background thread.
I've released an open source RSS/Atom Parser for iPhone and it makes reading and parsing web feeds extremely easy.
You can set it to download the data asynchronously, or you could run it in a background thread synchronously to collect the feed data.
Hope this helps!