I have problem with the following code leaking memory...
#property (nonatomic, retain) NSMutableArray *childrensArray;
-(void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(#"Connection finished loading.");
// Dismiss the network indicator when connection finished loading
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
// Parse the responseData of json objects retrieved from the service
SBJSON *parser = [[SBJSON alloc] init];
NSString *jsonString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
NSDictionary *jsonData = [parser objectWithString:jsonString error:nil];
childrensArray = [jsonData objectForKey:#"Children"];
// Callback to AttendanceReportViewController that the responseData finished loading
[attendanceReportViewController loadChildren];
[connection release];
[responseData release];
[jsonString release];
[parser release];
}
In the viewController the following also leaks memory...
#property (nonatomic, retain) NSMutableArray *childrensArray;
- (void)loadChildren {
// Retrieve a array with dictionaries of children from ServiceGetChildren
self.childrensArray = [[serviceGetChildren.childrensArray copy] autorelease];
int total = [childrensArray count];
totalLabel.text = [NSString stringWithFormat:#"%d", total];
[theTableView reloadData];
}
You only release childrensArray when the instance is deallocated. You should also release the instance variable before setting it:
- (void)loadChildren {
// Retrieve a array with dictionaries of children from ServiceGetChildren
[childrensArray release];
childrensArray = [serviceGetChildren.childrensArray copy];
}
A better way would be to actually use your property:
- (void)loadChildren {
// Retrieve a array with dictionaries of children from ServiceGetChildren
self.childrensArray = [[serviceGetChildren.childrensArray copy] autorelease];
}
(Note the autorelease)
This has the benefit of triggering KVO-Notifications should you ever use them.
Related
I have a json http://gdata.youtube.com/feeds/api/users/ruflixnet/playlists?v=2&alt=jsonc
and i want to get id,title and size, but don't now how to do that.
i recommend JSONKit (look at the Benchmarks)
A Very High Performance Objective-C JSON Library
Parsing
Use the SBJSON libraries for example, IVe been using them all time and they are pretty good.
You have a tutorial here:
http://blog.zachwaugh.com/post/309924609/how-to-use-json-in-cocoaobjective-c
download the JSON library and include that in your project than make this following class for your data
Make NSObject class
ObjectData.h
#interface ObjectData : NSObject {
NSString *id;
NSString *title;
NSString *size;
}
#property(nonatomic,retain) NSString *id;
#property(nonatomic,retain) NSString *title;
#property(nonatomic,retain) NSString *size;
#end
ObjectData.M
#import ObjectData.h
#implementation ObjectData
#synthesize id;
#synthesize title;
#synthesize size;
#end
Make another DataController Class
#import <Foundation/Foundation.h>
#interface DataController : NSObject {
}
+ (id)staticVersion;
- (NSMutableArray *) startParsing:(NSString *)theURLString;
end
#import "DataController.h"
#implementation DataController
DataController *theInstance;
+(id)staticVersion
{
if(!theInstance){
theInstance = [[DataController alloc] init];
}
return theInstance;
}
- (NSMutableArray *) startParsing:(NSString *)theURLString {
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"%#",theURLString]];
NSString *fileContent= [NSString stringWithContentsOfURL:url];
SBJSON *parser = [[SBJSON alloc] init];
NSDictionary *data = (NSDictionary *) [parser objectWithString:fileContent error:nil];
NSArray *items = (NSArray *) data ;
return items;
}
#end
And in View Did load
NSArray *tempArray =[[DataController staticVersion] startParsing:serverName];
for (int i = 0; i<[tempArray count]; i++) {
id *item = [tempArray objectAtIndex:i];
NSDictionary *dict = (NSDictionary *) item;
ObjectData *theObject =[[ObjectData alloc] init];
[theObject setid:[dict objectForKey:#"id"]];
[theObject settitle:[dict objectForKey:#"title"]];
[theObject setsize:[dict objectForKey:#"size"]];
[resultArray addObject:theObject];
[theObject release];
theObject=nil;
At last make preferenc.h file and give the your json data path
#import <Foundation/Foundation.h>
#define serverName #"http://gdata.youtube.com/feeds/api/users/ruflixnet/playlists?v=2&alt=jsonc"
hope this works fine
use SBJson
SBJsonParser *parser = [[SBJsonParser alloc] init];
NSMutableDictionary *dicRes = [parser objectWithString:stringFromServer error:nil];
I am stuck with something crazy. I used ASIHTTPRequest to receive my data from a web service and everything worked fine. I switched to using a NSURLConnection and I am receiving the same data and parsing it the same way but my code won't recognize the data with the NSURLConnection.
Here is the data I am receiving (from NSLog)
Did receive data: {"d":"[{\"id\":1.0,\"Category\":1,\"hPlan\":0.0,\"Tip\":\"It takes 3500
calories to gain a pound. If you want to lose a pound per week, reduce your calorie
intake by 250 calories and incorporate daily physical activity that will burn 250
calories.\",\"TipDate\":\"2012-05-12T00:00:00\",\"TimeStamp\":\"AAAAAAAAB9I=\"}]"}
2012-06-06 09:42:11.809 StaticTable[27488:f803] Jsson Array: 0
2012-06-06 09:42:11.809 StaticTable[27488:f803] Jsson Array: (null)
Code:
#import "UYLFirstViewController.h"
#import "MBProgressHUD.h"
#import "JSON.h"
#interface UYLFirstViewController ()
#end
#implementation UYLFirstViewController
#pragma mark -
#pragma mark === UIViewController ===
#pragma mark -
#synthesize MessageField;
#synthesize jsonArray = _jsonArray;
#synthesize TipLabelField;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.title = NSLocalizedString(#"Tickle!", #"Tickle!");
self.tabBarItem.image = [UIImage imageNamed:#"heart_plus"];
[self GetTipOfDay];
}
return self;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
-(BOOL)GetTipOfDay{
NSDate *date = [NSDate date];
NSDateFormatter *dateFormat = [[NSDateFormatter alloc]init];
[dateFormat setDateFormat:#"EEEE MMMM d, YYYY"];
NSString *dateString = [dateFormat stringFromDate:date];
NSString *yourOriginalString = #"Tip of the Day for ";
yourOriginalString = [yourOriginalString stringByAppendingString:dateString];
TipLabelField.text = yourOriginalString;
NSURL *url = [NSURL URLWithString:#"http://www.mysite.com/api/GetHealth.asmx/getTipOfDay"];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
[request setHTTPMethod:#"GET"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[NSURLConnection connectionWithRequest:request delegate:self];
// Clear text field
MessageField.text = #"";
// Start hud
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.labelText = #"Gathering Tip of the Day...";
return TRUE;
}
- (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[MBProgressHUD hideHUDForView:self.view animated:YES];
NSLog(#"Did receive data: %#", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
NSDictionary *responseDict = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] JSONValue];
NSString *jsonResponse = [responseDict objectForKey:#"d"];
self.jsonArray = [jsonResponse JSONValue];
NSLog(#"Jsson Array: %d", [jsonArray count]);
NSLog(#"Jsson Array: %#", jsonArray);
NSEnumerator *myEnumerator;
myEnumerator = [jsonArray objectEnumerator];
int i;
i=0;
id myObject;
while (myObject = [myEnumerator nextObject])
{
NSDictionary *itemAtIndex = (NSDictionary *)[self.jsonArray objectAtIndex:i];
NSLog(#"Checking for games");
NSString *myCheck = [itemAtIndex objectForKey:#"FName"];
if ([myCheck length] != 0)
{
// NSLog(myCheck);
MessageField.text = myCheck;
}
}
}
- (void)viewDidUnload {
[self setMessageField:nil];
[self setTipLabelField:nil];
[super viewDidUnload];
}
#end
#import <UIKit/UIKit.h>
#interface UYLFirstViewController : UIViewController{
NSMutableArray *jsonArray;
}
#property (weak, nonatomic) IBOutlet UILabel *MessageField;
#property (weak, nonatomic) NSMutableArray *jsonArray;
#property (weak, nonatomic) IBOutlet UILabel *TipLabelField;
-(BOOL)GetTipOfDay;
#end
-didRecieveData can be called multiple times as the bytes and chunks come in. You should move your logic to -connectionDidFinishLoading. This will let you know when the connection is completely done and the data is ready to be parsed.
You're only implementing one of the NSURLConnectionDelegate methods. Try adding this
- (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
//set up *receivedMutableString as instance variable in .h
if (!receivedMutableString) {
self.receivedMutableString = [[NSMutableString alloc] initWithData:data encoding:NSUTF8StringEncoding];
} else {
NSString *dataString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
[receivedMutableString appendString:dataString];
}
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
//Now receivedMutableString contains all json data
...continue with your code
}
NSURLConnection is a bit of overkill if you're just doing a simple GET request (and you're developing for an iOS version that supports blocks). You can do this in a dispatch_async block:
- (void) getData
{
dispatch_async(<some_queue>, ^{
NSError * error = nil;
NSString * response = [NSString stringWithContentsOfURL: stringWithContentsOfURL: requestUrl error: &error];
// process JSON
dispatch_async(dispatch_get_main_queue(), ^{
// Update UI on main thread
}
});
}
As you can see from my example code, you can also perform your JSON processing on the background queue (provided the method you're calling is thread safe). Just pass back to the main queue to update the UI.
Seems like the issue had nothing to do with fetching from the webservice. I had to define my array as __strong. Thanks for all the help. I did get some good ideas on how to do things better.
I'm going through the Beginning iOS 5 example and trying to tweak it for my usage. I have a DataManager singleton object that has properties of:
#property (nonatomic, strong) NSArray *frequencyArray;
#property (nonatomic, strong) NSMutableArray *networkArray;
As I'm using ARC, my singleton looks like:
- (id)init {
if (self = [super init]) {
_networkArray = [[NSMutableArray alloc] init];
}
return self;
}
+ (id)sharedInstance {
if (sharedDmgr == nil) {
sharedDmgr = [[super allocWithZone:NULL] init];
}
return sharedDmgr;
}
+ (id)allocWithZone:(NSZone *)zone {
return [self sharedInstance];
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
I followed the example code to conform this class to NSCoding and NSCopying. I created two methods in this class to archive itself, and unarchive itself. Those are:
- (NSString *)dataFilePath {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
return [documentsDirectory stringByAppendingFormat:ARCHIVE_FILE_NAME];
}
- (void)archiveAppData {
NSMutableData *data = [[NSMutableData alloc] init];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[archiver encodeObject:self forKey:ARCHIVE_DATA];
// [archiver encodeObject:_frequencyArray forKey:DMGR_FREQUENCY_ARRAY];
// [archiver encodeObject:_networkArray forKey:DMGR_NETWORK_ARRAY];
[archiver finishEncoding];
// BOOL success = [data writeToFile:[self dataFilePath] atomically:YES];
NSError *error;
BOOL success = [data writeToFile:[self dataFilePath] options:NSDataWritingAtomic error:&error];
if (!success) {
NSLog(#"Could not write file %#", [error description]);
}
}
- (void)unarchiveAppData {
NSData *data = [[NSMutableData alloc] initWithContentsOfFile:[self dataFilePath]];
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
sharedDmgr = [unarchiver decodeObjectForKey:ARCHIVE_DATA];
// self.frequencyArray = [unarchiver decodeObjectForKey:DMGR_FREQUENCY_ARRAY];
// self.networkArray = [unarchiver decodeObjectForKey:DMGR_NETWORK_ARRAY];
[unarchiver finishDecoding];
}
I get the error:
Could not write file Error Domain=NSCocoaErrorDomain Code=513 "The
operation couldn’t be completed. (Cocoa error 513.) "The operation
couldn’t be completed. Operation not permitted"}
which I googled and saw that it's referring to modifying the bundle. But I don't see where I'm doing that since I thought I was writing to the documents directoroy with my [self dataFilePath] code.
When my application resigns active, I call the archiveAppData method. When my first viewController loads, I call the unarchiveAppData. As you can see from my commented out code, I tried to just encode those two arrays instead, and then decode those as opposed to the DataManager object itself, but that didn't seem to work either. Any thoughts on what I'm doing wrong? Thanks.
I am developing my first iphone 'Diary' app, which uses custom 'Entry' objects that hold an NSString title, NSString text and NSDate creationDate. When I try to archive an NSMutableArray of Entry objects, and later retrieve them the next time the view loads, the app crashes. I have gone through a bunch of sample codes and examples that use NSKeyedArchivers, but still couldn't figure out why that happens. I am guessing there is a problem with the initialization of the array that holds the entries but not sure...
Here is the code, maybe you could find something that I have persistently overseen..."
//--------- Entry.m---------------
- (id) initWithCoder:(NSCoder *)aDecoder{
if ((self = [super init])) {
self.title = [[aDecoder decodeObjectForKey:#"title"] retain];
self.text = [[aDecoder decodeObjectForKey:#"text"] retain];
self.created = [[aDecoder decodeObjectForKey:#"created"] retain];
}
return self;
}
- (void) encodeWithCoder:(NSCoder *)aCoder{
[aCoder encodeObject:self.title forKey:#"title"];
[aCoder encodeObject:self.text forKey:#"text"];
[aCoder encodeObject:self.created forKey:#"created"];
}
//-------------- Diary View Controller.m
- (NSString *)dataFilePath {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:kFilename];
}
- (void) writeDataToArchive {
NSMutableData *data = [[NSMutableData alloc] init];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc]
initForWritingWithMutableData:data];
[archiver encodeObject:self.entriesArray forKey:#"entriesArray"];
[archiver finishEncoding];
BOOL result = [data writeToFile:[self dataFilePath] atomically:YES];
[archiver release];
[data release];
}
- (void)addItem:sender {
int count = [entriesArray count] +1;
NSString *newEntryTitle = [NSString stringWithFormat:#"Entry %d", count];
Entry *anEntry = [[Entry alloc] initWithTitle:newEntryTitle text:#"-"
created:[NSDate date]];
[entriesArray addObject:anEntry];
[self.tableView reloadData];
[anEntry release];
[self writeDataToArchive];
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *filePath = [self dataFilePath];
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
NSData *data = [[NSMutableData alloc]
initWithContentsOfFile:[self dataFilePath]];
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc]
initForReadingWithData:data];
NSMutableArray *array = [unarchiver decodeObjectForKey:#"entriesArray"];
entriesArray = [array mutableCopy];
[array release];
[unarchiver finishDecoding];
[unarchiver release];
[data release];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:
(NSIndexPath *)indexPath
{
// ... some other stuff
NSUInteger row = indexPath.row;
Entry *entry = [entriesArray objectAtIndex:row];
cell.textLabel.text = entry.title;
return cell;
}
Thanks a lot.
When you read an array back out with NSKeyedUnarchivers you always get an unmutable copy back. You would need to declare *array as NSArray or just get rid of array all together.
entriesArray = [[unarchiver decodeObjectForKey:#"entriesArray"] mutableCopy];
And #JeremyP points out another issue. Since you didn't alloc or retain *array you should not release it.
You should not release array in viewDidLoad because you do not own it.
Please review the Cocoa memory management Rules because there are a couple of other memory management issues in your code. In particular,
self.title = [[aDecoder decodeObjectForKey:#"title"] retain];
self.text = [[aDecoder decodeObjectForKey:#"text"] retain];
self.created = [[aDecoder decodeObjectForKey:#"created"] retain];
in your initWithCoder: method all leak on the assumption the properties are retain or copy.
I have one bit of code inside RootViewController.m:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSManagedObject *selectedObject = [[self fetchedResultsController] objectAtIndexPath:indexPath];
BlogRssParser *blogRss = [[BlogRssParser alloc] init];
blogRss.terms = [[selectedObject valueForKey:#"data"] description];
//[blogRss setSelectedObject:selectedObject];
NSLog(#"%#", blogRss.terms);
RssFunViewController *rssFun = [[RssFunViewController alloc] initWithNibName:#"RssFunViewController" bundle:nil];
[self.navigationController pushViewController:rssFun animated:YES];
[rssFun release];
}
So when the user clicks the row, it goes to RssFunViewController. But it uses BlogRssParser to fill RssFun's tableview. So i'm trying to get between that and send a value from RootView to BlogRss so it fills RssFun with the right data.
i have this in BlogRssParser.m:
-(BOOL)fetchAndParseRss{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
//To suppress the leak in NSXMLParser
[[NSURLCache sharedURLCache] setMemoryCapacity:0];
[[NSURLCache sharedURLCache] setDiskCapacity:0];
NSLog(#"%#", self.terms);
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"http://news.search.yahoo.com/rss?ei=UTF-8&p=%#&fr=news-us-ss", self.terms]];
NSLog(#"%#", url);
BOOL success = NO;
NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:url];
[parser setDelegate:self];
[parser setShouldProcessNamespaces:YES];
[parser setShouldReportNamespacePrefixes:YES];
[parser setShouldResolveExternalEntities:NO];
success = [parser parse];
[parser release];
[pool drain];
return success;
}
In the console, the log of terms in RootView has the right value. But in BlogRssParser, it comes up as (null). I have declared terms as a property in BlogRssParser.m.
This is how i declared it:
#interface BlogRssParser : NSObject {
BlogRss * _currentItem;
NSMutableString * _currentItemValue;
NSMutableArray * _rssItems;
id<BlogRssParserDelegate> _delegate;
NSOperationQueue *_retrieverQueue;
//NSManagedObject *selectedObject;
NSString *terms;
}
#property(nonatomic, retain) BlogRss * currentItem;
#property(nonatomic, retain) NSMutableString * currentItemValue;
#property(readonly) NSMutableArray * rssItems;
//#property(nonatomic,retain) NSManagedObject *selectedObject;
#property(nonatomic, retain) NSString *terms;
#property(nonatomic, assign) id<BlogRssParserDelegate> delegate;
#property(nonatomic, retain) NSOperationQueue *retrieverQueue;
Use self.terms instead. You shouldn't use the underlying ivar apart from inside the accessor methods.
Have you tried looking at the NSNotificationCenter. This is very helpful for these kinds of situations and can solve problems like this easily. Also, this way you can easily update multiple objects/views with one call.