I am trying to add an CCSpriteto an array so I can do stuff with in another method. The array is just simply declared in the #interface like this NSMutableArray *currentBombs;
. I then try to add the (CCSprite*)spriteto the array with [currentBombs addObject:sprite];
The problem is that when I log [currentBombs count] or try to use any objects in it or log it or whatever, its empty. As CCSprite is a subclass of NSObject I would think that you can add it to an array? What am I doing wrong here?
Edit: More detailed code:
-(void)aMethod:(CCSprite*)Sprite{
//...
currentBombs = [[NSMutableArray alloc]init];
[currentBombs addObject:sprite];
}
Then I access it a second after in the method
-(void)checkDamageForBomb{
currentBombs = [[NSMutableArray alloc]init];
int cB = [currentBombs count];
for (int q = 0; q <= cB;q++)
{
CCSprite *bomb = [currentBombs objectAtIndex:q];//Crashes
CGPoint bombPos = [self tileCoordForPosition:bomb.position];//Crashes
//........ }
This happens because every time that you call checkDamageForBomb: you reallocate the array. You should instantiate the object just once.
My suggest is to use a property with lazy initialization, and always call self.currentBombs:
#interface MyClass()
#property (nonatomic) NSMutableArray* currentBombs;
#end
#implementation MyClass
#pragma mark - Accessors
- (NSMutableArray*) currentBombs
{
if(!_currentBombs)
_currentBombs=[NSMutableArray array];
return _currentBombs;
}
This way you have to change your code and always call self.currentBombs:
-(void)checkDamageForBomb{
for(CCSprite* bomb in self.currentBombs) // I find fast enumeration more elegant.
{
CGPoint bombPos = [self tileCoordForPosition:bomb.position];
...
}
...
}
So that you don't care of allocating it, the accessor will do it for you the first time that you call it.
You are initializing the variable currentBombs twice. Initialize it once in e.g. ViewDidLoad
Related
I am looking to get an NSString value from a Text Field and add it to an array, I want to build an array with many strings in it ex:
[hello, goodbye, too soon].
This is my current solution:
- (IBAction)submitButton:(id)sender {
NSMutableArray *wordArray = [[NSMutableArray alloc] init];
NSString *input = textField.text;
[wordArray insertObject:input atIndex:arrayIndex];
arrayIndex++;
}
This works for the first item in the array, but when I press submit again it reinitializes.My issue is how do I initialize the NSMutableArray to use in the button function, without having it in there so that it doesn't initialize every time. Thank you
Your are using a local array that disappears as soon as the submitButton method is finished.
Make your wordArray an instance variable and initialize it once in viewDidLoad. Then in your submitButton: method (and any others), you reference the instance variable instead of creating local arrays.
Honey's answer is almost, but not, correct.
Your code uses a local variable in your submitButton method, and creates a new, empty array each time the method gets called. Both of those things are wrong.
Honey's answer has you create a different local variable in viewDidLoad. That's also wrong.
You need to make wordArray an instance variable or property of your class. If you class is called ViewController, say, it might look like this
#interface ViewController: UIViewController;
#property (nonatomic, strong) NSMutableArray *wordArray
...
#end
And then initialize it in viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
self.wordArray = [[NSMutableArray alloc] init];
}
Then in the rest of your program refer to self.wordArray, the property.
Here's the solution,
#implementation ViewController{
NSMutableArray *_wordArray;
}
- (void)viewDidLoad {
[super viewDidLoad];
_wordArray = [[NSMutableArray alloc] init];
}
- (IBAction)submitButton:(id)sender {
NSString *input = textField.text;
[wordArray addObject:input];
}
You was re init the array each time you make the action, which will let you always save the last value of the textfield.
but this creates an array as global variable so that you can add all the values entered in textfield.
Hope this help you :)
I have a table view with a search, and search scope buttons with two possible scopes. The table is empty until a search is executed. Each scope has it's own mutable array for the table's data source, we'll say scopeA_array and scopeB_array. To simplify some methods, I'm trying to create a generic pointer reference to whichever array is the currently active scope. So I tried this:
#property (nonatomic, assign) NSMutableArray *tableDataArray;
In viewDidLoad, I assign it to the default selected scope.
_tableDataArray = _scopeA_array;
I can log the memory address of each array, they're both the same.
However, if I execute a search, _scopeA_array gets populated. Then in my numberOfRowsInSection method, I take the count of _tableDataArray but it's empty. I log the addresses again, both are different.
How do I create an array property that just references an array, and always points to the same object in memory even if it changes?
EDIT: A simplified way to test this, with the following lines of code, would like a way for tableDataArray to have the contents of testArray, even though the contents of testArray are assigned after:
NSArray *testArray = [NSArray new];
NSArray *tableDataArray = [testArray copy];
testArray = [NSArray arrayWithObjects:#"my", #"test", #"array", nil];
NSLog(#"table data array: %#", tableDataArray);
// logs empty array
I think the best approach is use a method to return conditionally the array for the current scope. So you just always use this method to populate your UITableView
- (NSMutableArray*) tableArray
{
return [self isScopeA] ? _scopeA_array : _scopeB_array;
}
How do I create an array property that just references an array, and always points to the same object in memory even if it changes?
If you want to track changes to a variable then you use a pointer to the variable rather than a pointer to a single array instance. E.g.:
#implementation MyController
{
__strong NSArray* *_currentDataPtr;
NSArray* _dataA;
NSArray* _dataB;
}
- (id)init
{
if (self = [super init])
{
_currentDataPtr = &_dataA; // Ensure _currentDataPtr is never NULL
}
return self;
}
- (void)setSearchScope:(NSInteger)searchScope
{
switch (searchScope)
{
default :
NSAssert(NO, #"");
case 0 :
_currentDataPtr = &_dataA;
break;
case 1 :
_currentDataPtr = &_dataB;
break;
}
}
- (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section
{
return [*_currentDataPtr count];
}
If you want it to be a property then implement a property getter that dereferences the pointer:
#property (nonatomic, readonly) NSArray* currentData;
- (NSArray*)currentData { return *_currentDataPtr; }
Here is a class for a deck of cards that could have several different configurations that I define using a NSDictionary with string keys and array values of how the cards are to be added. I haven't completed the init function yet, but it gives me the error above on trying to access my NSDictionary property. Fairly new to objective-c sorry if this is trivial question.
Here is my .m class file:
#interface MarioCardDeck()
#property (strong, nonatomic)NSDictionary *cardConfigurations;
#end
#implementation MarioCardDeck
- (instancetype)init {
self = [super init];
if(self) {
unsigned index = arc4random() % [[cardConfigurations allKeys] count]; ** error line
}
return self;
}
- (NSDictionary *)cardConfigurations
{
if(!_cardConfigurations)
{
_cardConfigurations = #{
#"1" :
#[#"flower",#"coin20",#"mushroom",#"star",#"oneUp",#"flower",#"oneUp",#"flower",#"coin10",#"mushroom",#"coin20",#"star",#"mushroom",#"coin10",#"star",#"mushroom",#"flower",#"star"],
#"2" :
#[#"flower",#"coin10",#"oneUp",#"flower",#"oneUp",#"mushroom",#"star",#"mushroom",#"coin20",#"star",#"mushroom",#"coin10",#"star",#"flower",#"coin20",#"mushroom",#"flower",#"star"]
};
}
return _cardConfigurations;
}
#end
You need:
unsigned index = arc4random() % [[self.cardConfigurations allKeys] count];
You need to access the property by using self.
FYI - you should use:
unsigned index = arc4random_uniform([[self.cardConfigurations allKeys] count]);
You need to refer to it by self.cardConfigurations.
You need to change
unsigned index = arc4random() % [[cardConfigurations allKeys] count];
to
unsigned index = arc4random() % [[self.cardConfigurations allKeys] count];
Accessing properties in the init method is, however, dangerous thing to do in Objective-C. If the getter is overridden in a subclass you might get a nasty surprise. I would make another property for the index, assign the instance variable to NSNotFound, and do the calculation the first the time the getter method is called. Also, you should use NSUInteger as the type for storing the index.
I am new to iOS dev,here is my first app-calculator,
But the NSMuteableArray "_numberArrayWaitingForCalculate" always be "nil",I don't know what to do???
Here is the interface
#interface demoViewController ()
#property (strong,nonatomic)NSString *valueString;
#property (strong,nonatomic)NSMutableArray *numberArrayWaitingForCalculate;
#end
here is the implement 1
#implementation demoViewController
#synthesize numberArrayWaitingForCalculate=_numberArrayWaitingForCalculate;
- (NSMutableArray *)numberWaitingForCalculate
{
if(!_numberArrayWaitingForCalculate)
_numberArrayWaitingForCalculate=[[NSMutableArray alloc]init];
return _numberArrayWaitingForCalculate;
}
here is the tapNumber method
- (IBAction)tapNumber:(UIButton *)numberButton {
if(LastButtonWasMode)
{
_valueString=#"";
LastButtonWasMode=NO;
}
NSString *numberAsString = numberButton.currentTitle;
_valueString=[_valueString stringByAppendingString:numberAsString];
result.text=[NSString stringWithFormat:#"%#",_valueString];
}
here is tapPlus method
- (IBAction)tapPlus:(id)sender {
[_numberArrayWaitingForCalculate addObject:[NSNumber numberWithInt:[_valueString intValue]]];
resultOfAllNumberInputBefore +=[_valueString intValue];
[self setMode:1];
}
The following line should be using the property and not the instance variable. i.e. you're not actually calling the getter that allocates the array.
Change this line:
[_numberArrayWaitingForCalculate addObject:[NSNumber numberWithInt:[_valueString intValue]]];
to:
[self.numberArrayWaitingForCalculate addObject:[NSNumber numberWithInt:[_valueString intValue]]];
You created a getter that "lazy loads" the mutable array (meaning that you create it if it doesn't exist already. That's a valid approach.
However, if you do that, you need to ALWAYS use the getter. You're using the iVar directly (_numberArrayWaitingForCalculate). Don't do that. Replace all instances of "_numberArrayWaitingForCalculate" with [self numberArrayWaitingForCalculate] except in the implementation of your getters/setters and probably your dealloc method.
So your tapPlus method should read:
- (IBAction)tapPlus:(id)sender
{
[[self numberArrayWaitingForCalculate] addObject:[NSNumber numberWithInt:[_valueString intValue]]];
resultOfAllNumberInputBefore +=[_valueString intValue];
[self setMode:1];
}
EDIT:
By the way, for something as lightweight as an empty mutable array, I think I would take a different approach. Rather than lazy-loading the array in a getter, I would create an init method for my class that created an empty mutable array and installed it in the iVar.
Objects like view controllers can be initialized more than one way. It might get initialized with initWithNibName:bundle: or with initWithCoder:
What I do in that case is to create a method doInitSetup, and call it from both places.
Hi a very simple app it takes in 2 arguments via 2 text boxes, and then totals them and displays them in a label called result. The idea is to have it handled via an object called brain, for which in the later part i have given the code. problem is foo is zero and when you click the button the result goes to nothing.
The plan is to use this to build a better model view architecture for a bigger app i have completed.
#import "calbrain.h"
#import "ImmyViewController.h"
#interface ImmyViewController ()
#property (nonatomic, strong) calbrain *brain;
#end
#implementation ImmyViewController
#synthesize brain;
#synthesize num1;
#synthesize num2;
#synthesize result;
-(calbrain *) setBrain
{
if (!brain) {
brain = [[calbrain alloc] init];
}
return brain;
}
- (IBAction)kickit:(UIButton *)sender {
NSString *number1 = self.num1.text;
NSString *number2 = self.num2.text;
NSString *foo;
foo = [brain calculating:number1 anddouble:number2];
self.result.text = foo;
// self.result.text = [brain calculating:self.num1.text anddouble:self.num2.text];
}
#end
#implementation calbrain
-(NSString *) calculating:(NSString *)number1 anddouble:(NSString *)number2
{
double numb1 = [number1 doubleValue];
double numb2 = [number2 doubleValue];
double newresult = (numb1 + numb2);
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
NSString *numberAsString = [numberFormatter stringFromNumber:[NSNumber n numberWithFloat:newresult]];
return numberAsString;}
Check your brain using NSLog in the (IBAction)kickit:(UIButton *)sender function. I guess you didn't initialise brain. If this is not the case, you need to provide more code.
i did just that, i came to the conclusion the setter for brain isnt working properly
i put the alloc init line of code before i needed to alloc init the brain, and it works fine, i stubbed out the setter,
i will go back and see why it wasnt overriding the setter made by properties, but interesting stuff none the less. it means i can change my actual larger app to have a cleaner more organised architecture.
thanks for your time.
Try initializing your brain object in viewDidLoad() using your setter method. You have to call setter method to get your brain object initialized.
Something like this
viewDidLoad()
{
brain = [self setBrain];
//You can also do this
brain = [[calbrain alloc] init];
}
and use that brain object in your (IBAction)kickit: method.
Hope this helps.