NSMutableArray gets dealloc halfway through block - objective-c

I have an NSMutableArray getting populated with values within a enumerateObjects loop. About the 4th or 5th time the function to populate the MutableArray is getting called, I get a SIGSEGV error with the stack
0 libobjc.A.dylib 0x39e7e626 objc_msgSend + 6
1 CoreFoundation 0x2f663dc9 -[__NSArrayM dealloc] + 154
2 libobjc.A.dylib 0x39e83b6b _ZN11objc_object17sidetable_releaseEb + 172
3 MyAppName 0x0004bdff -[BluetoothManager(DataParse) processResponse:] (BluetoothManager+DataParse.m:231)
Setup
An appDelegate which has a strong pointer to BluetoothManager instance.
A function call on BluetoothManager class, with the signature processResponse: on BluetoothManager which is invoked every time didUpdateValueForCharacteristic: is called on the peripheral delegate.
processBleResponse: takes an NSData blob and converts it into an array of NSNumbers
What I'm noticing is that the 4th time or so that processReponse: is called, the mutable Array in which I store all my NSNumbers gets malloc'd within its own enumerateObjects loop.
I've simulated the test with a repeating NSTimer which invokes processResponse: every 30-40s with some dummy data, but the error is reproducible after every few iterations of the loop. The overall memory usage of the app is not spiking over 45MB
// method on BluetoothManager class
- (void)processBleResponse:(NSData *)myData
{
__block float baseValue = 34.500;
__block float incrementValue = 0.0500;
// convert hex data into array of 10 binary "strings"
NSArray *individualBinaryArray = [self extractBinaryValues:myData];
// Extract temperature fields
NSIndexSet *relevantIndices = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(1, individualBinaryArray.count - 1)];
__block NSUInteger indexOfFailure = NSNotFound;
NSMutableArray *decimalValues = [[NSMutableArray alloc] init];
[individualBinaryArray enumerateObjectsAtIndexes:relevantIndices options:NSEnumerationConcurrent usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSString *hexString = [self hexString:(NSString *)obj];
if (hexString.length)
{
NSScanner *decimalScanner = [NSScanner scannerWithString:hexString];
unsigned int decimalValue = 0;
if ([decimalScanner scanHexInt:&decimalValue])
{
// insert value
// THIS IS THE LINE WHERE THE CRASH OCCURS
[decimalValues addObject:#(baseValue + incrementValue * decimalValue)];
}
else
{
// throw alert/exception etc.
*stop = YES;
indexOfFailure = idx;
}
}
else
{
*stop = YES;
indexOfFailure = idx;
}
}];
// Insert list of NSNumbers into CoreData
}

Related

Using a va_list method without a count

