I am following the instructions for a tutorial but I cannot figure out what is wrong. I have double checked everything. I put the the compiler errors in the code's comments below. Sorry, this will probably show how much of a noob I am.
// main.m
#import <Foundation/Foundation.h>
#import "LotteryEntry.h"
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// Creates the date object
NSCalendarDate *now = [[NSCalendarDate alloc]init];
//Seed the random number generator
srandom(time(NULL));
NSMutableArray * array;
array = [[NSMutableArray alloc]init];
int i;
for (i = 0; i < 10; i++) {
//create a date/time object that is 'i' weeks from now
NSCalendarDate *iWeeksFromNow;
iWeeksFromNow = [now dateByAddingYears:0
months:0
days:(i * 7)
hours:0
minutes:0
second:0];
}
//create the LotteryEntry object
LotteryEntry *newEntry = [[LotteryEntry alloc]init];
[newEntry prepareRandomNumbers];
[newEntry setEntryDate: iWeeksFromNow];
//Error says "Use of undeclared identifier "iWeeksFromNow'. Did I not declare it above?
//add the lottery entry object to the array
[array addObject:newEntry];
}
for (LotteryEntry *entryToPrint in array) {
//Error says " Expected identifier or '('
//Display it's contents
NSLog(#"%#", entryToPrint);
}
[pool drain];
return 0;
//Error says " Expected identifier or '('
}
//Error says " Expected External declaration
You are declaring iWeeksFromNow inside a for loop, that's why the compiler doesn't consider it to exist outside
declare it outside, and assign values to it inside
You have an extra closing } as you call the -dateByAddingYears method.
First error : you declare iWeeksFromNew inside a for loop, thus it's unreachable from outside.
You have to declare before the beginning of the loop.
Second error : you have a bracket '}' after [array addObject:newEntry]; so the compiler thinks its the end of your method, remove it.
That should fix all other error you have
First, iWeeksFromNow is declared within the scope of a for loop, so it will be visible only within that loop. Second, as pointed out by Black Frog, you have an extra closing parenthesis.
Move the declaration out that loop block. You've got a scope problem here: The iWeeksFromNew only exists within the loop
NSCalendarDate *iWeeksFromNow;
int i;
for (i = 0; i < 10; i++) {
//create a date/time object that is 'i' weeks from now
iWeeksFromNow = [now dateByAddingYears:0
months:0
days:(i * 7)
hours:0
minutes:0
second:0];
}
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.
I'm getting the error "undeclared identifier" on the commented line:
- (BOOL) isInIntArray:(NSInteger[])array theElem:(int)elem{
int i = 0;
NSInteger sizeOfArray = (sizeof array) / (sizeof array[0]);
while(i < sizeOfArray){
if(array[i] == elem){
return TRUE;
}
i++;
}
return FALSE;
}
- (int)getNextUnusedID{
int i = rand()%34;
while ([isInIntArray:idsUsed theElem:i]) { //here: Use of undeclared identifier 'isInIntArray'
i = rand()%34;
}
return i;
}
I really don't understand why, they are in the same .m file.
Why would that be?
Also, this code:
NSInteger sizeOfArray = (sizeof array) / (sizeof array[0]);
is giving me the warning:
Sizeof on array function will return Sizeof 'NSInteger *' (aka: 'int *') instead of 'NSInteger[]'"
How should I properly determine the size of an array?
It looks like you've missed out self from this line
while ([isInIntArray:idsUsed theElem:i])
This should be:
while ([self isInIntArray:idsUsed theElem:i])
As #CaptainRedmuff pointed out, you are missing the target object in method invocation, that is self.
//[object methodParam:x param:y];
[self isInIntArray:idsUsed theElem:i];
To your second Q. In C language you cannot determine the size of an array. That's why they are not used, since we have objects for this. I recommend you to use these:
NSMutableArray *array = [[NSMutableArray alloc] init]; // to create array
array[0] = #42; // to set value at index, `#` creates objects, in this case NSNumber
[array insertObject:#42 atindex:0]; // equivalent to the above
NSInteger integer = array[0].integerValue; // get the value, call integerMethod to get plain int
integer = [[array objectAtIndex:0] integerValue]; // equivalent to the above
[array containsObject:#42]; // test if given object is in the array
[array indexOfObject:#42]; // get index of object from array, NSNotFound if not found
array.count; // to get the number of objects
Important: These arrays have variable size and they are not limited! But you can access elements only at indexes 0..(n-1) (where n in number of objects) and you can set values only for indexes 0..n.
In other words, you can not do array[3] = #42; for empty array, you need to fill first 3 positions first (indexes 0, 1 and 2).
write this in .h file (declare the function)
- (BOOL) isInIntArray:(NSInteger[])array theElem:(int)elem;
and call the method using following way
while ([self isInIntArray:idsUsed theElem:i]) { //here: Use of undeclared identifier 'isInIntArray'
i = rand()%34;
}
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.
I can't figure out why I get
use of undeclared identifier _cmd did you mean rcmd
on the line where NSAssert is.
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int x = 10;
NSAssert(x > 11, #"x should be greater than %d", x);
[pool drain];
return 0;
}
Inside every Objective-c method there are two hidden variables id self and SEL _cmd
so
- (void)foo:(id)bar;
is really
void foo(id self, SEL _cmd, id bar) { ... }
and when you call
[someObject foo:#"hello world"]
it is actually
foo( someObject, #selector(foo), #"hello world")
If you cmd-click on NSAssert to jump to it's definition you will see that it is a macro that uses the hidden _cmd variable of the method you are calling it from. This means that if you are not inside an Objective-c method (perhaps you are in 'main'), therefore you don't have a _cmd argument, you cannot use NSAssert.
Instead you can use the alternative NSCAssert.
NSAssert is only meant to be used within Objective-C methods. Since main is a C function, use NSCAssert instead.
Try to replace
NSAssert(x > 11, [NSString stringWithFormat:#"x should be greater than %d", x]);
with
NSCAssert(x > 11, [NSString stringWithFormat:#"x should be greater than %d", x]);
You have to wrap your string in a NSString class if you want to use format parameters. That is because #"" is a default constructor for a plain NSString. The way it is written now gives a third parameter to the NSAssert function and messes with it.
NSAssert(x > 11, [NSString stringWithFormat:#"x should be greater than %d", x]);
TL;DR - stick with stray NSAssert() - don't try this in production
Original code
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int x = 10;
NSAssert(x > 11, #"x should be greater than %d", x);
[pool drain];
return 0;
}
Build failure
Compiling file hello.m ...
hello.m:9:5: error: use of undeclared identifier '_cmd'
NSAssert(x > 11, #"x should be greater than %d", x);
^
/usr/include/Foundation/NSException.h:450:32: note: expanded from macro 'NSAssert'
handleFailureInMethod: _cmd \
^
hello.m:9:5: error: use of undeclared identifier 'self'
/usr/include/Foundation/NSException.h:451:17: note: expanded from macro 'NSAssert'
object: self \
^
2 errors generated.
Based on explanation by #hooleyhoop #Robert and
id
self
SEL,
the following dirty hack may be applicable if I insist on using
NSAssert() instead of
NSCAssert()
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int x = 10;
// Dirty hack
SEL _cmd=NULL;
NSObject *self=NULL;
NSAssert(x > 11, #"x should be greater than %d", x);
[pool drain];
return 0;
}
Build & run
Compiling file hello.m ...
Linking tool hello ...
2021-03-04 21:25:58.035 hello[39049:39049] hello.m:13 Assertion failed in (null)(instance), method (null). x should be greater than 10
./obj/hello: Uncaught exception NSInternalInconsistencyException, reason: hello.m:13 Assertion failed in (null)(instance), method (null). x should be greater than 10
Hooray it works! But, alas, please stay away from it :)
I'm currently trying to implement a pooling system, I have all the code, I just dont understand why a certain part of it doesn't work.
I have a c-array of NSMutable array made like this:
NSMutableArray *poolArray[xSize][ySize];
for (int n = 0; n < xSize; n++)
{
for (int m = 0; m < ySize; m++)
{
poolArray[n][m] = [[NSMutableArray alloc] init];
}
}
And whilst trying to access it I get the x and y coordinate of the pool and object is in and try to add it like this:
[poolArray[x][y] addObject:object]; //This raises a EXC_BAD_ACCESS error
I am totally open to editing how I write this - I am aware that I could declare a NSMutableArray and use indexes of ((y * width) + x) and I may have to rewite the code like that. But preferably I dont want to have to do that as I only want to actually create the arrays I'm using so something like this:
if (poolArray[x][y] == nil) poolArray[x][y] = [[NSMutableArray alloc] init];
[poolArray[x][y] addObject:object];
This is so that it can have 'holes' so I dont have to make anything at poolArray[2][3] for example if there is nothing there.
I don't know if there is anyway that I could rewrite that with objective-c types, but if I do I'm forced to keep creating a NSMutableArray at every space, the reason I dont want to do that is because I want to get every little bit of performance I can out of the system.
Thanks for taking the time to read this, and any response is appreciated :)
This works for me:
#import <Foundation/Foundation.h>
#define xSize 10
#define ySize 10
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSMutableArray *poolArray[xSize][ySize];
for (int n = 0; n < xSize; n++)
{
for (int m = 0; m < ySize; m++)
{
poolArray[n][m] = [[NSMutableArray alloc] init];
}
}
[poolArray[2][3] addObject: #"Hello"];
[poolArray[2][3] addObject: #"world!"];
NSLog(#"poolArray[2][3] objects: %# %#",
[poolArray[2][3] objectAtIndex: 0],
[poolArray[2][3] objectAtIndex: 1]);
[pool drain];
return 0;
}
(Yes, I know, I should release all NSMutableArray instances. Left out for brevity).
So there are a few things you should check:
Is object a valid object, i.e. was it initialized? The NSMutableArray will try to retain the object, and if it was never initialized, that will fail miserably, or if it was dealloc-ed already, it will fail too.
are x and y valid? You can easily go over the boundaries and not notice it.
Can't see anything wrong with the code you've provided, although a couple of ideas:
In the case where your checking poolArray[x][y] == nil have you actually reset all the values to nil when you initialize the array?
An alternative that should work, is to store the array on the heap. You could use calloc (which will initialize the memory to 0), or malloc and memset.
The following should work:
NSMutableArray ***poolArray = calloc(xSize * ySize, sizeof(NSMutableArray *));
if (poolArray[x][y] == nil) poolArray[x][y] = [[NSMutableArray alloc] init];
[poolArray[x][y] addObject:object];