NSFastEnumeration crashing on C array with ARC - objective-c

I'm initializing a C array of objects and setting the first element:
id __strong *_objs = (id __strong *)calloc(16,sizeof(*_objs));
_objs[0] = #1;
_count++;
Then I'm using the following implementation of NSFastEnumeration:
- (NSUInteger) countByEnumeratingWithState: (NSFastEnumerationState*)state
objects: (id __unsafe_unretained*)stackbuf
count: (NSUInteger)len
{
NSUInteger size = _count;
NSInteger count;
state->mutationsPtr = (unsigned long *)size;
count = MIN(len, size - state->state);
if (count > 0)
{
IMP imp = [self methodForSelector: #selector(objectAtIndex:)];
int p = state->state;
int i;
for (i = 0; i < count; i++, p++) {
stackbuf[i] = (*imp)(self, #selector(objectAtIndex:), p);
}
state->state += count;
}
else
{
count = 0;
}
state->itemsPtr = stackbuf;
return count;
}
Unfortunately it crashes with EXC_BAD_ACCESS when I run it:
for (id object in array){ // EXC_BAD_ACCESS
NSLog(#"%#",object)
}
Any idea why?
If you have CodeRunner, here is an executable version.

The problem is the mutationsPtr which points to the memory address 1 which you are not allowed to access (and which is not 4 byte aligned as well):
state->mutationsPtr = (unsigned long *)size;
Replace it with a valid pointer for starters (careful: the one below may make no sense at all in your scenario, but at least it fixes the EXC_BAD_ACCESS):
state->mutationsPtr = (unsigned long *)&_count;

#Jano
In case you want to get rid of the compiler warning you will get with recent versions of Xcode (4.6) for
- (NSUInteger) countByEnumeratingWithState: (NSFastEnumerationState*)state
objects: (id __unsafe_unretained*)stackbuf
count: (NSUInteger)len
because it does not match the original prototype for objects:..stackbuf
- (NSUInteger) countByEnumeratingWithState: (NSFastEnumerationState*)state
objects: (__autoreleasing id *)stackbuf
count: (NSUInteger)len
see Todd Lehmans answer in Automatic Reference Counting: Error with fast enumeration

Related

Sum two NSInteger gives incorrect result

Im haveing a problem suming two NSInteger, I have tried with simple int but cant find the answer. I Have this on my header file:
#interface ViewController : UIViewController {
NSMutableArray *welcomePhotos;
NSInteger *photoCount; // <- this is the number with the problem
//static int photoCount = 1;
}
The on my implementation fiel I have:
-(void)viewDidLoad{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
photoCount = 0;
welcomePhotos = [NSMutableArray array];
int sum = photoCount + 1;
NSLog(#"0 + 1 = %i", sum);
}
The las NSLog always prints 0 + 1 = 4
Also if if do:
if (photoCount < [welcomePhotos count]){
photoCount++;
NSLog(#"%i", photoCount);
}else{
photoCount = 0;
}
Several times i get: 4, 8, 12.
So it is skiping by four, but I can't get to understand why.
You are declaring your photoCount instance var as pointer to NSInteger. But NSInteger is a scalar type.
Remove the asterisk in your .h file and try again.
Replace
NSInteger *photoCount;
with
NSInteger photoCount;
You're printing out a pointer object I believe as you've declared it as
NSInteger* photocount;
Try changing it to
int photocount;
doing a variable++ on an integer adds the size of a pointer which is 4 bytes on iOS.
You used pointer to NSInteger...
Change it to NSInteger photoCount;
NSInteger is just an int, and you are treating it as an wrapper object. Pointer in not required.

EXC_BAD_ACCESS when passing a pointer-to-pointer in a struct?

I've got a c-array of CGPoints in a struct. I need to replace this array when another CGPoint is added. I'd swear I'm doing this right and it seems to work fine a few times but eventually I'll get a EXC_BAD_ACCESS. What am I missing?
Here's the struct, which I've truncated to remove a lot of items that don't pertain.
typedef struct{
CGPoint **focalPoints;
NSUInteger focalPointCount;
CGRect boundingRect;
}FocalPoints;
Here's how I initialize it:
CGPoint *fPoints = (CGPoint *)malloc(sizeof(CGPoint));
FocalPoints focalInfo = {&fPoints, 0, rect};
Note that focalInfo is passed by reference to another function, like so: anotherFunction(&focalInfo).
Now here's the function that replaces the Points array with a new one:
void AddFocalPoint (CGPoint focalPoint, FocalPoints *focal){
if (focalPoint.x == CGFLOAT_MAX) return;
if (!CGRectContainsPoint(focal->boundingRect, focalPoint)) return;
int origCount = focal->focalPointCount;
int newCount = origCount + 1;
CGPoint *newPoints = (CGPoint *) malloc((newCount) * sizeof(CGPoint));
for (int i = 0; i < newCount; i++)
newPoints[i] = (i < origCount) ? *focal->focalPoints[i] : focalPoint; //error occurs here
free(*focal->focalPoints);
*focal->focalPoints = newPoints;
focal->focalPointCount = newCount;
}
The EXC_BAD_ACCESS error occurs in the above code on line 8: newPoints[i] = (i < origCount) ? *focal->focalPoints[i] : focalPoint;. So what exactly am I doing wrong?
This is a bit of a long shot, but maybe there's an issue with operator priority in *focal->focalPoints[i]. Have you try adding parentheses according to what you are trying to achieve ?
I believe the issue comes with where GCPoint *fPoints allocated as &fPoints evaluates to an address of that ... which is no longer valid once the function exits.
(The data to which it points was allocated fine with malloc.)
Aside from the suggestion I made in a comment, of using a linked list/NSMutableArray, my other suggestion would be that you use realloc() instead of constantly using malloc(), copying by hand, and then free()ing the old allocation.
void * realloc(void *ptr, size_t size);
The realloc() function tries to change the size of the allocation pointed to by ptr to size, and returns ptr. If there is not enough room to enlarge the memory allocation pointed to by ptr, realloc() creates a new allocation, copies as much of the old data pointed to by ptr as will fit to the new allocation, frees the old allocation, and returns a pointer to the allocated memory.
This is pretty much exactly what you are doing, but you can let the library handle it for you.
(May I also humbly suggest using the word "focal" slightly less to name variables in your function?) (Also also, I'm not really clear on why focalPoints in your struct is a pointer-to-pointer. You just want an array of structs -- a single pointer should be fine.)
Consider the following (somewhat extensive) rewrite; hope that it's helpful in some way.
typedef struct{
CGPoint *points; // Single pointer
NSUInteger count;
CGRect boundingRect;
} FocalPoints;
// Renamed to match Apple's style, like e.g. CGRectIntersectsRect()
void FocalPointsAddPoint (FocalPoints *, CGPoint);
void FocalPointsAddPoint (FocalPoints *f, CGPoint thePoint){
if (thePoint.x == CGFLOAT_MAX) return;
if (!CGRectContainsPoint(f->boundingRect, thePoint)) return;
NSUInteger origCount = f->count; // |count| is typed as NSUInteger; |origCount|
NSUInteger newCount = origCount + 1; // and |newCount| should be consistent
// Greatly simplified by using realloc()
f->points = (CGPoint *) realloc(f->points, newCount * sizeof(CGPoint));
(f->points)[newCount-1] = thePoint;
f->count = newCount;
}
int main(int argc, const char * argv[])
{
#autoreleasepool {
// Just for testing; any point should be inside this rect
CGRect maxRect = CGRectMake(0, 0, CGFLOAT_MAX, CGFLOAT_MAX);
// Can initialize |points| to NULL; both realloc() and free() know what to do
FocalPoints fp = (FocalPoints){NULL, 0, maxRect};
int i;
for( i = 0; i < 10; i++ ){
FocalPointsAddPoint(&fp, CGPointMake(arc4random() % 100, arc4random() % 100));
NSLog(#"%#", NSStringFromPoint(fp.points[i]));
}
}
return 0;
}

Icomplete implementation and Pointer conversion error.

Compiler warning(1): Incomplete implementation
I've compared my .h and .m files to see any inconsistencies or spelling mistakes between whats declared and implemented and can't find any.
Compiler warning(2): Incompatible integer to pointer conversion sending 'NSInteger*' (aka 'int*') with and expression of type 'int'.
I've been mucking about with asterisks for 25 minutes in all sorts of combinations and the compiler is still unhappy.
#import "Game.h"
#import "stdlib.h"
const int MAXRAND = 15;
const int MAXCOL = 7;
const int MAXROW = 9;
NSInteger gameState[MAXROW][MAXCOL];
NSInteger answerBoard[MAXROW][MAXCOL];
#implementation Game//compiler warning 1
-(void)init:(NSInteger*) rows: (NSInteger*) columns: (NSInteger*) operators:(NSInteger*) operands{
NSLog(#"init sent");
numRows = *rows;
numColumns = *columns;
numOperators = *operators;
numOperands = *operands;
//seed random number generator
//generate rand nums for operands
int operandList[numOperands];
for (int i = 0; i < numOperands; i++) {
srandom(time(NULL));
operandList[i] = (random()%MAXRAND);
}
//generate state and answer board
BOOL gameState[numRows][numColumns];
NSInteger answerBoard[numRows][numColumns];
for (int i = 0; i < numRows; i++) {
for (int j = 0; j < numColumns; j++) {
gameState[i][j] = NO;
answerBoard[i][j] = (operandList[random()%numOperands])+
(operandList[random()%numOperands])-
(operandList[random()%numOperands]);
}
}
}
-(void)updateGame:(NSInteger*)enteredNum{
NSLog(#"updateGame sent");
for (int i = numColumns; i > 0; i--) {
for (int j = numRows; j > 0; j--) {
if (gameState[i][j] == NO){
if (*enteredNum == answerBoard[i][j]){
gameState[i][j] = YES;
}
}
}
}
}
#end//Game
#import <Foundation/Foundation.h>
#interface Game : NSObject
{
NSInteger numRows, numColumns, numOperators, numOperands;
}
-(void)init:(NSInteger*) rows: (NSInteger*) columns: (NSInteger*) operators:(NSInteger*) operands;
-(void)updateGame:(NSInteger*) enteredNum;
#end
Where the instance of my class is declared and initialized:
NSInteger *rows = 7, *columns = 6, *operators = 2, *operands = 6;//compiler warning 2
Game *game = [Game new];
[game init:rows :columns :operators :operands];
NSInteger *rows = 7, *columns = 6, *operators = 2, *operands = 6;
rows,columns, operators, operands are of type NSInteger *. You need to allocate memory and then need to place 7,6,2,6 in the memory locations they are pointing at. In C-terms,
int *ptr = 7; // Wrong. Because, ptr is pointing no where to keep 7 in that location.
Did you try?
NSInteger rows = 7, columns = 6, operators = 2, operands = 6;
And what is?
[Game new];
The compiler is probably expecting you to implement a function named new.
EDIT:
Try removing all '*' from your NSInteger's.

Initi and write array in objective-c

In my h file I declare a var that later should be an array:
#interface myClass : CCNode {
CGPoint *mVertices;
}
#end
In my init method:
mVertices = malloc(size * size * sizeof(CGPoint));
mVertices[0][0] = ccp(0,0);
At this last line I get an error Subscripted value is neither array nor pointer.
Why do I get this error and how to solve that problem?
mVertices is a pointer, but you treat it like a two-dimensional array which is not allowed (you may treat it like a one-dimensional array, though).
Creating a dynamic multi-dimensional array in (Objective)-C is tricky insofar as the compiler would need to know the size of all but the first dimension to actually compile where in memory the element is situated.
But you can do the calculation yourself:
mVertices[(row * size) + column] = ccp(row, column);
You might want to define a macro for that:
#define VERTICE_ACCESS(row,colum) mVertices[(row * size) + column]
Your array is not two dimensional. It's just a list of vertices.
If you want to allocate space for a dynamic two dimensional array in C you could do:
CGPoint** mVertices;
NSInteger nrows = 10;
NSInteger ncolumns = 5;
mVertices = calloc(sizeof(CGPoint*), nrows);
if(mVertices == NULL){NSLog(#"Not enough memory to allocate array.");}
for(NSUInteger i = 0; i < nrows; i++)
{
mVertices[i] = calloc(sizeof(CGPoint), ncolumns);
if(mVertices[i] == NULL){NSLog(#"Not enough memory to allocate array.");}
}
mVertices[0][5] = CGPointMake(12.0, 24.0);
mVertices[1][5] = CGPointMake(22.0, 24.0);
mVertices[2][5] = CGPointMake(32.0, 24.0);
mVertices[2][1] = CGPointMake(32.0, 24.0);
for(NSUInteger i = 0; i < nrows; i++)
{
for (int k = 0; k < ncolumns; k++)
{
NSLog(#"Point %#", NSStringFromPoint(NSPointFromCGPoint(mVertices[i][k])));
}
}
I used calloc instead of malloc to get CGPoints initialized with 0.0.

What's the Best Way to Shuffle an NSMutableArray?

If you have an NSMutableArray, how do you shuffle the elements randomly?
(I have my own answer for this, which is posted below, but I'm new to Cocoa and I'm interested to know if there is a better way.)
Update: As noted by #Mukesh, as of iOS 10+ and macOS 10.12+, there is an -[NSMutableArray shuffledArray] method that can be used to shuffle. See https://developer.apple.com/documentation/foundation/nsarray/1640855-shuffledarray?language=objc for details. (But note that this creates a new array, rather than shuffling the elements in place.)
I solved this by adding a category to NSMutableArray.
Edit: Removed unnecessary method thanks to answer by Ladd.
Edit: Changed (arc4random() % nElements) to arc4random_uniform(nElements) thanks to answer by Gregory Goltsov and comments by miho and blahdiblah
Edit: Loop improvement, thanks to comment by Ron
Edit: Added check that array is not empty, thanks to comment by Mahesh Agrawal
// NSMutableArray_Shuffling.h
#if TARGET_OS_IPHONE
#import <UIKit/UIKit.h>
#else
#include <Cocoa/Cocoa.h>
#endif
// This category enhances NSMutableArray by providing
// methods to randomly shuffle the elements.
#interface NSMutableArray (Shuffling)
- (void)shuffle;
#end
// NSMutableArray_Shuffling.m
#import "NSMutableArray_Shuffling.h"
#implementation NSMutableArray (Shuffling)
- (void)shuffle
{
NSUInteger count = [self count];
if (count <= 1) return;
for (NSUInteger i = 0; i < count - 1; ++i) {
NSInteger remainingCount = count - i;
NSInteger exchangeIndex = i + arc4random_uniform((u_int32_t )remainingCount);
[self exchangeObjectAtIndex:i withObjectAtIndex:exchangeIndex];
}
}
#end
You don't need the swapObjectAtIndex method. exchangeObjectAtIndex:withObjectAtIndex: already exists.
Since I can't yet comment, I thought I'd contribute a full response. I modified Kristopher Johnson's implementation for my project in a number of ways (really trying to make it as concise as possible), one of them being arc4random_uniform() because it avoids modulo bias.
// NSMutableArray+Shuffling.h
#import <Foundation/Foundation.h>
/** This category enhances NSMutableArray by providing methods to randomly
* shuffle the elements using the Fisher-Yates algorithm.
*/
#interface NSMutableArray (Shuffling)
- (void)shuffle;
#end
// NSMutableArray+Shuffling.m
#import "NSMutableArray+Shuffling.h"
#implementation NSMutableArray (Shuffling)
- (void)shuffle
{
NSUInteger count = [self count];
for (uint i = 0; i < count - 1; ++i)
{
// Select a random element between i and end of array to swap with.
int nElements = count - i;
int n = arc4random_uniform(nElements) + i;
[self exchangeObjectAtIndex:i withObjectAtIndex:n];
}
}
#end
If you import GameplayKit, there is a shuffled API:
https://developer.apple.com/reference/foundation/nsarray/1640855-shuffled
let shuffledArray = array.shuffled()
A slightly improved and concise solution (compared to the top answers).
The algorithm is the same and is described in literature as "Fisher-Yates shuffle".
In Objective-C:
#implementation NSMutableArray (Shuffle)
// Fisher-Yates shuffle
- (void)shuffle
{
for (NSUInteger i = self.count; i > 1; i--)
[self exchangeObjectAtIndex:i - 1 withObjectAtIndex:arc4random_uniform((u_int32_t)i)];
}
#end
In Swift 3.2 and 4.x:
extension Array {
/// Fisher-Yates shuffle
mutating func shuffle() {
for i in stride(from: count - 1, to: 0, by: -1) {
swapAt(i, Int(arc4random_uniform(UInt32(i + 1))))
}
}
}
In Swift 3.0 and 3.1:
extension Array {
/// Fisher-Yates shuffle
mutating func shuffle() {
for i in stride(from: count - 1, to: 0, by: -1) {
let j = Int(arc4random_uniform(UInt32(i + 1)))
(self[i], self[j]) = (self[j], self[i])
}
}
}
Note: A more concise solution in Swift is possible from iOS10 using GameplayKit.
Note: An algorithm for unstable shuffling (with all positions forced to change if count > 1) is also available
This is the simplest and fastest way to shuffle NSArrays or NSMutableArrays
(object puzzles is a NSMutableArray, it contains puzzle objects. I've added to
puzzle object variable index which indicates initial position in array)
int randomSort(id obj1, id obj2, void *context ) {
// returns random number -1 0 1
return (random()%3 - 1);
}
- (void)shuffle {
// call custom sort function
[puzzles sortUsingFunction:randomSort context:nil];
// show in log how is our array sorted
int i = 0;
for (Puzzle * puzzle in puzzles) {
NSLog(#" #%d has index %d", i, puzzle.index);
i++;
}
}
log output:
#0 has index #6
#1 has index #3
#2 has index #9
#3 has index #15
#4 has index #8
#5 has index #0
#6 has index #1
#7 has index #4
#8 has index #7
#9 has index #12
#10 has index #14
#11 has index #16
#12 has index #17
#13 has index #10
#14 has index #11
#15 has index #13
#16 has index #5
#17 has index #2
you may as well compare obj1 with obj2 and decide what you want to return
possible values are:
NSOrderedAscending = -1
NSOrderedSame = 0
NSOrderedDescending = 1
From iOS 10, you can use NSArray shuffled() from GameplayKit. Here is an helper for Array in Swift 3:
import GameplayKit
extension Array {
#available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
func shuffled() -> [Element] {
return (self as NSArray).shuffled() as! [Element]
}
#available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
mutating func shuffle() {
replaceSubrange(0..<count, with: shuffled())
}
}
There is a nice popular library, that has this method as it's part, called SSToolKit in GitHub.
File NSMutableArray+SSToolkitAdditions.h contains shuffle method. You can use it also. Among this, there seem to be tons of useful things.
The main page of this library is here.
If you use this, your code will be like this:
#import <SSCategories.h>
NSMutableArray *tableData = [NSMutableArray arrayWithArray:[temp shuffledArray]];
This library also has a Pod (see CocoaPods)
If elements have repeats.
e.g. array: A A A B B or B B A A A
only solution is: A B A B A
sequenceSelected is an NSMutableArray which stores elements of class obj, which are pointers to some sequence.
- (void)shuffleSequenceSelected {
[sequenceSelected shuffle];
[self shuffleSequenceSelectedLoop];
}
- (void)shuffleSequenceSelectedLoop {
NSUInteger count = sequenceSelected.count;
for (NSUInteger i = 1; i < count-1; i++) {
// Select a random element between i and end of array to swap with.
NSInteger nElements = count - i;
NSInteger n;
if (i < count-2) { // i is between second and second last element
obj *A = [sequenceSelected objectAtIndex:i-1];
obj *B = [sequenceSelected objectAtIndex:i];
if (A == B) { // shuffle if current & previous same
do {
n = arc4random_uniform(nElements) + i;
B = [sequenceSelected objectAtIndex:n];
} while (A == B);
[sequenceSelected exchangeObjectAtIndex:i withObjectAtIndex:n];
}
} else if (i == count-2) { // second last value to be shuffled with last value
obj *A = [sequenceSelected objectAtIndex:i-1];// previous value
obj *B = [sequenceSelected objectAtIndex:i]; // second last value
obj *C = [sequenceSelected lastObject]; // last value
if (A == B && B == C) {
//reshufle
sequenceSelected = [[[sequenceSelected reverseObjectEnumerator] allObjects] mutableCopy];
[self shuffleSequenceSelectedLoop];
return;
}
if (A == B) {
if (B != C) {
[sequenceSelected exchangeObjectAtIndex:i withObjectAtIndex:count-1];
} else {
// reshuffle
sequenceSelected = [[[sequenceSelected reverseObjectEnumerator] allObjects] mutableCopy];
[self shuffleSequenceSelectedLoop];
return;
}
}
}
}
}
NSUInteger randomIndex = arc4random() % [theArray count];
Kristopher Johnson's answer is pretty nice, but it's not totally random.
Given an array of 2 elements, this function returns always the inversed array, because you are generating the range of your random over the rest of the indexes. A more accurate shuffle() function would be like
- (void)shuffle
{
NSUInteger count = [self count];
for (NSUInteger i = 0; i < count; ++i) {
NSInteger exchangeIndex = arc4random_uniform(count);
if (i != exchangeIndex) {
[self exchangeObjectAtIndex:i withObjectAtIndex:exchangeIndex];
}
}
}
Edit: This is not correct. For reference purposes, I did not delete this post. See comments on the reason why this approach is not correct.
Simple code here:
- (NSArray *)shuffledArray:(NSArray *)array
{
return [array sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
if (arc4random() % 2) {
return NSOrderedAscending;
} else {
return NSOrderedDescending;
}
}];
}