Find out when all processes in (void) is done? - iphone

I need to know how you can find out when all processes (loaded) from a - (void) are done, if it's possible.
Why? I'm loading in data for a UITableView, and I need to know when a Loading... view can be replaced with the UITableView, and when I can start creating the cells.
This is my code:
- (void) reloadData {
NSAutoreleasePool *releasePool = [[NSAutoreleasePool alloc] init];
NSLog(#"Reloading data.");
NSURL *urlPosts = [NSURL URLWithString:[NSString stringWithFormat:#"%#", URL]];
NSError *lookupError = nil;
NSString *data = [[NSString alloc] initWithContentsOfURL:urlPosts encoding:NSUTF8StringEncoding error:&lookupError];
postsData = [data componentsSeparatedByString:#"~"];
[data release], data = nil;
urlPosts = nil;
self.numberOfPosts = [[postsData objectAtIndex:0] intValue];
self.postsArrayID = [[postsData objectAtIndex:1] componentsSeparatedByString:#"#"];
self.postsArrayDate = [[postsData objectAtIndex:2] componentsSeparatedByString:#"#"];
self.postsArrayTitle = [[postsData objectAtIndex:3] componentsSeparatedByString:#"#"];
self.postsArrayComments = [[postsData objectAtIndex:4] componentsSeparatedByString:#"#"];
self.postsArrayImgSrc = [[postsData objectAtIndex:5] componentsSeparatedByString:#"#"];
NSMutableArray *writeToPlist = [NSMutableArray array];
NSMutableArray *writeToNoImagePlist = [NSMutableArray array];
NSMutableArray *imagesStored = [NSMutableArray arrayWithContentsOfFile:[rootPath stringByAppendingPathComponent:#"imagesStored.plist"]];
int loop = 0;
for (NSString *postID in postsArrayID) {
if ([imagesStored containsObject:[NSString stringWithFormat:#"%#.png", postID]]){
NSLog(#"Allready stored, jump to next. ID: %#", postID);
continue;
}
NSLog(#"%#.png", postID);
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[postsArrayImgSrc objectAtIndex:loop]]];
// If image contains anything, set cellImage to image. If image is empty, try one more time or use noImage.png, set in IB
if (imageData == nil){
NSLog(#"imageData is empty before trying .jpeg");
// If image == nil, try to replace .jpg with .jpeg, and if that worked, set cellImage to that image. If that is also nil, use noImage.png, set in IB.
imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[[postsArrayImgSrc objectAtIndex:loop] stringByReplacingOccurrencesOfString:#".jpg" withString:#".jpeg"]]];
}
if (imageData != nil){
NSLog(#"imageData is NOT empty when creating file");
[fileManager createFileAtPath:[rootPath stringByAppendingPathComponent:[NSString stringWithFormat:#"images/%#.png", postID]] contents:imageData attributes:nil];
[writeToPlist addObject:[NSString stringWithFormat:#"%#.png", postID]];
} else {
[writeToNoImagePlist addObject:[NSString stringWithFormat:#"%#", postID]];
}
imageData = nil;
loop++;
NSLog(#"imagePlist: %#\nnoImagePlist: %#", writeToPlist, writeToNoImagePlist);
}
NSMutableArray *writeToAllPlist = [NSMutableArray arrayWithArray:writeToPlist];
[writeToPlist addObjectsFromArray:[NSArray arrayWithContentsOfFile:nowPlist]];
[writeToAllPlist addObjectsFromArray:[NSArray arrayWithContentsOfFile:[rootPath stringByAppendingPathComponent:#"imagesStored.plist"]]];
[writeToNoImagePlist addObjectsFromArray:[NSArray arrayWithContentsOfFile:[rootPath stringByAppendingPathComponent:#"noImage.plist"]]];
[writeToPlist writeToFile:nowPlist atomically:YES];
[writeToAllPlist writeToFile:[rootPath stringByAppendingPathComponent:#"imagesStored.plist"] atomically:YES];
[writeToNoImagePlist writeToFile:[rootPath stringByAppendingPathComponent:#"noImage.plist"] atomically:YES];
[releasePool release];
}

It is as simple as returning a bool at the bottom of the selector being run in the background, and reaload the UITableView.
Thanks to #iWasRobbed:
I have never done this, but just speculating: have you tried returning a BOOL at the very end so that the reloadData function will return TRUE when it gets to that point? I am assuming (possibly incorrectly) that the device serially handles tasks one-at-a-time, so give it a shot.

Related

How to display array of JSON values in custom tableView cells

I want to pass values inside for(NSDictionary *jsonDictionary in myJsonArray) which I get in NSLog to [array addObject:[[SaveList alloc] initWithEmail:email withPhone:phone withDate:date withName:name]];
Code is here
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSError *error = nil;
NSURL *url = [NSURL URLWithString:#" http:// Some url "];
NSString *json = [NSString stringWithContentsOfURL:url encoding:NSASCIIStringEncoding error:&error];
NSLog(#"\n\n JSON : %#, \n Error: %#", json, error);
if(!error)
{
NSData *jsonData = [json dataUsingEncoding:NSASCIIStringEncoding];
NSArray *myJsonArray = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:nil];
// NSDictionary *jsonDictionary = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:&error];
for(NSDictionary *jsonDictionary in myJsonArray)
{
NSLog(#"JSON Dictionary = %#", jsonDictionary);
NSString *name = jsonDictionary[#"Name"];
NSString *date = jsonDictionary[#"Date"];
NSString *email = jsonDictionary[#"Email"];
NSString *phone = jsonDictionary[#"Phone"];
NSLog(#"Name = %#", name);
NSLog(#"Date = %#", date);
NSLog(#"Email = %#", email);
NSLog(#"Phone = %#", phone);
}
}
});
//Table implementation
array = [[NSMutableArray alloc]init];
//**Get email, phone, date, name here**
[array addObject:[[SaveList alloc] initWithEmail:email withPhone:phone withDate:date withName:name]];
self.tableView.dataSource = self;
self.tableView.delegate = self;
Why don't you add the objects as you receive them? Since this block of code will be executed asynchronously you could prepare your array, set your tableview and then execute the block where you fill your array and refresh the tableview.
Something like this:
// Prepare your array
array = [NSMutableArray arrayWithCapacity:0];
// Set your tableview's datasource & delegate
self.tableView.dataSource = self;
self.tableView.delegate = self;
// Fetch data asynchronously
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSError *error = nil;
NSURL *url = [NSURL URLWithString:#" http:// Some url "];
NSString *json = [NSString stringWithContentsOfURL:url encoding:NSASCIIStringEncoding error:&error];
NSLog(#"\n\n JSON : %#, \n Error: %#", json, error);
if(!error)
{
NSData *jsonData = [json dataUsingEncoding:NSASCIIStringEncoding];
NSArray *myJsonArray = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:nil];
NSMutableArray *tmp = [NSMutableArray arrayWithCapacity:myJsonArray.count];
for(NSDictionary *jsonDictionary in myJsonArray)
{
NSString *name = jsonDictionary[#"Name"];
NSString *date = jsonDictionary[#"Date"];
NSString *email = jsonDictionary[#"Email"];
NSString *phone = jsonDictionary[#"Phone"];
//**Get email, phone, date, name here**
[tmp addObject:[[SaveList alloc] initWithEmail:email
withPhone:phone
withDate:date
withName:name]];
}
// Reload your tableview
dispatch_sync(dispatch_get_main_queue(), ^{
array = tmp; // Or add them to your datasource array, whatever suits you...
[self.tableView reloadData];
});
}
});
set number of rows
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
[array count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
SavedList *savedList = [array objectAtIndex:indexPath.row];
cell.text = savedList.name;
}
return cell;
}
I hope it will be helpful.
// U need to reload table view data whenever you add new object in it. It does work like a thread.
dispatch_sync(dispatch_get_main_queue(), ^{
array = myJsonArray;
[self.tableView reloadData];

Some strange error occurs when manipulating events in the calendar of iOS device

I added some events to the calendar and save their eventIdentifier to file. When i want to remove all my events i read the eventIdentifier from that file to an array and remove each event with its event id. Here is the code to add event to calendar and save their event id to file:
- (void) addEventToCalendar: (id)object
{
#autoreleasepool {
int i = 0;
NSString *string_to_file = #"";
eventStore=[[EKEventStore alloc] init];
for(Schedule *sche in scheduleArray){
EKEvent *addEvent=[EKEvent eventWithEventStore:eventStore];
addEvent.title=sche.course_Name;
addEvent.startDate = [self stringToDate:sche.from_Date];
addEvent.endDate = [self stringToDate:sche.thru_Date];
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
[addEvent setCalendar:[eventStore defaultCalendarForNewEvents]];
NSDate *date_alarm = [addEvent.startDate dateByAddingTimeInterval:-(10*60)];
addEvent.alarms=[NSArray arrayWithObject:[EKAlarm alarmWithAbsoluteDate:date_alarm]];
NSError *err;
// do save event to calendar
[eventStore saveEvent:addEvent span:EKSpanThisEvent error:&err];
if (err == nil) {
NSString* str = [[NSString alloc] initWithFormat:#"%#", addEvent.eventIdentifier];
string_to_file = [string_to_file stringByAppendingString:str];
string_to_file = [string_to_file stringByAppendingString:#"\n"];
NSLog(#"String %d: %#",i, str);
}
else {
NSLog(#"Error %#",err);
}
i++;
}
// create file to save
[[NSFileManager defaultManager] createFileAtPath:filePath contents:nil attributes:nil];
inFile = [NSFileHandle fileHandleForWritingAtPath: filePath];
NSData *data = [string_to_file dataUsingEncoding:NSUTF16StringEncoding];
[inFile writeData:data];
}
}
And the code below to remove all events i have added to calendar
- (void) deleteEventInCalender {
filemgr = [NSFileManager defaultManager];
NSString *filePath = [self getFilePath:#"saveeventid.txt"];
NSFileHandle *inFile;
inFile = [NSFileHandle fileHandleForReadingAtPath:filePath];
NSData *dataFile;
dataFile = [inFile readDataToEndOfFile];
NSString *tmp = #"";
NSString *temp = #"";
tmp = [NSString stringWithCharacters:[dataFile bytes] length:[dataFile length]/sizeof(unichar)];
if(![tmp isEqualToString:#""]){
tmp = [tmp substringFromIndex:1];
event_idArray = [[NSMutableArray alloc] init];
int j = 0;
while (![tmp isEqualToString:#""]){
int index_find_string = [tmp rangeOfString:#"\n"].location;
temp = [tmp substringWithRange:NSMakeRange(0, index_find_string)];
[event_idArray addObject:temp];
tmp = [tmp substringFromIndex:index_find_string + 1];
}
EKEventStore* store = [[EKEventStore alloc] init];
j = 0;
for(NSString *eventid in event_idArray){
EKEvent* event2 = [store eventWithIdentifier:eventid];
if (event2 != nil) {
NSLog(#"log: %d log id: %#", j, eventid);
NSError* error = nil;
// remove event
[store removeEvent:event2 span:EKSpanThisEvent error:&error];
}
j++;
}
[filemgr removeItemAtPath:filePath error:nil];
}
}
All codes above work well when i test on the iOS simulator with calendar.sqlitedb. But it makes some strange errors when i run on iPad device 5.0. That is sometime the calendar not remove event or when all events has been remove then after some minutes all events appear again... I don't understand, i don't know why and i very confuse. Does anyone has the same issue with me? Please share your solution!
Added another question: where the calendar database stored in the iOS 5.0 device.

read and write using NSKeyedArchiver, ios

Below is a class to read and write data using nsarchive
Data.m
-(id)init {
self = [super init];
if(self) {
arr = [[NSMutableArray alloc] init];
}
return self;
}
-(NSString *)getPath {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentPath;
if ([paths count] > 0)
documentPath = [paths objectAtIndex:0];
NSString *draftDataPath = [documentPath stringByAppendingPathComponent:#"draftData.dat"];
return draftDataPath;
}
-(void)saveDataToDisk {
NSString *path = [self getPath];
[NSKeyedArchiver archiveRootObject:arr toFile:path];
}
-(void)loadDataFromDisk {
NSString *path = [self getPath];
self.arr = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
}
At later on, I am adding some objects into arr by doing
CustomerClass.m
- (void) viewDidLoad {
Data *data = [[Data alloc] init];
[data.arr addObject:myObject1]
[data.arr addObject:myObject2]
[data.arr addObject:myObject3]
[data saveDataToDisk];
}
At DisplayData.m, I want to check data.arr by
- (void) viewDidLoad {
Data *data = [[Data alloc] init];
[data loadDataFromDisk];
NSLog(#"length of array is %d",[data.arr count]);
}
On the console, I am getting
length of array is 1
I thought it should be 3 after all.
Please point out what I have just made a mistake in the middle of work if you have any clues about it.
So, I suspect that your "myObjects" are not NSCoding compliant. I just did this:
NSMutableArray *arr = [NSMutableArray arrayWithCapacity:3];
[arr addObject:#"Hello"];
[arr addObject:#" "];
[arr addObject:#"World"];
BOOL ret = [NSKeyedArchiver archiveRootObject:arr toFile:[self getPath]];
NSArray *arr2 = [NSKeyedUnarchiver unarchiveObjectWithFile:[self getPath]];
NSLog(#"count = %d", [arr2 count]);
And the results was "count = 3"
I feel like there's too much code here to do what you're looking for. I think all you need is:
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:dataClass] forKey:NSUserDefaultString];
[[NSUserDefaults standardUserDefaults] synchronize];
to save it.
And:
NSData *someData = [[NSUserDefaults standardUserDefaults] objectForKey:NSUserDefaultString];
if (settingsData != nil)
{
dataClass = [NSKeyedUnarchiver unarchiveObjectWithData:settingsData];
}
to retrieve it.

Not able to resolve a leak detected by leaks tool. Can i ignore it?

I am not able to find a way to remove a leak detected by leaks tool.
Here is my problem...
I have a singleton object in my delegate which stores data on a global level. Now, I have a array of objects which i maintain here and add or modify it from controllers.
Below is a function which fills the objects and sets the above global array,
Now, the highlighted lines(marked by //LEAK) are where the leaks tool tell me its a leak. I require this array for my session. I release the array at the end when i logout.
Should i be worried about this kind leak?
-(LayoutInfo *) fillLayout: (GDataXMLElement *) layoutElement {
LayoutInfo *layout = [[LayoutInfo alloc] init];
layout.dataTableCount = 0;
layout.chartsCount = 0;
NSArray *templateNameArr = [layoutElement elementsForName:#"TemplateName"];
NSMutableArray *chartElements = [[NSMutableArray alloc] init]; // LEAK
NSMutableArray *dtElements = [[NSMutableArray alloc] init];
NSArray *charts = [layoutElement elementsForName:#"chart"]; // LEAK
if (charts.count > 0) {
for (GDataXMLElement *singleChart in charts) {
chart *chartInfo = [[chart alloc] init]; // LEAK
layout.chartsCount = layout.chartsCount + 1;
NSArray *imageName = [singleChart elementsForName:#"imageName"];
if (imageName.count > 0) {
GDataXMLElement *imageNameStr = (GDataXMLElement *) [imageName objectAtIndex:0];
chartInfo.imageName = imageNameStr.stringValue; // LEAK
}
NSArray *imagePath = [singleChart elementsForName:#"imagePath"];
if (imagePath.count > 0) {
GDataXMLElement *imagePathStr = (GDataXMLElement *) [imagePath objectAtIndex:0];
chartInfo.imagePath = imagePathStr.stringValue; // LEAK
}
NSArray *imageFileName = [singleChart elementsForName:#"imageFileName"];
if (imageFileName.count > 0) {
GDataXMLElement *imageFileNameStr = (GDataXMLElement *) [imageFileName objectAtIndex:0];
chartInfo.imageFileName = imageFileNameStr.stringValue;
}
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:chartInfo.imagePath]];
[request setDownloadDestinationPath:[[NSHomeDirectory() stringByAppendingPathComponent:#"Documents"]
stringByAppendingPathComponent:chartInfo.imageFileName]];
[request setDidFinishSelector:#selector(fillLayout_requestDone:)];
[request setDidFailSelector:#selector(fillLayout_requestWentWrong:)];
[request startSynchronous];
NSString *imagePath1 = [[NSHomeDirectory() stringByAppendingPathComponent:#"Documents"] stringByAppendingPathComponent:chartInfo.imageFileName];
if([[NSFileManager defaultManager] fileExistsAtPath:imagePath1]) {
NSLog(#" --- IMAGE SAVED -- %#", imagePath1);
}
[chartElements addObject:chartInfo];
} //for
layout.chartElement = chartElements; // THIS IS WHERE I ASSIGN THE GLOBAL ARRAY
//[chartElements release];
}
return layout;
}
-(LayoutInfo *) fillLayout: (GDataXMLElement *) layoutElement {
LayoutInfo *layout = [[LayoutInfo alloc] init];
layout.dataTableCount = 0;
layout.chartsCount = 0;
NSArray *templateNameArr = [layoutElement elementsForName:#"TemplateName"];
NSMutableArray *chartElements = [[NSMutableArray alloc] init]; // LEAK
//NSMutableArray *dtElements = [[NSMutableArray alloc] init];
NSArray *charts = [layoutElement elementsForName:#"chart"]; // LEAK
if (charts.count > 0) {
for (GDataXMLElement *singleChart in charts) {
chart *chartInfo = [[chart alloc] init]; // LEAK
layout.chartsCount = layout.chartsCount + 1;
NSArray *imageName = [singleChart elementsForName:#"imageName"];
if (imageName.count > 0) {
GDataXMLElement *imageNameStr = (GDataXMLElement *) [imageName objectAtIndex:0];
chartInfo.imageName = imageNameStr.stringValue; // LEAK
}
NSArray *imagePath = [singleChart elementsForName:#"imagePath"];
if (imagePath.count > 0) {
GDataXMLElement *imagePathStr = (GDataXMLElement *) [imagePath objectAtIndex:0];
chartInfo.imagePath = imagePathStr.stringValue; // LEAK
}
NSArray *imageFileName = [singleChart elementsForName:#"imageFileName"];
if (imageFileName.count > 0) {
GDataXMLElement *imageFileNameStr = (GDataXMLElement *) [imageFileName objectAtIndex:0];
chartInfo.imageFileName = imageFileNameStr.stringValue;
}
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:chartInfo.imagePath]];
[request setDownloadDestinationPath:[[NSHomeDirectory() stringByAppendingPathComponent:#"Documents"]
stringByAppendingPathComponent:chartInfo.imageFileName]];
[request setDidFinishSelector:#selector(fillLayout_requestDone:)];
[request setDidFailSelector:#selector(fillLayout_requestWentWrong:)];
[request startSynchronous];
NSString *imagePath1 = [[NSHomeDirectory() stringByAppendingPathComponent:#"Documents"] stringByAppendingPathComponent:chartInfo.imageFileName];
if([[NSFileManager defaultManager] fileExistsAtPath:imagePath1]) {
NSLog(#" --- IMAGE SAVED -- %#", imagePath1);
}
[chartElements addObject:chartInfo];
[chartInfo release]; // it's retained in chartElements until removed, or until chartElements is deallocced
} //for
if(layout.charElement){
[layout.charElement release]; // you should however consider in making charElement property as retain;
layout.charElement = nil; // this isn't required here (since you're assigning it a new value), but you should usually set it to nil after a release to prevent EXC_BADACCESS
}
layout.chartElement = chartElements; // THIS IS WHERE I ASSIGN THE GLOBAL ARRAY
//[chartElements release];
}
return [layout autorelease]; // in case you don't want it autoreleased you should call your method something like: createFilledLayout ('create' is usually used so anyone that uses the method knows it's responsible for releasing the return value)
}
you should have a look at Memory Management Programming Guide

iPhone: Memory leak when using NSOperationQueue

I'm sitting here for at least half an hour to find a memory leak in my code.
I just replaced an synchronous call to a (touch) method with an asynchronous one using NSOperationQueue.
The Leak Inspector reports a memory leak after I did the change to the code.
What's wrong with the version using NSOperationQueue?
Version without a MemoryLeak
-(NSData *)dataForKey:(NSString*)ressourceId_
{
NSString *cacheKey = [self cacheKeyForRessource:ressourceId_]; // returns an autoreleased NSString*
NSString *path = [self cachePathForKey:cacheKey]; // returns an autoreleased NSString*
NSData *data = [[self memoryCache] objectForKey:cacheKey];
if (!data)
{
data = [self loadData:path]; // returns an autoreleased NSData*
if (data)
{
[[self memoryCache] setObject:data forKey:cacheKey];
}
}
[[self touch:path];
return data;
}
Version with a MemoryLeak (I do not see any)
-(NSData *)dataForKey:(NSString*)ressourceId_
{
NSString *cacheKey = [self cacheKeyForRessource:ressourceId_]; // returns an autoreleased NSString*
NSString *path = [self cachePathForKey:cacheKey]; // returns an autoreleased NSString*
NSData *data = [[self memoryCache] objectForKey:cacheKey];
if (!data)
{
data = [self loadData:path]; // returns an autoreleased NSData*
if (data)
{
[[self memoryCache] setObject:data forKey:cacheKey];
}
}
NSInvocationOperation *touchOp = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(touch:) object:path];
[[self operationQueue] addOperation:touchOp];
[touchOp release];
return data;
}
And of course, the touch method does nothing special too. Just change the date of the file.
-(void)touch:(id)path_
{
NSString *path = (NSString *)path_;
NSFileManager *fm = [NSFileManager defaultManager];
if ([fm fileExistsAtPath:path])
{
NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:[NSDate date], NSFileModificationDate, nil];
[fm setAttributes: attributes ofItemAtPath:path error:nil];
}
}