C-style array of pointers to Objective-C objects under ARC - ios

I have a 2D array of pointers to Objective-C instances to keep track of game objects on a map grid.
Now I am transitioning my code to ARC, and Xcode pointed the error. I knew pointers to objects aren't allowed as struct members, but this one caught me (almost) off guard.
I understand the rationale behind the ARC constrains, but:
I can't afford the overhead of objective-C arrays when looking up objects in the grid, and
The objects themselves are already owned by an NSArray ivar defined in the same class that has the C-style grid as an ivar; the c-style array is only a conveniently structured shortcut. Futhermore, when objects are removed from the owning NSArray, I set the corresponding grid slot to NULL.
That is, the 2D array (grid) is just a collection of fast (but dumb) pointers to objects safely retained somewhere else (the NSArray ivar).
Is there a way to get away with this using casts? For example, define and alloc my grid as:
void*** _grid;
instead of
MyMapObjectClass*** _grid
and use (appropriately bridged) casts between void* <-> MyMapObjectClass* when setting or getting the pointers in each slot?
EDIT: So here is how I solved it
I changed the ivar declaration as described above. In addition, when setting an entry of my look-up grid, I did this:
// (Done **Only Once** at map initialization)
// _objectArray is an instance of NSMutableArray
MyMapObjectClass* mapObject = [[MyMapObjectClass alloc] init];
// ...configure map object, etc...
// Add to Obj-C array:
[_objectArray addObject:mapObject];
// Add pointer to 2D C array:
_grid[i][j] = (__bridge void*)mapObject;
When accessing the object at (x,y), I do the opposite:
MyMapObjectClass* object = (__bridge MyMapObjectClass*) _grid[x][y];
[object performSomeMethod];
// etc...
When removing the object from the map, I do this:
MyMapObjectClass* object = (__bridge MyMapObjectClass*) _grid[x][y];
[_objectArray removeObject:object];
_grid[x][y] = NULL;
Map objects are created once at the beginning of the game, and removed according to game progress. If I need to replace a map object for another, I would do this:
MyMapObjectClass* oldObject = (__bridge MyMapObjectClass*) _grid[x][y];
// (should mark as weak?)
[_objectArray removeObject:oldObject];
_grid[x][y] = NULL;
MyMapObjectClass* newObject = [[MyMapObjectClass alloc] init];
[_objectArray addObject:newObject];
_grid[x][y] = (__bridge void*)newObject;

Circumventing ARC using casts is generally a bad idea. The better way would be to disable ARC for your map.m (or break out just the lookup part into a separate class).Then do manual memory management inside it with retain / release and the C structures you like, as long as you do it correctly it will work fine and you will be able to call it from other classes, avoiding the overhead of nested NSArrays etc..

Related

Passing an object around increases retain count

iOS, transitioning to ARC. I've observed a curious behavior regarding CF/NS bridging. In the following scenario:
CFStringRef cfs = ComesFromSomewhere();
NSString *ns = (__bridge NSString*)cfs;
the retain count of the string object is 2 at the end. However, in the following:
NSString *ToNS(CFStringRef cfs)
{
return (__bridge NSString*)cfs;
}
CFStringRef cfs = ComesFromSomewhere();
NSString *ns = ToNS(cfs);
the retain count is 3 at the end. What's going on, please? Who holds the extra reference? Is the object being added to the autorelease pool by the mere act of passing it around?
Preemptive response to "don't worry, ARC just works": I'm mixing Core Foundation with Cocoa here, no way around it. This is leak prone. Without the ability to account for the retain counts explicitly, I'm flying blind.
EDIT: it's an artifact of the debug build. In the release build, the retain count under the latter scenario is still 2.
There's a tangible difference between a fragment that leaves large autoreleased objects around and one that doesn't; you don't want the former in a big loop without a pool in the loop body. Helps to know it's an artifact of zero optimization, but still, not cool.
CFStringRef cfs = ComesFromSomewhere();
// retainCount -> 1
NSString *ns = ToNS(cfs);
// ToNS(cfs)
//
// ToNS is not object creating method,
// thus the returned object was automatically autoreleased
// retainCount += 1
// NSString *ns
//
// It's __strong variable, ns variable has an ownership of the object
// retainCount += 1
// retainCount -> 3
The definition of object creating method is a method whose name begins with “alloc”, “new”, “copy”, or “mutableCopy”, of a Objective-C class. See Basic Memory Management Rules - You own any object you create.
In the release build, the retain count under the latter scenario is still 2.
Also compiler can omit to send autorelease message to object if it's eligible.
EDITED
You can use C++ reference to avoid autorelease.
void ToNS(CFStringRef cfs, NSString __strong *& ns)
{
ns = (__bridge NSString*)cfs;
}
NSString *nsstr;
ToNS(cfstr, nsstr);
// retainCount -> 2
EDITTED
NS_RETURNS_RETAINED NSString *ToNS(CFStringRef cfs)
{
return (__bridge NSString*)cfs;
}
NS_RETURNS_RETAINED makes the framework treat the function as an object creating one (which it really is). Cocoa has a name convention that lets you designate a method as an object creator, but the convention only applies to Objective C class methods, not to C style functions and not to C++ class member functions.

