I am saving arrays of doubles in an NSData* object that is persisted as a binary property in a Core Data (SQLite) data model. I am doing this to store sampled data for graphing in an iPhone app. Sometimes when there are more than 300 doubles in the binary object not all the doubles are getting saved to disk. When I quit and relaunch my app there may be as few as 25 data points that have persisted or as many as 300.
Using NSSQLitePragmasOption with synchronous = FULL and this may be making a difference. It is hard to tell, as bug is intermittent.
Given the warnings about performance problems as a result of using synchronous = FULL, I am seeking advice and pointers.
Thanks.
[[Edit: here is code.]]
The (as yet unrealized) intent of -addToCache: is to add each new datum to the cache but only flush (fault?) Data object periodically.
From Data.m
#dynamic dataSet; // NSData * attribute of Data entity
- (void) addDatum:(double_t)datum
{
DLog(#"-[Data addDatum:%f]", datum);
[self addToCache:datum];
}
- (void) addToCache:(double_t)datum
{
if (cache == nil)
{
cache = [NSMutableData dataWithData:[self dataSet]];
[cache retain];
}
[cache appendBytes:&datum length:sizeof(double_t)];
DLog(#"-[Data addToCache:%f] ... [cache length] = %d; cache = %p", datum, [cache length], cache);
[self flushCache];
}
- (void) wrapup
{
DLog(#"-[Data wrapup]");
[self flushCache];
[cache release];
cache = nil;
DLog(#"[self isFault] = %#", [self isFault] ? #"YES" : #"NO"); // [self isFault] is always NO.
}
- (void) flushCache
{
DLog(#"flushing cache to store");
[self setDataSet:cache];
DLog(#"-[Data flushCache:] [[self dataSet] length] = %d", [[self dataSet] length]);
}
- (double*) bytes
{
return (double*)[[self dataSet] bytes];
}
- (NSInteger) count
{
return [[self dataSet] length]/sizeof(double);
}
- (void) dump
{
ALog(#"Dump Data");
NSInteger numDataPoints = [self count];
double *data = (double*)[self bytes];
ALog(#"numDataPoints = %d", numDataPoints);
for (int i = 0; i
I was trying to get behavior as if my Core Data entity could have an NSMutableData attribute. To do this my NSManagedObject (called Data) had an NSData attribute and an NSMutableData ivar. My app takes sample data from a sensor and appends each data point to the data set - this is why I needed this design.
On each new data point was appended to the NSMutableData and then the NSData attribute was set to the NSMutableData.
I suspect that because the NSData pointer wasn't changing (though its content was), that Core Data did not appreciate the amount of change. Calling -hasChanged on the NSManagedObjectContext showed that there had been changes, and calling -updatedObjects even listed the Data object as having changed. But the actual data that was being written seems to have been truncated (sometimes).
To work around this I changed things slightly. New data points are still appended to NSMutableData but NSData attribute is only set when sampling is completed. This means that there is a chance that a crash might result in truncated data - but for the most part this work around seems to have solved the problem.
Caveat emptor: the bug was always intermittent, so it is possible that is still there - but just harder to manifest.
Related
I am trying to create a http body that I am going to pass in using NSURLRequest post.
I have my connection class all set up.
The thing is i have several methods that return NSStrings and UInt32's and one construction method that I want to use to put all of these methods into one http body which will be of data type format.
However I'm not sure how to call these methods that return the correct data from my construction method to gather the data into one data object.
here is some code that I have (shortened so its a little clearer)
these methods are used to return the data needed
- (UInt32) addDataVer
{
UInt32 dataVer = 0;
return dataVer;
}
- (NSString *) addReg
{
NSString *reg = [[NSString alloc] initWithString:#"abcd1"];
return reg;
}
- (NSString *) addActiv
{
NSString *activ = [[NSString alloc] initWithString:#"abcd2"];
return activ;
}
from here I'm not sure what to do, or how to get the data. I have created a construction method, that I want to use to grab the data and then I want to use that data to build a NSData object where I put the returning data into it in order.
this is my construction class
- (void) constructRequest
{
//what the heck do I call in here? lol
}
the last thing I will have to do is figure out how to put the nsdata representation of each return value into the data object... if that makes sense...
any help would be appreciated.
UPDATE::
So I figured out how to get the return value into my construction method, by following the force!
- (void) constructRequest
{
NSString *mystring = [[NSString alloc] initWithString:[self addReg]];
NSLog(#"mystring %#", mystring);
}
however I am not sure how to do this with a returning UInt32, or how to convert this in to a NSData structure
From Apple Docs on String formatting https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Strings/Articles/formatSpecifiers.html#//apple_ref/doc/uid/TP40004265-SW1
[NSString stringWithFormat:"my unsigned 32-bit int: %d", [self addDataVer]];
from Apple Docs about NSString class
To convert your entire string to data:
[myNSString dataUsingEncoding:NSUTF8StringEncoding];
I'm trying to send files over a bluetooth connection.
I've got this to work thanks to my previous post, but the method wasn't memory efficient.
The whole file was loaded into memory before it was sent, and this created problems (and crashed the app) for files > ~20 MB. So I've come up with a new method of only reading parts of the file I need at a specific time, creating packets from the data, sending them and repeating the process for each 8KB chunk of the file.
So I made a class method that generates the packet, informs the controller that a packet is available (through a protocol) and repeats this process for each packet that's available.
Here's the code for the packet generator:
+ (void)makePacketsFromFile:(NSString *)path withDelegate:(id <filePacketDelegate>)aDelegate {
if (![[NSFileManager defaultManager] fileExistsAtPath:path] || aDelegate == nil) return;
id <filePacketDelegate> delegate;
delegate = aDelegate;
const NSUInteger quanta = 8192;
uint filesize;
NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:nil];
filesize = [[fileAttributes objectForKey:NSFileSize] intValue];
int numOfPackets = (int)ceil(filesize/quanta);
if (numOfPackets == 0) numOfPackets = 1;
NSLog(#"filesize = %d, numOfPackets = %d or %.3f", filesize, numOfPackets, (float)ceil(filesize/quanta));
NSFileHandle *handle = [NSFileHandle fileHandleForReadingAtPath:path];
int offset = 0;
int counter = 0;
while (counter != (numOfPackets + 1)) {
uint len = (filesize < quanta) ? filesize : quanta;
if (counter == numOfPackets) {
len = filesize - offset;
}
[handle seekToFileOffset:offset];
NSData *fileData = [handle readDataOfLength:len];
file_packet *packet = [[file_packet alloc] initWithFileName:[path lastPathComponent] ofType:0 index:counter];
packet.packetContents = fileData;
[fileData release];
packet.checksum = #"<to be done>";
packet.numberOfPackets = [NSString stringWithFormat:#"%d", numOfPackets];
[delegate packetIsReadyForSending:packet];
[packet release];
offset += quanta;
counter++;
}
[handle closeFile];
}
And receiving and sending the file:
- (void)packetIsReadyForSending:(file_packet *)packet {
NSData *fileData = [packet dataForSending];
[self.connectionSession sendDataToAllPeers:fileData withDataMode:GKSendDataReliable error:nil];
}
- (void)sendFileViaBluetooth {
[file_packet makePacketsFromFile:selectedFilePath withDelegate:self];
}
However, the memory use is quite large. Not what I expected.
I'm a bit stuck on this, as I wouldn't like to restrict bluetooth sharing to files smaller than 20MB.
Any help appreciated.
Edit:
I've been thinking about this for a while and have come to the conclusion that it's not my code that's causing the memory allocation issue, it's GameKit's stack for sending the packets.
I think I'm generating too many packets too fast, and I don't think GameKit is sending them quick enough.
So I am now thinking about a way to see when GameKit has sent a packet, and only generating another one once GameKit confirms it was sent.
There’s one clear memory management issue in your code:
NSData *fileData = [handle readDataOfLength:len];
fileData wasn’t obtained via NARC (a method whose name contains new, alloc, retain, copy), so you don’t own it, hence you don’t release it.
[fileData release];
Oops.
As for the memory use, one thing to consider is that even though you release packet you can’t really tell what’s going on inside -[GKSession sendDataToAllPeers:withDataMode:error:]. It is possible that the method internally creates autoreleased objects that only end up being released after +makePacketsFromFile:withDelegate: has finished executing. I suggest you use a new autorelease pool in every loop iteration:
while (counter != (numOfPackets + 1)) {
NSAutoreleasePool *pool = [NSAutoreleasePool new];
uint len = (filesize < quanta) ? filesize : quanta;
…
counter++;
[pool drain];
}
By doing this, any autoreleased object inside that loop will be effectively released at the end of each loop iteration.
Checkout the NSInputStream. With it you can open a stream and only read out a buffer of bytes at a time.
Is there any way I can convert the value of a [NSData bytes] to a float so that I can add it to a progress bar?
Thanks,
Kevin
In a nutshell: [data length]
Here is the snippet of how the download bar I use works.
// Can get called numerous times during download process
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
// Accumulate incoming data into mutable data object
[fileData appendData:data];
byteCount += [data length];
float progress = byteCount/(mapToDownload.fileSize);
[self performSelectorOnMainThread:#selector(updateProgress:) withObject:[NSNumber numberWithFloat:progress] waitUntilDone:NO];
}
Let me know if you need more information.
[Added Oct 26 to address your other question:]
I have not worked with NSStream. My example is from an asynchronous NSURLConnection example. Therefore, let's ignore my previous code example.
You mentioned that you have [NSData bytes]. [NSData length] should return you how much data you have. Assuming you know the size to be downloaded then:
float progressPercentage = [yourNSData length]/knownFileSize;
should give you the percentage needed to update the progress bar. You could then set your progress bar:
[yourProgressBar setProgress:progressPercentage];
I have a method that asynchronously downloads images. If the images are related to an array of objects (a common use-case in the app I'm building), I want to cache them. The idea is, I pass in an index number (based on the indexPath.row of the table I'm making by way through), and I stash the image in a static NSMutableArray, keyed on the row of the table I'm dealing with.
Thusly:
#implementation ImageDownloader
...
#synthesize cacheIndex;
static NSMutableArray *imageCache;
-(void)startDownloadWithImageView:(UIImageView *)imageView andImageURL:(NSURL *)url withCacheIndex:(NSInteger)index
{
self.theImageView = imageView;
self.cacheIndex = index;
NSLog(#"Called to download %# for imageview %#", url, self.theImageView);
if ([imageCache objectAtIndex:index]) {
NSLog(#"We have this image cached--using that instead");
self.theImageView.image = [imageCache objectAtIndex:index];
return;
}
self.activeDownload = [NSMutableData data];
NSURLConnection *conn = [[NSURLConnection alloc]
initWithRequest:[NSURLRequest requestWithURL:url] delegate:self];
self.imageConnection = conn;
[conn release];
}
//build up the incoming data in self.activeDownload with calls to didReceiveData...
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(#"Finished downloading.");
UIImage *image = [[UIImage alloc] initWithData:self.activeDownload];
self.theImageView.image = image;
NSLog(#"Caching %# for %d", self.theImageView.image, self.cacheIndex);
[imageCache insertObject:image atIndex:self.cacheIndex];
NSLog(#"Cache now has %d items", [imageCache count]);
[image release];
}
My index is getting through okay, I can see that by my NSLog output. But even after my insertObject: atIndex: call, [imageCache count] never leaves zero.
This is my first foray into static variables, so I presume I'm doing something wrong.
(The above code is heavily pruned to show only the main thing of what's going on, so bear that in mind as you look at it.)
You seem to never initialize the imageCache and probably got lucky with it having the value 0. The initialization would best be done in the class' initialization, e.g.:
#implementation ImageDownloader
// ...
+(void)initialize {
imageCache = [[NSMutableArray alloc] init];
}
// ...
I need to save my own created class to file, I found on the internet, that good approach is to use NSKeyedArchiver and NSKeyedUnarchiver
My class definition looks like this:
#interface Game : NSObject <NSCoding> {
NSMutableString *strCompleteWord;
NSMutableString *strWordToGuess;
NSMutableArray *arGuessedLetters; //This array stores characters
NSMutableArray *arGuessedLettersPos; //This array stores CGRects
NSInteger iScore;
NSInteger iLives;
NSInteger iRocksFallen;
BOOL bGameCompleted;
BOOL bGameOver;
}
I've implemented methods initWithCoder: and encodeWithCoder: this way:
- (id)initWithCoder:(NSCoder *)coder
{
if([coder allowsKeyedCoding])
{
strCompleteWord = [[coder decodeObjectForKey:#"CompletedWord"] copy];
strWordToGuess = [[coder decodeObjectForKey:#"WordToGuess"] copy];
arGuessedLetters = [[coder decodeObjectForKey:#"GuessedLetters"] retain];
// arGuessedLettersPos = [[coder decodeObjectForKey:#"GuessedLettersPos"] retain];
iScore = [coder decodeIntegerForKey:#"Score"];
iLives = [coder decodeIntegerForKey:#"Lives"];
iRocksFallen = [coder decodeIntegerForKey:#"RocksFallen"];
bGameCompleted = [coder decodeBoolForKey:#"GameCompleted"];
bGameOver = [coder decodeBoolForKey:#"GameOver"];
}
else
{
strCompleteWord = [[coder decodeObject] retain];
strWordToGuess = [[coder decodeObject] retain];
arGuessedLetters = [[coder decodeObject] retain];
// arGuessedLettersPos = [[coder decodeObject] retain];
[coder decodeValueOfObjCType:#encode(NSInteger) at:&iScore];
[coder decodeValueOfObjCType:#encode(NSInteger) at:&iLives];
[coder decodeValueOfObjCType:#encode(NSInteger) at:&iRocksFallen];
[coder decodeValueOfObjCType:#encode(BOOL) at:&bGameCompleted];
[coder decodeValueOfObjCType:#encode(BOOL) at:&bGameOver];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)coder
{
if([coder allowsKeyedCoding])
{
[coder encodeObject:strCompleteWord forKey:#"CompleteWord"];
[coder encodeObject:strWordToGuess forKey:#"WordToGuess"];
[coder encodeObject:arGuessedLetters forKey:#"GuessedLetters"];
//[coder encodeObject:arGuessedLettersPos forKey:#"GuessedLettersPos"];
[coder encodeInteger:iScore forKey:#"Score"];
[coder encodeInteger:iLives forKey:#"Lives"];
[coder encodeInteger:iRocksFallen forKey:#"RocksFallen"];
[coder encodeBool:bGameCompleted forKey:#"GameCompleted"];
[coder encodeBool:bGameOver forKey:#"GameOver"];
}
else
{
[coder encodeObject:strCompleteWord];
[coder encodeObject:strWordToGuess];
[coder encodeObject:arGuessedLetters];
//[coder encodeObject:arGuessedLettersPos];
[coder encodeValueOfObjCType:#encode(NSInteger) at:&iScore];
[coder encodeValueOfObjCType:#encode(NSInteger) at:&iLives];
[coder encodeValueOfObjCType:#encode(NSInteger) at:&iRocksFallen];
[coder encodeValueOfObjCType:#encode(BOOL) at:&bGameCompleted];
[coder encodeValueOfObjCType:#encode(BOOL) at:&bGameOver];
}
}
And I use these methods to archive and unarchive data:
[NSKeyedArchiver archiveRootObject:currentGame toFile:strPath];
Game *currentGame = [NSKeyedUnarchiver unarchiveObjectWithFile:strPath];
I have two problems.
1) As you can see, lines with arGuessedLettersPos is commented, it's because every time I try to encode this array, error comes up(this archiver cannot encode structs), and this array is used for storing CGRect structs.
I've seen solution on the internet. The thing is, that every CGRect in the array is converted to an NSString (using NSStringFromCGRect()) and then saved. Is it a good approach?
2)This is bigger problem for me. Even if I comment this line and then run the code successfully, then save(archive) the data and then try to load (unarchive) them, no data is loaded. There aren't any error but currentGame object does not have data that should be loaded.
Could you please give me some advice? This is first time I'm using archivers and unarchivers.
Thanks a lot for every reply.
The problem with loading and saving solved another way...
Instead of implementing - (id)initWithCoder:(NSCoder )coder and - (void)encodeWithCoder:(NSCoder)coder I used this solution:
NSMutableData *data = [NSMutableData alloc];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[archiver encodeObject:self.strCompleteWord forKey:#"CompleteWord"];
[archiver encodeObject:self.strWordToGuess forKey:#"WordToGuess"];
[archiver encodeObject:self.arGuessedLetters forKey:#"GuessedLetters"];
//[coder encodeObject:self.arGuessedLettersPos forKey:#"GuessedLettersPos"];
[archiver encodeInteger:self.iScore forKey:#"Score"];
[archiver encodeInteger:self.iLives forKey:#"Lives"];
[archiver encodeInteger:self.iRocksFallen forKey:#"RocksFallen"];
[archiver encodeBool:self.bGameCompleted forKey:#"GameCompleted"];
[archiver encodeBool:self.bGameOver forKey:#"GameOver"];
[archiver finishEncoding];
[data writeToFile:strPath atomically:YES];
[data release];
and
NSMutableData *data = [[NSMutableData alloc] initWithContentsOfFile:strPath];
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
self.strCompleteWord = [[unarchiver decodeObjectForKey:#"CompletedWord"] copy];
self.strWordToGuess = [[unarchiver decodeObjectForKey:#"WordToGuess"] copy];
self.arGuessedLetters = [[unarchiver decodeObjectForKey:#"GuessedLetters"] retain];
//self.arGuessedLettersPos = [[unarchiver decodeObjectForKey:#"GuessedLettersPos"] retain];
self.iScore = [unarchiver decodeIntegerForKey:#"Score"];
self.iLives = [unarchiver decodeIntegerForKey:#"Lives"];
self.iRocksFallen = [unarchiver decodeIntegerForKey:#"RocksFallen"];
self.bGameCompleted = [unarchiver decodeBoolForKey:#"GameCompleted"];
self.bGameOver = [unarchiver decodeBoolForKey:#"GameOver"];
[unarchiver finishDecoding];
[data release];
And this works totally fine :)
I might be missing it, but I don't see any obvious bugs in this code.
Here are some ideas that might help:
Add some NSLog statements, and watch the debug output (open with command-shift-R in xcode) to see if your encode/decode methods are actually being called.
Check that the archive file is saved: Running in the simulator, you can save to any local path you want, such as /tmp/my_archive_file. Try to save to that file, and see if (a) the file exists with the right timestamp, and (b) you print out the file, you can see some recognizable strings (like "RocksFallen") in amongst the binary gooblygoo.
I also don't think it's necessary to check for allowsKeyed(En)coding since you know that's always going to be true when you're explicitly using NSKeyed(Un)archiver to do your dirty work for you. So you can throw away the other half of your code.
About coding those arrays of CGRects: I don't think you can directly add structs to an NSMutableArray, right? So they must be some kind of object, which means you can add NSCoding support to that object. If it's not your own object, you can subclass it and add that protocol, or try adding it as a category.
Thanks for reply Tyler.
I looked in the saved file and there are recognizable strings like RocksFallen and so on, also there are some recognizable values that might be saved in string variables.. So this seems to be good.
I tried to put some NSLogs in the code and not everything seems to be good. So...
When I launch simulator for the first time, there's no archive file, yes it is obvious, because nothing is saved. Then I change something and exit the application. When the application is being terminated, data might be archived, everything's fine. NSLog statements are written in the console, file is on the disk. But what is strange is that when I launch the application then, decode method(initwithcoder) is not being called.. Don't know why. Then I exit the simulator and run the simulator again. When I run it again, decode method is being called at the launchtime. But only at the first launchtime after running simulator, when I then work with simulator like exit the app and run it again, initWithCoder method is not being called and that's very strange for me..
So there are problems unarchiving it i think.
The best way to archive structs (that are not holding any pointers) with would be to wrap the values in an NSData block.
here, i'm dumping a struct array to the archiver:
for (int i = 0; i < items; i++) {
NSString *keyValue = [NSString stringWithFormat:#"YourStruct%i", i ];
NSData *dataset = [NSData dataWithBytes:(void*)&activeprofile[i] length:(sizeof(profileset))];
[encoder encodeObject:dataset forKey:keyValue]; // makes a nice, solid data block
}
and reading the data back in:
for (int i = 0; i < items; i++) {
NSString *keyValue = [NSString stringWithFormat:#"YourStruct%i", i ];
NSData *dataset = [decoder decodeObjectForKey:keyValue];
[dataset getBytes:&activeprofile[i] length:sizeof(profileset)];
}
There you have it: easy as pie and extremely flexible with datatypes!
A similar method for non-keyed archiving can be found here: http://borkware.com/quickies/one?topic=NSCoder Though I suggest sticking with keyed archiving, as NSArchive is not supported on the iPhone and has been classified as legacy code.