I'm writing a category on NSArray to add JavaScript array methods to NSArray. In JavaScript, the splice() method both adds/removes items to/from an array. But the number of objects added to the array may vary. So I used va_list to allow for a more flexible input of object values.
As it stands the method requires a count input value. How could I rewrite this without one?
Interface
#interface NSArray (JavaScriptArray)
- (NSArray *)splice:(NSUInteger)index remove:(NSUInteger)remove count:(NSUInteger)count arguments:(id)firstObject,...;
#end
Implementation
#implementation NSArray (JavaScriptArray)
- (NSArray *)splice:(NSUInteger)index remove:(NSUInteger)remove count:(NSUInteger)count arguments:(id)firstObject,...
{
NSMutableArray *mSplice = [NSMutableArray arrayWithArray:self];
if (remove != 0) {
NSUInteger removeIndex = index;
for (NSUInteger i = 0; i < remove; i++) {
[mSplice removeObjectAtIndex:removeIndex];
removeIndex = removeIndex + 1;
}
}
if (count != 0) {
NSUInteger addIndex = index;
id eachObject;
va_list argumentList;
if (firstObject) {
[mSplice insertObject:firstObject atIndex:addIndex];
addIndex = addIndex + 1;
va_start(argumentList, firstObject);
eachObject = va_arg(argumentList, id);
for (NSUInteger i = 0; i < count; i++) {
[mSplice insertObject:eachObject atIndex:addIndex];
addIndex = addIndex + 1;
}
va_end(argumentList);
}
}
return [NSArray arrayWithArray:mSplice];
}
#end
Calling the Method
- (void)viewDidLoad
{
[super viewDidLoad];
NSArray *fruit = #[#"Banana", #"Orange", #"Apple", #"Mango"];
NSArray *fruitSplice = [fruit splice:2 remove:0 count:4 arguments:#"Lemon", #"Kiwi", #"Kiwi", #"Kiwi"];
NSLog(#"fruitSplice %#", fruitSplice);
}
#end
Debugger Window
fruitSplice (
Banana,
Orange,
Lemon,
Kiwi,
Kiwi,
Kiwi,
Kiwi,
Apple,
Mango
)
Removing the count argument is no problem, because actually your "add" loop appears to be incorrect. After you've gotten the second item from the va_list with eachObject = va_arg(argumentList, id);, you never get another object. The only reason your example works is that all the later items are the same: #"Kiwi". If your test call was
NSArray *fruitSplice = [fruit splice:2
remove:0
count:4
arguments:#"Lemon", #"Albatross", #"Kiwi", #"Kiwi"];
you'd see
fruitSplice (
Banana,
Orange,
Lemon,
Albatross,
Albatross,
Albatross,
Albatross,
Apple,
Mango
)
as output.
You need to redo your unpacking of the va_list, and you can keep your own counter while you're iterating it, but the catch is that there has to be a sentinel value: an value that can't possibly appear as a valid list value, that indicates you've come to the end. For object-type variadic arguments you would generally use nil as the sentinel.
When you're using a va_list you must have either a sentinel or a count. There's no other way for you to know when to stop popping arguments.
Your signature can become*:
- (NSArray *)KRSpliceAt:(NSUInteger)index
removingCount:(NSUInteger)remove
addingObjects:(id)firstObject, ... NS_REQUIRES_NIL_TERMINATION;
The NS_REQUIRES_NIL_TERMINATION is strictly optional, but will make the compiler notify you if the method is called without a sentinel.
Then your adding loop changes:
// Insertion index starts at given splice point
NSUInteger addIndex = index;
// Initialize the va_list
va_list objs;
va_start(objs, firstObj);
// Start at the beginning
id nextObj = firstObj;
// Test for sentinel nil
while( nextObj ){
[mSplice insertObject:nextObj atIndex:addIndex];
// Update insertion point
addIndex++;
// Get next argument
nextObj = va_arg(objs, id);
}
// Signal completion of list
va_end(objs);
*Methods you add to classes you don't own should always be prefixed. It's a bit annoying, but it's good practice to prevent a catastrophic clash if you should happen to pick the same name as another method.

Pointers to primitive objects not properly changing value

I am having a bit of an issue passing a reference to a primitive type through chaining, and having the value represented by the pointer change correctly. The weird part is, if I call getBytes directly from main function, byteLocation is properly adjusted, but if I chain it through a convenience function, it seems get a junk value. Actually, even weirder, it at first gets the correct value when stepping through the debugger, but executes the return clause twice. The first return clause gets the correct value, the second loads byteLocation with a junk value. Any ideas?
EDIT (Actual Code):
#property (strong, nonatomic, nonnull) NSData* data;
#property (assign, nonatomic) CFByteOrder byteOrder;
- (void)convertBytesToHostOrder:(nonnull void*)buffer length:(NSUInteger)length {
if(length > 1 && self.byteOrder != CFByteOrderGetCurrent()) {
// Swap bytes if the packet endiness differs from the host
char* fromBytes = buffer;
for(NSUInteger i=0; i < length/2; i++) {
NSUInteger indexes[2] = {i, length-i-0};
char byte = fromBytes[indexes[0]];
fromBytes[indexes[0]] = fromBytes[indexes[1]];
fromBytes[indexes[1]] = byte;
}
}
}
- (nonnull void*)getBytes:(nonnull void*)buffer startingFrom:(nonnull NSUInteger*)location length:(NSUInteger)length {
NSRange range = NSMakeRange(*location, length);
[self.data getBytes:buffer range:range]; // self.data is an instance of NSData
[self convertBytesToHostOrder:buffer length:length];
NSUInteger update = range.location + range.length;
*location = update;
return buffer;
}
- (NSTimeInterval)readTimeIntervalStartingFrom:(nonnull NSUInteger*)byteLocation {
uint32_t seconds;
uint16_t milliseconds;
// This line of code screws up the byteLocation pointer for some reason
[self getBytes:&seconds startingFrom:byteLocation length:sizeof(seconds)];
[self getBytes:&milliseconds startingFrom:byteLocation length:sizeof(milliseconds)];
NSTimeInterval ti = seconds + milliseconds / ((double) 1000 * (1 << 6));
return ti;
}
- (void)readData {
NSUInteger byteLocation = 0;
self.sequenceNumber = *(uint8_t*) [self getBytes:&_sequenceNumber startingFrom:&byteLocation length:sizeof(_sequenceNumber)];
self.flags = *(uint8_t*) [self getBytes:&_flags startingFrom:&byteLocation length:sizeof(_flags)];
// Continue to process packet data if we didn't get a goodbye message
if(!(self.flags & LBRadarPongFlagGoodbye)) {
// Parse accelerations
int16_t int16;
self.accelerationX = (*(int16_t*) [self getBytes:&int16 startingFrom:&byteLocation length:sizeof(int16)]) / kGToRaw;
self.accelerationY = (*(int16_t*) [self getBytes:&int16 startingFrom:&byteLocation length:sizeof(int16)]) / kGToRaw;
self.accelerationZ = (*(int16_t*) [self getBytes:&int16 startingFrom:&byteLocation length:sizeof(int16)]) / kGToRaw;
// Parse peripheral states
self.batteryVoltage = [self readFloat16From:&byteLocation];
self.chargeCurrent = [self readFloat16From:&byteLocation];
self.systemCurrent = [self readFloat16From:&byteLocation];
// All previous lines of code work properly and as expected.
// Buffers are read properly, and byteLocation properly reflects 14, which is the number of bytes read up to this point.
self.pongReceivedTimeIntervalSince1970 = [self readTimeIntervalStartingFrom:&byteLocation];
}
}
The problem seems to be with incrementing the location. In both cases you should copy from position zero, up to the size of the variable. In the following code:
[self readBytes:&seconds location:byteLocation length:sizeof(seconds)]
[self readBytes:&milliseconds location:byteLocation length:sizeof(milliseconds)]
The first call starts at position zero and reads 32 bits. The second one starts at position 32, which doesn't even fit in the variable's 16 bits. This is overflowing the buffer. Try this instead:
- (void*)readBytes:(void*)buffer location:(NSUInteger*)location length:(NSUInteger)length {
// The difference is in the next line. Zero instead of *location
[NSData getBytes:&buffer range:NSMakeRange(0, length)];
*location = *location + length;
return buffer; // Seems to be called twice, first time location* has the correct byteLocation inside it, second time location* has a junk value
}
At a guess[*] your error is on the line:
[self.data getBytes:&buffer range:NSMakeRange(*location, length)];
You are passing a void * value by taking the address of buffer - which is already a void *. Changing this to:
[self.data getBytes:buffer range:NSMakeRange(*location, length)];
will at least produce non-garabge results.
[*] I can only guess as the code you posted did not even compile, I edited your question to correct some of the more obvious errors - but even that involved some guessing! You should post real code.
I should have just posted the actual code, sorry guys. Turns out the error was in a helper function (convertBytesToHostOrder). This was reading out of bounds of buffer. Since buffer was the parameter right before byteLocation, it seems that writing at a location 1 spot beyond buffer was the byteLocation location. Fixed now and everything is working.
- (void)convertBytesToHostOrder:(nonnull void*)buffer length:(NSUInteger)length {
if(length > 1 && self.byteOrder != CFByteOrderGetCurrent()) {
// Swap bytes if the packet endiness differs from the host
char* fromBytes = buffer;
for(NSUInteger i=0; i < length/2; i++) {
NSUInteger indexes[2] = {i, length-i-0};
char byte = fromBytes[indexes[0]];
fromBytes[indexes[0]] = fromBytes[indexes[1]];
fromBytes[indexes[1]] = byte;
}
}
}
Should be:
NSUInteger indexes[2] = {i, length-i-1};

ObjC NSNumber stores pointer to Object instead of returned Value

I wrote my own Objective C class for dealing with 2D Vectors.
#interface Vector2D : NSObject {
struct _vecdata {
double x;
double y;
double x_norm;
double y_norm;
} _data;
}
#property(nonatomic) double x; //custom getter + setter
#property(nonatomic) double y; //custom getter + setter
#property(nonatomic, readonly) double xNormed; //custom getter
#property(nonatomic, readonly) double yNormed; //custom getter
+ (id)vectorWithX:(double)x_ext andY:(double)y_ext;
- (id)initWithX:(double)x_ext andY:(double)y_ext;
[...]
- (double)length;
#end
#import <math.h>
#implementation Vector2D
+ (id)vectorWithX:(double)x_ext andY:(double)y_ext{
return [[self alloc] initWithX:x_ext andY:y_ext];
}
- (id)initWithX:(double)x_ext andY:(double)y_ext {
if ((self = [super init])) {
_data.x = x_ext;
_data.y = y_ext;
[self normalize];
}
return self;
}
- (void)setX:(double)x {
_data.x = x;
[self normalize];
}
- (double)x {
return _data.x;
}
- (double)xNormed {
return _data.x_norm;
}
//setters and getter for y omitted (same as for x)
[...]
- (void)normalize {
double abs = [self length];
if (abs != 0) {
_data.x_norm = _data.x/abs;
_data.y_norm = _data.y/abs;
}
}
- (double)length {
return sqrt(_data.x*_data.x + _data.y*_data.y);
}
#end
Now I need to wrap the result of a call to an Vector2D instances length call to an NSNumber.
NSNumber* aNSNum = #([[Vector2D vectorWithX:someXValue andY:someYValue] length]);
Since I display the value of aNSNum later on, I noticed, that all the values (of all NSNumbers created that way) are around 1.40537252E+14 (since i call [aNSNum floatValue] later on).
So I recompiled and rerun the app and suddenly all the values were around 4.73280427E+14.
So I wondered, since the length of the Vectors should be in the range 0 to 20000, but not more.
I started playing around in the debugger to understand whats happening and got the following results:
(lldb) po [[Vector2D vectorWithX:someXValue andY:someYValue] length]
0x0000000000000001
(lldb) po (double)[[Vector2D vectorWithX:someXValue andY:someYValue] length]
84.693565280958623
(lldb) po (unsigned long)[[Vector2D vectorWithX:someXValue andY:someYValue] length]
1
(lldb) po #([[Vector2D vectorWithX:someXValue andY:someYValue] length])
84.69356528095862
(lldb) po #((double)[[Vector2D vectorWithX:someXValue andY:someYValue] length])
84.69356528095862
(lldb) po #((unsigned long)[[Vector2D vectorWithX:someXValue andY:someYValue] length])
1
(lldb) po aNSNum
140537282189312
(lldb) po [aNSNum floatValue]
1.40537286E+14
(lldb) po (unsigned long)[aNSNum doubleValue]
<Vector2D: 0x7fd162c85800>
(lldb) po (double)[(unsigned long)[aNSNum doubleValue] length]
84.693565280958623
So the really intresting part is about the second last line. So why is the pointer adress to the Vector2D Object stored in the NSNumber and not the value of the return value of the call to -length?
Since then I tried to change the bogus line of code to the following variants: (with no sucess)
NSNumber* aNSNum = #((double)[[Vector2D vectorWithX:someXValue andY:someYValue] length]);
NSNumber* aNSNum = #([((Vector2D *)[Vector2D vectorWithX:someXValue andY:someYValue]) length]);
NSNumber* aNSNum = #((double)[((Vector2D *)[Vector2D vectorWithX:someXValue andY:someYValue]) length]);
What worked so far was the following:
static SEL lengthSEL;
static IMP lengthIMP;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
lengthSEL = #selector(length);
lengthIMP = class_getMethodImplementation([Vector2D class], lengthSEL);
});
Vector2D* vec = [Vector2D vectorWithX:someXValue andY:someYValue];
double len = ((double (*) (id,SEL))lengthIMP)(vec,lengthSEL);
NSNumber* aNSNum = #(len);
But hope someone could help me to bring it back again to a one-liner. Or has a clue where it gets wrong...
Edit:
The compiler gets confused because the return type of
+ (id)vectorWithX:(double)x_ext andY:(double)y_ext;
is id; It does not know til runtime which length method it will get. Change the declaration to
+ (instancetype )vectorWithX:(double)x_ext andY:(double)y_ext;
and watch how much nicer things get.
Learn more at apple docs
You are relying on the literal syntax #(someNumber) to detect the type of someNumber and handle it properly, in this case, the (double) return value of your length method. The rules for number literals are here.
I think the safe thing to do, rather than typecast your variable and hope that the compiler picks up on it, is to create the number with explicit typing, that is,
NSNumber* aNSNum = [NSNumber numberWithDouble:[[Vector2D vectorWithX:someXValue andY:someYValue] length]];
NSNumber literals are great shorthand for constants and the like, but this seems a case where they make the code harder to understand instead of easier.

