Lescai Ionel
Lescai Ionel

Reputation: 4373

Variable sized array of struct?

I want to make an array of structs of which the size is known only at runtime

NSMutableArray *styleSettingsArray = [NSMutableArray array];

NSString *fontAlignmentAttribute = [element attributeNamed:@"TextAlignment"];
if(fontAlignmentAttribute)
{
    CTTextAlignment alignment = [self getTextAlignment:fontAlignmentAttribute];
    CTParagraphStyleSetting styleSetting = {kCTParagraphStyleSpecifierAlignment, sizeof(CTTextAlignment), &alignment};
    [styleSettingsArray addObject:[NSValue valueWithBytes:&styleSettings objCType:@encode(CTParagraphStyleSetting)]];
}

// other posible attributes

CTParagraphStyleRef paragraphStyleRef = CTParagraphStyleCreate((__bridge const CTParagraphStyleSetting *)(styleSettingsArray), [styleSettingsArray count]);
[dictionary setObject:(__bridge id)(paragraphStyleRef) forKey:(NSString*)kCTParagraphStyleAttributeName];
CFRelease(paragraphStyleRef);

This code does not work.

EDIT:

CTParagraphStyleCreate takes a pointer to an array of CTParagraphStyleSetting, such as

CTParagraphStyleSetting styleSettings[] = {
    { kCTParagraphStyleSpecifierAlignment, sizeof(CTTextAlignment), alignment},
    {...},
    {...}
};

How can I allocate this array, and add stuff to it without knowing how much stuff will be in it ? (how do I use malloc ?)

Upvotes: 2

Views: 227

Answers (2)

Nikolai Ruhe
Nikolai Ruhe

Reputation: 81856

To avoid having to collect the number of attributes and then create them in a second pass I suggest to use NSMutableData instead of a plain C array or malloc. This style would allow you to use your existing code with only minimal changes:

NSMutableData *styleSettingsArray = [NSMutableData array];

NSString *fontAlignmentAttribute = [element attributeNamed:@"TextAlignment"];
CTTextAlignment alignment;
if (fontAlignmentAttribute)
{
    alignment = [self getTextAlignment:fontAlignmentAttribute];
    CTParagraphStyleSetting styleSetting = { kCTParagraphStyleSpecifierAlignment, sizeof(CTTextAlignment), &alignment};
    [styleSettingsArray appendBytes:&styleSetting length:sizeof(styleSetting)];
}

// other posible attributes
CTParagraphStyleRef paragraphStyleRef = CTParagraphStyleCreate([styleSettingsArray bytes], [styleSettingsArray length] / sizeof(CTParagraphStyleSetting));

Edit: Please note the extension of the lifetime of the alignment variable, compared to your code. This is necessary because it is referenced after the block ends in which it was declared before.

Upvotes: 1

trojanfoe
trojanfoe

Reputation: 122381

You cannot mix Objective-C or Core Foundation collections with plain C arrays in this way. Also as you have found out you need to wrap those CTParagraphStyleSetting structs in NSNumber objects in order to store them. What a mess.

The approach I would take would be to do 2-passes; the first to determine how many attributes you have and the second to generate those attributes.

  1. Iterate over your conditions to find out how many attributes there are
  2. Allocate a C array (see malloc()).
  3. Iterate over your conditions and store the attributes in the dynamic array.
  4. Use the array to create the style.
  5. Free the array (see free()).

NOTE I have edited this answer as my previous answer was way off.

Upvotes: 2

Related Questions