I read this answer https://stackoverflow.com/a/833124/5175709 and some other questions. From what I understand it's that since the object could expand and run out of space then the memory location my also change. I never understood why we shouldn't use ** for NSMutableData or NSMutableArray since they could also expand with any adding or appending and be in need of more space.
Please correct me where I understood wrong. I am confused.
NSMutableArray * and NSMutableData * are not pointers directly to the contained data. They are something more along this line:
struct MutableData {
void * data;
void (*appendBytes)(void * bytes);
// etc.
};
When they need to reallocate for expansion (or contraction), there is an internal pointer that they are manipulating. You, as a user of the containing instance, don't interact with that.
Object references are only required to be single pointers as it does not relate to the memory they manage. For example the NSData class (the super class of NSMutableData) probably contains the following instance variables:
(this is conjecture as I don't have access to the source code)
#interface NSData : NSObject
{
void *_data;
NSUInteger _length;
}
#end
If the NSMutableData subclass wants to add to the data buffer it can perform a realloc() on _data and increase _length.
The pointer to the NSMutableData object itself does not change at all and the memory it manages is hidden from the developer.
Related
i have two Objective-C classes, say ParentLayer and ChildLayer. in my child instance, i want to access a C-Array in my parent instance. so i have something like this in my cocos2d code:
#define kNumOfElements 10
#implementation ParentLayer{
int array[kNumOfElements];
}
-(id)init{
//...
for(int i=0;i<kNumOfElements;i++){
array[i] = i;
}
[self addChild:childLayer];
[childLayer initializeValues];
//...
}
-(int *)getArray{
return array;
}
#end
//meanwhile in my child layer...
//...
-(void)initializeValues{
int *arr = [(ParentLayer *)[self parent] getArray];
//NSLog(#"%d",arr[0]); <------- this gives you bad exec access point, and looks like it's 0x00 for memory address
}
//...
what's the proper way to do this?
maybe i dont understand the right memory management behind C Arrays.
i was under the impression that C Arrays didn't need to be allocated,
and that they could be passed by value, on the stack?
also, shouldn't my parent instance still be around? i thought if i
put a C Array as an ivar of my parent, it shouldn't get destroyed
any help is appreciated. thanks!
what's the proper way to do this?
Ideally, you should never pass a C-style array pointer outside of the object that owns it. You open yourself up to all sorts of problems if a piece of code tries to use the array after the object is deallocated, or writes past the end, or something else. It is easier to guarantee that none of this happens if you can make sure the reference never leaves the object's source file.
maybe i dont understand the right memory management behind C Arrays. i was under the impression that C Arrays didn't need to be allocated, and that they could be passed by value, on the stack?
It is not that simple.
A C-style array is just a memory address. That's it. It doesn't carry around the other useful information that an object might, such as number of elements, retain count.
If you declare an array like this:
int array[100];
Then the memory is allocated in either the stack or the heap, depending on where you put the declaration. If it's a local variable inside a function or method, it's on the stack. If it's in global scope or a member variable of an object, it's on the heap.
Furthermore, if it's an instance variable, you're actually setting aside 100 ints worth of memory inside the block of memory allocated to hold the object. It isn't a separate thing.
Since array is just a memory address, you are basically passing it around by reference. Technically, you are passing the address by value, but any changes you make to the memory will be seen by anyone looking at the same address, so it acts like pass by reference.
also, shouldn't my parent instance still be around? i thought if i put a C Array as an ivar of my parent, it shouldn't get destroyed
The way you have coded it, that array will be valid as long as the parent object is around. Once the parent gets deallocated, that memory could be reclaimed. Since the array variable is just a memory address, however, you have no way of knowing whether the data it points to is valid or not. This is the danger of using C-style arrays rather than objects.
Since the last line is giving you NULL (0) address, my guess is that [self parent] is nil. That would put a 0 in arr; when you try to dereference NULL, you will get an exception.
In Objective C, you can use property for this.
#define kNumOfElements 10
#interface ParentLayer: NSObject
{
int *array;
}
#property(nonatomic, assign) int *array;
#end
#implementation ParentLayer
-(id)init{
//...
self.array =(int*)malloc(sizeof(int) * kNumOfElements);
for(int i=0;i<kNumOfElements;i++){
self.array[i] = i;
}
[self addChild:childLayer];
[childLayer initializeValues];
//...
}
//-(int *)getArray{
// return array;
//}
-(void)dealloc
{
if(self.array)
{
free(self.array); self.array = NULL;
}
[super dealloc];
}
#end
-(void)initializeValues{
ParentLayer *player = (ParentLayer *)[self parent] ;
int *arr = player.array;
//NSLog(#"%d",arr[0]); <------- this gives you bad exec access point, and looks like it's 0x00 for memory address
}
can't seem to add a reply to benzado's post. but depending on how to declare your object, it might be automatically deallocated. to ensure that it is retained, use a retain keyword.
[obj retain];
especially using the cocos2d framework, they have quite a number of auto release objects. typically initWith shouldn't be auto release.
In Objective-C, how can I define a C array of 'unknown size' as an instance variable, and later populate the array?
(Background) : I have a class in Objective-C which handles loading game data. The data is stored in an array of C structs, contained in an external datafile. I can load in the array and access it, but I need it to be accessible throughout the class. What I'm trying to do is declare an 'empty' array in my instance variables and then (when loaded) point this empty array or replace it with the one I've loaded in.
This is how I'm loading in my data...
FILE *fptr = NULL;
fptr = fopen([path cStringUsingEncoding:NSUTF8StringEncoding], "r");
// Create an empty array of structs for the input file with known size
frame2d newAnimationData[28];
// Read in array of structs
fread(&newAnimationData, sizeof(newAnimationData), 1, fptr);
fclose(fptr);
So this code works fine to recreate my array of frame2d structs - I just need to know how I can use this as an instance variable.
Any help is appreciated,
thanks.
Declare it as frame2d*, then figure out how big it needs to be at runtime and initialize it with calloc(numberOfFrame2Ds, sizeof(frame2d));
Or use an NSMutableArray and wrap the structs in NSValue objects if resizing and safety at runtime is more important than efficiency.
Use a pointer to a frame2d object as an instance variable:
frame2D *animationData;
You'll need to allocate the array at runtime using malloc.
If the entire file is nothing but frames, just read it into an NSData object:
// in the interface
#interface MyClass
{
NSData *animationData;
frame2D *animationFrames;
}
// in the implementation
animationData = [[NSData alloc] initWithContentsOfFile:path];
animationFrames = (frame2D*) [myData bytes];
-(void) dealloc {
[animationData release];
}
When you say you need the loaded data to "be accessible throughout the class," it sounds like you will only have a single array that you want all your objects of that class to use. If so, forget the instance variable. You can expose a global frame2d * and have your objects access that:
// Class.h
extern frame2d *gClassFrames;
// Class.m
frame2d *gClassFrames;
/* Somewhere in the class, read in the data and point `gClassFrames` at it.
* If the array is actually of known size, just declare the entire array rather
* than a pointer and read the data into that static storage, in order to avoid
* dynamic memory allocation.*/
Just because you're writing Obj-C doesn't mean you have to throw out everything that works fine in C. Having each object store a pointer to the same array would be a waste of memory.
If you want a more objecty way of getting at the information, you can add a class method to access it, whether it's + (frame2d *)frames or + (frame2d *)frameAtIndex:(NSUInteger)u.
I have tried many times to understand the const keyword, but it just doesn't work out for me.
I want to declare an object that cannot be changed, which is to say, a constant object. For example, in the .h file:
extern MyClass *use_this_object;
and in the .m file:
MyClass *use_this_object;
+ (void) Initialize {
use_this_object = [MyClass new];
}
Now, where can I put a const so that other classes can access use_this_object but not modify it (assuming MyClass is immutable), while the MyClass class can initialize the variable?
Is this even possible? Or should I be using a static method to retrive the constant and not declare it extern at all?
There is no such thing as a "const object" in Objective-C. There are const pointers and there are immutable objects. A const pointer to an immutable object is what you're talking about, but you can't allocate those at run time.
For objects that can be allocated at compile time (and I only know of one, NSString), you can do this:
NSString * const kMyString = #"string";
This is a constant pointer to an (immutable) NSString. You read these things right-to-left.
To create what you want, you need a function or method with an internal static like this:
+ (Something *)something {
static Something *something = nil;
if (! something) {
something = [Something new];
}
return something;
}
This is preferable to using globals anyway for things other than true constants (like strings and integers).
It is up to you to make sure that Something is immutable.
EDIT Just a note about the above code. This is just an example of how to create a static object at runtime. There are many ways to do it with various trade-offs including using +initialize with a file static (which is currently my preferred way to create a singleton). Don't take the above code as the one-and-only-way. It's just the way that is closest to const because no other part of the program can get directly to the pointer.
I’d use the static method, seems much simpler.
I was playing with the respondsToSelector method in Objective-C on MacOS-X 10.6.7 and Xcode 4.0.2, to identify if an object would respond to certain messages. According to the manuals, NSString should not respond to appendString: while NSMutableString should. Here's the piece of code which tests it:
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString *myString = [[NSString alloc] init];
if ([myString respondsToSelector:#selector(appendString:)]) {
NSLog(#"myString responds to appendString:");
} else {
NSLog(#"myString doesn't respond to appendString:");
}
// do stuff with myString
[myString release];
[pool drain];
return 0;
}
and here's the output:
Class02[10241:903] myString responds to appendString:
I'd sort of expected the opposite. How does an NSString object respond to appendString: ? What's going on here that I'm missing ?
Short answer: That string is of type NSCFString, a class that inherits from NSMutableString, hence it responds to the selectors for the methods declared in NSMutableString, including superclasses.
Not so short answer: Foundation strings are toll-free bridged with Core Foundation strings. Developers use the opaque types CFStringRef (bridged with NSString) and CFMutableStringRef (bridged with NSMutableString) to refer to these strings so, at first glance, there are two different types of strings: immutable and mutable.
From a Core Foundation internal implementation perspective, there’s a private type called struct __CFString. This private type keeps a bit field that stores, amongst other information, whether the string is mutable or immutable. Having a single type simplifies implementation since many functions are shared by both immutable and mutable strings.
Whenever a Core Foundation function that operates on mutable strings is called, it first reads that bit field and checks whether the string is mutable or immutable. If the argument is supposed to be a mutable string but it in fact isn’t, the function returns an error (e.g. _CFStringErrNotMutable) or fails an assertion (e.g. __CFAssertIsStringAndMutable(cf)).
At any rate, these are implementation details, and they might change in the future. The fact that NSString doesn’t declare -appendString: doesn’t mean that every NSString instance doesn’t respond to the corresponding selector — think substitutability. The same situation applies to other mutable/immutable classes such as NSArray and NSMutableArray. From the developer perspective, the important thing is that the object that’s been returned is of a type that matches the return type — it could be the type itself or any subtype of that type. Class clusters make this a tad more convoluted but the situation is not restricted to class clusters per se.
In summary, you can only expect that a method returns an object whose type belongs to the hierarchy (i.e., either the type itself or a subtype) of the type for the return value. Unfortunately, this means that you cannot check whether a Foundation object is mutable or not. But then again, do you really need this check?
You can use the CFShowStr() function to get information from a string. In the example in your question, add
CFShowStr((CFStringRef)myString);
You should get an output similar to:
Length 0
IsEightBit 1
HasLengthByte 0
HasNullByte 1
InlineContents 0
Allocator SystemDefault
Mutable 0
Contents 0x0
where
Mutable 0
means that the string is in fact immutable.
This probably has to do with the implementation. NSString is a class cluster, which means that NSString is just a public interface and the actual implementing class is different (see what the class message gives you).
And at the same time NSString is also toll-free bridged to CFString, meaning that you can switch before those two types freely just by casting:
NSString *one = #"foo";
CFStringRef two = (CFStringRef)one; // valid cast
When you create a new string you really get a NSCFString back, a thin wrapper around CFString. And the point is that when you create a new mutable string, you also get an instance of NSCFString.
Class one = [[NSString string] class]; // NSCFString
Class two = [[NSMutableString string] class]; // NSCFString
I guess this was convenient from the implementation point of view – both NSString and NSMutableString can be backed by a common class (= less code duplication) and this class makes sure you don’t violate the immutability:
// “Attempt to mutate immutable object with appendString:”
[[NSString string] appendString:#"foo"];
There’s a lot of guess work in this answer and I don’t really understand the stuff, let’s hope somebody knows better.
You should not make assumptions about a method being not there. That method might be used internally or for whatever reason it exists. Technically, it's just private API.
You only have a contract to the public declarations (docs), and they don't show that message. So be prepared to get into trouble rather quickly if you use other features.
I was wondering if there is any sample code out there for objective-C for implementing a NSMutableArray of type struct. Inside, I need there to be 2 mutable arrays (via NSMutableArray also) declared in the struct. All the code samples in my book show me how to make an array of defined size via C array syntax (with the brackets), but I don't know how to get one going with NSMutableArray. Has anyone else done this before? Here's my code so far...It compiles fine I have defined the size of the arrays (2 and 5 are used in my code below as an example, but I need to set it so I can have them mutable. I can work with simple structs when they just have some of the "easier-to-understand" data types like int, double, short, long, BOOL (you get the idea). When it gets into pointers though, this is where I become lost (I can use pointers fine, but knowing how to put them in a struct is the difficult part). Once the code is working with NSMutableArray's, would I put "network" in the interface as a pointer to type "Network"? I tried this before, but I got errors. In the end, I basically want to be able to write
network.input[2].list[1].value = 5.0;
on an arbitrarily defined array of type "Network". Could anyone offer suggestion or links to information about making a NSMutableArray of type "Network" which includes a struct of two NSMutableArray's? Thanks for any help!
SomeFile.h
#import <Foundation/Foundation.h>
struct lists{
double value;
};
// supporting structs
struct inputs{
struct lists list[2];
};
struct Network {
struct inputs input[5];
struct lists output[5];
}
#interface SomeFile : NSObject {
}
#end
SomeFile.m
#import "SomeFile.h"
#implementation SomeFile
#end
NSArray and NSMutableArray can only contain Objective-C objects, so you can't store structs in them. If the contents must be structs and you want something similar to NSArray, use NSPointerArray, available in 10.5 and later.
You can store pointers to Objective-C objects (like NSPointerArray* or id) inside a struct just like any other pointer type. For example, you could declare a struct for a doubly-linked list node that stores an Objective-C object like this:
typedef struct DoublyLinkedListNode {
id object;
__strong struct DoublyLinkedListNode *next;
__strong struct DoublyLinkedListNode *prev;
} DoublyLinkedListNode;
The __strong attribute is used in connection with garbage collection in Objective-C 2.0, since pointers to Objective-C objects act as strong references, but C pointer types do not by default. This way, as long as one node in the list is referenced from a __strong reference, the list won't disappear. (Read the Garbage Collection Programming Guide for details, and particularly the second half of Using Core Foundation with Garbage Collection.) You'll probably want to consider doing this for your structs.
As far as your desired syntax, I may not have fully understood your question, but you won't be able to use the bracket syntax to access objects in a Cocoa collections like an NSPointerArray. (Also, odds are you'll have to use the "->" operator instead of "." for the structs, since they're likely to be allocated on the heap. All Objective-C objects must be, and I assume you'll want to store these structs outside of the local scope of a method.)
Since Objective-C doesn't have generics, you also can't "implement [an] NSMutableArray of type struct". In fact, one of your previous SO questions has more detail on the subject. If that's not what you meant, feel free to clarify.
This is not be a complete answer; I’m not sure what you mean by not knowing how to put pointers in structs. I’m going to proceed by assuming you want to model an network of multiple inputs and outputs with a dynamic number of both.
You have a few choices here:
Use value objects instead of structs to store your values:
[[[network inputs] objectAtIndex:2] replaceObjectAtIndex:1 withObject:[NSNumber numberWithDouble:5.0]];
Model your Network with an object:
#interface Network : NSObject {
// ivars
}
- (void) setInput:(double)value atIndex:(NSInteger)valueIndex ofInputAtIndex:(NSInteger)inputIndex;
- (double) outputAtIndex:(NSInteger)index;
#end
Just use structs like you’re already doing; if you need to change the size up-front, use your friend malloc:
struct Network_value {
double value;
}
struct Network {
struct Network_value **inputs;
struct Network_value *outputs;
};
void Network_alloc(Network *n, unsigned inputs, unsigned input_values, unsigned outputs) {
n->outputs = calloc(sizeof(Network_value), outputs);
n->inputs = calloc(sizeof(Network_value *), inputs);
while (inputs --> 0) {
n->inputs[inputs] = calloc(sizeof(Network_value), input_values);
}
}
void Network_free(Network *n, unsigned inputs) {
free(n->outputs);
while (inputs --> 0) {
free(n->inputs[inputs]);
}
free(n->inputs);
}
Network network;
Network_alloc(&network, 5, 2, 5);
network.inputs[2][1].value = 5.0;
Network_free(&network, 2);
Combine ideas 2 and 3 by presenting a Network object but internally store the values with structs. This is probably a good idea if the number of inputs and outputs is very large.