NSString and crashes - objective-c

I have this code:
-(void)getData:(NSString *)data: (id) tv: (id) soc
{
NSLog(#"\nin get data with data\n");
NSLog(data);
After a few hours the app crashes and it reaches in get data with data and doesn't print the data so it's crashing on the print of the data. The debug references the failure in something like a string length function. In XCode, data has a warning that it is not a string literal and potentially insecure. Now my experience tells me the most likely culprit is data is somehow null. But it also printed in the log something that looks like it received a typical message. it said it has 131 bytes from the socket. When i tested and it crashed last time it has 189 bytes. but it never prints it.
The data is sent in from the socket liket this in receive data:
UInt8 buffer[len];
NSLog(#"Received %d bytes from socket %d\n",
len, CFSocketGetNative(s));
CFDataGetBytes(df, range, buffer);
NSString *oldtext = [mTextViewAlias text];
char buffer2[len];
for(int a=0; a<len; a++)
buffer2[a]=buffer[a];
NSMutableData *buffer3 = [[NSMutableData alloc] init];
[buffer3 appendBytes:buffer2 length:len];
NSString *newdata = [[NSString alloc] initWithData: buffer3 encoding:NSASCIIStringEncoding];
and the call to the class method that is crashing when it prints the data is just:
[mytelnet getData:newdata:mTextViewAlias:(__bridge id)(s)];
Could i have a memory leak or something and after a few hours i'm out of memory causing the assignment of new data to be null even if i start out with some 100 bytes? Would it crash only when i tried to print data in the nslog and not earlier if it failed to allocate memory?
Mike

Instead of
NSLog(data);
use
NSLog(#"%#",data);
That may not solve your root cause but you get rid of the warning and may get some more reasonalbe and helpful debug output.
You might get rid of that crash too. Hovever this is not a promise.

Related

How to reliably retrieve NSData objects from NSInputStream in XCode

So my application works along these lines:
An iPod continuously sends NSDictionaries that contain: an image encoded in JPEG and some image properties as NSStrings.
The NSDictionary is encoded using NSPropertyListSerialization with the format BinaryFormat_v1_0 and sent in packets of 1024 bytes via NSStream to the central computer running an app on OSX.
The OSX app receives the data packets, continuously appending to a single NSMutableData object, until it sees the first packet of the next NSData object (which in binary format I've found starts as 'bplist').
The NSData is converted back to an NSDictionary to be used by the OSX app, by calling NSPropertyListSerialization.
Once the NSData was successfully converted (or not),the NSData object is set back to zero to start reading the next round of packets.
A few more notes: both the NSInputStream and NSOutput streams are running on their respective device's currentRunLoop in NSDefaultRunLoopMode.
When running this process, sometimes the conversion back to NSDictionary works fine with no errors (about 1/3 of the attempts), but the other times the conversion returns this error:
Error: Failed to convert NSData to NSDict : Error Domain=NSCocoaErrorDomain Code=3840 "Unexpected character b at line 1" UserInfo={NSDebugDescription=Unexpected character b at line 1, kCFPropertyListOldStyleParsingError=Error Domain=NSCocoaErrorDomain Code=3840 "Conversion of string failed." UserInfo={NSDebugDescription=Conversion of string failed.}}
Following are the parts of the program that parse the data from the stream:
... method to handle stream events:
-(void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode {
switch(eventCode) {
case NSStreamEventHasBytesAvailable: {
uint8_t buf[1024];
unsigned int len = (unsigned)[(NSInputStream *)aStream read:buf maxLength:1024];
if(len) {
[self handleEventBuffer:buf WithLength:len];
}
...
... and the method that takes care of the data:
-(void)handleEventBuffer:(uint8_t*)buf WithLength:(unsigned int)len {
...
NSString *bufStr = [NSString stringWithFormat:#"%s",(const char*)buf];
if ([bufStr containsString:#"bplist00"] && [self.cameraData length] > 0) {
// Detected new file, enter in all the old data and reset for new data
NSError *error;
NSDictionary *tempDict = [[NSDictionary alloc] init];
tempDict = [NSPropertyListSerialization propertyListWithData:self.cameraData
options:0
format:NULL
error:&error];
if (error != nil) {
// Expected good file but no good file, erase and restart
NSLog(#"Error: Failed to convert NSData to NSDict : %#", [error description]);
[self.cameraData setLength:0];
}
...
[self.cameraData setLength:0];
[self.cameraData appendBytes:buf length:len];
} else {
// Still recieving data
[self.cameraData appendBytes:buf length:len];
}
So, the question that I'm getting at is:
How can I fix my parsing method to give me reliable results that don't randomly fail to convert?
OR is there a better way than this to parse buffer streams for this purpose?
OR am I just doing something stupid or missing something obvious?
You appear to be relying on each write to the stream resulting in a matching read of the same size, do you know this is guaranteed by NSStream? If not then any read could contain parts of two (or more) of your encoded dictionaries, and you would get the parsing errors you see.
Alternative approach:
For each encoded dictionary to send:
Write end:
Send a message containing the size in bytes of the encoded dictionary that will follow.
Write the encoded dictionary in chunks, the last chunk may be short
Repeat
Read end:
Read the size message specifying its exact length in bytes.
Read the encoded dictionary in chunks, making sure you read only the number of bytes reported by (1).
Repeat.
Provided you are using a reliable communication stream this should enable you to read each encoded dictionary reliably. It avoids you trying to figure out where the boundary between each encoded dictionary is, as that information is part of your protocol.
HTH

NSString gets corrupted, changes to NSPrincipalClass

I have an NSDragOperation that gets a property lists path upon the user dragging it into the window. That seems to work just fine, and I can save the path information to an NSString:
NString *thisPath = draggedFilePath;
NSLog(#" %#",thisPath);
output: 2014-02-09 09:19:46.072 app[5944:303] /Users/Me/Desktop/file.plist
The problem starts when I go into a dispatch queue. When I try and read the NSString from inside the background queue the output becomes NSPrincipalClass. Does anyone know why this is happening, or if I'm supposed to convert the NSString to some other format before entering dispatch_queue_t?
dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(backgroundQueue, ^{
for (NSUInteger i = 0; i < 1; i++) {
dispatch_async(dispatch_get_main_queue(), ^{
});
}
NSLog(#" %#",thisPath);
output: 2014-02-09 09:19:56.234 app[5944:12203] NSPrincipalClass
EDIT: the output of the string using NSLog inside dispatch queue turns up random symbols, and also crashes. The last output was <__NSMallocBlock__: 0x55a860> - I have other strings that seem to be just fine within the same function, so I really don't know what is causing this.
one of three things is happening...
it is being deallocated, and a new object is being allocated in its place... you can try zombies...
it is getting assigned to a garbage value ie. draggedFilePath isn't ever initialized to zero, and isn't set to a good value.
your stack is getting smashed and it just happens to be there when it crashes... this is the hardest to find.
you are going to have to turn on zombies, the exception breakpoint and just step through it in the debugger... if that fails you get to either run in instruments with the malloc tool or turn on malloc history logging.
Does it work if you prefix the declaration of thisPath with `__block'? Like this:
__block NString *thisPath = draggedFilePath;

NSTask: why program is blocking when read from NSPipe?

I use the NSTask to run shell command and output the data via NSPipe. At first, I using bellow method to read output data, it is no any problem.
- (void)outputAvailable:(NSNotification *)aNotification {
NSString *newOutput;
NSMutableData *allData = [[NSMutableData alloc] init];
NSData *taskData = nil;
if((taskData = [readHandle availableData]) && [taskData length])
newOutput = [[NSString alloc] initWithData:allData encoding:NSASCIIStringEncoding];
NSLog(#"%#", newOutput);
[readHandle readInBackgroundAndNotify];
}
The problem about the method is that it only output 4096 bytes data. So I using while loop to get more data, modify the method like this:
- (void)outputAvailable:(NSNotification *)aNotification {
NSString *newOutput;
NSMutableData *allData; //Added.
NSData *taskData = nil;
while ((taskData = [readHandle availableData]) && [taskData length]) {
[allData appendData:taskData];
}
newOutput = [[NSString alloc] initWithData:allData encoding:NSASCIIStringEncoding];
NSLog(#"%#", newOutput);
[readHandle readInBackgroundAndNotify];
}
Then problem occurs: the program is blocking in the while loop and can not perform the following statements. I ensure that allData is what I wanted, but after appending the last data chunk, it is blocking.
Could you give me some solutions? Thanks.
Your while() loop effectively blocks further notifications, causing the whole program to block waiting for something to flush the buffer.
You should readInBackgroundAndNotify, then pull off availableBytes on each notification, appending it to your NSMutableData (which is likely held in an instance variable). When you handle the notification, don't attempt to wait for more data or do any kind of a while loop. The system will notify you when more data is available.
I.e. the system pushes data to you, you do not pull data from the system.
Ahh... OK. You should still only pull data when there is data available. Your while() loop is doing that. Not enough coffee. My bad.
The final block is most likely because your external process is not closing the pipe; no EOF is received and, thus, the program is waiting forever for more data that never arrives.
Either:
make sure the background task exits
detect when you've received enough data and terminate the process
If you are doing some kind of conversion program (say, tr) where you write data on the processes standard input, then you might need to close the standard input pipe.

How to read text chunks from a huge text file?

I am trying to read a text file containing characters in billions. Using the function
contentOfFile is not working, as my application get crashed due to it.
So anybody please send me the sample code so that I get the chunks according to my requirement.Whichever i need i wanna get that one only.
please reply as soon as possible.
I'm guessing this is an iOS app. In that case, you are likely hitting the memory limit by calling contentsOfFile: because that method is trying to read the entire contents of the file into a variable (memory). Remember that on iOS your app must play nice and if it decides to consume too much memory, then the watchdog process will kill your app to save the device from rebooting (which happens because there is no disk to swap to on iOS devices).
Have you had a look at NSFileHandle? NSFileHandle supports seeking within a text a file. With some simple iteration you can use the following to methods to seek within the file and read chunks of data:
- (NSData *)readDataOfLength:(NSUInteger)length;
- (void)seekToFileOffset:(unsigned long long)offset;
It might look something like this. Assume pathToFile is an NSString containing the path to the text file to be read in.
uint64 offset = 0;
uint32 chunkSize = 1024; //Read 1KB chunks.
NSFileHandle *handle = [NSFileHandle fileHandleForReadingAtPath:pathToFile];
NSData *data = [handle readDataOfLength:chunkSize];
while ([data length] > 0)
{
//Make sure for the next line you choose the appropriate string encoding.
NSString *dataString = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
/* PERFORM STRING PROCESSING HERE */
/* END STRING PROCESSING */
offset += [data length];
[handle seekToFileOffset:offset];
data = [handle readDataOfLength:chunkSize];
}
[handle closeFile];
A good idea is to look at the textedit source because I've opened massive files with it before and there should be a way to do it. Not sure why your app is crashing though. It shouldn't have a problem.

Limitations of NSMutableData for NSKeyedUnarchiver

I have read on another post (Archiving / Unarchiving results in initForReadingWithData incomprehensible archive) that you can't store more than 250kBytes on a NSMutableArray. Unfortunately, in order to recover such data with NSKeyedUnarchiver, you must use a NSMutableArray. I am trying to get back an image with a size around 500kB.
MTMessage *message = [NSKeyedUnarchiver unarchiveObjectWithData:data];
The error I get is :
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSKeyedUnarchiver initForReadingWithData:]: incomprehensible archive (0x0, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x0, 0x1)'
Apparently it's a pretty common situation (even-though i have not found a solution yet). Would you have any idea of how to bypass the use of NSMutableData.
Thank you.
EDIT : Actually it says that data has a size of 524 288 bytes, which is correct, so the problem might come from the unarchiver.
NSKeyedArchiver does not depend on an NSArray (immutable or not).
I'm also not aware of a bug correlated with NSKeydArchiver and depending on archive size.
The following code runs fine on Lion:
NSMutableData *data = [NSMutableData data];
for (uint32_t i = 0; i < 1024 * 1024; ++i)
[data appendBytes:&i length:sizeof(uint32_t)];
NSData *archive = [NSKeyedArchiver archivedDataWithRootObject:[NSMutableArray arrayWithObject:data]];
NSArray *array = [NSKeyedUnarchiver unarchiveObjectWithData:archive];
assert([data isEqual:[array lastObject]]);
Please provide more code for more insight in your actual problem. Are you maybe trying to unarchive an encoded image instead of an archive?