1

I know this code doesn't work, but how could I actually initialize this correctly?:

NSUInteger highestModelID = 34605;
NSUInteger highestColorID = 328;
NSUInteger** modelColors[highestModelID][highestColorID] = malloc(highestModelID * highestColorID * sizeof(NSUInteger));

So having 2 dynamic depths. I have this massive buffer in a multi-dimensional NSMutableDictionary which hogs memory. I would really love to just do this primitive.

Guess it would be even more amazing to make a class out of this to be able to use it on more places where Objective-C dictionaries and even NSMutableArray are really just overkill. Over time I'm really becoming more and more annoyed by using NSNumber for something that would be super light what I'm used to in golang, suddenly making it a major factor in slowing my app down..

  • 1
    This question might be helpful: http://stackoverflow.com/questions/10575544/difference-between-declaration-and-malloc. Basically if you're malloc'ing the memory, you need to remove the `[highestModelID][highestColorID]` suffix on your `modelColors` declaration – as that's used for c arrays with automatic storage. – Hamish Apr 18 '16 at 12:06
  • Thanks for this tip! I hardly ever use native C anymore. I think the last time I seriously used low-level C was 17 years ago in Visual Basic 6(?). I guess it's good to maybe do some oldschool NASM/C tuts again just to get the hang of it again :) –  Apr 18 '16 at 12:29

2 Answers2

2

The way of creating it isn't really that different from creating NSArray of NSArrays. You need to alloc array of NSUInteger* first, then alloc each of its elements.

NSUInteger  **modelColors;
modelColors = malloc(highestModelID * sizeof(NSUInteger*));
for (int i = 0; i < highestModelID; i++) {
    modelColors[i] = malloc(highestColorID * sizeof(NSUInteger));
}
Mashiro
  • 76
  • 5
  • 1
    Wrong. You're doing `highestModelID` allocations, while this can be done with a single `malloc` of size `highestModelID * highestColorID`. – Tamás Zahola Apr 18 '16 at 12:05
  • Premature optimization? :D OP's primary concern was memory usage. You're probably aware that `malloc` doesn't allocate the exact number of bytes you're asking for, but rounds it up to the closest size class. Therefore using `highestModelID` number of allocations, you're wasting N * `highestModelID` bytes. (where N is the difference between `highestColorID` and the closest size class) – Tamás Zahola Apr 18 '16 at 12:58
  • @TamásZahola Ah, didn't see the memory concern in the question, my bad! – Hamish Apr 18 '16 at 12:59
2

You need this:

NSUInteger* modelColors = malloc(highestModelID * highestColorID * sizeof(NSUInteger));

Which you can use like this:

NSUInteger getModelColor(int modelID, int colorID, int highestModelID,  NSUInteger* modelColors) {
    return modelColors[colorID * highestModelID + modelID];
}

void setModelColor(NSUInteger color, int modelID, int colorID, int highestModelID,  NSUInteger* modelColors) {
    modelColors[colorID * highestModelID + modelID] = color;
}

Basically this is a 2D array, where modelID indexes the rows, and colorID indexes the columns (assuming row-major layout).

Tamás Zahola
  • 9,271
  • 4
  • 34
  • 46
  • Cool this works perfect. I'm gonna embed it in a class called `EB2DArray` so I can easily remember those 2 highest ID's for the rest of the array's lifespan, making it easier to just do `get*` and `set*` by only giving the `modelID` and `colorID`. Opens a lot of possibilities for different typed arrays too. Thanks! –  Apr 18 '16 at 12:25
  • It's my own responsibility to free the array isn't it? I know ARC doesn't see the `@property` inside my class as part of the `EB2DArray` object I would make. Maybe I need to override `dealloc` to specifically call `free()` on the `@property` that holds the array `- (void)dealloc { free(self.theArray); }`? –  Apr 18 '16 at 12:42
  • I might've missed something here, but for some reason when having the logic inside my class, the fetching of the NSUInteger doesn't work :(. Maybe I missed something: http://pastebin.com/fRbBncQd ? –  Apr 18 '16 at 13:14
  • Oh gosh lol, never mind me. The `intSetAtLevel1Key` was `level2Key + self.level1Capacity + level1Key` but should've been `level2Key * self.level1Capacity + level1Key`. –  Apr 18 '16 at 13:17
  • Yep. And also I would recommend replacing the `create...` method with a proper initializer like: `-(instancetype)initWithLevel1Capacity:(NSUInteger)level1Capacity level2Capacity:(NSUInteger)level2Capacity` – Tamás Zahola Apr 18 '16 at 13:18
  • Thanks! Changed that too. Looks nice and tidy in my main code now :) –  Apr 18 '16 at 13:27
  • Hmm I'm getting bad access if I do `free(self.theArray)` and then `malloc` it again and try to access entries from it. –  Apr 18 '16 at 15:10
  • http://pastebin.com/Z3qP995D pasted the class and the code that tries to call it, never on the first run. It always happens when it gets called again. I tried a reset call because `nil`ing it doesn't seem to work correctly either. It's inside my Core singleton object so it's there the whole lifespan of the app. It crashes on the `setIntValue` method after the first time called. –  Apr 18 '16 at 15:20
  • The initializer was not right, fixed that. Also added some sanity-checks in the indexing methods: http://pastebin.com/c8wkCBhg Also, don't bother with `reset`, just create a new array – this will deallocate the old one and create a new one, just like your tried with `reset`, but it's less code. – Tamás Zahola Apr 18 '16 at 15:29
  • Shouldn't the `init`-call be `+` (class-method)? Also the self cannot be accessed so it's giving errors. How can I properly instantiate it? :) –  Apr 18 '16 at 15:33
  • Nope. `init` is always an instance method (prefixed with `-`). Just look into any of the Apple headers and you'll see. – Tamás Zahola Apr 18 '16 at 15:34
  • Works now! Was a small typo in the example. –  Apr 18 '16 at 15:37
  • This is quite strange, b/c it's declared in the class extension. Aka the `@interface AM2DArray()` part. – Tamás Zahola Apr 18 '16 at 15:37
  • Yeah it was just variable wrongly named. I also had to do `highestModelID + 1` and `highestColorID + 1` because it can exactly hit out of range on the highest value. Works stable so far :) –  Apr 18 '16 at 15:45