NSMutableArray with size of 9

I need to have an NSMutableArray with a constant count of 9 where I can make index-specific insertions and deletions. I know that array = [[NSMutableArray alloc] initWithCapacity:9]; will declare an array with a capacity of 9, but when you get the size of the array, it returns 0.
My first attempt at a solution was to declare an array with capacity 9 (see above) and then fill it with NSNull objects. This code crashes with the error
[NSMutableArray insertObjects:atIndexes:]: array argument is not an NSArray'
- (void) setBlankArray: (NSMutableArray*)array {
for (int i = 0; i < 9; i++) {
[array insertObjects:[NSNull null] atIndexes:i];
}
}
-(void) addCurrentTile: (TileView*)aTile {
[currentTurnTilesArray insertObject:aTile atIndex: aTile.getValue-1];
}
-(void) removeCurrentTile: (TileView*)aTile {
[currentTurnTilesArray removeObjectAtIndex: aTile.getValue-1];
}
Is there a better way to accomplish it?
Not sure what you are trying to accomplish or why, but your removeCurrentTile will break it, because it will reduce the size of the array by 1. What you need to do is wrap this array with a class that guards it such that it can never never never have any other number of elements than 9.
Personally, I think what you're trying to do is silly. If you know you will always have exactly 9 slots, then you should start with a normal array, not a mutable array. It is the objects at each index that you want to mutate - not the array itself. For example, if these things were to be strings, then you would make an immutable array of 9 NSMutableString objects:
NSArray* arr = #[
[NSMutableString string],
[NSMutableString string],
[NSMutableString string],
[NSMutableString string],
[NSMutableString string],
[NSMutableString string],
[NSMutableString string],
[NSMutableString string],
[NSMutableString string]
];
Now each string can be mutated into another value, but no strings can be added or removed such as to change the length of the array.
Of course that's just an example (using strings, I mean). For maximum flexibility, this would be an NSArray of nine NSMutableDictionary objects. Now every NSMutableDictionary can contain anything, or nothing. But the number of NSMutableDictionaries will always be exactly nine, because the array itself is immutable.
You're looking for insertObject:atIndex:, or more simply addObject:.
[[NSMutableArray alloc] initWithCapacity:9] does not create an array with 9 elements.
It creates an empty array initialized with enough memory to hold 9 objects.
The purpose of this method is to allocate that much memory at once as you declare, so you can add elements to this array and system has not to allocate memory every time. This is only for optimization.
NSMutableArray reference
I just read your question, and I think I understand exactly what you need. Here is the code:
Declare a property:
#property (nonatomic, retain) NSMutableArray *myArray;
Synthesize it:
#synthesize myArray = _myArray;
Overrride its getter like this:
- (NSMutableArray *)myArray
{
if (!_myArray)
{
_myArray = [[NSMutableArray alloc] initWithCapacity:9];
for (int i = 0; i < 9; i++)
{
[self.myArray addObject:[NSNull null]];
}
}
return _myArray;
}
The "setBlankArray" method will simly set the property to nil, and next time you call the getter of the array property it will get automatically initialized with exactly what you need:
- (void)setBlankArray:(NSMutableArray *)array
{
self.myArray = nil;
}
VERY IMPORTANT: Do not write this code:
for (int i = 0; i < 9; i++)
{
[self.myArray addObject:[NSNull null]];
}
in the method just mentioned as this will make the array to contain 18 elements.
Then write the other 2 methods:
// you can also change the parameter from "id" to "TileView *"
- (void)addCurrentTile:(id)sender
{
NSInteger tileIndex = 1; // replace 1 with ((TileView *) sender).getValue - 1
[self.myArray replaceObjectAtIndex:tileIndex
withObject:sender];
}
// you can also change the parameter from "id" to "TileView *"
- (void)removeCurrentTile:(id)sender
{
NSInteger tileIndex = 1; // replace 1 with ((TileView *) sender).getValue - 1
[self.myArray replaceObjectAtIndex:tileIndex
withObject:[NSNull null]];
}
But, DO NOT FORGET to replace "id" with "TileView *", and TO SET the value of tileIndex to "((TileView *) sender).getValue - 1".
Hope this all makes sense, and is helpful for you.
Best regards

