You could do it the Objective-C way and have an NSArray
of NSArray
of NSNumber
s. This is the most direct way to do it and you need not worry too much about allocating and deallocating as it all happens automatically with those Objective-C objects. There will be some conversion to get all those shorts into NSNumber
s and you won't be able to access them as one single chunk or pointer, you will have to traverse them.
Here is some sample code to convert one of those and append it to your list.
@interface MyList()
@property (nonatomic,strong) NSMutableArray < NSArray * > * list;
@end
@implementation MyList
// Lazy
- ( NSMutableArray < NSArray * > * ) list
{
if ( ! _list )
{
_list = NSMutableArray.array;
}
return _list;
}
// Work
// Return index at which it was added
- ( NSUInteger ) addShorts:( short [] ) data
ofLength:( NSUInteger ) n
{
NSMutableArray * m = [NSMutableArray arrayWithCapacity:n];
while ( n )
{
[m addObject:@( * data )]; // Convert to short and add
data ++;
n --;
}
// Add to list
[self.list addObject:m];
return self.list.count - 1;
}
// Just to test
- ( id ) init
{
self = super.init;
if ( self )
{
short data[ 4 ] = { 1, 2, 3, 4 };
[self addShorts:data
ofLength:4];
NSLog ( @"How about %@", self.list );
}
return self;
}
@end
I think this is your best option.
However, it is certainly not your only one. If you need performance or to copy blocks of memory at a time you could use C pointers here. Since this can be blended into Objective-C easily this is a real viable option provided you know some C and are comfortable with pointers.
Those are sort of the extremes.
Then there are some inbetween options. You could e.g. wrap the short[]
into NSValue
s and store them into the array.
Update
Based on continued discussion in comments, herewith some updated code that I used to test.
@interface MyList()
@property (nonatomic,strong) NSMutableArray < NSData * > * list;
@end
@implementation MyList
// Lazy
- ( NSMutableArray < NSData * > * ) list
{
if ( ! _list )
{
_list = NSMutableArray.array;
}
return _list;
}
// Work
// Return index at which it was added
- ( NSUInteger ) addShorts:( short [] ) data
ofLength:( NSUInteger ) n
{
[self.list addObject:[NSData dataWithBytesNoCopy:data
length:n * sizeof ( short )
freeWhenDone:YES]];
return self.list.count - 1;
}
// Print a list
- ( void ) dumpListAtIndex:( NSUInteger ) index
ofLength:( NSUInteger ) n
{
NSData * data = [self.list objectAtIndex:index];
short * p = ( short * ) data.bytes;
NSLog ( @"Index %lu pointer %p", index, p );
NSLog ( @"\t%d values", ( int ) n );
for ( int i = 0; i < n; i ++ )
{
NSLog ( @"\t\t%d] %d", i, ( int )( * ( p + i ) ) );
}
}
// Just to test
- ( id ) init
{
self = super.init;
if ( self )
{
short * data = calloc( 4, sizeof( short ) );
for ( int i = 0; i < 4; i ++ )
{
* ( data + i ) = ( short ) i;
}
[self addShorts:data
ofLength:4];
NSLog ( @"Allocated pointer is %p", data );
[self dumpListAtIndex:0
ofLength:4];
NSLog ( @"Change some values - is this allowed" );
for ( int i = 0; i < 4; i ++ )
{
* ( data + i ) = ( short ) ( 10 - i );
}
[self dumpListAtIndex:0
ofLength:4];
}
return self;
}
@end
Based on this it seems you can use just NSData
and not NSMutableData
. It feels as if we are breaking the rules as we are changing the data pointed to by NSData.bytes
but I think it is safe and even preferable, since we are not changing the pointer itself. The pointer does not and should not change, so I think NSData
is better. Of course, it is crucial that you not change the length ever of the short pointer and that you not write past its end and so on.
This is a nice use case. You now have a short *
wrapped inside NSData
which is stored inside a NSMutableArray
. So Objective-C takes care of the freeing of the pointer for you when the NSData
goes out of scope, but you can still access and even change those shorts pointed to.
Performance
The pointers should do better. If the shorts are stored as NSNumber
's inside an NSArray
there are overhead in terms of converting to and from the NSNumber
and in managing the NSArray
. Here is some code you can use to test.
int main ( int argc, const char * argv [] )
{
@autoreleasepool
{
// insert code here...
NSLog(@"Hello, World!");
// Warm up
for ( int i = 0; i < 10000; i ++ )
{
for ( int j = 0; j < 10000; j ++ )
{
double x = atan ( i + j );
double y = asin ( i + j );
double z = x / ( 1 + fabs ( y ) );
}
}
// Test parameters
int n = 30; // Arbitrary length of array of shorts
// Note - the way in which the test is written this should be less
// than the maximum short
int b = n * sizeof ( short ); // Size of memory we require as a result
int l = 20000; // Arbitrary length of list
int m = 20; // Arbitrary number of times we loop
int r = 500; // Arbitrary number of reads per loop
int w = 20; // Arbitrary number of writes per loop
// Test array of arrays of shorts wrapped inside NSNumber
NSLog(@"Start NSNumber test" );
@autoreleasepool
{
NSMutableArray * list = [NSMutableArray arrayWithCapacity:l];
// Create list
for ( int i = 0; i < l; i ++ )
{
// Array of shorts
NSMutableArray * shorts = [NSMutableArray arrayWithCapacity:n];
for ( int j = 0; j < n; j ++ )
{
[shorts addObject:[NSNumber numberWithShort:0]];
}
[list addObject:shorts];
}
// Loop
for ( int i = 0; i < m; i ++ )
{
NSLog ( @"\tLoop %d/%d", i + 1, m );
// Write tests
for ( int t = 0; t < w; t ++ )
{
for ( NSMutableArray * shorts in list )
{
// Note n must be less than maximum short
for ( short j = 0; j < n; j ++ )
{
[shorts replaceObjectAtIndex:j
withObject:[NSNumber numberWithShort:j]];
}
}
}
// Read tests
for ( int t = 0; t < r; t ++ )
{
for ( NSMutableArray * shorts in list )
{
// Calculate total as read test
int total = 0;
for ( NSNumber * s in shorts )
{
total += s.shortValue;
}
}
}
}
list = nil;
}
NSLog(@"End NSNumber test" );
// Test array of arrays of shorts wrapped inside NSData
NSLog(@"Start NSData test" );
@autoreleasepool
{
NSMutableArray * list = [NSMutableArray arrayWithCapacity:l];
// Create list
for ( int i = 0; i < l; i ++ )
{
// NSData of shorts
short * p = malloc ( b );
[list addObject:[NSData dataWithBytes:p
length:b]];
}
// Loop
for ( int i = 0; i < m; i ++ )
{
NSLog ( @"\tLoop %d/%d", i + 1, m );
// Write tests
for ( int t = 0; t < w; t ++ )
{
for ( NSData * shorts in list )
{
short * p = shorts.bytes;
// Note n must be less than maximum short
for ( short j = 0; j < n; j ++ )
{
* ( p + j ) = j;
}
}
}
// Read tests
for ( int t = 0; t < r; t ++ )
{
for ( NSData * shorts in list )
{
short * p = shorts.bytes;
// Calculate total as read test
int total = 0;
for ( int j = 0; j < n; j ++ )
{
total += * ( p + j );
}
}
}
}
list = nil;
}
NSLog(@"End NSData test" );
}
return 0;
}
This code is pretty arbitrary but there are lots of parameters you can use to calibrate to your needs. On my very ancient mac mini the results look like this.
2021-01-17 11:35:56.695846+0200 ListOfShorts[733:19458] Hello, World!
2021-01-17 11:36:01.328276+0200 ListOfShorts[733:19458] Start NSNumber test
2021-01-17 11:36:01.354546+0200 ListOfShorts[733:19458] Loop 1/20
2021-01-17 11:36:07.241021+0200 ListOfShorts[733:19458] Loop 2/20
2021-01-17 11:36:13.136964+0200 ListOfShorts[733:19458] Loop 3/20
2021-01-17 11:36:19.054930+0200 ListOfShorts[733:19458] Loop 4/20
2021-01-17 11:36:24.906127+0200 ListOfShorts[733:19458] Loop 5/20
2021-01-17 11:36:31.045219+0200 ListOfShorts[733:19458] Loop 6/20
2021-01-17 11:36:37.189506+0200 ListOfShorts[733:19458] Loop 7/20
2021-01-17 11:36:43.231712+0200 ListOfShorts[733:19458] Loop 8/20
2021-01-17 11:36:49.301254+0200 ListOfShorts[733:19458] Loop 9/20
2021-01-17 11:36:55.194978+0200 ListOfShorts[733:19458] Loop 10/20
2021-01-17 11:37:01.021958+0200 ListOfShorts[733:19458] Loop 11/20
2021-01-17 11:37:06.867754+0200 ListOfShorts[733:19458] Loop 12/20
2021-01-17 11:37:12.694702+0200 ListOfShorts[733:19458] Loop 13/20
2021-01-17 11:37:18.553492+0200 ListOfShorts[733:19458] Loop 14/20
2021-01-17 11:37:24.389069+0200 ListOfShorts[733:19458] Loop 15/20
2021-01-17 11:37:30.253186+0200 ListOfShorts[733:19458] Loop 16/20
2021-01-17 11:37:36.081344+0200 ListOfShorts[733:19458] Loop 17/20
2021-01-17 11:37:41.938501+0200 ListOfShorts[733:19458] Loop 18/20
2021-01-17 11:37:47.760841+0200 ListOfShorts[733:19458] Loop 19/20
2021-01-17 11:37:53.559841+0200 ListOfShorts[733:19458] Loop 20/20
2021-01-17 11:37:59.441152+0200 ListOfShorts[733:19458] End NSNumber test
2021-01-17 11:37:59.441281+0200 ListOfShorts[733:19458] Start NSData test
2021-01-17 11:37:59.449322+0200 ListOfShorts[733:19458] Loop 1/20
2021-01-17 11:38:00.532628+0200 ListOfShorts[733:19458] Loop 2/20
2021-01-17 11:38:01.631107+0200 ListOfShorts[733:19458] Loop 3/20
2021-01-17 11:38:02.736492+0200 ListOfShorts[733:19458] Loop 4/20
2021-01-17 11:38:03.835955+0200 ListOfShorts[733:19458] Loop 5/20
2021-01-17 11:38:04.952482+0200 ListOfShorts[733:19458] Loop 6/20
2021-01-17 11:38:06.064449+0200 ListOfShorts[733:19458] Loop 7/20
2021-01-17 11:38:07.167619+0200 ListOfShorts[733:19458] Loop 8/20
2021-01-17 11:38:08.292993+0200 ListOfShorts[733:19458] Loop 9/20
2021-01-17 11:38:09.402838+0200 ListOfShorts[733:19458] Loop 10/20
2021-01-17 11:38:10.500115+0200 ListOfShorts[733:19458] Loop 11/20
2021-01-17 11:38:11.596806+0200 ListOfShorts[733:19458] Loop 12/20
2021-01-17 11:38:12.685986+0200 ListOfShorts[733:19458] Loop 13/20
2021-01-17 11:38:13.792819+0200 ListOfShorts[733:19458] Loop 14/20
2021-01-17 11:38:14.883854+0200 ListOfShorts[733:19458] Loop 15/20
2021-01-17 11:38:15.985879+0200 ListOfShorts[733:19458] Loop 16/20
2021-01-17 11:38:17.167663+0200 ListOfShorts[733:19458] Loop 17/20
2021-01-17 11:38:18.462419+0200 ListOfShorts[733:19458] Loop 18/20
2021-01-17 11:38:19.743327+0200 ListOfShorts[733:19458] Loop 19/20
2021-01-17 11:38:21.044292+0200 ListOfShorts[733:19458] Loop 20/20
2021-01-17 11:38:28.677989+0200 ListOfShorts[733:19458] End NSData test
Program ended with exit code: 0
This shows roughly two minutes for the first alternative, the array of arrays of NSNumber
's and roughly half a minute when using pointers. I did not think the results would be that much different. Of course, you must attach a huge disclaimer to any performance test but yeah pointers! They are fast. PS: Earlier I said 1 minute, is 2!!!! for the first.
The write operation in the test will come at a very small penalty for the pointers. There the same region of memory is reused and just updated with new values. On the other side, with the array of arrays of shorts, the penalty is much bigger. There a new value means a new object (NSNumber
) is created and this object is then inserted into the array, replacing the old value.
YMMV