everyone. My "engrish" is not very good but I hope you will understand my problem.
Let's say I have a code like this
-(id) init
{
if ( self = [super init] )
{
deck = [[NSMutableArray alloc] initWithCapacity:52];
Card *newCard = [[Card alloc] init];
for ( int suit = 0; suit < 4; suit++ )
for ( int rank = 0; rank < 13; rank++ )
{
newCard.suit = suit;
newCard.rank = rank;
[deck addObject:newCard];
}
[newCard release];
}
return self;
}
I'm feeling like I have a mistake in the aforementioned code. I want to create 52 different objects in nested loops and add every object in array. But I suspect that I'll have 52 same objects and in array will be 52 pointers that point to the same address, right?
What happens if I make like this.
-(id) init
{
if ( self = [super init] )
{
deck = [[NSMutableArray alloc] initWithCapacity:52];
for ( int suit = 0; suit < 4; suit++ )
for ( int rank = 0; rank < 13; rank++ )
{
Card *newCard = [[Card alloc] init]; // I guess every time newCard
newCard.suit = suit; // created, it will point to
newCard.rank = rank; // another chunk of memory,
[deck addObject:newCard]; // right?
[newCard release] // Should I release newCard every time?
}
}
return self;
}
So which way I can create 52 different cards?
Thank you. If you need more explanations, ask me.
First question: Your second code sniplet produces 52 individual objects which you add to the array.
The first sniplet, as you already suggested yourself, produces just one object which you add to the array. As it is one object only all the members of the array will carry the same values for .suit and .rank.
Als suggested already, when you nslog the objects then you see at least their address in memory. When the address is identical then it is the identical object.
Second question:
Yes, you should release it unless you use ARC. addObject will automatically retain each added object and release it upon removal from the array. So be careful when you fetch the object from the array later and intend to use it further. Then you may have to retain it again.
Alternative to your -correct- code you can autorelease the object using:
Card *newCard = [[[Card alloc] init] autorelease]; // I guess every time newCard
newCard.suit = suit; // created, it will point to
newCard.rank = rank; // another chunk of memory,
[deck addObject:newCard]; // right?
//[newCard release]; // not required, autoreleased
(However, a semicolon is missing following the release statement. )
Related
I'm an Objective-C noob working through a tutorial and I'm hitting a strange snag I want to understand better.
I'm looping to make a portfolio of stock objects that have a 50/50 chance of being a "foreignStock" or just being "stock" - the difference being a conversion rate property. The object stock is a superclass, with foreignStock being a subclass of stock.
I want to create a pointer, flip a coin to decide which type it is, and then assign the values I need. Since both the subclass and superclass have things like currentSharePrice, why can't I set them after the coin toss?
This is my main.m for review:
int main(int argc, const char * argv[]) {
#autoreleasepool {
// Declare portfolio and set the conversion rate
BNRPortfolio *mikesPortfolio = [[BNRPortfolio alloc] init];
NSUInteger globalConRate = 1.2;
// Array of ticker names - remove them as they are used
NSMutableArray *tickerNames = [[NSMutableArray alloc] init];
[tickerNames addObject:#"ibm"];
[tickerNames addObject:#"ppg"];
[tickerNames addObject:#"google"];
[tickerNames addObject:#"vrzn"];
[tickerNames addObject:#"apple"];
[tickerNames addObject:#"barq"];
// Create and add the stocks to the portfolio
for (int i = 0; i < 6; i++) {
id newStock;
// Coin flip to determine type
NSUInteger randomType = random() % 2;
if (randomType == 0) {
newStock = [[BNRStockHolding alloc] init];
} else {
newStock = [[BNRForeignStockHolding alloc] init];
newStock.conversionRate = globalConRate;
}
// Assign remaining values
newStock.purchaseSharePrice = 15 * (random() % i);
newStock.currentSharePrice = newStock.purchaseSharePrice * 1.4;
NSUInteger randomTickerValue = random() % [tickerNames count];
newStock.symbol = tickerNames[randomTickerValue];
[tickerNames removeObjectAtIndex:randomTickerValue];
[mikesPortfolio addHoldings:newStock];
}
}
The line inside the else{} block newStock.conversionRate... gives an Xcode pre-compile error stating "property not found for object of type __strong id" - I guess because it can't tell if newStock will actually be what I just declared it to be? But the assign statements at the end of main.m are showing the same line error as though newStock doesn't have those properties, even though BOTH classes will have access to them.
How do I make newStock understand that it will definitely be a class that has those properties but might ALSO have that conversion rate associated with the subclass?
I tried this:
BNRStockHolding newStock; <-- starting with superclass
// Coin flip to determine type
NSUInteger randomType = random() % 2;
if (randomType == 0) {
newStock = [[BNRStockHolding alloc] init];
} else {
newStock = [[BNRForeignStockHolding alloc] init];
newStock.conversionRate = globalConRate;
}
Which will make the errors on the bottom lines go away but still won't compile at the subclass method in the else{} block.
Where did I go wrong?
(I'm going to assume this is actually main.m, not main.c.)
Your primary mistake is using id rather than types.
id newStock;
This says that newStock is "some kind of object, I have no idea what." In ObjC, id can be sent any message, but it does not have any properties. So you can't use dot-notation on it. There are ways around that (don't use dot notation), but the better solution is to use a type:
BNRStockHolding *newStock = nil;
That will cause a problem here:
newStock = [[BNRForeignStockHolding alloc] init];
newStock.conversionRate = globalConRate;
Which you can fix this way:
BNRForeignStockHolding *foreignStock = [[BNRForeignStockHolding alloc] init];
foreignStock.conversionRate = globalConRate;
newStock = foreignStock;
But if the only thing that makes a foreign stock "foreign" is that it has a conversion rate, I would strongly recommend against subclassing here. Just make all stocks have a conversion rate. If it's domestic, make the conversion rate 1.
My map object has a set of coordinates. It doesn't always have the same number of coordinates.
In java I'd just declare the object as Double[] xpoints and would set it's size when instantiating a map like this: xpoints = new double[npoints];
How can I do this with objective-c?
I tried doing this: #property(nonatomic) double * xpoints; but somehow all of it's values turn to 0 when I print it with NSLog.
Map's init:
-(id)initWithXpoints:(double[]) xpointss Ypoints:(double[]) ypointss Npoints:(int)npointss
{
self = [super init];
if (self)
{
self.xpoints = xpointss;
self.ypoints = ypointss;
self.npoints = npointss;
}
return self;
}
Something weird happens though. The values are changed to zero when I print xpoints[0] from the object that created the map. The first time I print it it works. The second time it just prints zero.
I think it happens because xpointss sent to init is removed from the memory. How can I "instantiate" the xpoints property if it's a pointer?
Is there a better way to do this?
added: I tried creating a temporary xpoints like this:
double tempxpoints[npointss];
double tempypoints[npointss];
for (int i = 0; i < npointss; i++)
{
tempxpoints[i] = xpointss[i];
tempypoints[i] = ypointss[i];
}
self.xpoints = tempxpoints;
self.ypoints = tempypoints;
But it still didn't work.
Edit: Thanks for all the answers. This ended up being my final Init code:
-(id)initWithXpoints:(double[]) xpointss Ypoints:(double[]) ypointss Npoints:(int)npointss
{
self = [super init];
if (self)
{
_xpoints = [[NSMutableArray alloc] init];
_ypoints = [[NSMutableArray alloc] init];
for (int i = 0; i < npointss; i++)
{
NSNumber *tempx = [NSNumber numberWithDouble:xpointss[i]];
NSNumber *tempy = [NSNumber numberWithDouble:ypointss[i]];
[_xpoints addObject:tempx];
[_ypoints addObject:tempy];
}
_npoints = npointss;
}
return self;
}
If you allocate the arrays as local variables, then they will be allocated on the stack. When execution leaves the function, those memory areas are freed up. You must use malloc() to allocate arrays that you can pass around and use free() to free them up.
// to allocate
double[] tempxpoints = (double[])malloc(sizeof(double) * npointss);
// to free when not used any more
free(tempxpoints);
But actually NSArray has been designed to handle these cases. And with ARC you don't even have to care about freeing the memory.
NSMutableArray *tempxpoints = [[NSMutableArray alloc] init];
[tempxpoints addObject:#2]; // wrap the double in an NSNumber object
If you were being fully Objective-C about it, you'd use an NSArray, fill it with NSNumbers and never specify a length. You can usually give them hints about how much space is likely to be required but Objective-C's collections all always size dynamically.
As of recent versions of the compiler, you can use array[x] notation on NSArray and write direct NSNumber constants as e.g. #4.5f if that sweetens the deal at all.
If you literally want C-style arrays then you'll need to descend to the C level of thought. So, something like:
#property(nonatomic, readonly) double * xpoints;
And:
-(id)initWithXpoints:(double[]) xpointss Ypoints:(double[]) ypointss Npoints:(int)npointss
{
self = [super init];
if (self){
size_t sizeOfArraysInBytes = sizeof(double)*npointss;
_xpoints = (double *)malloc(sizeOfArraysInBytes);
memcpy(_xpoints, xpointss, sizeOfArraysInBytes);
/* ... etc ... */
/* you never use self. notation in an init because it's a method call,
and method calls on objects that are not yet fully instantiated aren't
safe. Sample cause of failure: a subclass overrides the setter */
}
return self;
}
- (void)dealloc
{
free(_xpoints);
/* ... etc ... */
}
The array itself will be read/write elsewhere (it's the pointer that's read-only, not the things it points to) as class.xpoints[0], etc.
I have a very strange error using NSMutableArray in cocos2d/xcode
In my code, I have a NSMutableArray containing object A
NSMutableArray *peasantArray;
and another NSMutableArray contain object B
NSMutableArray *treeArray;
in the scheduled update function, I called another function which is essentially the following functionality:
I would loop through all the object A inside the *peasantArray, and if any peasant object has a variable var == GameEntityCommandIdling, I would modify the object B in the second NSMutableArray *treeArray,
The issue is that I notice sometimes after I modified the object A inside the peasantArray, the modified variable (var) is being modified/updated inside the object A after by printing out the variable status in a scheduled fashion; but if I am to loop through the NSMutableArray *peasantArray again in the next schedule (1/30s), I will again find the object A with the older/un-updated variable (var), and this is causing my algorithm to be wrong,
However, if I loop through the NSMutableArray *peasantArray less than 1second, each time I would see the variable (var) for object A correctly as the updated variable value,
So is there a limit on how fast I can iterate over the NSMutableArray?
here are some piece of code that I basically just mentioned,
NSMutableArray *peasantArray;
NSMutableArray *treeArray;
.....
peasantArray = [[[NSMutableArray alloc] initWithCapacity:1]retain];
for(int i = 0; i < 1; i++)
{
Peasant *A = [[Peasant alloc] init];
[peasantArray addObject:A];
}
....
//inside the update()
for (int i = 0;i < [peasantArray count];i++)
{
Peasant *A = [peasantArray objectAtIndex:i];
if (A.status == something)
{
printf("A.status is something\n");
A.status = sometingelse;
...
//update other things not related to this NSMutableArray or object
}
}
.....
SO the error is that sometimes I will have multiple printf of "A.status is something", although only one of it should be printed,
I really appreciate any help,
Thanks,
So is there a limit on how fast I can iterate over the NSMutableArray?
Definitely no. That would be the stupidest implementation of an array I'd ever encountered. :)
The simplest explanation is usually the answer. You say the message is printed more than once. Well have you checked that maybe more than one Peasant has its status set to something?
If that's not the case, are you certain the status update is printed twice in the same frame, and not twice in two subsequent updates?
Earlier on you seemed to indicate that the effect of iterating over one array and modifying the other array's objects is invalidated somehow. That made me want to point out that if you have the same object in both arrays, modifying the object's properties in array A will also modify the properties of the same object contained in array B.
You may want to give breakpoints a(nother) try. The problem should be easy to locate when you step through the code.
Here you have a memory leak
for(int i = 0; i < 1; i++)
{
Peasant *A = [[Peasant alloc] init];
[peasantArray addObject:A];
}
you should release A after adding it to the array since the addObject adds a reference count to the object.
for(int i = 0; i < 1; i++)
{
Peasant *A = [[Peasant alloc] init];
[peasantArray addObject:A];
[A release];
}
I have a simple loop with an int counter that gets incremented inside a while loop when a special case exists. My question is simply - how should I manage memory inside this function with regards to the int specifically? I've been using NSNumber almost exclusively and what little time I've spent with int seems to make me think I'm not doing releasing it correctly.
Any other improvements are also welcome but I'm very interested in the int question
- (NSArray *)parseJson:(NSArray *) items
{
NSMutableArray* hats = [[NSMutableArray alloc] init];
NSEnumerator *enumerator = [items objectEnumerator];
NSDictionary* item;
int counterz = 0;
while (item = (NSDictionary*)[enumerator nextObject]) {
Hat* hat = [[Hat alloc] init];
hat.addr = [item objectForKey:#"Address"];
BOOL* hasHat = [item objectForKey:#"HasHat"];
if ([hasHat boolValue]) {
if (counterz < 10) {
[hats addObject:hat];
counterz++;
}
}
}
return hats;
}
Thank you in advance!
You don't need to release a "normal" (i.e.: non-object based) int - it'll happily life out its (brief, tragic) life on the stack until it falls out of scope.
You've got a couple unnecessary things and some memory leaks...
- (NSArray *)parseJson:(NSArray *) items {
NSMutableArray *hats = [NSMutableArray array];
int counter = 0;
for (NSDictionary *item in items) {
Hat *hat = [[Hat alloc] init];
[hat setAddr:[item objectForKey:#"Address"]];
BOOL hasHat = [[item objectForKey:#"HasHat"] boolValue];
if (hasHat && counter < 10) {
[hats addObject:hat];
counter++;
}
[hat release];
}
return hats;
}
And heck, once you reach a counter of 10, you could break out of the loop, because you're never going to do anything useful once 10 is reached.
Some other comments:
The name of the method is wrong. Nothing about this method has to do with parsing JSON. At best you're interpreting an array of dictionaries that happened to originate from a JSON string, but there's nothing about the nature of this code that says "this is parsing JSON".
-[NSDictionary objectForKey:] returns an object. A BOOL is not an object, it's a primitive (like an int or char). Appending * to the type does not make it an object either. :)
Since the method name does not begin with new or alloc and does not contain the word copy, you're supposed to return an autoreleased object from it. The method in the question was returning an owned object (+1 retain count), since you invoked alloc, but never autorelease. Using the convenience constructor +array fixes this.
In your loop, you allocated a Hat object, but never released it. This is a classic memory leak.
I currently try to fill and NSMutableArray with something like this:
deck = [[NSMutableArray alloc] initWithCapacity:52];
for (int suit = 0; suit <= 3; suit++) {
for (int value = 1; value <= 13; value++) {
ANormalCard *card = [[ANormalCard alloc] initWithSuit:suit value:value];
[deck addObject:card];
[card autorelease];
}
}
Now the problem is when I go over the array, only the last object I create is 52x in the array. Any idea what I am doing wrong ?
Edit:
the -initWithSuit looks like this:
- (id) initWithSuit:(int)suit value:(int)val {
if ((self = [super init])) {
theSuit = suit;
theValue = val;
}
return self;
}
I'm using NSEnumerator * enumerator = [deck objectEnumerator]; and a while loop to iterate over the array.
This code snippet looks perfectly fine. The problem is likely in your -initWithSuit:value: method — I'd check whether you're accidentally initializing the cards incorrectly.
Edit: The init method just posted by the asker looks fine as well. What is the exact output you're seeing that leads you to believe that only the last object has been added 52 times? How exactly are you examining the array? (Your comment says you're using an NSEnumerator, but can you edit your question to include the snippet you're using for that?