Passing An Array By Reference In Objective-C

I would like to pass a NSMutableArray by reference so that it can be altered by another method. What would be the correct syntax for this?
Thanks,
Objective-C objects are always passed by reference (using pointers) - you can't pass them by value.
I.e. the following is fine:
- (void)mutateArray:(NSMutableArray*)array {
// alter array ...
}
... and can be e.g. invoked like this:
NSMutableArray *array = ...;
[self mutateArray:array];
There is also the possibility of passing a pointer by reference:
- (void)newArray:(NSMutableArray **)array;
In that case array is used as an out-parameter - you pass a reference to a pointer to receive an instance:
- (void)newArray:(NSMutableArray **)array {
*array = [[NSMutableArray alloc] init];
}
... which could be called like so:
NSMutableArray *array = nil;
[self newArray:&array];
Using out-parameters is usually only seen if the return-value is already used and additional information has to be returned. An example would be error-information as dreamlax noted.
In addition to Georg Fritzche's answer, it may be worth noting that some methods expect to be given the address of an object pointer. For example:
NSError *anError; // points to garbage now
NSStringEncoding enc;
NSString *aString = [NSString stringWithContentsOfFile:#"/some/file.txt"
usedEncoding:&enc
error:&anError];
if (aString == nil)
{
// anError now points to an initialised NSError object.
}
It gets tricky because some documented methods require you to release objects obtained in this manner, and some don't (for an example of one that does require explicit releasing, see NSPropertyListSerialization).
As Georg Fritzsche said NSMutableArray passed be reference automatically, but not the NSArray. The best option is too look at the code bellow:
void mutateImmutableArray(NSArray *array);
void mutateMutableArray(NSMutableArray *array);
void mutateImmutableArrayByRef(NSArray **array);
void mutateMutableArrayByRef(NSMutableArray **array);
int main(int argc, const char * argv[]) {
#autoreleasepool {
//Change immutable array in method that expects immutable array
NSArray *immutable = #[#1,#2,#3];
mutateImmutableArray(immutable);
NSLog(#"After 1: %#",immutable); // 1,2,3
//Change mutable array in method that expects immutable array
NSMutableArray *mutable = [#[#1,#2,#3]mutableCopy];
mutateImmutableArray(mutable);
NSLog(#"After 2: %#",mutable); //1,2,3
//Change mutable array in method that expects mutable array
mutable = [#[#1,#2,#3]mutableCopy];
mutateMutableArray(mutable);
NSLog(#"After 3: %#",mutable); //1,2,3, Four
//Change immutable array in method that expects immutable array by reference
immutable = #[#1,#2,#3];
mutateImmutableArrayByRef(&immutable);
NSLog(#"After 4: %#",immutable); //4,5,6
//Change mutable array in method that expects mutable array by reference
mutable = [#[#1,#2,#3]mutableCopy];
mutateMutableArrayByRef(&mutable);
NSLog(#"After 5: %#",mutable); //1,2,3, Four
}
return 0;
}
void mutateImmutableArray(NSArray *array)
{
array = #[#4,#5,#6];
}
void mutateImmutableArrayByRef(NSArray **array)
{
*array = #[#4,#5,#6];
}
void mutateMutableArray(NSMutableArray *array)
{
[array addObject:#"Four"];
}
void mutateMutableArrayByRef(NSMutableArray **array)
{
[*array addObject:#"Four"];
}