I'm using the function below to set new rtfd data to an attribute of type binary in a core data entity.
If its not equal, then I set the data to the managed object.
The problem here is that the equality only works when the rtfd data is only text. When there are pictures attached this method is returning false.
-(BOOL)setRTFData:(NSData*)data forKey:(NSString*)key
{
NSData *actualData = [self getDataValueForKey:key];
NSDictionary *dict = nil;
NSAttributedString *actualAttribString = [[NSAttributedString alloc] initWithRTFD:actualData documentAttributes:&dict];
NSAttributedString *newAttribString = [[NSAttributedString alloc] initWithRTFD:data documentAttributes:&dict];
BOOL isEqual = [actualAttribString isEqualToAttributedString:newAttribString];
[actualAttribString release];
[newAttribString release];
if (!isEqual)
{
[self willChangeValueForKey:key];
[self setPrimitiveValue:data forKey:key];
[self didChangeValueForKey:key];
return TRUE;
}
return FALSE;
}
this is how I get my *rtfd data*from a NSTextView:
NSData* data = [notesTextField RTFDFromRange:NSMakeRange(0,[[notesTextField textStorage] length])];
I'm using sdk 10.7
Related
I'm struggling with the problem to draw an eps file on a NSView.
When I first load the eps file from a file and draw it with drawInRect: the image is displayed correctly. However, the image will not be drawn when I load it from an archive file.
I've prepared a dirty small example that you can copy/paste and try out. Create a new Cocoa App project and add this to the delegate method.
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Just a sample eps file
NSURL *url = [NSURL URLWithString: #"http://embedded.eecs.berkeley.edu/concurrency/latex/figure.eps"];
NSImage *epsImage = [[[NSImage alloc] initWithContentsOfURL: url] autorelease];
// Encode data
NSMutableData *mutableData = [[[NSMutableData alloc] init] autorelease];
NSKeyedArchiver *coder = [[[NSKeyedArchiver alloc] initForWritingWithMutableData: mutableData] autorelease];
[coder encodeObject: epsImage forKey: #"image"];
[coder finishEncoding];
NSString *dataFile = [#"~/Desktop/image.data" stringByExpandingTildeInPath];
[mutableData writeToFile: dataFile atomically: YES];
// Decode data
NSData *data = [NSData dataWithContentsOfFile: dataFile];
NSKeyedUnarchiver *decoder = [[NSKeyedUnarchiver alloc] initForReadingWithData: data];
NSImage *loadedImage = [decoder decodeObjectForKey: #"image"];
// Draw image
NSRect rect;
rect.origin = NSZeroPoint;
rect.size = loadedImage.size;
NSView *view = [[NSApp mainWindow] contentView];
[view lockFocus];
[loadedImage drawInRect: rect fromRect: rect operation: NSCompositeSourceOver fraction: 1.0];
[view unlockFocus];
}
To prove that the first loaded image draws correctly just change the line [loadedImage drawInRect:...] to [epsImage drawInRect:...].
I'm using NSKeyedArchiver and NSKeyedUnarchiver here for simulating encodeWithCoder: and initWithCoder:. So please focus on the fact that NSImage with NSEPSImageRep representation, which does not contain a preview (from a resource fork?) and loaded purely as eps commands, is not drawn on a NSView correctly.
Any help is appreciated.
Due to the way that cacheing works on NSImage, I've often found it more effective to actually grab the NSImageRep if I know what the type is.
In our code, we found that the most reliable way to save off images is in their original format, but that requires either saving off the data in its original format somewhere else, or requesting the data from the NSImageRep. Unfortunately, there's not a generic -(NSData*)data method of NSImageRep, so we ended up specifically checking for various types of NSImageRep and saving them off depending on what we knew them to be.
Fortunately, loading is simple, as NSImage::initWithData: will figure out the type based on the data.
Here's our long-standing code for doing this. Basically, it prefers PDF then EPS then it makes a TIFF of anything it doesn't understand.
+ (NSData*) dataWithImage:(NSImage*)image kindString:( NSString**)kindStringPtr
{
if (!image)
return nil;
NSData *pdfData=nil, *newData=nil, *epsData=nil, *imageData=nil;;
NSString *kindString=nil;
NSArray *reps = [image representations];
for (NSImageRep *rep in reps) {
if ([rep isKindOfClass: [NSPDFImageRep class]]) {
// we have a PDF, so save that
pdfData = [(NSPDFImageRep*)rep PDFRepresentation];
PDFDocument *doc = [[PDFDocument alloc] initWithData:pdfData];
newData = [doc dataRepresentation];
if (newData && ([newData length]<[pdfData length])) {
pdfData = newData;
}
break;
}
if ([rep isKindOfClass: [NSEPSImageRep class]]) {
epsData = [(NSEPSImageRep*)rep EPSRepresentation];
break;
}
}
if (pdfData) {
imageData=pdfData;
kindString= #"pdfImage";
} else if (epsData) {
imageData=epsData;
kindString=#"epsImage";
} else {
// make a big copy
NSBitmapImageRep *rep0 = [reps objectAtIndex:0];
if ([rep0 isKindOfClass: [NSBitmapImageRep class]]) {
[image setSize: NSMakeSize( [rep0 pixelsWide], [rep0 pixelsHigh])];
}
imageData = [image TIFFRepresentation];
kindString=#"tiffImage";
}
if (kindStringPtr)
*kindStringPtr=kindString;
return imageData;
}
Once we have the NSData*, it can be saved in a keyed archive, written to disk or whatever.
On the way back in, load in the NSData* and then
NSImage *image = [[NSImage alloc] initWithData: savedData];
and you should be all set.
I'm trying to add images into core data and load it when needed. I'm currently adding the NSImage to the core data as follows:
Thumbnail *testEntity = (Thumbnail *)[NSEntityDescription insertNewObjectForEntityForName:#"Thumbnail" inManagedObjectContext:self.managedObjectContext];
NSImage *image = rangeImageView.image;
testEntity.fileName = #"test";
testEntity.image = image;
NSError *error;
[self.managedObjectContext save:&error];
Thumbnail is the entity name and I have two attributes under the Thumbnail entity - fileName(NSString) and image (id - transformable).
I'm trying to retreive them as below:
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest * fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *imageEntity = [NSEntityDescription entityForName:#"Thumbnail" inManagedObjectContext:[context valueForKey:#"image"]];
[fetchRequest setEntity:imageEntity];
NSError *error;
NSArray * array = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (array == nil) {
NSLog(#"Testing: No results found");
}else {
_coreDataImageView.image = [array objectAtIndex:0];
}
I end up with this error:
[<NSManagedObjectContext 0x103979f60> valueForUndefinedKey:]: this class is not key value coding-compliant for the key image.
The image is added but couldn't retrieve.
Any idea on how to go about with this? Am I doing it right ?
The error is in this line
NSEntityDescription *imageEntity = [NSEntityDescription entityForName:#"Thumbnail"
inManagedObjectContext:[context valueForKey:#"image"]];
You cannot apply valueForKey:#"image" to a managed object context. You have to apply it to the fetched objects (or use the image property of the fetched object).
Note also that executeFetchRequest: returns nil only if an error occurs. If no entities are found, it returns an empty array.
NSEntityDescription *imageEntity = [NSEntityDescription entityForName:#"Thumbnail" inManagedObjectContext:context];
[fetchRequest setEntity:imageEntity];
NSError *error;
NSArray * array = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (array == nil) {
NSLog(#"Testing: Fetch error: %#", error);
} else if ([array count] == 0) {
NSLog(#"Testing: No results found");
}else {
Thumbnail *testEntity = [array objectAtIndex:0];
NSImage *image = testEntity.image;
_coreDataImageView.image = image;
}
I have a Cocoa Webview subclass, and I need to make all text copied from it be plaintext only. I have tried overriding -copy and -pasteboardTypesForSelection, but no luck, and debugging code seems to indicate that those methods are never called. I've also tried setting -webkit-user-modify to read-write-plaintext-only in the css (this would also work in this situation) but that seemed to have no effect.
Any ideas?
Okay this seems to work (with the subclass instance as its own editing delegate):
- (BOOL)webView:(WebView *)webView doCommandBySelector:(SEL)command
{
if (command == #selector(copy:)) {
NSString *markup = [[self selectedDOMRange] markupString];
NSData *data = [markup dataUsingEncoding: NSUTF8StringEncoding];
NSNumber *n = [NSNumber numberWithUnsignedInteger: NSUTF8StringEncoding];
NSDictionary *options = [NSDictionary dictionaryWithObject:n forKey: NSCharacterEncodingDocumentOption];
NSAttributedString *as = [[NSAttributedString alloc] initWithHTML:data options:options documentAttributes: NULL];
NSString *selectedString = [as string];
[as autorelease];
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
[pasteboard clearContents];
NSArray *objectsToCopy = [NSArray arrayWithObject: selectedString];
[pasteboard writeObjects:objectsToCopy];
return YES;
}
return NO;
}
Not sure if this is the best way.
I don't undestand... i succeed in populate my table view but when i scroll it crash without message..
i create my tabQuestion with a NSMutablearray and then add this in my delegate : cellForRowAtIndexPath
NSDictionary *question = [self.tabQuestion objectAtIndex:indexPath.row];
[cell setAccessoryType: UITableViewCellAccessoryDisclosureIndicator];
cell.textLabel.text = [NSString stringWithFormat:#"%#", [question objectForKey:#"question"]];
cell.detailTextLabel.text = [NSString stringWithFormat:#"%#", [question objectForKey:#"reponse"]];
if ([[question objectForKey:#"bOk"] isEqualToString:#"1"]) {
cell.imageView.image = [UIImage imageNamed:#"ok.png"];
}
else
{
cell.imageView.image = [UIImage imageNamed:#"ok.png"];
}
[question release];
return cell;
You shouldn't release objects that were returned by method objectAtIndex: of NSArray. It happens very rare.
Try to remove first of all line: [question release];
Then check if your self.tabQuestion contains needful amount of objects.
Help me please with the following problem:
- (NSDictionary *)getGamesList
{
NSMutableDictionary *gamesDictionary = [[NSMutableDictionary dictionary] retain];
// I was trying to change this on the commented code below, but did have no effect
// NSMutableDictionary *gamesDictionary = [[NSMutableDictionary alloc] init];
// [gamesDictionary retain];
while (sqlite3_step(statement) == SQLITE_ROW)
{
NSString *key = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 1)];
NSArray *gameDate = [key componentsSeparatedByString:#" "];
NSNumber *_id = [[NSNumber alloc] initWithInt:sqlite3_column_int(statement, 0)];
NSString *date_time = [NSString stringWithFormat:#"%#, %#",[gameDate objectAtIndex:0],[gameDate objectAtIndex:2]];
if (![gamesDictionary valueForKey:date_time]) [gamesDictionary setValue:[NSMutableArray array] forKey:date_time];
[[gamesDictionary valueForKey:date_time] addObject:[[_id copy] autorelease]];
[_id release];
}
sqlite3_reset(statement);
return gamesDictionary;
}
The leak starts in another method of another class, there the getGamesList method is called, like this:
NSMutableDictionary *gamesDictionary;
gamesDictionary = [[NSMutableDictionary dictionaryWithDictionary:[appDelegate getGamesList]] retain];
After that there are a lot of leaks that points to NSCFArray in the string:
NSArray *keys = [[NSArray arrayWithArray:[gamesDictionary allKeys]] retain];
in this method:
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
NSArray *keys = [[NSArray arrayWithArray:[gamesDictionary allKeys]] retain];
if ([keys count] != 0) return [[keys objectAtIndex:section] uppercaseString];
return #"";
}
I assume these things are connected to each other, but I still can not understand all of the memory management tips.
Thanks a lot!
Didn't use Cocoa for years (that's why I can't tell you an exact answer :/). But I guess your problem is that you systematically use retain on your objects.
Since the object reference count never get to 0, all dictionaries are keep in memory and not freed.
Try to remove the retain on [NSArray arrayWithArray] and [NSMutableDictionary dictionaryWithDictionary
http://en.wikibooks.org/wiki/Programming_Mac_OS_X_with_Cocoa_for_beginners/Some_Cocoa_essential_principles#Retain_and_Release
It does look like you are over-retaining your array.
When you create the gamesDictionary it is created with an retain count of +1. You then retain it (count becomes +2). When you get the value outside this function you retain again (count becomes +3).
You are correct that if you create an object you are responsible for it's memory management. Also, when you get an object from a method, you should retain it if you want to keep it around for longer than the span of the function. In your case, you just want to get at some of the properties of the object, so you don't need to retain it.
Here is a suggestion:
- (NSDictionary *)getGamesList
{
NSMutableDictionary *gamesDictionary = [NSMutableDictionary dictionary]; // Remove the retain.
while (sqlite3_step(statement) == SQLITE_ROW)
{
NSString *key = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 1)];
NSArray *gameDate = [key componentsSeparatedByString:#" "];
NSNumber *_id = [[NSNumber alloc] initWithInt:sqlite3_column_int(statement, 0)];
NSString *date_time = [NSString stringWithFormat:#"%#, %#",[gameDate objectAtIndex:0],[gameDate objectAtIndex:2]];
if (![gamesDictionary valueForKey:date_time]) [gamesDictionary setValue:[NSMutableArray array] forKey:date_time];
[[gamesDictionary valueForKey:date_time] addObject:[[_id copy] autorelease]];
[_id release];
}
sqlite3_reset(statement);
return gamesDictionary;
}
This next bit is messy. you create a new dictionary and retain it. The original dictionary is not autoreleased, so the count isn't decremented and it always hangs around. Just assign the dictionary rather than create a new one.
NSMutableDictionary *gamesDictionary = [[appDelegate getGamesList] retain];
// Retaining it, becuase it looks like it's used elsewhere.
Now, in this method:
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
NSString *returnString;
// Don't need to retain the keys because you are only using it within the function
// and since you didn't alloc, copy or retain the array it contains, you aren't responsible for it's memory management.
NSArray *keys = [NSArray arrayWithArray:[gamesDictionary allKeys]];
if ([keys count] != 0) {
returnString = [[NSString alloc] initWithString:[[keys objectAtIndex:section] uppercaseString]];
return [returnString autorelease];
}
return #"";
}