Crashlytics reported this crash in one of my apps and I am not able to reproduce it at all, no matter what I do.
This happens to about 5% of the users, so it's a pretty big deal.
I'm posting screenshots with the crash report and also the methods that are mentioned in the crash report.
Any idea how to solve this?
This is where the app crashed:
#pragma mark - custom transformations
-(BOOL)__customSetValue:(id<NSObject>)value forProperty:(JSONModelClassProperty*)property
{
if (!property.customSetters)
property.customSetters = [NSMutableDictionary new];
NSString *className = NSStringFromClass([JSONValueTransformer classByResolvingClusterClasses:[value class]]);
if (!property.customSetters[className]) {
//check for a custom property setter method
NSString* ucfirstName = [property.name stringByReplacingCharactersInRange:NSMakeRange(0,1)
withString:[[property.name substringToIndex:1] uppercaseString]];
NSString* selectorName = [NSString stringWithFormat:#"set%#With%#:", ucfirstName, className];
SEL customPropertySetter = NSSelectorFromString(selectorName);
//check if there's a custom selector like this
if (![self respondsToSelector: customPropertySetter]) {
property.customSetters[className] = [NSNull null]; // this is line 855
return NO;
}
//cache the custom setter selector
property.customSetters[className] = selectorName;
}
if (property.customSetters[className] != [NSNull null]) {
//call the custom setter
//https://github.com/steipete
SEL selector = NSSelectorFromString(property.customSetters[className]);
((void (*) (id, SEL, id))objc_msgSend)(self, selector, value);
return YES;
}
return NO;
}
This is the originating method:
-(void)reloadUserInfoWithCompletion:(void (^) (LoginObject *response))handler andFailure:(void (^)(NSError *err))failureHandler {
NSString *lat;
NSString *lon;
lat = [NSString stringWithFormat:#"%.6f",[[LocationManager sharedInstance] getPosition].coordinate.latitude];
lon = [NSString stringWithFormat:#"%.6f",[[LocationManager sharedInstance] getPosition].coordinate.longitude];
NSMutableDictionary *params = [NSMutableDictionary new];
[params setObject:lat forKey:#"latitude"];
[params setObject:lon forKey:#"longitude"];
[[LoginHandler sharedInstance] getLoginToken:^(NSString *response) {
NSDictionary *headers;
if (response) {
headers = #{#"Login-Token":response};
}
GETRequest *req = [GETRequest new];
[req setCompletionHandler:^(NSString *response) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(#"response: %#",response);
NSError *err = nil;
self.loginObject.userDetails = [[User alloc] initWithString:response error:&err]; // <- this is the line reported in the crash
[self storeLoginObject];
NSLog(#"%#",self.loginObject.userDetails);
// [Utils updateFiltersFullAccessIfAll];
dispatch_async(dispatch_get_main_queue(), ^{
if (handler) {
handler(self.loginObject);
}
});
});
}];
[req setFailedHandler:^(NSError *err) {
if (failureHandler) {
failureHandler(err);
}
}];
NSLog(#"%#",params);
[req requestWithLinkString:USER_DETAILS parameters:nil andHeaders:headers];
}];
}
So setObject:forKey: can cause problems in two ways. 1. If object is nil or 2. the key is nil. Both could cause the crash you are seeing. Given that you are setting the object to [NSNull null] it is probably safe to assume that it is the key giving you problems (on line 855).
Walking back from there that would reveal that className is nil. If you look, your code does not protect against this. You make an assumption here that NSStringFromClass (a couple lines before) is giving you back a valid string, which assumes that the value originally passed into the method is non-nil. If it is nil it would make it past all of your checks, including !property.customSetters[className], since this would be !nil allowing it to enter the if.
If I am reading your code right (a bit hard since I cannot test any of my assumptions) NSLog(#"response: %#",response); would print out a nil response.
Try seeing how your code handles these unexpected nils and let me know in the comments how things go.
If you don't use model custom setters you can replace JSONModel __customSetValue:forProperty: with swizzling or Aspects library
#import "JSONModel+Aspects.h"
#import "JSONModel.h"
#import "Aspects.h"
#implementation JSONModel (Aspects)
+(void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[JSONModel aspect_hookSelector:#selector(__customSetValue:forProperty:) withOptions:AspectPositionInstead usingBlock:^(id<AspectInfo> aspectInfo) {
return NO;
} error:NULL];
});
}
#end
Related
I'm not that strong with Objective-C, so this is probably a simple issue. I cannot understand why the the last line in the error completion block is causing an exception:
- (void)sendInappropriateNewsfeedComment:(NSString *)comment newsfeedEventId:(NSString *)newsfeedEventId completion:(void (^)(NSString *, NSInteger))completion error:(void (^)(NSString *, NSInteger))error {
PAInappropriateNewsFeedRequest *inappropriateNewsfeedRequest = [[PAInappropriateNewsFeedRequest alloc] initWithComment:comment newsfeedEventId:newsfeedEventId];
[inappropriateNewsfeedRequest executeWithCompletionBlock:^(id obj) {
completion(#"SUCCESS", (NSInteger)1);
} error:^(NSError *e, id obj) {
NSString * message = [obj objectForKey:#"message"];
error(message, [obj integerForKey:#"code"]);
}];
}
I've also attached a screenshot showing that the "obj" object has a key called "code" that is of type "(long)-1".
What is the proper way to declare the error block and pass the "-1" value back to the call site?
Simply because NSDictionary has no method called integerForKey. That's what "unrecognized selector" means. Selector is basically a method name.
The fact that this can be even compiled is caused by using id for the parameter type. You can call anything on id but it will crash your app if the method does not exist. You should cast obj to a proper type as soon as possible.
NSDictionary *dictionary = (NSDictionary *) obj;
NSString *message = dictionary[#"message"];
NSNumber *code = dictionary[#"code"];
If obj can be a different type, you should make sure to check [obj isKindOfClass:NSDictionary.self] before casting.
The whole solution taking Sulthan's suggestions into account might looks something like this
typedef void (^NewFeedCompletion)(NSString *, NSInteger);
typedef void (^NewsFeedError)(NSString *, NSInteger);
- (void) sendInappropriateNewsfeedComment: (NSString *)comment
newsfeedEventId: (NSString *)newsfeedEventId
completion: (NewFeedCompletion) completion
error: (NewsFeedError) error
{
PAInappropriateNewsFeedRequest *inappropriateNewsfeedRequest = [[PAInappropriateNewsFeedRequest alloc] initWithComment:comment newsfeedEventId:newsfeedEventId];
[inappropriateNewsfeedRequest executeWithCompletionBlock: ^(id obj) {
completion(#"SUCCESS", (NSInteger)1);
} error:^(NSError *e, id obj) {
assert([obj isKindOfClass: NSDictionary.class])
NSDictionary *errorDictionary = (NSDictionary *) obj;
NSString *message = [errorDictionary objectForKey: #"message"];
NSNumber *code = [errorDictionary objectForKey: #"code"]
error(message, [code integerValue]);
}];
}
I've been searching for answers for this but still haven't been able to solve how to correct the problem. This -[NSNull length]: unrecognized selector sent to instance and this [NSNull length]: unrecognized selector sent to instance 0x43fe068 did not help.
I'm working on a chat app with a Parse back-end and I was having a timestamp problem with a chat message showing up out of order so I deleted the rows that were out of order from my Parse database using the Databrowser. When I tested the app, that seemed to fix the problem on my iPhone 6 Plus and on the iPhone 6 simulator both running iOS 8. However, when opening up the same chat room on my iPhone 5s running iOS 7, the app crashes consistently with the following error.
-[NSNull length]: unrecognized selector sent to instance
I have no idea why deleting a row would cause this to happen and why only on iOS 7? I set an All Exceptions Breakpoint and here is the offending line along with a screenshot.
self.lastMessageLabel.textColor = [UIColor redColor];
I still get the NSNull length crash even when I comment out the above line, but it breaks at the generic main.m.
Any suggestions on how to solve this would be appreciated. Thanks.
EDIT 1: Here's the code from my ChatView.m that's being loaded by my PrivateInbox.
- (void)loadMessages {
if (isLoading == NO)
{
isLoading = YES;
JSQMessage *message_last = [messages lastObject];
PFQuery *query = [PFQuery queryWithClassName:PF_CHAT_CLASS_NAME];
[query whereKey:PF_CHAT_ROOM equalTo:chatroomId];
if (message_last != nil) {
[query whereKey:PF_CHAT_SENTDATE greaterThan:[self.dateFormatter stringFromDate:message_last.date]];
}
[query includeKey:PF_CHAT_USER];
[query orderByAscending:PF_CHAT_SENTDATE];
[query addAscendingOrder:PF_CHAT_CREATEDAT];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error)
{
if (error == nil)
{
for (PFObject *object in objects)
{
PFUser *user = object[PF_CHAT_USER];
[users addObject:user];
if(![object[PF_CHAT_TEXT] isKindOfClass:[NSNull class]]) {
NSDate* sentDate;
if(object[PF_CHAT_SENTDATE] != nil)
sentDate = [self.dateFormatter dateFromString:object[PF_CHAT_SENTDATE]];
else
sentDate = object.createdAt;
JSQTextMessage *message = [[JSQTextMessage alloc] initWithSenderId:user.objectId senderDisplayName:user.objectId date:sentDate text:object[PF_CHAT_TEXT]];
[messages addObject:message];
} else if(object[PF_CHAT_PHOTO] != nil) {
NSDate* sentDate;
if(object[PF_CHAT_SENTDATE] != nil)
sentDate = [self.dateFormatter dateFromString:object[PF_CHAT_SENTDATE]];
else
sentDate = object.createdAt;
PFFile* photoFile = object[PF_CHAT_PHOTO];
JSQPhotoMediaItem *photoItem = [[JSQPhotoMediaItem alloc] init];
JSQMediaMessage *photoMessage = [[JSQMediaMessage alloc] initWithSenderId:user.objectId
senderDisplayName:user.objectId
date:sentDate
media:photoItem];
[messages addObject:photoMessage];
{
[photoFile getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
photoItem.image = [UIImage imageWithData:data];
[self.collectionView reloadItemsAtIndexPaths:[NSArray arrayWithObjects:[NSIndexPath indexPathForItem:[messages indexOfObject:photoMessage] inSection:0], nil]];
}];
}
} else if(object[PF_CHAT_VIDEO] != nil) {
NSDate* sentDate;
if(object[PF_CHAT_SENTDATE] != nil)
sentDate = [self.dateFormatter dateFromString:object[PF_CHAT_SENTDATE]];
else
sentDate = object.createdAt;
PFFile* videoFile = object[PF_CHAT_VIDEO];
JSQVideoMediaitem *videoItem = [[JSQVideoMediaitem alloc] initWithFileURL:[NSURL URLWithString:[videoFile url]] isReadyToPlay:YES];
JSQMediaMessage *videoMessage = [[JSQMediaMessage alloc] initWithSenderId:user.objectId
senderDisplayName:user.objectId
date:sentDate
media:videoItem];
[messages addObject:videoMessage];
}
}
if ([objects count] != 0) {
[JSQSystemSoundPlayer jsq_playMessageReceivedSound];
[self resetUnreadCount];
[self finishReceivingMessage];
}
}
else [ProgressHUD showError:#"Network error."];
isLoading = NO;
}];
}
}
EDIT 2: I tried NSNullSafe from Nick Lockwood https://github.com/nicklockwood/NullSafe and that allowed the Private Inbox to open without crashing and gets me past the NSNull Length error but I think that just masks the problem and I still don't know why it didn't crash on iOS 8 but did crash on iOS 7.
I don't think that this could be related to the differences between the 2 operative systems.
The crash is pretty clear, you are sending a message to an object of class NSNUll that can't handle it.
The fact you are using parse or a web services in general makes me think that this object was generated by the back end as a null in a JSON and translated into a NSNull object by the JSON parsing.
You should find a way to handle NSNull object probably at parsing level.
#Andrea is correct, if you can't figure it out from your API (server) side then, here's categories of NSDictionary and NSArray which removes any NSNull object and your app wouldn't crash.
#interface NSDictionary (NullReplacement)
- (NSDictionary *)dictionaryByReplacingNullsWithBlanks;
#end
#interface NSArray (NullReplacement)
- (NSArray *)arrayByReplacingNullsWithBlanks;
#end
#implementation NSDictionary (NullReplacement)
- (NSDictionary *)dictionaryByReplacingNullsWithBlanks {
const NSMutableDictionary *replaced = [self mutableCopy];
const id nul = [NSNull null];
const NSString *blank = #"";
for (NSString *key in self) {
id object = [self objectForKey:key];
if (object == nul) [replaced setObject:blank forKey:key];
else if ([object isKindOfClass:[NSDictionary class]]) [replaced setObject:[object dictionaryByReplacingNullsWithBlanks] forKey:key];
else if ([object isKindOfClass:[NSArray class]]) [replaced setObject:[object arrayByReplacingNullsWithBlanks] forKey:key];
}
return [NSMutableDictionary dictionaryWithDictionary:[replaced copy]];
}
#end
#implementation NSArray (NullReplacement)
- (NSArray *)arrayByReplacingNullsWithBlanks {
NSMutableArray *replaced = [self mutableCopy];
const id nul = [NSNull null];
const NSString *blank = #"";
for (int idx = 0; idx < [replaced count]; idx++) {
id object = [replaced objectAtIndex:idx];
if (object == nul) [replaced replaceObjectAtIndex:idx withObject:blank];
else if ([object isKindOfClass:[NSDictionary class]]) [replaced replaceObjectAtIndex:idx withObject:[object dictionaryByReplacingNullsWithBlanks]];
else if ([object isKindOfClass:[NSArray class]]) [replaced replaceObjectAtIndex:idx withObject:[object arrayByReplacingNullsWithBlanks]];
}
return [replaced copy];
}
#end
Caution : Only appropriate if you're parsing small amount of data.
Source : https://stackoverflow.com/a/16702060/1603234
The other two answers are correct - this is not an issue of OS version, but rather that you are being passed a NULL value and are not catching it.
The line you caught in the Exception:
self.lastMessageLabel.textColor = [UIColor redColor];
Is indicating a symptom, not the cause. I am not 100% sure how _setTextColor actually works, but I would make a strong bet that it works by using the length attribute of NSString/NSAttributedString to make a NSRange so that it knows where to start applying the color and end applying the color. If the data is NULL, then as the other users mentioned, you are trying to access the length attribute of the wrong class, causing your crash.
Judging from your stack trace, lines 5 (setMessage:forUser) and 6 (cellForRow...) are where you should be trying to catch the NULL value. Either that, or you should modify your data controller so that when a NULL value is passed back in the JSON, you replace it with a placeholder, such as "No Message".
At either rate, your crash is probably happening when you assign a value to self.lastMessageLabel.text when you are building your tableView cells in cellForRow.... Check the NSString that you use there for class type (isKindOfClass[NSNULL class]) and see if that helps.
I am uploading images chunk wise, in a background thread, each chunk will be size of 512kb,to the best of my knowledge,i have taken care of memory leaks using release,nsautoreleasepool.
Below is the code for uploading images chunkwise.
- (void)FetchDataFromDB : (NSNumber *) isOffline
{
#autoreleasepool {
#try {
NSLog(#"FetchDatafromDB");
isThreadStarted = YES;
VYukaDBFunctions *vdb = [VYukaDBFunctions getInstance];
NSMutableArray *fileNames = [vdb GetFileNames:[isOffline integerValue]];
for(int j=0 ; j<[fileNames count] ; j++)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString * filename = fileNames [j] ;
int _outgoingMsgId = [[vdb SelectMsgId:filename] intValue];
int _totalchunk =[[vdb SelectTotalChunk:filename]intValue];
int currentChunk = [vdb GetCurrentChunk:filename];
for( int i=currentChunk ; i <= _totalchunk ; i++)
{
NSAutoreleasePool *innerPool = [[NSAutoreleasePool alloc] init];
NSString *AsyncRequest = [[NSString alloc] init];
AsyncRequest = [vdb SelectAsyncRequest: i : _outgoingMsgId];
if(![AsyncRequest isEqual:#""])
{
BOOL status = [self UploadChunkWise :AsyncRequest : 1 : i : vdb : filename : _outgoingMsgId];
// AsyncRequest = NULL;
// [AsyncRequest release];
if(status){
if(i==_totalchunk)
{
NSLog(#"Deleting from medialist , FileName :%#", filename);
[vdb DeleteFromMediaList : filename];
}
}
else{
[vdb DeleteFromMediaList : filename];
break;
}
}
[innerPool drain];
}
[pool drain];
}
[fileNames removeAllObjects];
// [fileNames release];
//recurssive call to check any pending uploads..
if([[vdb GetFileNames:[isOffline integerValue]] count] > 0)
{
NSLog(#"Calling Recursively..");
[self FetchDataFromDB:[isOffline integerValue]];
}
}
#catch (NSException *exception) {
NSLog(#"Exception caught on Uploading from FetchDataFromDB:%#", exception);
}
#finally {
}
}
NSLog(#"thread quit ");
isThreadStarted = NO;
[NSThread exit];
}
-(BOOL) UploadChunkWise :(NSString *) AsyncRequest : (int) count : (int)currentChunk : (VYukaDBFunctions * ) vdb : (NSString *) currentFileName : (int) outgoingMsgId
{
NSHTTPURLResponse *response ;
NSError *error;
//Yes, http
NSMutableURLRequest *httpRequest = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"Url goes here"]];
NSData* data = [AsyncRequest dataUsingEncoding:NSUTF8StringEncoding];
[httpRequest setHTTPMethod:#"POST"];
[httpRequest setHTTPBody:data];
[httpRequest setValue:#"application/xml" forHTTPHeaderField:#"Content-Type"];
NSData *returnedData = [NSURLConnection sendSynchronousRequest: httpRequest returningResponse:&response error:&error] ;
NSString *result= [[NSString alloc] initWithData:returnedData encoding:NSASCIIStringEncoding];
[httpRequest release];
returnedData= NULL;
[returnedData release];
data = NULL;
[data release];
if ([result rangeOfString:#"success"].location != NSNotFound )
{
NSLog(#" success");
[vdb DeleteCurrentChunkFromOutgoingTable:currentChunk : outgoingMsgId];
[result release];
return YES ;
}
else if ([result rangeOfString:#"fail"].location != NSNotFound )
{
[result release];
if (count < 3) {
return [self UploadChunkWise :AsyncRequest : count+1 : currentChunk: vdb : currentFileName : outgoingMsgId ];
}
else
{
NSLog(#" failed");
[vdb DeleteAllCurrentFileChunksFromOutgoingTable:currentFileName];
return NO ;
}
}
return NO;
}
I am starting thread as below
[NSThread detachNewThreadSelector:#selector(FetchDataFromDB:) toTarget:self withObject:[NSNumber numberWithInt:0]];
The problem is after uploading 9 to 12 chunks, i am getting memory error. i am getting 4 to 5 times memory warning and after that app crashes.in console i am getting memory warning first at app delegate class, followed by 4 classes which are extending UIViewController. why i am getting warning at app delegate, and other classes which is of type UIViewController.Why i have to release object of other class if the separate thread is giving me memory error? what i am doing wrong here? I cannot use ARC, as i have integrated this with old code, which is not using ARC, i tried enabling ARC class wise, but it dint work. Can any one help me to find out if there is any memory leaks in this code. Suggestions are welcomed and appreciated.Thanks in advance..
Two things- first, I see this:
NSString *AsyncRequest = [[NSString alloc] init];
AsyncRequest = [vdb SelectAsyncRequest: i : _outgoingMsgId];
This should be consolidated to this:
NSString *asyncRequest = [vdb SelectAsyncRequest: i : _outgoingMsgId];
You instead are creating a new instance, then immediately either generating or referencing another instance.
Second:
Your code is very hard to read and doesn't follow the Objective-C smalltalk conventions.
Variable names should begin with a lowercase letter. Method names should also start with lowercase letters. Class names and functions should begin with capital letters. It makes it difficult to read because I and many others have been trained to see capital letters and think CLASS NAME instead of POSSIBLE VARIABLE NAME. Just FYI
Finally, some of your methods take multiple parameters, like the one above. You really should add a prefix to each parameter so that it's easy to understand what the parameter is for. This:
[vdb SelectAsyncRequest: PARAMETER : PARAMETER];
would look much better if it was :
[vdb selectAsyncRequestForParameter: PARAMETER withOtherParameter:OTHERPARAM];
EDIT: I also don't think you need so many autorelease pools. The entire thing is wrapped in a big autorelease pool already.
EDIT2: I also see a lot of release calls that aren't necessary. In your UploadChunkWise method you are calling release on *data and *returnedData which are both already implicitly autoreleased. Methods that return objects to you will already have ownership given up and "handed over" to you. Essentially, those methods will do this:
NSData *data = [[NSData alloc] init];
return [data autorelease];
When you get it, if you want to keep it you will have to retain it yourself, otherwise it will be destroyed at the return of your method.
However, it is correct for you to call release on the NSString *result instance you created with -init.
I'm trying to learn objective-c (I'm very new to that) and I have issues with memory management...
I'm developing an iPad app that uses TouchXML.
I've created my class that extends CXMLDocument and does some initialisation by reading some contents and saving into properties.
Here is my code (SimpleManifest.h):
#interface SimpleManifest : CXMLDocument {
CXMLNode *_defaultOrganization;
NSString *_title;
NSDictionary *dictionary;
}
#property (readonly) CXMLNode *defaultOrganization;
#property (readonly) NSString* title;
- (id) initWithPath:(NSString *)path options:(NSUInteger)options error:(NSError **)error;
#end
(SimpleManifest.m):
#import "SimpleManifest.h"
#import "CXMLNode_XPathExtensions.h"
#implementation SimpleManifest
- (id) initWithPath:(NSString *)path options:(NSUInteger)options error:(NSError **)error
{
/*
NSURL *theURL = [[[NSURL alloc] initFileURLWithPath:path] autorelease];
self = [self initWithContentsOfURL:theURL options:options error:error];
*/
NSData *data = [NSData dataWithContentsOfFile:path];
NSString *s = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
self = [self initWithXMLString:s options:options error:error];
if (self==nil) return nil;
// load main props
dictionary = [NSDictionary dictionaryWithObjectsAndKeys:
#"http://www.imsglobal.org/xsd/imscp_v1p1", #"imscp",
#"http://ltsc.ieee.org/xsd/LOM", #"lom", nil];
// defualt organization
#try {
CXMLNode *orgsElem = [[[self childAtIndex:0] nodesForXPath:#"//imscp:organizations" namespaceMappings:dictionary error:nil] objectAtIndex:0];
NSString *xpath = [NSString stringWithFormat:#"//imscp:organization[#identifier='%#']", [[orgsElem attributeForName:#"default"] stringValue]];
_defaultOrganization = [[[self childAtIndex:0] nodesForXPath:xpath namespaceMappings:dictionary error:nil] objectAtIndex:0];
/*
NSArray *nodes = [[self childAtIndex:0] nodesForXPath:#"//imscp:organizations" namespaceMappings:dictionary error:nil];
NSString *xpath = [NSString stringWithFormat:#"//imscp:organization[#identifier='%#']", [[[nodes objectAtIndex:0] attributeForName:#"default"] stringValue]];
_defaultOrganization = [[[self childAtIndex:0] nodesForXPath:xpath namespaceMappings:dictionary error:nil] objectAtIndex:0];
*/
CXMLNode *titleElem = [[[self childAtIndex:0]
nodesForXPath:#"//lom:general/lom:title/lom:string"
namespaceMappings:dictionary
error:nil] objectAtIndex:0];
_title = [[titleElem stringValue] copy];
} #catch (NSException * e){
self = nil;
return nil;
}
return self;
}
#end
Later on in another class I do:
- (BOOL) isValidSCORMLesson:(NSString*) path {
NSString *manifPath = [path stringByAppendingPathComponent:#"imsmanifest.xml"];
if (![[NSFileManager defaultManager] fileExistsAtPath: manifPath isDirectory: NO])
return NO;
SimpleManifest *manifest = [[[SimpleManifest alloc] initWithPath:manifPath options:0 error:nil] autorelease];
NSLog(#"%#", manifest.defaultOrganization);
NSLog(#"%#", manifest.title);
return (manifest!=nil);
}
It gives me tons of "pointer being freed was not allocated" errors...
The thing changes if I comment out the NSLog calls above or just log the manifest.title property.
Project is not using ARC, so I'm sure I'm doing something wrong with memory management.
Can someone please help me understand where I'm doing wrong? Thanks!
There isn't anything obviously wrong with that code that would cause malloc errors. Best guess is that there is a bug in the CXMLDocument class/library or some mistake in the way you are using it.
Note that a "pointer being freed was not allocated" means that someone called free() (or dealloc, effectively) on a pointer to a piece of memory that was not allocated in the first place. It usually gives you a breakpoint you can set that will then give you a backtrace of exactly where it happened.
Some comments:
(1) Do not #try/#catch in that fashion. Just don't catch at all. The pattern you are using will hide any errors. Exceptions are not meant to be recoverable in iOS/Cocoa.
(2) You can create an NSString instance directly from a file; no need to load via NSData first.
(3) You should use ARC.
is addOperation:waitUntilFinished available only for iOS 4.3 and above?
Why do I get this warning, am I missing out on something?
Even tough I get this warning message my app works and with it?
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/NSOperationQueue_class/Reference/Reference.html
Edited with some code:
My HttpRequestWrapper Operation class:
in .h:
#interface HttpRequestWrapper : NSOperation
in .m
+ (id)httpRequestWrapper:(NSString *)xmlString withUser : (NSString *) user andPassword: (NSString *) password
{
HttpRequestWrapper * operation = [[self alloc] initWithString:xmlString andUser: user andPass: password];
//return [operation autorelease];
return operation;
}
- (id)initWithString: (NSString*) xmlString andUser: (NSString* )user andPass: (NSString *) pass
{
self = [super init];
if (self == nil)
return nil;
_urlPart = [xmlString copy];
_userString = [user copy];
_passString = [pass copy];
_isExecuting = NO;
_isFinished = NO;
[self main];
return self;
}
My caller line is like this:
httpRequestWrapper = [HttpRequestWrapper httpRequestWrapper:[NSString stringWithFormat:#"/list?xsl="] withUser:NULL andPassword: NULL];
[appDelegate.operationQueue addOperation:httpRequestWrapper waitUntilFinished:YES];
Thanks for the help.
Lily
I can assume that warning can be caused by your call [self main];. Note: the main method is called inside operation queue. I cannot see the reason to call it by yourself.
Or. I see the problem. There is no call like this:
[appDelegate.operationQueue addOperation:httpRequestWrapper waitUntilFinished:YES];
See reference more close:
- (void)addOperations:(NSArray *)ops waitUntilFinished:(BOOL)wait
you should pass the array of operations.
Fix to:
NSArray *opsArray = [NSArray arrayWithObject:httpRequestWrapper];
[appDelegate.operationQueue addOperations:opsArray waitUntilFinished:YES];
The third - check if appDelegate.operationQueue is not nil, i.e. if you created operationQueue before.