ios application crashes randomly - ios

I have an NSMutableArray (timeZoneTree) that is loaded using this line
timeZoneTree = [[Timezone getTimeZoneTree] retain];
The definition of Timezone is
#interface Timezone : NSObject
{
NSString *name;
int rawOffset;
}
and the getTimeZoneTree method is the following:
+ (NSMutableArray*) getTimeZoneTree
{
if (offsetGroups == nil)
{
// initialize a new mutable array
offsetGroups = [[[NSMutableArray alloc] init] autorelease];
// build the path to the timezones file
NSString *path = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"TimeZones.dat"];
// get the data of the file
NSData *data = [[NSData alloc] initWithContentsOfFile:path];
BufferReader *reader = [[BufferReader alloc] initWithBuffer:(const char*)[data bytes] length:[data length]];
OffsetGroup *curOffsetGroup;
RegionGroup *curRegionGroup;
int offsetGroupCount = [reader readInt32];
// go through all raw offset groups
for(int curOffsetGroupIndex=0; curOffsetGroupIndex<offsetGroupCount; ++curOffsetGroupIndex)
{
int rawOffset = [reader readInt32];
// initialize a new offset group
curOffsetGroup = [[OffsetGroup alloc] init];
curOffsetGroup.rawOffset = rawOffset;
// and add it to the offset groups
[offsetGroups addObject:curOffsetGroup];
int regionGroupCount = [reader readInt32];
// go through all region group of the war offset group
for(int curRegionGroupIndex=0; curRegionGroupIndex<regionGroupCount; ++curRegionGroupIndex)
{
int regionNameLength = [reader readInt8];
char *regionNameUTF8 = malloc(regionNameLength + 1);
[reader readBytes:regionNameUTF8 withLength:regionNameLength];
regionNameUTF8[regionNameLength] = '\0';
// initialize a new region group
curRegionGroup = [[RegionGroup alloc] init];
curRegionGroup.name = [NSString stringWithCString:regionNameUTF8 encoding:NSUTF8StringEncoding];
// and add it to the region groups of the offset group
[curOffsetGroup.regionGroups addObject:curRegionGroup];
int timeZoneCount = [reader readInt32];
// go through all time zones
for(int curTimeZoneIndex=0; curTimeZoneIndex<timeZoneCount; ++curTimeZoneIndex)
{
int timeZoneNameLength = [reader readInt8];
char *timeZoneNameUTF8 = malloc(timeZoneNameLength+1);
[reader readBytes:timeZoneNameUTF8 withLength:timeZoneNameLength];
timeZoneNameUTF8[timeZoneNameLength] = '\0';
// create a new time zone name
NSString *timeZoneName = [NSString stringWithCString:timeZoneNameUTF8 encoding:NSUTF8StringEncoding];
// if the name is not nil
if (timeZoneName != nil)
// then add it to the region group
[curRegionGroup.timeZones addObject:timeZoneName];
free(timeZoneNameUTF8);
}
[curRegionGroup release];
free(regionNameUTF8);
}
[curOffsetGroup release];
}
[reader release];
[data release];
}
return offsetGroups;
}
What is the problem? Why the code crashed sometimes on this line? I am building with ROOTSDK 7.0

I'd say it was the use of autorelease in what appears to be an global variable:
offsetGroups = [[[NSMutableArray alloc] init] autorelease];
// ^^^^^^^^^^^
This means as soon as the thread hits an autorelease pool drain (in the run loop, probably) the array is released.
Remove the use of autorelease, and if you do this you need to remove the retain on the first line of code.

Related

Corebluetooth NSMutableArray with characteristic values