Right way to use __attribute__((NSObject)) with ARC?

I just use CFNumber as a example,so it can be any type don't have a Fundation toll-free part!
I just write some test code like this:
typedef __attribute__((NSObject)) CFNumberRef MYNumberRef;
int main(int argc, const char * argv[])
{
#autoreleasepool {
MYNumberRef ptr = NULL;
double myDouble = 10.1;
ptr = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &myDouble);
CFIndex count = CFGetRetainCount(ptr);
}
return 0;
}
It is very strange that the count is 2. But if I use CFNumberRef, the count is 1. It seems the arc don't take the CFType name convention into account, it just retains the return value.
So if I use the __attribute__((NSObject)) to declare CFType property. This post said you shouldn't have to explicitly nil them out in dealloc. But if I use like this:
#property (strong, nonatomic, readwrite) __attribute__((NSObject)) CFNumberRef number;
Then:
self.number = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &myDouble);
There is no memory leak if I don't release it in the dealloc method? Maybe I should use it like this:
CFNumbeRef ref = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &myDouble);
self.number = ref;
CFRelease(ref);
Does Apple say something about this?
The type attribute __attribute__((NSObject)) does not mean that ARC will manage the memory for that type exactly the same way as if that type was an Objective-C object.
The attribute only means that in situations where the compiler automatically generates code for handling that type and the generated code is different depending on whether that type is a normal pointer or an Objective-C object pointer, the compiler should generate the code version for Objective-C objects.
This basically only influences two situations (as of today):
When using these pointers within blocks (^{ }), as objects get automatically retained when being captured by blocks and released again when the block is destroyed.
When assigning values to properties (#property) because when assigning objects to strong (= retain) properties, they get retained and the old property value gets released.
Both situations have nothing to do with ARC. Even in a non-ARC environment the compiler will generated code that works this way.
The reason why your code didn't work as expected is simply because it is wrong.
self.number = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &myDouble);
CFNumberCreate() creates a new object with a retain count of (at least) 1 but this object is not managed by ARC, applying the attribute __attribute__((NSObject)) to the type won't change that. So somewhere this create must be balanced with a release.
When you assign the value to self.number, it gets retained again, this time because of the __attribute__((NSObject)) attribute, so now the retain count is (at least) 2. When your object is deallocated, self.number is set to nil, which balances the second retain but there is nothing that would balance the initial create and thus the object will leak.
Consider this code:
#property(nonatomic) NSNumber * number;
self.number = [[NSNumber alloc] initWithInt:10];
Without ARC this code would leak as well, because again, alloc/init produces an object with a retain count of (at least) 1, when you assign it to self.number, the retain count becomes (at least) 2. Even if you do self.number = nil; in dealloc, this only balances the retain of the property, but where would the alloc/init get balanced?
Only with ARC this code won't leak as with ARC, the code the compiler generates looks like this:
NSNumber * tmp = [[NSNumber alloc] initWithInt:10];
self.number = tmp;
[tmp release];
And last line balances the first line, the object is still kept alive by self.number, unless you set self.number = nil; in dealloc which ARC will also do for you so with ARC, there is nothing else you have to do because ARC takes care of any object created with alloc.
That said, keep in mind that
[NSNumber numberWithInt:10]
which is the same as #(10) in modern Obj-C, is in fact equal to
[[[NSNumber alloc] initWithInt:10] autorelease]
But even when you use ARC, it won't care for references created by CFNumberCreate(). To fix your code, it should be:
CFNumberRef num = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &myDouble);
self.number = num;
CFRelease(num);
Now the last line balances the first line, but the property keeps the CFNumberRef alive and this is automatically balanced by dealloc when using ARC or when not using ARC, must be balanced with a self.number = nil; in dealloc.
If you use ARC, you can also do this instead:
self.number = (__bridge CFNumberRef)(__bridge_transfer id)
CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &myDouble);
(__bridge_transfer id) tells the compiler that the value returned by CFNumberCreate() is an Objective-C object that has a retain which needs to be balanced and ARC should take ownership of this object and take care of the balance. (__bridge CFNumberRef) then casts the type back to CFNumberRef but without transferring ownership away from ARC. Without ARC the code above leaks again, yet the compiler will warn you without ARC that bridge casts do nothing.
By the way, to transfer ownership away from ARC, the cast would be (__bridge_retained CFNumberRef), in which case ARC will once retain the object for you and then give you a reference that you must release yourself to balance that retain (now you own the object reference and ARC won't manage it anymore for you).
Do not do this.
Apple does have something to say about it in the Clang documentation:
The use of __attribute__((NSObject)) typedefs is not recommended. If it’s absolutely necessary to use this attribute, be very explicit about using the typedef, and do not assume that it will be preserved by language features like __typeof and C++ template argument substitution.
CFGetRetainCount is meaningless. Worse than meaningless because you think it might mean something.

NSArray mutableCopy creates new array but still points to old contents

I have an NSMutableArray called playersArray in my singleton class which holds for my applications main datasource.
Each object of playersArray is a NSDictionary and the content is like :
{
sgfID = 1;
sgfPlayer = "<PlayerContact: 0xbf851b0>";
}
PlayerContact is a NSObject subclass containing properties like:
NSString * playerName, playerTeam, BOOL PlayerSelected and so on.
In one of my ViewControllers, in viewDidLoad, I want to take a deep copy of playersArray in to a NSMutableArray named duplicatePlayersArray. I do this by
playersArray = [[SGFHelper sharedHelpers] SGFContactsArray];
duplicatePlayersArray = [playersArray mutableCopy];
Now that I have two separate copies, I was under the impression that playersArray and duplicatePlayersArray are two totally different arrays in the memory. However I found that they are NOT!
Even if the debugger shows that they have different memory addresses, their contents have same memory addresses. So when i do this:
[((NSMutableDictionary *)[duplicatePlayersArray objectAtIndex:0]) setObject:#"333" forKey:#"sgfID"];
playersArray's dictionary at index:0 has ALSO "333" as key "sgfID" instead of "1" as it used to before the above line of code ran.
BUT, if I run the below code, only then, the two arrays start to differ
[duplicatePlayersArray replaceObjectAtIndex:0 withObject:tempDict];
Still this doesn't address my concern because the two arrays which I wanted to believe are different are still "connected". A change in one, results in the other array to change its contents.
Can you friends please show me a way to DEEP COPY the array I explained the contents of in a way where all of their contents are kept in different objects.
Use initWithArray:copyItems: to copy each entry in the array
NSMutableArray *duplicatePlayersArray = [[NSMutableArray alloc] initWithArray:playersArray copyItems:YES];

CFBridgingRelease of a CFArray containing CFString

Suppose I have a CFArray containing CFString's:
CFStringRef strs[3];
CFArrayRef anArray;
strs[0] = CFSTR("String One");
strs[1] = CFSTR("String Two");
strs[2] = CFSTR("String Three");
anArray = CFArrayCreate(NULL, (void *)strs, 3, &kCFTypeArrayCallBacks);
If I use CFBridgingRelease to cast the CFArrayRef into an NSArray * (and in the process transfer the ownership of the array object to ARC), then does each element of the original array also get a CFBridgingRelease call automatically? It seems like, once I do:
NSArray * arrayInArc = CFBridgingRelease(anArray);
I can treat the elements of the NSArray as NSString's without having explicitly called CFBridgingRelease on each of the original CFStringRef:
NSString * a0 = arrayInArc[0];
Is there any documentation saying that when you transfer of the ownership of a collection (e.g., CFArray) to ARC, the ownership of its elements are also transferred?
Thanks,
the CFBridingRelease doesn't change the type of the array.. CFArrayRef <> NSArray* is the same even before, It just tells the compiler to manage retain/release calls for you
how the array releases its contents is not affected by it.
YOU don't have references to the content, the array does! And the CFArrayRef/NSArray* manages the retain/release calls with/without arc.
the basic idea: only care about releasing stuff YOU own. (In this case the array itself)

Xcode / iOS: Simple example of a mutable C-Array as a class instance variable?

For some reason I just cant seem to get my head around the process of creating a C-Array instance variable for a class that can have elements added to it dynamically at runtime.
My goal is to create a class called AEMesh. All AEMesh objects will have a c-array storing the vertexdata for that specific AEMesh's 3D model for use with OpenGL ES (more specifically it's functionality for drawing a model by passing it a simple C-Array of vertexdata).
Initially I was using an NSMutableArray, on the assumption that I could simply pass this array to OpenGL ES, however that isnt the case as the framework requires a C-Array. I got around the issue by essentially creating a C-Array of all of the vertexdata for the current AEMesh when it came time to render that specific mesh, and passing that array to OpenGL ES. Obviously the issue here is performance as I am constantly allocating and deallocating enough memory to hold every 3D model's vertexdata in the app about a dozen times a second.
So, Im not one to want the answer spoon fed to me, but if anyone would be willing to explain to me the standard idiom for giving a class a mutable c-array (some articles Ive read mention using malloc?) I would greatly appreciate it. From the information Ive gathered, using malloc might work, but this isn't creating a standard c-array I can pass in to OpenGL ES, instead its more of a pseudo-c-array that works like a c-array?
Anyways, I will continue to experiment and search the internet but again, if anyone can offer a helping hand I would greatly appreciate it.
Thanks,
- Adam Eisfeld
The idea would just be to add a pointer to an array of AEMesh structures to your class, and then maintain the array as necessary. Following is a little (untested) code that uses malloc() to create such an array and realloc() to resize it. I'm growing the array 10 meshes at a time:
#interface MyClass : NSObject
{
int meshCount;
AEMesh *meshes;
}
#end
#implementation MyClass
-(id)init {
if ((self = [super init])) {
meshCount = 0;
meshes = malloc(sizeof(AEMesh)*10);
}
return self;
}
-(void)addMesh:(AEMesh)mesh {
if (meshCount % 10 = 0) {
meshCount = realloc(sizeof(AEMesh) * (meshCount + 10));
}
if (meshCount != nil) {
meshes[meshCount] = mesh;
meshCount++;
}
}
#end
It might be worthwhile to factor the array management into it's own Objective-C class, much as Brian Coleman's answer uses std::vector to manage the meshes. That way, you could use it for C arrays of any type, not just AEMesh.
From the information Ive gathered, using malloc might work, but this
isn't creating a standard c-array I can pass in to OpenGL ES, instead
its more of a pseudo-c-array that works like a c-array?
A C array is nothing more than a series of objects ("objects" used here in the C sense of contiguous memory, not the OO sense) in memory. You can create one by declaring it on the stack:
int foo[10]; // array of 10 ints
or dynamically on the heap:
int foo[] = malloc(sizeof(int)*10); // array of 10 ints, not on the stack
int *bar = malloc(sizeof(int)*10); // another way to write the same thing
Don't forget to use free() to deallocate any blocks of memory you've created with malloc(), realloc(), calloc(), etc. when you're done with them.
I know it doesn't directly answer your question, but an even easier approach would be to work with an NSMutableArray instance variable until the point where you need to pass it to the API, where you would use getObjects:range: in order to convert it to a C-Array. That way you won't have to deal with "mutable" C-Arrays and save yourself the trouble.
If you're willing to use ObjectiveC++ and stray outside the bounds of C and ObjectiveC, then you can use a std::vector to amortise the cost of resizing the array of vertex data. Here's what things would look like:
include <vector>
include <gl.h>
#interface MyClass {
std::vector<GLfloat> vertexData;
}
-(void) createMyVertexData;
-(void) useMyVertexData;
#end
#implementation
-(void) createMyVertexData {
// Erase all current data from vertexData
vertexData.erase(vertexData.begin(),
std::remove(vertexData.begin(),
vertexData.end());
// The number of vertices in a triangle
std::size_t nVertices = 3;
// The number of coordinates required to specify a vertex (x, y, z)
std::size_t nDimensions = 3;
// Reserve sufficient capacity to store the vertex data
vertexData.reserve(nVertices * nDimensions);
// Add the vertex data to the vector
// First vertex
vertexData.push_back(0);
vertexData.push_back(0);
vertexData.push_back(0);
// And so on
}
-(void) useMyVertexData {
// Get a pointer to the first element in the vertex data array
GLfloat* rawVertexData = &vertexData[0];
// Get the size of the vertex data
std::size_t sizeVertexData = vertexData.size();
// Use the vertex data
}
#end
The neat bit is that vertexData is automatically destroyed along with the instance of MyClass. You don't have to add anything to the dealloc method in MyClass. Remember to define MyClass in a .mm file

Resources