NSThread Causing memory Leaks in iPhone - ios

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.

Related

Corruption of NSString or encoding issue in Objective C

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];

NSMutableDictionary inside JSONModel - EXC_BAD_ACCESS KERN_INVALID_ADDRESS

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

make retain count in ARC

I am using an external library in my project which is being build in an ARC environment. As per the library the socket object gets deallocated only when the retain count=0. As far as I know its not liable to use retain count in ARC but I am forced to remove all the reference of the socket object which is not possible in my project. How can I resolve this issue? A gist of code issue is below:
-(void)callConnect{
for(int i = 0; i<[userArray count];i++){
[self connect:(NSString*)[userArray objectAtIndex:i]];
}
}
-(void)connect:(NSString *)username{
RTMPCLient *socket = [[RTMPClient alloc] init];
BroadCastClient *stream = [[BroadCastClient alloc] initWithClient:socket];
NSMutableDictionary *stream = [NSMutableDictionary dictionaryWithObject:stream forKey:username];
}
-(void)disconnect{
for(int i = 0; i<[userArray count];i++){
[stream objectForKey:[NSString stringWithFormat:#"%#",[userArray objectAtIndex:i]]] = nil; //error on this line
BroadCastClient *tempStream = [stream objectForKey:[userArray objectAtIndex:i]];
tempStream = nil;
}
}
I am trying to make the stream object nil which gives an error. Cannot save it another variable as it increases the references of socket object.By making the tempStream nil doesn't affect the original instance created.
I want to remove the reference of socket object from stream in the disconnect method. How can I do so?
ARC will put the invisible release message in your code (in connect), but the array will have strong reference on them, so they will stay in memory. All you have to do in disconnect remove all the objects from your collection ([stream removeAllObjects] and [userArray removeAllObjects]) and the collection will release them.
UPDATE:
By following your code I see the following:
In this code you are creating an instance of BroadCastClient and adding it to NSDictionnary (stream), but NSDictionary has no reference to it, so it will be deallocated after the method call
-(void)callConnect{
for(int i = 0; i<[userArray count];i++){
[self connect:(NSString*)[userArray objectAtIndex:i]];
}
}
-(void)connect:(NSString *)username{
RTMPCLient *socket = [[RTMPClient alloc] init];
BroadCastClient *stream = [[BroadCastClient alloc] initWithClient:socket];
NSMutableDictionary *stream = [NSMutableDictionary dictionaryWithObject:stream forKey:username];
}
Now here the disconnect stream dictionary (I don't know what is this object, because in your code I don't see any creating or adding to it) the object BroadCastClient is retained by the dictionary, so just removing this object from the dictionary will free it from memory (assuming you have no other strong reference to it)
-(void)disconnect{
for(int i = 0; i<[userArray count];i++){
[stream objectForKey:[NSString stringWithFormat:#"%#",[userArray objectAtIndex:i]]] = nil; //error on this line
BroadCastClient *tempStream = [stream objectForKey:[userArray objectAtIndex:i]];
tempStream = nil;
}
}
I would recommend some refactoring for your code, but before that please have some time to read this guid: https://developer.apple.com/library/mac/documentation/cocoa/conceptual/memorymgmt/Articles/mmPractical.html
IN ARC, you have to just make the objects to nil to maintain RC. So you can do it in the following way.
-(void)disconnect{
socket = nil;
stream = nil;
stream = nil;
}
-(void)connect:(NSString *)username{
if (socket != nil )
socket = nil;
RTMPCLient *socket = [[RTMPClient alloc] init];
if (stream != nil )
stream = nil;
BroadCastClient *stream = [[BroadCastClient alloc] initWithClient:socket];
NSMutableDictionary *stream = [NSMutableDictionary dictionaryWithObject:stream forKey:username]; // Make it using alloc...then you must use nil only
}
It looks like stream is an instance variable of type NSMutableDictionary *. So if you want to remove the references in your stream dictionary, you could do it like this:
- (void)disconnect {
for (int i = 0; i<[userArray count]; i++) {
[stream removeObjectForKey:[userArray objectAtIndex:i]];
}
}
// Alternative version using Fast Enumeration:
- (void)disconnect {
for (id key in userArray) {
[stream removeObjectForKey:key];
}
}
But if all you want to do is remove all references from stream, simply do:
- (void)disconnect {
[stream removeAllObjects];
}

Objective C memory management - "pointer being freed was not allocated" errors

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.

iOS - App crash without error during loop process

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.

Resources