Using the setNotify=YES property I want to create an array populated with the converted 'characteristic.value' values from the below code (Xcode7.2). So far I've only got a null array, all the same values, or only one value at a time
I've implemented
#property (strong, nonatomic) NSMutableArray *voltageArray; //didn't work so
I deleted and tried the following...
The method that I tried to use it in was didUpdateCharacteristic,
if (characteristic.isNotifying)
{
NSString *stringFromData = [[NSString alloc] initWithData:characteristic.value
encoding:NSUTF8StringEncoding];
NSNumber *number = [NSNumber numberWithUnsignedShort:stringFromData];
number = #([number floatValue] *5/2034);
NSLog(#"Characteristic Value: %#", _voltageArray);
}
I also tried the following in the didUpdateValueMethod, along with countless other things,
_voltageArray=[[NSMutableArray alloc] init];
[self.voltageArray addObject:number];
NSLog(#"Array: %#", _voltageArray);
The best I've gotten is all the same value or all null. How do I get these unsigned voltage values into an array?
Updated Solution
#property (strong, nonatomic) NSMutableArray *voltageArray;
-(void)peripheral: didUpdateValueForCharacteristic: error:{
if (characteristic.isNotifying)
{
if(!self.voltageArray){//Checks if voltageArray has been initialised
self.voltageArray = [[NSMutableArray alloc] init];//If not, initialise the array
}
// generate characteristic value
NSString *stringFromData = [[NSString alloc] initWithData:characteristic.value encoding:NSUTF8StringEncoding];
unsigned short baseValue = (unsigned short)stringFromData;
NSNumber *number = [NSNumber numberWithUnsignedShort:baseValue];
number = #([number floatValue] *5/2034);
NSLog(#"Characteristic Value: %#", number);
// give potential recording
self.voltageLabel.text = [NSString stringWithFormat:#"%#",number];
// datasource points y-axis
[_voltageArray addObject:number];
NSLog(#"Array: %#", _voltageArray);
There are a couple of errors that may be causing your issue.
First:
NSString *stringFromData = [[NSString alloc] initWithData:characteristic.value
encoding:NSUTF8StringEncoding];
NSNumber *number = [NSNumber numberWithUnsignedShort:stringFromData];
number = #([number floatValue] *5/2034);
The first method requires a unsigned short type as an input, but you are passing a NSString. You could do this instead:
NSString *stringFromData = [[NSString alloc] initWithData:characteristic.value
encoding:NSUTF8StringEncoding];
unsigned short baseValue = (unsigned short)stringFromData.intValue;
float convertedValue = (float)baseValue *5.0/2034.0; //5.0/2034.0 -> so it typecasts the result value as a float, not int
NSNumber *number = [NSNumber numberWithFloat: convertedValue];
[self.voltageArray addObject:number];
Second, it seems that you are initialising your array every time the didUpdateValueForCharacteristic gets called. Try this instead:
-(void)peripheral: didUpdateValueForCharacteristic: error:{
if (characteristic.isNotifying)
{
if(!self.voltageArray){//Checks if voltageArray has been initialised
self.voltageArray = [[NSMutableArray alloc] init];//If not, initialise the array
}
NSString *stringFromData = [[NSString alloc] initWithData:characteristic.value
encoding:NSUTF8StringEncoding];
unsigned short baseValue = (unsigned short)stringFromData.intValue;
float convertedValue = (float)baseValue *5.0/2034.0;
NSNumber *number = [NSNumber numberWithFloat: convertedValue];
[self.voltageArray addObject:number];
}
}
Hope this helps.

NSCoding - saving array to file or nsdefaults

Afternoon all,
Working on my first iphone app.
I am trying to save an array of an array either to file or nsuserdefaults.
Data is like this...
MainArray (contains 3 below arrays)
Array1 (contains 3 strings)
Array2 (contains 3 strings)
Array3 (contains 3 strings)
So far I've been reading about saving things to nsuserdefaults, and saving to file. Not sure which is the right way or benefits of either but I start trying saving to file.
below is my custom object to save information.
#implementation UserSettingsClass
+ (instancetype)sharedUserData{
static id sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [self loadInstance];
//sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
-(void)encodeWithCoder:(NSCoder *)encoder{
[encoder encodeObject:self.arrayUserSettings forKey:#"someArray"];
[encoder encodeObject:self.userDescription forKey:#"testDesc"];
}
- (id)initWithCoder:(NSCoder *)decoder{
if ((self = [super init])){
self.userDescription = [decoder decodeObjectForKey:#"testDesc"];
self.arrayUserSettings = [decoder decodeObjectForKey:#"someArray"];
}
return self;
}
+(NSString *)filePath{
static NSString *filePath = nil;
if (!filePath){
filePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingPathComponent:#"gamedata"];
}
return filePath;
}
+(instancetype)loadInstance{
NSData *decodedData = [NSData dataWithContentsOfFile:[UserSettingsClass filePath]];
if (decodedData){
UserSettingsClass *gameData = [NSKeyedUnarchiver unarchiveObjectWithData:decodedData];
return gameData;
}
return [[UserSettingsClass alloc] init];
}
-(void)save{
NSData *encodedData = [NSKeyedArchiver archivedDataWithRootObject:self];
[encodedData writeToFile:[UserSettingsClass filePath] atomically:YES];
}
and here is the main class where I am trying to use it.
//initialize variables
//_userArray = [[NSMutableArray alloc] init];
//_userDescription = [[NSString stringWithFormat:#"testDescription"] init];
//_userLoginID = [[NSString stringWithFormat:#"testLogin"] init];
//_userPW = [[NSString stringWithFormat:#"testPassword"] init];
// [_userArray addObject:[UserSettingsClass sharedUserData].userDescription];
//[_userArray addObject:_userLoginID];
//[[UserSettingsClass sharedUserData].arrayUserSettings addObject: [UserSettingsClass sharedUserData].userDescription];
NSMutableArray *tempArray = [UserSettingsClass sharedUserData].arrayUserSettings;
//[_userArray addObject:_userPW];
//save data to shared singleton class
//[[UserSettingsClass sharedUserData].arrayUserSettings addObject:_userArray];
//NSMutableArray *tempArray = [[NSMutableArray alloc] init];
//tempArray = [UserSettingsClass sharedUserData].arrayUserSettings;
//[UserSettingsClass sharedUserData].highScore = 10;
//int i = [UserSettingsClass sharedUserData].highScore;
//[UserSettingsClass sharedUserData].userDescription = #"hello";
NSString *temp2 = [UserSettingsClass sharedUserData].userDescription;
I am able to save the single string, but I must be doing something wrong.
The single string I saved was just to see if I can get it working. My goal is to save the main array to file (or nsuserdefaults), which contain about 3 objects (array)... and each of those arrays contains 3 strings each.
any I doing something blatantly wrong?
You are trying to hard.
If what you want to save is just NSArrays and NSStrings to you do not need so add an NSCoding, these types already conform to NSCoding. Just Archive to a file or "shudder" save to NSUserDefaults "/shudder".
It is really better to create a Data Model class and use NSArchiver to save and restore from a file in the Documents directory.

How to split NSString and Rejoin it into two NSStrings?

I have a NSString like this one:
NSString* allSeats = #"1_Male,2_Female,3_Female,4_Male";
I want to split the NSString based on the keywords _Male & _Female and then make two separate strings like these:
NSString* maleSeats = #"1,4";
NSString* femaleSeats = #"2,3";
based on the contents of allSeats variable declared above.
How it will be possible to split NSString and then make 2 seperate strings?
You have to do it yourself. There is no "all done" solution. There are a few ways to do it.
Note: I didn't try my code, I just wrote it, it may don't even compile. But the important thing is that you get the whole idea behind it.
One way could be this one:
NSString *maleSufffix = #"_Male";
NSString *femaleSufffix = #"_Female";
NSMutableArray *femaleSeatsArray = [[NSMutableArray alloc] init];
NSMutableArray *maleSeatsArray = [[NSMutableArray alloc] init];
NSArray *array = [allSeats componentsSeparatedByString:#","];
for (NSString *aSeat in array)
{
if ([aSeat hasSuffix:maleSuffix])
{
[maleSeatsArray addObject:[aSeat stringByReplacingOccurencesOfString:maleSuffix withString:#""]];
}
else if ([aSeat hasSuffix:femaleSuffix])
{
[femalSeatsArray addObject:[aSeat stringByReplacingOccurencesOfString:femaleSuffix withString:#""]];
}
else
{
NSLog(#"Unknown: %#", aSeat);
}
}
NSString *maleSeats = [maleSeatsArray componentsJoinedByString:#","];
NSString *femaleSeats = [femaleSeatsArray componentsJoinedByString:#","];
Of course, you could use different methods on array, enumerating it, use a NSMutableString instead of a NSMutableArray (for femaleSeatsArray or maleSeatsArray, and use adequate methods then in the for loop).
I derived an idea from Larme's Clue and it works as :
Make a method as and call it anywhere :
-(void)seperateSeat
{
maleSufffix = #"_Male";
femaleSufffix = #"_Female";
femaleSeatsArray = [[NSMutableArray alloc] init];
maleSeatsArray = [[NSMutableArray alloc] init];
array = [self.selectedPassengerSeat componentsSeparatedByString:#","];
for (aSeat in array)
{
if ([aSeat hasSuffix:maleSufffix])
{
aSeat = [aSeat substringToIndex:[aSeat length]-5];
NSLog(#"%# is value in final seats ::",aSeat );
[maleSeatsArray addObject:aSeat];
}
else if ([aSeat hasSuffix:femaleSufffix])
{
aSeat = [aSeat substringToIndex:[aSeat length]-7];
NSLog(#"%# is value in final seats ::",aSeat );
[femaleSeatsArray addObject:aSeat];
}
}
totalMales = [maleSeatsArray componentsJoinedByString:#","];
totalFemales = [femaleSeatsArray componentsJoinedByString:#","];
NSLog(#"maleSeatsAre::::%#",totalMales);
NSLog(#"maleSeatsAre::::%#",totalFemales);
}

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

NSThread Causing memory Leaks in iPhone

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.

Resources