memory management for CTRunDelegateRef iPhone - ios

I focus on a project to add image support based on OmniGroup's rtf editor.I met the problem when I use CTFramesetterCreateWithAttributedString, and it receives
EXC_BAD_ACCESS
which is caused by a zombie object.
For simplicity, I get the NSAttributedString from a rtfd file which only has a photo. I add the method to parser the image tag on iOS based on the Omni's sample. And the part to create the NSAttributedString is followed by this tutorial from raywenderlich, and looks like:
CTRunDelegateCallbacks callbacks;
callbacks.version = kCTRunDelegateCurrentVersion;
callbacks.getAscent = ascentCallback;
callbacks.getDescent = descentCallback;
callbacks.getWidth = widthCallback;
callbacks.dealloc = deallocCallback;
CTRunDelegateRef delegate = CTRunDelegateCreate(&callbacks, imgAttr);
NSDictionary *imgAttr = [NSDictionary dictionaryWithObjectsAndKeys:imgWidth,kImageWidth,imgHeight,kImageHeight, nil];//recored the width and height
NSDictionary *attrDictionaryDelegate = [NSDictionary dictionaryWithObjectsAndKeys:(id)delegate, (NSString*)kCTRunDelegateAttributeName,nil];
[_attributedString appendString:#" " attributes:attrDictionaryDelegate];
CFRelease(delegate);
here appendString:attributes: is provided by Omni and is a category for NSMutableAttributedString:
- (void)appendString:(NSString *)string attributes:(NSDictionary *)attributes;
{
NSAttributedString *append;
append = [[NSAttributedString alloc] initWithString:string attributes:attributes];
[self appendAttributedString:append];
[append release];
}
In the Parser class I test CTFramesetterCreateWithAttributedString and it creates successfully and no memory problem happens. However, when I call CTFramesetterCreateWithAttributedString in my view class, it receives the EXC_BAD_ACESS problem. I send the CTFramesetterCreateWithAttributedString to my view in the viewDidLoad:
NSArray *arr = [OUIRTFReader parseRTFString2:rtfString];
self.editFrame.attributedText = [arr objectAtIndex:0];
//for OUIRTFReader
+ (NSArray *)parseRTFString2:(NSString *)rtfString
{
OUIRTFReader *parser = [[self alloc] _initWithRTFString:rtfString];
NSMutableArray *temp = [NSMutableArray arrayWithCapacity:1];
NSAttributedString *tempAstr = [[NSAttributedString alloc] initWithAttributedString:parser.attributedString];
[temp addObject:tempAstr];
[tempAstr release];
if (parser.zjImageArray) {
[temp addObject:parser.zjImageArray];
}
[parser release];
}
here editFrame is ivar of OUIEditableFrame provided by OmniFramework.
After that, in the layoutSubview: of OUIEditableFrame, CTFramesetterCreateWithAttributedString failed. Profiling with instrument, it points there is a zombie object in :
NSDictionary *imgAttr = [NSDictionary dictionaryWithObjectsAndKeys:imgWidth,kImageWidth,imgHeight,kImageHeight, nil];
It demonstrates that
An Objective-C message was sent to a deallocated object(zombie) at address
I'm not so familiar with core foundation like as UIKit or others. So I'd like to know what's wrong with the method to create a attributed string for image with CTRunDelegateRef in my code?
Thanks!

CTRunDelegateCreate create a delegate with an arbitrary context pointer (void *). That means the dictionary imgAttr isn't retained by the delegate.
You need to retain the context dictionary when creating the delegate and release it in the dealloc callback.

Related

See the variable in the same method=null ios

I use library soap the return of this function in on nsdictionary i change it into nsarray after i close ];} and type nslog it give that nsarray =null but if i put it before close of the method in the method give data
- (void)showlinks
{
SYSoapClient *show_links_obj = [SYSoapClient new];
links_tags = [[NSMutableArray alloc] initWithObjects:#"empid", #"type", nil];
links_vars = [[NSMutableArray alloc] initWithObjects:txt_username, type_user, nil];
[show_links_obj callSoapServiceWithParameters__functionName:#"getlinks"
tags:links_tags
vars:links_vars
callback:^(NSDictionary *result,
BOOL response)
{
link_raw_Data = [result allValues];
link_con_Data = [link_raw_Data componentsJoinedByString:#""];
//NSArray *links = [con_Data componentsSeparatedByString:#"#"];
links = [link_con_Data componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:#"#^"]];
NSUInteger x = links.count;
NSLog(#"%#",link_raw_Data);
//NSLog(#"%d",x);
//NSLog(#"%#",links[5]);
}];
NSLog(#"%#",links[5]);
}
Judging by the callback argument to the soap service, it's an asynchronous call. This means that it will return right away, do the networking on a background thread and call the callback sometime later when it is done.
Since it's asynchronous and doesn't wait/block, the log statement just after it will be executed before getting any data back from the soap service.

JSON with Dictionary - nested objects to convert to strings and display

I came across few posts here related to what I am doing but I am working with some nested objects that I want to extract.
This is a sample of my returned data - https://gist.github.com/ryancoughlin/8043604
I have this in my header so far :
#import "TideModel.h"
#protocol TideModel
#end
#implementation TideModel
-(id)initWithDict:(NSDictionary *)json {
self = [super init];
if(self) {
self.maxheight = [dictionary valueForKeyPath:#"tide.tideSummaryStats.minheight"];
self.minheight = [dictionary valueForKeyPath:#"tide.tideSummaryStats.maxheight"];
self.tideSite = [dictionary valueForKeyPath:#"tide.tideInfo.tideSite"];
}
return self;
}
#end
I have declared a property for each string and i am accessing it accordingly.
But what I have above doesn't work, maybe because it wont know what to drill in to correct?... Or will it?
tide.tideSummaryStats returns an array.
tide.tideInfo returns an array.
So you can't do -valueForKeyPath: all the way.
Also, this is incorrect: [dictionary valueForKeyPath:...];
it should be : [json valueForKeyPath:...];
because json is the name of the NSDictionary variable passed (not dictionary)
Try this (not sure):
-(id)initWithDict:(NSDictionary *)json {
self = [super init];
if(self) {
NSArray *arrOfTideSummaryStats = [json valueForKeyPath:#"tide.tideSummaryStats"];
NSDictionary *dctOfTideSummaryStats = [arrOfTideSummaryStats objectAtIndex:0];
//since self.maxheight and self.minheight are NSString objects and
//the original keys "minheight" & "maxheight" return float values, do:
self.maxheight = [NSString stringWithFormat:#"%f", [dctOfTideSummaryStats valueForKey: #"maxheight"]];
self.minheight = [NSString stringWithFormat:#"%f", [dctOfTideSummaryStats valueForKey: #"minheight"]];
/*============================================================*/
NSArray *arrOfTideInfo = [json valueForKeyPath:#"tide.tideInfo"];
NSDictionary *dctOfTideInfo = [arrOfTideInfo objectAtIndex:0];
self.tideSite = [dctOfTideInfo valueForKey:#"tideSite"];
}
return self;
}
Similar Questions:
How to parsing JSON object in iPhone SDK (XCode) using JSON-Framework
Getting array elements with valueForKeyPath
Keypath for first element in embedded NSArray
Recently had to create a app that worked with a remote RESTful server that returned JSON data and was then deserialised into an object for graphing.
I used unirest for the requests and responses and then deserialised the returned JSON into an object. Below is an extract of the code where "hourlySalesFigures" within dictionary "jsonResponseAsDictionary" was a JSON collection of 24 figures which I put into an array. Please note the function is a lot larger but I removed anything which I thought was distracting.
- (PBSSales*) deserializeJsonPacket2:(NSDictionary*)jsonResponseAsDictionary withCalenderType:(NSString *)calendarViewType
{
PBSSales *pbsData = [[PBSSales alloc] init];
if(jsonResponseAsDictionary != nil)
{
// Process the hourly sales figures if the day request and returned is related to Daily figures
if([calendarViewType isEqualToString:#"Day"]){
NSArray *hourlyFiguresFromJson = [jsonResponseAsDictionary objectForKey:#"hourlySalesFigures"];
PBSDataDaySales *tmpDataDay = [[PBSDataDaySales alloc] init];
NSMutableArray *hSalesFigures = [tmpDataDay hourlySalesFigures];
for(NSInteger i = 0; i < [hourlyFiguresFromJson count]; i++){
hSalesFigures[i] = hourlyFiguresFromJson[i];
}
[[pbsData dataDay] setHourlySalesFigures:hSalesFigures];
[pbsData setCalViewType:#"Day"];
}
}
return pbsData;
}

Simple way to store NSMutableAttributedString in CoreData

I'm trying to store an NSMutableAttributedString in CoreData, but am running into problems since some of the attributes of my NSMutableAttributedString contain Core Foundation objects that can't be archived. Is there an easy way to get this object to store in CoreData without having to do some messy stuff manually?
NSMutableAttributedString conforms to NSCoding, which means that it knows how to convert itself to/from an NSData and does so via a protocol that Core Data knows how to use.
Make the attribute "transformable", and then just assign attributed strings to it. Since it's transformable, Core Data will use NSCoding to convert it to NSData when you assign a value, and to convert it back to an attributed string when you read it.
Note, you won't be able to use a predicate to filter results on this field. But storing and retrieving it is simple.
While the above answer is right, it has one big disadvantage:
It is not possible to build a fetch request / predicate that queries the content of the NSAttributedString object!
A predicate like this will cause an exception when executed:
[NSPredicate predicateWithFormat:#"(content CONTAINS[cd] %#)", #"test"]];
To store an 'fetchable' NSAttributedString in Core Data is is needed to spilt the NSAttributedString into two parts: A NSString side (which can be fetched) and a NSData side, which stores the attributes.
This split can be achieved by creating three attributes in the Core Data entity:
a shadow NSString attribute ('contentString')
a shadow NSData attribute ('contentAttributes')
an 'undefined' transient attributed ('content')
In the custom entities class the 'content' attributed the created from its shadows and changes to the attribute are also mirrored to its shadows.
Header file:
/**
MMTopic
*/
#interface MMTopic : _MMTopic {}
#property (strong, nonatomic) NSAttributedString* content;
#end
Implementation file:
/**
MMTopic (PrimitiveAccessors)
*/
#interface MMTopic (PrimitiveAccessors)
- (NSAttributedString *)primitiveContent;
- (void)setPrimitiveContent:(NSAttributedString *)pContent;
#end
/**
MMTopic
*/
#implementation MMTopic
static NSString const* kAttributesDictionaryKey = #"AttributesDictionary";
static NSString const* kAttributesRangeKey = #"AttributesRange";
/*
awakeFromFetch
*/
- (void)awakeFromFetch {
[super awakeFromFetch];
// Build 'content' from its shadows 'contentString' and 'contentAttributes'
NSString* string = self.contentString;
NSMutableAttributedString* content = [[NSMutableAttributedString alloc] initWithString:string];
NSData* attributesData = self.contentAttributes;
NSArray* attributesArray = nil;
if (attributesData) {
NSKeyedUnarchiver* decoder = [[NSKeyedUnarchiver alloc] initForReadingWithData:attributesData];
attributesArray = [[NSArray alloc] initWithCoder:decoder];
}
if ((content) &&
(attributesArray.count)) {
for (NSDictionary* attributesDictionary in attributesArray) {
//NSLog(#"%#: %#", NSStringFromRange(((NSValue*)attributesDictionary[kAttributesRangeKey]).rangeValue), attributesDictionary[kAttributesDictionaryKey]);
[content addAttributes:attributesDictionary[kAttributesDictionaryKey]
range:((NSValue*)attributesDictionary[kAttributesRangeKey]).rangeValue];
}
[self setPrimitiveContent:content];
}
}
/*
content
*/
#dynamic content;
/*
content (getter)
*/
- (NSAttributedString *)content {
[self willAccessValueForKey:#"content"];
NSAttributedString* content = [self primitiveContent];
[self didAccessValueForKey:#"content"];
return content;
}
/*
content (setter)
*/
- (void)setContent:(NSAttributedString *)pContent {
[self willChangeValueForKey:#"content"];
[self setPrimitiveValue:pContent forKey:#"content"];
[self didChangeValueForKey:#"content"];
// Update the shadows
// contentString
[self setValue:pContent.string
forKey:#"contentString"];
// contentAttributes
NSMutableData* data = [NSMutableData data];
NSKeyedArchiver* coder = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
NSMutableArray* attributesArray = [NSMutableArray array];
[pContent enumerateAttributesInRange:NSMakeRange(0, pContent.length)
options:0
usingBlock:^(NSDictionary* pAttributesDictionary, NSRange pRange, BOOL* prStop) {
//NSLog(#"%#: %#", NSStringFromRange(pRange), pAttributesDictionary);
[attributesArray addObject:#{
kAttributesDictionaryKey: pAttributesDictionary,
kAttributesRangeKey: [NSValue valueWithRange:pRange],
}];
}];
[attributesArray encodeWithCoder:coder];
[coder finishEncoding];
[self setValue:data
forKey:#"contentAttributes"];
}
#end
Fetching can now be done by:
[NSPredicate predicateWithFormat:#"(contentString CONTAINS[cd] %#)", #"test"]];
While any access to the NSAttributedString goes like this:
textField.attributedText = pTopic.content;
The rules for working with 'Non-Standard attributes' in Core Data are documented here: Apple docs
Well I am not sure what you are trying to do with the attributed string, but if it's formatted text then can't you use NSFont, etc..
Take a look here http://ossh.com.au/design-and-technology/software-development, I posted some stuff on formatting styles and images with uitextview and nstextview, but mostly it's about attributed strings.
This stuff is all stored in core data.
I started using CoreText when iOS5 was out, and thus used the Core Foundation values as attributes. However I now realize that since iOS6 came out, I can now use NSForegroundColorAttributeName, NSParagraphStyleAttributeName, NSFontAttributeName, etc. in the attributes dictionary, and those keys are accompanied by objects like UIColor, NSMutableParagraphStyle, and UIFont which can be archived.

EXC_BAD_ACCESS on performSelectorInBackground

I want to call this method in background,
-(void)downloadImage_3:(NSString* )Path AtIndex:(int)i
I am calling in this way but it crashes and shows EXC_BAD_ACCESS
[self performSelectorInBackground:#selector(downloadImage_3:AtIndex:) withObject:[NSArray arrayWithObjects:#"http://www.google.com",i, nil]];
How to call downloadImage_3: method in background ?
where i am doing mistake ?
try this
[self performSelectorInBackground:#selector(downloadImage_3:AtIndex:) withObject:[NSArray arrayWithObjects:#"http://www.google.com",i, nil] afterDelay:15.0];
Or try this
NSString* number = [NSString stringWithFormat:#"%d",i];
NSArray* arrayValues = [[NSArray alloc] initWithObjects:[[msg_array objectAtIndex:i] valueForKey:#"Merchant_SmallImage"],number, nil];
NSArray* arrayKeys = [[NSArray alloc] initWithObjects:#"Path",#"Index",nil];
NSDictionary* dic = [[NSDictionary alloc] initWithObjects:arrayValues forKeys:arrayKeys];
[self performSelectorInBackground:#selector(downloadImage_3:) withObject:dic];
Define downloadImage_3 function like this:
-(void)downloadImage_3:(NSDictionary *)dic
{
NSString *path = [dic valueForKey:#"Path"];
int i = [[dic valueForKey:#"Index"] intValue];
//Your code
}
You can not call parameterized function in #selector. Create NSDictionary and then pass that dictionary in withObject:
int atIndex = 2; // whatever index you want to pass
NSArray *arr = [NSArray arrayWithObjects:obj1,obj2,nil];
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:atIndex],#"AtIndex",arr,#"ImagesUrl", nil];
[self performSelectorInBackground:#selector(downloadImage_3:) withObject:dictionary];
Define downloadImage_3 function like this:
-(void)downloadImage_3:(NSDictionary *)dic {
// use dic and write your code
}
You can't use performSelectorInBackground:withObject: for selectors that take multiple arguments. The suggestions other answers give sort of work, but they all assume you can manipulate the method you are calling, which is not always possible (or even a good idea for clarity).
I recommend using NSInvocation instead, as it allows multiple arguments, or alternatively using GCD to dispatch a block to a background queue (or any queue other than the main for that matter).
Here's an example usage of NSInvocation
NSMethodSignature *sig = [[self class] instanceMethodSignatureForSelector:#selector(downloadImage_3:AtIndex:)];
NSInvocation *inv = [NSInvocation invocationWithMethodSignature:sig];
[inv setTarget:self];
[inv setSelector:#selector(downloadImage_3:AtIndex:)];
[inv setArgument:(__bridge void *)#"http://www.google.com" atIndex:2];
NSUInteger i = 1;
[inv setArgument:&i atIndex:3];
[inv performSelectorInBackground:#selector(invoke) withObject:nil];
It's worth double checking, I wrote the code in the browser so I might've missed something the compiler will pick up.
As additional note, you should really re-think your method naming conventions, a much more standard way to name methods would have the method be named -(void)downloadImage3:(NSString* )path atIndex:(int)i. Notice how atIndex begins with lowercase and how there is no underscore (which just looks weird in the middle of a method name). Also it might be worth noting that using NSUInteger is preferred for the index, as NSArray in general works with NSUIntegers (both should work, but there are cases in which int might not suffice).

IOS: read txt file when I launch an app

Everytime I launch an app, I should to read 5 txt file where are stored some information; then the methods that read and stored data in array from these file should be write in my firstview controller (class of my first view) or in class appdelegate?
In the relevant view controller (probably viewDidLoad).
It would look something like this (untested):
- (void)viewDidLoad {
NSArray *fileNames = [NSArray arrayWithObjects:#"fileName1.txt", #"fileName2.txt", #"etc", nil];
NSMutableArray *fileStrings = [[NSMutableArray alloc] init];
for (int i = 0; i<[fileNames count]; i++) {
NSString *aFileName = [fileNames objectAtIndex:i];
NSString *aFilePath = [[NSBundle mainBundle] pathForResource:aFileName];
NSString *aFileContents = [[NSString alloc] initWithContentsOfFile:aFilePath];
[fileStrings addObject:aFileContents];
[aFileContents release];
}
myStrings = fileStrings; // Some array to store to
}
I am guessing this is configuration info that you are reading. I would suggest using a pList instead of using text files.
Apple has really optimized reading from & to a plist. Hope this helps...

Resources