I am trying to map moderately complex JSON into a nested object structure. The Shops object is recursive, but the ErrorStatus object is not. However, a debugging breakpoint taken just after the reationshipMappingFromKeyPath:#"ErrorStatus" below, shows that entityMapping -> ErrorStatus is deeply recursive through _mutablePropertyMappings. Unfortunately the debug doesnt seem to support copying, otherwise I would show it here. Any hint at what's up would be greatly appreciated.
#implementation LLSResponse
+ (RKObjectMapping *)jsonMapping
{
RKLogConfigureByName("RestKit/ObjectMapping", RKLogLevelTrace); //??? debugging
RKLogConfigureByName("RestKit/Networking", RKLogLevelTrace); //??? debugging
NSLog(#"===== LLSResponse.jsonMapping/RestKitMapping");
RKObjectMapping * entityMapping = [RKObjectMapping mappingForClass:[LLSResponse class]];
[entityMapping addPropertyMapping:
[RKRelationshipMapping relationshipMappingFromKeyPath:#"ErrorStatus"
toKeyPath:#"ErrorStatus"
withMapping:[ErrorStatus jsonMapping]]];
[entityMapping addPropertyMapping:
[RKRelationshipMapping relationshipMappingFromKeyPath:#"Shops"
toKeyPath:#"Shops"
withMapping:[Shops jsonMapping]]];
[entityMapping addPropertyMapping:
[RKRelationshipMapping relationshipMappingFromKeyPath:#"Events"
toKeyPath:#"Events"
withMapping:[Events jsonMapping]]];
return entityMapping;
}
- (NSArray *) getAllEvents
{
NSLog(#"============ getAllEvents");
Events * events = self.events;
NSArray *allEvents = events.events;
return allEvents;
}
#end
#implementation ErrorStatus
+ (RKObjectMapping *)jsonMapping
{
NSLog(#"===== ErrorStatus.jsonMapping");
RKObjectMapping * entityMapping = [RKObjectMapping mappingForClass:[ErrorStatus class]];
[entityMapping addAttributeMappingsFromDictionary:#{
#"#ID" : #"_id",
#"#Status" : #"Status",
}];
return entityMapping;
}
#end
#implementation Events
+ (RKObjectMapping *)jsonMapping
{
NSLog(#"===== Events.jsonMapping");
RKObjectMapping * entityMapping = [RKObjectMapping mappingForClass:[Events class]];
[entityMapping addPropertyMapping:
[RKRelationshipMapping relationshipMappingFromKeyPath:#"Event"
toKeyPath:#"Event"
withMapping:[EventDO jsonMapping]]];
return entityMapping;
}
#end
#implementation Shop
+ (RKObjectMapping *)jsonMapping
{
NSLog(#"===== Shop.jsonMapping");
RKObjectMapping * entityMapping = [RKObjectMapping mappingForClass:[Shop class]];
[entityMapping addAttributeMappingsFromDictionary:#{
#"#ID" : #"_id",
#"#Title" : #"title",
}];
[entityMapping addPropertyMapping:
[RKRelationshipMapping relationshipMappingFromKeyPath:#"Shop"
toKeyPath:#"Shop"
withMapping:entityMapping]];
return entityMapping;
}
#end
#implementation Shops
+ (RKObjectMapping *)jsonMapping
{
NSLog(#"===== Shops.jsonMapping");
RKObjectMapping * entityMapping = [RKObjectMapping mappingForClass:[Shops class]];
[entityMapping addPropertyMapping:
[RKRelationshipMapping relationshipMappingFromKeyPath:#"Shop"
toKeyPath:#"Shop"
withMapping:[Shop jsonMapping]]];
return entityMapping;
}
#end
I posted a more general question with more code and the JSON in question, but this may capture the essence of the problem. See RestKit not mapping recursive JSON to objects
Related
I am sorry for the long post but I am at my wits end and have been stumped for days over this. Here's the scenario. My app loads a response from core data, converts the values to NSStrings so that I can add them to an NSDictionary. Then the NSDictionary is converted to NSData so I can attach it as a file to email. The purpose of this is so I can create a database of information including images, videos, etc. I was able to get everything to work except I am having an issue with an NSMutableArray. Here's the process:
I create an event and then load the data for exporting with this code.
EventDB *per = [[EventDB alloc]init];
per.customLayoutArray = [record.customLayoutArray description] ?
[record.customLayoutArray description] : #"";
NSDictionary *dict = [per dictionaryWithValuesForKeys:#[#"customLayoutArray"];
NSData *data = [NSJSONSerialization dataWithJSONObject:dict options:0 error:NULL];
Then I email the data using MFMailComposer. Then I have a custom UTI that allows me the open the url from the email and then I import the data and load it into my coredata entity with this
if([[url pathExtension] isEqualToString:#"ipix"]) {
NSData *data = [NSData dataWithContentsOfURL:url];
NSError *error;
NSDictionary *jsonData = [NSJSONSerialization JSONObjectWithData:data
options:kNilOptions
error:&error];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"TSPItem"
inManagedObjectContext:self.managedObjectContext];
TSPItem *record = (TSPItem *)[[NSManagedObject alloc] initWithEntity:entity
insertIntoManagedObjectContext:self.managedObjectContext];
if (record) {
NSString *datetime = [jsonData objectForKey:#"customLayoutArray"];
record.customLayoutArray = [[datetime propertyList] mutableCopy];
}
That works fine. It does import the way I want but when I launch the event I get this crash message
** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason:
'-[__NSCFString apply]: unrecognized selector sent to instance 0x1c81a5f60
Now here's the code where it crashes.
NSMutableArray *archiveArray = self.record.customLayoutArray;
NSString *mycustom = [NSString stringWithFormat:#"%#_customlayout",
self.record.eventname];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:archiveArray];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:mycustom];
self.customLayoutArray = [NSKeyedUnarchiver unarchiveObjectWithData:data];
NSLog(#"BOOTH EVENT ID %#", self.customLayoutArray);
[self.customLayoutArray makeObjectsPerformSelector:#selector(apply)];
This is the log from BOOTH EVENT ID
BOOTH EVENT ID (
"<Rectangle:0x102d38cb0self.scaleValue=1.842393\n, self.rotateValue=0.000000\n, self.width=368.478516\n, self.height=368.478516\n, self.radius=0\n, self.frame={{104, 113.5}, {200, 200}}\n, self.isApplied=NO\n>",
"<Rectangle:0x102d393c0self.scaleValue=1.000000\n, self.rotateValue=0.000000\n, self.width=200.000000\n, self.height=200.000000\n, self.radius=0\n, self.frame={{253, 273.5}, {200, 200}}\n, self.isApplied=NO\n>"
)
The app crashes here. Now if I load the original event on my iPad (the one that I didn't export) the app works perfect and the NSLog response for BOOTH EVENT ID is identical.
The "apply" section refers to this file.
#import "Rectangle.h"
#import "DeviceSize.h"
#implementation Rectangle
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
self.clipsToBounds = YES;
self.userInteractionEnabled = YES;
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)coder {
[coder encodeObject:[NSNumber numberWithFloat:self.scaleValue] forKey:#"ScaleValue"];
[coder encodeObject:[NSNumber numberWithFloat:self.rotateValue] forKey:#"RotateValue"];
[coder encodeObject:[NSNumber numberWithFloat:self.width] forKey:#"Width"];
[coder encodeObject:[NSNumber numberWithFloat:self.height] forKey:#"Height"];
[coder encodeObject:[NSNumber numberWithInteger:self.radius] forKey:#"Radius"];
[coder encodeObject:[NSNumber numberWithBool:self.isApplied] forKey:#"isApplied"];
[coder encodeObject:self.image forKey:#"Image"];
[coder encodeObject:self.backgroundColor forKey:#"BackgroundColor"];
[coder encodeObject:[NSValue valueWithCGPoint:self.center] forKey:#"CenterPoint"];
}
- (id)initWithCoder:(NSCoder *)coder {
self = [super init];
if (self) {
self.scaleValue = [[coder decodeObjectForKey:#"ScaleValue"] floatValue];
self.rotateValue = [[coder decodeObjectForKey:#"RotateValue"] floatValue];
self.width = [[coder decodeObjectForKey:#"Width"] floatValue];
self.height = [[coder decodeObjectForKey:#"Height"] floatValue];
self.radius = [[coder decodeObjectForKey:#"Radius"] integerValue];
self.isApplied = [[coder decodeObjectForKey:#"isApplied"] boolValue];
[self.layer setCornerRadius:self.radius];
self.image = [coder decodeObjectForKey:#"Image"];
[self setBackgroundColor:[coder decodeObjectForKey:#"BackgroundColor"]];
//
if (self.width == self.height)
{
CGRect rect = CGRectMake(0, 0,200, 200);
self.frame = rect;
}
if (self.width > self.height)
{
CGRect rect = CGRectMake(0, 0,200, 150);
self.frame = rect;
}
if (self.width < self.height)
{
CGRect rect = CGRectMake(0, 0,150, 200);
self.frame = rect;
}
self.center = [[coder decodeObjectForKey:#"CenterPoint"] CGPointValue];
}
return self;
}
- (void)drawRect:(CGRect)rect
{
/* Set UIView Border */
// Get the contextRef
CGContextRef contextRef = UIGraphicsGetCurrentContext();
// Set the border width
CGContextSetLineWidth(contextRef, 5.0);
// Set the border color to RED
CGContextSetRGBStrokeColor(contextRef, 255.0, 0.0, 0.0, 1.0);
// Draw the border along the view edge
CGContextStrokeRect(contextRef, rect);
}
- (BOOL)canBecomeFirstResponder {
return YES;
}
- (NSString *)description {
NSMutableString *description = [NSMutableString stringWithFormat:#"<%#:%p", NSStringFromClass([self class]), self];
[description appendFormat:#"self.scaleValue=%f\n", self.scaleValue];
[description appendFormat:#", self.rotateValue=%f\n", self.rotateValue];
[description appendFormat:#", self.width=%f\n", self.width];
[description appendFormat:#", self.height=%f\n", self.height];
[description appendFormat:#", self.radius=%li\n", (long)self.radius];
[description appendFormat:#", self.frame=%#\n", NSStringFromCGRect(self.frame)];
[description appendFormat:#", self.isApplied=%#\n", self.isApplied ? #"YES" : #"NO"];
[description appendString:#">"];
return description;
}
- (id)copyWithZone:(NSZone *)zone {
Rectangle *copy = [[[self class] allocWithZone:zone] init];
if (copy != nil) {
copy.scaleValue = self.scaleValue;
copy.rotateValue = self.rotateValue;
copy.width = self.width;
copy.height = self.height;
copy.radius = self.radius;
copy.frame = self.frame;
copy.isApplied = self.isApplied;
}
return copy;
}
#end
#implementation Rectangle(ApplyRotate)
#pragma mark -
- (Rectangle *)apply {
if (self.isApplied) {
return self;
}
Rectangle *rectangle = self;
CGPoint centerPoint = rectangle.center;
CGAffineTransform rotate = CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(rectangle.rotateValue));
CGAffineTransform scale = CGAffineTransformMakeScale(rectangle.scaleValue, rectangle.scaleValue);
CGAffineTransform scaleAndRotate = CGAffineTransformConcat(rotate, scale);
rectangle.transform = scaleAndRotate;
rectangle.center = centerPoint;
rectangle.isApplied = YES;
return rectangle;
}
#end
I have a small test function and I am getting memory allocation issue and memory warning after an hour of running. The problem is from the call to
NSData* data = [NSURLConnection sendSynchronousRequest:req returningResponse:&response error:&error1];
Below is my code.
Any helps would be appreciated.
Here is my app stat after 1 hour running:
Low: 712Kb, High 275.4 MB, Durarion 1 hour 26 min
2015-01-31 14:38:11.811 testMem[5268:546116] Received memory warning.
2015-01-31 14:38:16.836 testMem[5268:546116] Received memory warning.
2015-01-31 14:38:24.573 testMem[5268:546116] Received memory warning.
2015-01-31 14:38:29.850 testMem[5268:546116] Received memory warning.
2015-01-31 14:38:34.779 testMem[5268:546116] Received memory warning.
2015-01-31 14:38:40.058 testMem[5268:546116] Received memory warning.
2015-01-31 14:38:44.922 testMem[5268:546116] Received memory warning.
MainView.m
#import "ViewController.h"
#import "global.h"
#import "ServiceSvc.h"
#import "ClsTickets.h"
#interface ViewController ()
{
NSTimer* g_BACKUPTIMER;
}
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
g_BACKUPTIMER = [NSTimer scheduledTimerWithTimeInterval:.4 target:self selector:#selector(runBackupProcess) userInfo:nil repeats:YES];
}
-(void)runBackupProcess
{
if (stopTimer == 0)
{
[NSThread detachNewThreadSelector: #selector(runtest) toTarget:self withObject:nil];
}
else
{
}
}
- (void) runtest
{
#autoreleasepool {
#try
{
ServiceSvc_DoAdminUnitIpad *req2 = [[ServiceSvc_DoAdminUnitIpad alloc] init];
req2.UnitDesc = #"Unit 6";
req2.CustomerID = #"800014";
req2.MachineID = nil;
req2.UnitID = #"1";
req2.GPSData = nil;
ServiceSynchronous* synCrhous = [[ServiceSynchronous alloc] init];
NSData* data = [synCrhous makeSynchrounousCall:req2];
data = nil;
req2 = nil;
}
#catch (NSException *exception)
{
}
#finally
{
}
} // end auto
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
ServiceSvc.m
#implementation ServiceSynchronous
-(NSData*) makeSynchrounousCall:(ServiceSvc_DoAdminUnitIpad*) parameters
{
NSURLResponse* response = nil;
NSError* error1 = nil;
NSString *operationXMLString = #"<?xml version=\"1.0\"?> <soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:ServiceSvc=\"https://xxx/service.asmx\" xsl:version=\"1.0\"> <soap:Body> <ServiceSvc:DoAdminUnitIpad> <ServiceSvc:UnitID>1</ServiceSvc:UnitID> <ServiceSvc:UnitDesc>Unit 6</ServiceSvc:UnitDesc> <ServiceSvc:CustomerID>800014</ServiceSvc:CustomerID> </ServiceSvc:DoAdminUnitIpad> </soap:Body> </soap:Envelope>";
NSString *msgLength = [NSString stringWithFormat:#"%d",
[operationXMLString length]];
NSMutableURLRequest * req = [[NSMutableURLRequest alloc] init];
[req setURL:[NSURL URLWithString:#"https://xxx/service.asmx"]];
[req addValue:#"text/xml; charset=utf-8" forHTTPHeaderField:#"Content-Type"];
[req addValue:#"http://tempuri.org/webservice-name/method-name" forHTTPHeaderField:#"SOAPAction"];
[req addValue:msgLength forHTTPHeaderField:#"Content-Length"];
[req setHTTPMethod:#"POST"];
[req setHTTPBody:[operationXMLString dataUsingEncoding:NSUTF8StringEncoding]];
NSData* data = [NSURLConnection sendSynchronousRequest:req returningResponse:&response error:&error1];
[[NSURLCache sharedURLCache] removeCachedResponseForRequest:req];
if (error1 == nil)
{
}
else
{
}
operationXMLString = nil;
req = nil;
response = nil;
return data;
}
#end
#implementation ServiceSvc_DoAdminUnitIpad
- (id)init
{
if((self = [super init])) {
UnitID = 0;
UnitDesc = 0;
CustomerID = 0;
MachineID = 0;
GPSData = 0;
}
return self;
}
- (NSString *)nsPrefix
{
return #"ServiceSvc";
}
- (xmlNodePtr)xmlNodeForDoc:(xmlDocPtr)doc elementName:(NSString *)elName elementNSPrefix:(NSString *)elNSPrefix
{
}
/* elements */
#synthesize UnitID;
#synthesize UnitDesc;
#synthesize CustomerID;
#synthesize MachineID;
#synthesize GPSData;
/* attributes */
- (NSDictionary *)attributes
{
NSMutableDictionary *attributes = [NSMutableDictionary dictionary];
return attributes;
}
+ (ServiceSvc_DoAdminUnitIpad *)deserializeNode:(xmlNodePtr)cur
{
ServiceSvc_DoAdminUnitIpad *newObject = [ServiceSvc_DoAdminUnitIpad new];
[newObject deserializeAttributesFromNode:cur];
[newObject deserializeElementsFromNode:cur];
return newObject;
}
- (void)deserializeAttributesFromNode:(xmlNodePtr)cur
{
}
- (void)deserializeElementsFromNode:(xmlNodePtr)cur
{
}
#end
ServiceSvc.h
#interface ServiceSvc_DoAdminUnitIpad : NSObject {
/* elements */
NSString * UnitID;
NSString * UnitDesc;
NSString * CustomerID;
NSString * MachineID;
NSString * GPSData;
/* attributes */
}
- (NSString *)nsPrefix;
- (xmlNodePtr)xmlNodeForDoc:(xmlDocPtr)doc elementName:(NSString *)elName elementNSPrefix:(NSString *)elNSPrefix;
- (void)addAttributesToNode:(xmlNodePtr)node;
- (void)addElementsToNode:(xmlNodePtr)node;
+ (ServiceSvc_DoAdminUnitIpad *)deserializeNode:(xmlNodePtr)cur;
- (void)deserializeAttributesFromNode:(xmlNodePtr)cur;
- (void)deserializeElementsFromNode:(xmlNodePtr)cur;
/* elements */
#property (retain) NSString * UnitID;
#property (retain) NSString * UnitDesc;
#property (retain) NSString * CustomerID;
#property (retain) NSString * MachineID;
#property (retain) NSString * GPSData;
/* attributes */
- (NSDictionary *)attributes;
#end
#interface ServiceSynchronous : NSObject {
}
- (NSData*) makeSynchrounousCall:(ServiceSvc_DoAdminUnitIpad*) parameters;
#end
After some research, I came to the conclusion that Apple's NSURLConnection is the issue. The problem was reported back in 2008 and Apple had acknowledged it but now it is 2015 and the problem is still there. I have switched to asynchronous call and the memory allocation issue is still there, but smaller. Those geeks at Apple are a joke.
this is my first question here :)
OK so I have a projet with ReskKit 0.23.3 via cocoapods. I use RestKit/CoreData.
I fetch an URL, the result got mapped to my object and is correctly saved by core data. I want to use Key-Value Validation to check some values retrieved against the one already persisted. I read that i could use the methods validateKey:error: on my NSManagedObject. Somehow, it is never called. I'm frustrated...
Here are my files (for simplicity, i concatenated logic code into one flat file here):
JSON response /collections/{id}
{
"id": "00000000-0000-0000-0000-00000000000",
"image_url": "http://server/image.png",
"name": "Collection C",
"etag": 42,
"ctag": 42
}
Collection.h
#interface Collection : NSManagedObject
#property(nonatomic, strong) NSString *collectionId;
#property(nonatomic, strong) NSString *name;
#property(nonatomic, strong) NSURL *imageUrl;
#property(nonatomic, strong) NSNumber *etag;
#property(nonatomic, strong) NSNumber *ctag;
#end
Collection.m
#implementation Collection
#dynamic collectionId, name, imageUrl, etag, ctag;
- (BOOL)validateCollectionId:(id *)ioValue error:(NSError **)outError {
NSLog(#"Validating id");
NSLog(#"Coredata collection id: %#", self.collectionId);
NSLog(#"GET collection id: %#", (NSString *)*ioValue);
return YES;
}
- (BOOL)validateEtag:(id *)ioValue error:(NSError **)outError {
NSLog(#"Validating etag");
NSLog(#"Coredata collection etag: %#", self.etag);
NSLog(#"GET collection etag: %#", (NSString *)*ioValue);
return YES;
}
#end
Code
NSManagedObjectModel *managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];
[managedObjectStore createPersistentStoreCoordinator];
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingString:#"/MyApp.sqlite"];
NSError *error;
NSPersistentStore *persistentStore = [managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:nil withConfiguration:nil options:nil error:&error];
NSAssert(persistentStore, #"Failed to add persistent store with error: %#", error);
[managedObjectStore createManagedObjectContexts];
managedObjectStore.managedObjectCache = [[RKInMemoryManagedObjectCache alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
[RKManagedObjectStore setDefaultStore:managedObjectStore];
NSURL *url = [NSURL URLWithString:#"http://server/api"];
RKObjectManager *objectManager = [self managerWithBaseURL:url];
objectManager.requestSerializationMIMEType = RKMIMETypeJSON;
objectManager.managedObjectStore = [RKManagedObjectStore defaultStore];
RKEntityMapping *collectionMapping = [RKEntityMapping mappingForEntityForName:#"Collection" inManagedObjectStore:[RKManagedObjectStore defaultStore]];
[collectionMapping addAttributeMappingsFromDictionary:#{#"id": #"collectionId",
#"image_url": #"imageUrl"}];
[collectionMapping addAttributeMappingsFromArray:#[#"name", #"etag", #"ctag"]];
[collectionMapping setIdentificationAttributes:#[#"collectionId"]];
RKResponseDescriptor *collectionResponseDescriptors = [RKResponseDescriptor responseDescriptorWithMapping:collectionMapping
method:RKRequestMethodGET pathPattern:#"collections/:collectionId"
keyPath:nil
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptor:collectionResponseDescriptor];
[objectManager getObjectsAtPath:#"collections/00000000-0000-0000-0000-00000000000" parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
Collection *collection = (Collection *)[mappingResult.array firstObject];
NSLog(#"Collection: %#", collection);
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"Oh noes :(");
}];
Output
2014-09-13 12:39:04.242 MyApp[41958:607] I restkit:RKLog.m:33 RestKit logging initialized...
2014-09-13 12:39:05.028 MyApp[41890:607] Collection: <NSManagedObject: 0x9108a60> (entity: Collection; id: 0x94166d0 <x-coredata://6645F428-7631-45F0-A8AF-E2352C50F35E/Collection/p1> ; data: {
collectionId = "00000000-0000-0000-0000-00000000000";
ctag = 42;
etag = 42;
imageUrl = "http://server/image.png";
name = "Collection C";
})
So, I get my Log with the Collection, but None of the NSLog in the validate<Key>:error: methods got triggered... Could not figure out why!
Edit
With some breaks, i figured this is RKMappingOperation who is responsible for calling those validation methods on my object. Precisely it's validateValue:atKeyPath:
RKMappingOperation.m
...
- (BOOL)validateValue:(id *)value atKeyPath:(NSString *)keyPath
{
BOOL success = YES;
if (self.objectMapping.performsKeyValueValidation && [self.destinationObject respondsToSelector:#selector(validateValue:forKeyPath:error:)]) {
NSError *validationError;
success = [self.destinationObject validateValue:value forKeyPath:keyPath error:&validationError];
...
}
...
But self.destinationObject is an NSManagedObject and not a Collection object...
Console
(lldb) po [self.destinationObject class]
NSManagedObject
I hope you could lead me to the right way :) Thank you!
It appears that you have not specified that the entity should use the Collection class in the Core Data model. If you don't specify anything then NSManagedObject will be used by default.
I've done exactly what this official documentation instructed. I created a console project for testing purpose. And then I created a class called RequestSender (see code below) and I made an instance of this class in the main function.
RequestSender.h:
#import <Foundation/Foundation.h>
#interface RequestSender : NSObject <NSURLConnectionDelegate> {
NSMutableData* d;
}
#end
RequestSender.m:
#import "RequestSender.h"
#implementation RequestSender
- (id)init {
self = [super init];
if (self) {
NSString* s = #"http://www.apple.com/";
NSURL* u = [[NSURL alloc] initWithString:s];
NSURLRequest* r = [[NSURLRequest alloc] initWithURL:u];
NSURLConnection* c = [[NSURLConnection alloc] initWithRequest:r delegate:self];
if (c) {
d = [NSMutableData data];
}
return self;
} else {
return nil;
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[d setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[d appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
}
#end
This will no doubt be executed in the main thread and default run loop. However non of the delegation methods are called.
I've done a lot of testing and read many posts but I still don't have a clue.
Please help me. This is driving me mad.
Thanx!
I believe you're missing the part where you start the connection. The connection class offers another method for this, or you could just call it off of your "c" object.
NSURLConnection* c = [[NSURLConnection alloc] initWithRequest:r delegate:self];
if (c) {
d = [NSMutableData data];
[c start];
}
or
NSURLConnection* c = [[NSURLConnection alloc] initWithRequest:r delegate:self startImmediately:YES];
if (c) {
d = [NSMutableData data];
}
I'm using NSXMLParser and I get a memory leak that points to NSConcreteMapTable, whatever that is:
The leak occurs at this line of code when calling the parser from my AppDelegate.m:
I have searched for a solution and can't see what I'm doing wrong.
Here is my code.
Any help is greatly appreciated.
lq
// * * * XMLParser.h * * *
#import <Foundation/Foundation.h>
#protocol NSXMLParserDelegate;
#interface XMLParser : NSObject
<NSXMLParserDelegate>
{
NSMutableArray *xmlArray;
BOOL storingCharacters;
float xmlDataVersion;
}
#property (nonatomic, retain) NSMutableArray *xmlArray;
#property (nonatomic) BOOL storingCharacters;
#property (nonatomic, assign) float xmlDataVersion;
-(BOOL)parseXMLFileAtURL:(NSURL *)URL parseError:(NSError **)error;
#end
// * * * XMLParser.m * * *
#import "XMLParser.h"
#implementation XMLParser
#synthesize xmlArray;
#synthesize storingCharacters;
#synthesize xmlDataVersion;
- (BOOL)parseXMLFileAtURL:(NSURL *)URL parseError:(NSError **)error {
BOOL result = YES;
if (xmlArray == nil) {
// this array holds row data extracted from the XML parser didStartElement method
xmlArray = [[NSMutableArray alloc] init];
}
[[NSURLCache sharedURLCache] setMemoryCapacity:0];
[[NSURLCache sharedURLCache] setDiskCapacity:0];
NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:URL];
if (parser != nil) {
[parser setDelegate:self];
[parser setShouldProcessNamespaces:NO];
[parser setShouldReportNamespacePrefixes:NO];
[[parser setShouldResolveExternalEntities:NO];
}
[parser parse];
if (parseError && error) {
*error = parseError;
result = NO;
}
[parser release];
return result;
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
if (qName) {
elementName = qName;
}
// Check the data version of the XML Data against my stored value
if ([elementName isEqualToString:#"data"]) {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
self.xmlDataVersion = [[attributeDict objectForKey:#"version"] floatValue];
float storedDataVersion = [userDefaults floatForKey:kDataVersion];
if (self.xmlDataVersion <= storedDataVersion) {
// - - - - -> Abort parsing if the same or earlier data versions
[parser abortParsing];
}
}
if ([elementName isEqualToString:#"FirstColumnName"]) {
storingCharacters = YES;
} else if ([elementName isEqualToString:#"SecondColumnName"]) {
storingCharacters = YES;
// ... total of 16 elements
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if (storingCharacters) {
[self.xmlArray addObject:string];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if (qName) {
elementName = qName;
}
// - - - - -> If at the end of a data row, save changes to object model
if ([elementName isEqualToString:#"ROW"]) {
// - - - - -> Make sure the data has the required number of elements before taking any action
if ([self.xmlArray count] == 16) {
// … //Store or Update Data in SQLite store depending on data values
}
[self.xmlArray removeAllObjects];
}
storingCharacters = NO;
}
-(void)dealloc {
[xmlArray release];
[super dealloc];
}
// * * * AppDelegate.m * * *
#import "XMLParser.h"
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(UIApplication *)application {
NSURL *xmlURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"FileName" ofType:#"xml"]];
NSError *parseError = nil;
XMLParser *xmlParse = [[XMLParser alloc] init];
[xmlParse parseXMLFileAtURL:xmlURL parseError:&parseError];
[xmlParse release];
. . .
}
I found a solution in another SO post:
Use:
NSData * dataXml = [[NSData alloc] initWithContentsOfURL:URL];
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:dataXml];
[dataXml release];
Instead of:
NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:URL];
The leak goes away.
This is probably a leak in Apple code, since Foundation is reported as "Responsible Library". There's probably nothing you can do, but to report the bug to apple.