I'm working on an iPad app.
When the app is launched, user have to enter a number. With this id, I check in a csv file to get informations about the user.
Informations are saved with a singleton. This singleton is causing me problems :
step 1 : initialization of my singleton
+(ASMagasin*) sharedInstance {
if (myMagasin == nil){
myMagasin = [[ASMagasin alloc]init];
}
return myMagasin;
}
step 2 : I call my function which work with the csv file
- (id)init {
if (self = [super init]) {
NSError * error;
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString * num_magasin = [defaults objectForKey:kUserDefautNumMagasin];
[self loadMagFromCsv:filePathCsv withMagasin:num_magasin];
}
return self;
}
step 3 :
-(void)loadMagFromCsv:(NSString *)filePath withMagasin:(NSString *) num_magasin
{
NSError *error;
NSString *csvData = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:&error];
NSArray *gcRawData = [csvData componentsSeparatedByString:#"\n"];
NSArray *singleGC = [NSArray array];
for (int i = 0; i < gcRawData.count; i++)
{
NSString *nextGCString = [NSString stringWithFormat:#"%#", gcRawData[i]];
singleGC = [nextGCString componentsSeparatedByString:#","];
if ([singleGC[0]isEqualToString:num_magasin]){
_num=singleGC[0];
_libelle=singleGC[1];
_client_defaut_nom=singleGC[2];
_client_defaut_prenom=singleGC[3];
_client_defaut_tel=singleGC[4];
_client_defaut_mail=singleGC[5];
_cp=singleGC[6];
_ville=singleGC[7];
_pays=singleGC[8];
}
}
}
In this function, my variable error have this value before I initialize it :
(NSError *) error = 0x0000000000000001 domain: read memory from 0x19
failed (0 of 8 bytes read) - code: read memory from 0x11 failed (0 of
8 bytes read)
I don't know how to solve that and where is this error come from.
You are looking at random memory in the heap. It is meaningless. Your NSError hasn't even been initialised at this point, let alone assigned a value.
You can (and should) check error for a value after you execute the line
NSString *csvData = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:&error];
Before that error doesn't have a useful value.
I am posting it with a hope that it might help others facing same issue. Not sure about its exact cause, but I was experiencing it on a variable with _block before it. Removing _block fixed this issue and didn't give any warning either. Not sure what is going on here as I experienced it after updating to iOS 14. Will update if i find anything more interesting.
Related
When profiling the project, leaks were found and located to [NSString availableStringEncodings] in below function. No idea how to fix it :
- (NSString *)stringFromArchive:(NSString *)fileName inDirectory:(NSString *)folderName
{
NSString *rStr = #"";
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *folderPath = [documentsDirectory stringByAppendingPathComponent:folderName];
NSString *filePath = [folderPath stringByAppendingPathComponent:fileName];
NSFileManager * fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:filePath]) {
NSError *error;
const NSStringEncoding *encodings = [NSString availableStringEncodings];
NSStringEncoding encoding1;
while ((encoding1 = *encodings++) != 0)
{
rStr = [NSString stringWithContentsOfFile:filePath encoding:encoding1 error:&error];
if (rStr == nil) {
NSLog(#"error: %#", error);
}else{
break;
}
}
}
return rStr;
}
enter image description here
As seen often and it is very common to find Object declarations without initial value but found out in hard learning curve that a lot of leaks I found where bound to the easy peasy practice of unitialised declarations...
in Short: proper declaration helps the debugger/instruments as well.
NSError *error; very often seen. Can even leak, at least instruments finds it suspicious
NSError *error = nil; better, and also not NULL (which leaks for sure).
NSStringEncoding encoding1;
is an ENUM of NSUInteger and you seem to try to iterate thru all encodings from the bottom up until you find a match that properly reads the file.. then you may want to start not with an undefined value but with 1 which is the most simple one (NSASCIIStringEncoding). Which is why you use break; and compare against !=0, so it iterates up to the end of the zero-terminated list (tells you also why the enum does not start with 0 but NSASCIIStringEncoding==1).
and also when you use const NSStringEncoding *var and then *var++ what is increased? The pointers address or the value that was stored at that address.., *(var++) vs (*var)++. Both could work fine but one is more risky the other iterates thru encodings that do not exist. So guessing you want to go thru the list of encodings up to its zero-termination, take the values and try reading...
NSString *rStr = #"";
NSError *error = nil;
const NSStringEncoding *a = [NSString availableStringEncodings];
NSStringEncoding b = NSASCIIStringEncoding;
// increase addr of a, take value to b
// and check for b not 0==end-of-list
while ( (b=*(a++))!=0 ) {
//iterate until encoding allows reading successful..
rStr = [NSString stringWithContentsOfFile:#"yourfile.." encoding:b error:&error];
if (rStr != nil) break; // rStr has content, so end loop
// print reason on any missed trial
NSLog(#"error: %#", error);
}
edit: made a leak on purpose just for fun if Instruments will detect it, the leaking code i used...
extern "C" int leakingCppFunction(const char * s)
{
Example *e = new Example(s); //e is never deleted, so it leaks.
return e->getLen();
}
clearly finds this leak and none in the encoding iteration discussed above. So guessing your extension (NSStringOtherEncodings) is causing availableStringEncodings to leak.
Please see code below:
+ (void)splashDataFromJSON:(NSData *)objectNotation error:(NSError **)error
{
NSError *localError = nil;
NSDictionary *parsedObject = [NSJSONSerialization JSONObjectWithData:objectNotation options:0 error:&localError];
if (localError != nil) {
*error = localError;
}
NSMutableArray* btms = [[NSMutableArray alloc] init];
NSMutableDictionary* btmManufacturerResolutionDictionary = [[BTMCache sharedManager] btmManufacturerResolutionDictionary];
NSArray *results = [parsedObject valueForKey:#"results"];
NSLog(#"Count %d", parsedObject.count);
NSString* imageBaseUrl = [[parsedObject valueForKey:#"general"] valueForKey:#"image_base_url"];
imageBaseUrl = [imageBaseUrl stringByAppendingString:#"hdpi/"];
NSString* splashImageName = [[[parsedObject valueForKey:#"general"] valueForKey:#"splash"] valueForKey:#"img"];
NSString* splashAdvertiserURL = [[[[parsedObject valueForKey:#"general"] valueForKey:#"splash"] valueForKey:#"url"] copy];
NSMutableString* appendedString = [[NSMutableString alloc] init];
for(int i =0 ;i<[splashAdvertiserURL length]; i++) {
char character = [splashAdvertiserURL characterAtIndex:i];
printf(&character);
sleep(0.1);
if (character != "!")
{
[appendedString appendFormat:#"%c", character];
}
}
[[SplashData sharedManager] setSplashAdvertiserURL:appendedString];
[[SplashData sharedManager] setSplashImageName:splashImageName];
splashAdvertiserURL = [[SplashData sharedManager] splashAdvertiserURL];
}
The point of interest is in splashAdvertiserURL. When I receive this data and print it out using po, it comes out as "https://radar.com/ref/go/84/". This is fine and what was expected. When I look at the incoming data in JSONLint it looks like this:
"general": {
"image_base_url": "https:\/\/radar.com\/img\/manufacturers\/",
"splash": {
"img": "image1.png",
"url": "https:\/\/radar.com\/ref\/go\/84\/"
}
},
As you can see, further on I put the NSString into a singleton with an NSString property. Nothing abnormal here. I then proceed to retrieve it to see that all is ok. Further to this the program continues. In another class I wish to retrieve this information, and when I try and do that, it throws EXC_BAD_ACCESS. There appears to be garbage in there.
I then put in a loop in the code as you can see to print out the characters one at a time. Very curiously, when I print that out using po I get:
https://
r
a
d
ar.com/ref/go/8 4!/"
Exactly in that format. If I then proceed to hardcode the string https://radar.com/ref/go/84/ - including escape characters and everything, then all works fine. No issues. If I handle a normal string incoming without escape characters it stores fine in the singleton as well, no issue. enter code here
I am pretty stumped here as to what is going on. Can someone assist?
Thank you
For URL you received as string you need to encode before use it to in your app. Have a look at below code:
NSString *sampleUrl = #"https:\/\/radar.com\/ref\/go\/84\/";
NSString *encodedUrl = [sampleUrl stringByAddingPercentEscapesUsingEncoding:
NSUTF8StringEncoding];
I'm new to Objective C and XML, so this is going to look pretty rough.
My code is as follows:
#import "dbCommunicator.h"
#import "BookInfo.h"
#import "TouchXML.h"
#implementation dbCommunicator
-(void)getNextBooks {
if(self.bookListing == nil) {
for(int i = 0; i < 5; i++) {
BookInfo *newBook = [[BookInfo alloc] init];
[self.bookListing addObject:newBook];
}
self.currentPage = 0;
}
if(self.currentPage == 10) {
self.currentPage = 0;
}
NSString *test = #"<Authors><Book.Author><Id>1026</Id><Name>Mark Twain</Name></Book.Author></Authors>";
NSString *bookQueryString = [NSString stringWithFormat:#"http://bookworm.azurewebsites.net/api/book/list/5/%d",_currentPage];
NSURL *bookQueryURL = [NSURL URLWithString: bookQueryString];
self.currentPage++;
NSError *theError = NULL;
NSDictionary *mappings = [NSDictionary dictionaryWithObjectsAndKeys:
#"http://schemas.datacontract.org/2004/07/Bookworm.Models.ResponseModels",
#"datacontract",
nil];
CXMLDocument *xmlReturn = [[CXMLDocument alloc] initWithXMLString:test options:0 error:&theError];
NSArray *returnedBooks = [xmlReturn nodesForXPath:#"//Authors" error:&theError];
for(CXMLElement *resultElement in returnedBooks) {
NSLog(#"%s", "We actually got here");
}
}
There's a lot of junk code in there at the moment. The intention is to pull an XML file from a database and put its information into an array of BookInfo classes. For the moment, I simplified by just using a test XMLstring to ensure it wasn't an issue with what the database was sending me. This makes the dictionary (to deal with the namespace issues TouchXML has) superfluous. Anyways.
It always crashes with a unrecognized selector error on this line:
[theArray addObject:[CXMLNode nodeWithLibXMLNode:theNode freeOnDealloc:NO]];
In this context:
if (xmlXPathNodeSetIsEmpty(theXPathObject->nodesetval))
theResult = [NSArray array]; // TODO better to return NULL?
else {
NSMutableArray *theArray = [NSMutableArray array];
int N;
for (N = 0; N < theXPathObject->nodesetval->nodeNr; N++) {
xmlNodePtr theNode = theXPathObject->nodesetval->nodeTab[N];
[theArray addObject:[CXMLNode nodeWithLibXMLNode:theNode freeOnDealloc:NO]];
}
}
and with that, I'm totally at a loss. I've tried plenty of things, scoured every StackOverflow post even closely related and tried their fixes, nothing's working. Any suggestions?
array is a static method on NSArray a super class of NSMutableArray that returns an NSArray object. NSArray does not respond to the method addObject:
Try instead NSMutableArray *theArray = [NSMutableArray new];
The problem probably is on this selector:
nodeWithLibXMLNode:freeOnDealloc:
if you try to see your crash log in the console, should be written the selector unrecognized and should be this. If so check the class reference of the CXMLNode.
I checked now, and that method doesn't exists. Just a method might be useful for you in some way, that is:
- (NSArray *)nodesForXPath:(NSString *)xpath error:(NSError **)error;
Enjoy ;)
during a loop process, my App crash without error. The array count is equal to 175260. With profiler I don't have leaks, so I don't know why the App exit, maybe the CPU usage 100% during a lot of time?
Thank you for your help.
Just this code following crash the App :
for(unsigned int i = 0; i <14;i++)
{
if(findSensor[i]==YES)
{
for(unsigned int j = 1; j <[array count];j++)
{
#autoreleasepool {
if([[[[array objectAtIndex:j] componentsSeparatedByString:#";"] objectAtIndex:0] isEqualToString:[NSString stringWithFormat:#"%d",10*(i+1)]])
{
//Code here
}
}
}
}
}
The full code is :
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *fileName = [NSString stringWithFormat:#"%#/%#",documentsDirectory,[ibNavSettings interfaceSettings].selectedFileToDataBase];
NSFileHandle *fh = [NSFileHandle fileHandleForReadingAtPath:fileName];
NSFileHandle *output = [NSFileHandle fileHandleForReadingAtPath:[NSString stringWithFormat:#"%#/%#10",documentsDirectory,[ibNavSettings interfaceSettings].selectedFileToDataBase]];
if(output == nil)
{
NSManagedObjectContext *context = [self managedObjectContext];
_recordlocal = [NSEntityDescription insertNewObjectForEntityForName:#"RECORD" inManagedObjectContext:context];
_recordlocal.date = [ibNavSettings interfaceSettings].selectedFileToDataBase;
NSData *inputData = [NSData dataWithData:[fh readDataToEndOfFile]];
NSString *inputString = [[NSString alloc] initWithData:inputData encoding:NSUTF8StringEncoding];
NSArray *array = [[NSArray alloc] initWithArray:[inputString componentsSeparatedByString:#"\n"]];
for(unsigned int i = 0; i <14;i++)
{
if(findSensor[i]==YES)
{
[[NSFileManager defaultManager] createFileAtPath:[NSString stringWithFormat:#"%#/%#%d",documentsDirectory,[ibNavSettings interfaceSettings].selectedFileToDataBase,10*(i+1)] contents:nil attributes:nil];
NSMutableString *saveString = [[NSMutableString alloc] init];
int count = 0;
for(unsigned int j = 1; j <[array count];j++)
{
#autoreleasepool {
if([[[[array objectAtIndex:j] componentsSeparatedByString:#";"] objectAtIndex:0] isEqualToString:[NSString stringWithFormat:#"%d",10*(i+1)]])
{
[saveString appendString:[array objectAtIndex:j]];
[saveString appendString:#"\n"];
if(i == 0)
count++;
progress++;
pourcent = progress/total;
load = pourcent*100;
if(load%5==0)
[self performSelectorInBackground:#selector(changeUI:)withObject:[NSNumber numberWithFloat:(pourcent)]];
}
}
}
[saveString writeToFile:[NSString stringWithFormat:#"%#/%#%d",documentsDirectory,[ibNavSettings interfaceSettings].selectedFileToDataBase,10*(i+1)] atomically:YES encoding:NSUTF8StringEncoding error:nil];
if(i == 0)
_recordlocal.count = [[NSNumber alloc] initWithInt:(count/50)];
}
}
_recordlocal.load = [[NSNumber alloc] initWithBool:YES];
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Core data error %#, %#", error, [error userInfo]);
abort();
}
I would guess that your app is crashing without a readable exception because it is running out of available RAM, especially since you indicated that it is running through a large number of iterations.
For a test, I would recommend doing what Rikkles suggests with the autorelease pool. In addition, since the value of i (and as a result the comparison string) rarely changes, I would create that string outside the j loop as well. This would avoid the creation of a lot of extra strings laying around.
Beyond that, since it appears that you are looking for a string at the beginning of a string that is delimited by a semicolon, I would recommend instead of doing componentsSeparatedByString and then examining element zero that you use the NSString method hasPrefix to check for the condition you are looking for.
Here is an example:
for(unsigned int i = 0; i <14;i++)
{
NSString *searchString = [NSString stringWithFormat:#"%d;", 10*(i+1)];
if(findSensor[i]==YES)
{
for(unsigned int j = 1; j <[array count];j++)
{
if([[array objectAtIndex:j] hasPrefix:searchString])
{
//Code here
}
}
}
}
(I hope this compiles and runs, if it doesn't it should require more than minor tweaks. I am away from my Mac right now.)
If this doesn't help, then something going on inside //Code here must be the culprit.
Why are you creating [array count] autoreleasepools? What's the point of creating so many of them? It could crash because of that. Put the #autoreleasepool outside the for loop.
The only reason I could think that you would do that is if you create so many transient objects inside each iteration of the for loop that you'd want to get rid of them as soon as you got out of the iteration. But there are other ways to do that, including reusing those objects within each iteration.
First suggestion
Just use fast enumeration for the inner loop, you aren't actually using the index 'j' for anything
https://developer.apple.com/library/mac/documentation/General/Conceptual/DevPedia-CocoaCore/Enumeration.html
Second suggestion
Put some NSLog's in place, it will slow everything down, but you need to figure out what point you are failing at. That will help point everyone in the right direction.
Third suggestion
Actually use NSError objects and output their value if an error is thrown:
NSError *writeError = nil;
[saveString writeToFile:[NSString stringWithFormat:#"%#/%#%d",documentsDirectory,[ibNavSettings interfaceSettings].selectedFileToDataBase,10*(i+1)]
atomically:YES
encoding:NSUTF8StringEncoding
error:&writeError];
if(error != nil) NSLog(#"error writing file: %#", [[writeError userInfo]description]);
Fourth suggestion
You appear to try to be updating the UI from a background thread. This will not work or will cause a crash. UI code can only be called from a main thread. So dont do this:
[self performSelectorInBackground:#selector(changeUI:)withObject:[NSNumber numberWithFloat:(pourcent)]];
If you are already on a background thread this will probably crash because you are creating threads on threads on threads. You instead would want to call:
[self performSelectorOnMainThread:#selector(changeUI:)withObject:[NSNumber numberWithFloat:(pourcent)]];
Fifth suggestion
You may be going over the maximum length for NSString (it's big but I did it once on accident before). You should probably just be appending the file on each iteration of the loop instead, so you don't have an ever growing NSMutableString:
NSString *path = [NSString stringWithFormat:#"%#/%#%d",documentsDirectory,[ibNavSettings interfaceSettings].selectedFileToDataBase,10*(i+1)]
NSFileHandle *fh = [NSFileHandle fileHandleForWritingAtPath:filePath];
NSData *newLine = [#"\n" dataUsingEncoding:NSUTF8StringEncoding];
for(NSString *rowString in array)
{
if([[[rowString componentsSeparatedByString:#";"] objectAtIndex:0] isEqualToString:[NSString stringWithFormat:#"%d",10*(i+1)]])
{
NSData *stringData = [rowString dataUsingEncoding:NSUTF8StringEncoding];
[fh truncateFileAtOffset:[fh seekToEndOfFile]];
[fh writeData:stringData];
[fh truncateFileAtOffset:[fh seekToEndOfFile]];
[fh writeData:newLine];
if(i == 0)
count++;
progress++;
pourcent = progress/total;
load = pourcent*100;
if(load%5==0)
[self performSelectorOnMainThread:#selector(changeUI:)withObject:[NSNumber numberWithFloat:(pourcent)]];
}
}
}
And this has the added benefit of helping you ditch the autoreleasepools
This was invalid
If your array does in fact have 175260 rows, that is probably your issue. You are looping using unsigned int as your index var. Unsigned ints in c only have a max value of 65535. Use an unsigned long int, max 4294967295.
This question already has an answer here:
EXC_BAD_ACCESS using iCloud on multiple devices
(1 answer)
Closed 9 years ago.
I have a pretty simple IOS app using iCloud document storage. Everything was working and then at some point I began encountering a EXC_BAD_ACCESS error in my document load method for at least one iCloud document, although most files load just fine.
- (BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError *__autoreleasing *)outError {
file = (NSFileWrapper*) contents;
NSFileWrapper *infoFile = [[file fileWrappers] objectForKey:InfoFile];
NSData *infoData = [infoFile regularFileContents];
if(nil != infoData) {
NSPropertyListFormat format = NSPropertyListBinaryFormat_v1_0;
NSError *propertyListError;
// EXC_BAD_ACCESS occurs here
NSDictionary *dictionary = [NSPropertyListSerialization propertyListWithData:infoData options:NSPropertyListImmutable format:&format error:&propertyListError];
if(nil == propertyListError) {
_name = [dictionary objectForKey:#"name"];
_date = [dictionary objectForKey:#"date"];
_index = [dictionary objectForKey:#"index"];
_paperSize = [GritzPaperSizeEnum enumWithType:[dictionary objectForKey:#"paperSize"]];
TFLog(#"loading doc %#", _name);
_pages = [[NSMutableArray alloc] init];
for (NSString *key in file.fileWrappers) {
NSFileWrapper *subDir = [[file fileWrappers] objectForKey:key];
if(subDir.isDirectory) {
GritzPage *page = [[GritzPage alloc] initFromFile:subDir];
[_pages addObject:page];
}
}
_currentPage = [_pages objectAtIndex:0];
return YES;
}
}
return NO;
}
I would expect that I can 'catch' and handle bad data and ignore the corrupt file; but I can't seem to figure out how. A EXC_BAD_ACCESS error causes the app to crash.
What should I be doing differently to determine ahead of time that the data or file is going to fail and skip it (or delete it).
verify it is a NSFileWrapper using isKindOfClass, else treating it as one is weird (also look at the given typeName :))
using a #try { .. } #catch construct to catch any exception wont work in THIS case though as you cause a BAD_ACCESS which is a UNIX SIGNAL
The NSPropertyListFormat variable format should be declare as point. And i think you should call the propertyListWithData: Method with format as pointer not with the address of format.