it has been a while since I did anything in ObjC. I am sure the mistake is trivial but I can't seem to find it.
I created a class called Game with the following .h file:
#import <Foundation/Foundation.h>
#interface FSGame : NSObject
#property NSInteger numberOfGames;
#end
In the implementation file of one of my view controllers, a method then uses the user's selection from a segm. control to determine the number of games:
FSGame *game;
// Number of Games
NSInteger index = _segCtrlNumberGames.selectedSegmentIndex;
if (index == 0) {
game.numberOfGames = 1;
} else if (index == 1) {
game.numberOfGames = 3;
} else {
game.numberOfGames = 5;
}
NSLog(#"Games: %i; index: %i", game.numberOfGames, index);
The NSLog returns: Games: 0; index: 2
I have the same issue with two other classes I created. I grab text from a UITextField and the NSLog displays it correctly so I know I am actually able to get the user's input - it just wont store it as an ivar.
This must be something really obvious. Any pointers?
You create the Game object:
Game *game;
But you should create NSGame instead and you need to allocate and initialise it as well:
FSGame *game = [[FSGame alloc] init];
Related
Here is explanation
List of class properties in Objective-C
how to use class_copyPropertyList to get at runtime all properties of class.
I have tested this and it is working fine.
I have notice that it will only get properties from that class only, and not it subclass.
CODE:
#interface JustForThisUT : NSObject
#property NSUInteger public_access;
#end
#interface JustForThisUT ()
#property NSUInteger private_access;
#end
#implementation JustForThisUT
#end
#interface JustForThisUTParent : JustForThisUT
#property NSUInteger my_public_access;
#end
#interface JustForThisUTParent ()
#property NSUInteger my_private_access;
#end
#implementation JustForThisUTParent
#end
If I use it on JustForThisUT I will get 2 properties (public_access & private_access)
If I use it on JustForThisUTParent I will ALSO get 2 properties (my_public_access & my_private_access)
But for JustForThisUTParent I was expecting to get 4, 2 from JustForThisUT and 2 from JustForThisUTParent.
My question is
How to get properties from current class and all subclasses ?
Is it even possible ?
You have to first find all the subclasses, then use the class_copyPropertyList() to list the properties for each class, uniquing as needed.
However, this has a bad code smell. This level of dynamic, reflection heavy, programming is counter to what ObjC is really designed for. You'll end up with a fragile codebase that is difficult to maintain.
If you really want dynamism of this nature, then implement a class method on each class that returns the list of property names that you want to be able to dynamically access.
It's fairly simple to find all the superclasses (I presume this is what you meant, rather than subclasses); you simply loop until class.superclass == NSObject.class.
Do note #bbum's comments about this being a bad code smell though. Read his answer and consider alternative approaches, and really think about why you want to do this and if there's a better way.
That said, here's a full code example that retrieves all the BOOL properties from a class. It would be easy to extend it for other property types - see the linked questions.
/**
* Get a name for an Objective C property
*
*/
- (NSString *)propertyTypeStringOfProperty:(objc_property_t) property {
NSString *attributes = #(property_getAttributes(property));
if ([attributes characterAtIndex:0] != 'T')
return nil;
switch ([attributes characterAtIndex:1])
{
case 'B':
return #"BOOL";
}
assert(!"Unimplemented attribute type");
return nil;
}
/**
* Get a name->type mapping for an Objective C class
*
* Loosely based on http://stackoverflow.com/questions/754824/get-an-object-properties-list-in-objective-c
*
* See 'Objective-C Runtime Programming Guide', 'Declared Properties' and
* 'Type Encodings':
* https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html#//apple_ref/doc/uid/TP40008048-CH101
* https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html#//apple_ref/doc/uid/TP40008048-CH100-SW1
*
* #returns (NSString) Dictionary of property name --> type
*/
- (NSDictionary *)propertyTypeDictionaryOfClass:(Class)klass {
NSMutableDictionary *propertyMap = [NSMutableDictionary dictionary];
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList(klass, &outCount);
for(i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
const char *propName = property_getName(property);
if(propName)
{
propertyMap[#(propName)] = [self propertyTypeStringOfProperty:property];
}
}
free(properties);
return propertyMap;
}
- (NSDictionary *)settingsProperties
{
if (!_settingsProperties)
{
Class class = _settings.class;
NSMutableDictionary *propertyMap = [[NSMutableDictionary alloc] init];
do
{
[propertyMap addEntriesFromDictionary:[self propertyTypeDictionaryOfClass:class]];
}
while ((class = class.superclass) != NSObject.class);
_settingsProperties = propertyMap;
}
return _settingsProperties;
}
I am making an iOS game with sprite builder. And I designed it around a board game style. I want the user to press the play button that triggers the play method. It then generates a random number that should be displayed on a label. The label and button are on the gameplay scene. The game play scene is a subclass of CCNode. The code is on the Gameplay class that is a subclass of CCNode. I have found out that the label is nil. How do I make it not nil? The code connection for my label is doc root var assigned to _randNumLabel. My gameplay code connection is assigned Gameplay. This is my log after I open the scene and click the button:
2014-06-09 17:20:12.565 Sunk[6037:60b] CCBReader: Couldn't find member variable: _randNumLabel
2014-06-09 17:20:12.567 Sunk[6037:60b] CCBReader: Couldn't find member variable: _affectLabel
2014-06-09 17:20:19.513 Sunk[6037:60b] Nil`
Ignore the _affectLabel as it will get fixed if _randNumLabel gets fixed.
#import "Gameplay.h"
#implementation Gameplay
CCLabelTTF *_randNumLabel;
- (void)play {
if (_randNumLabel == nil)
{
CCLOG(#"Nil");
}
if (_randNumLabel !=nil)
{
CCLOG(#"play button pressed!");
int max = 6;
int randNumber = (arc4random() % max) + 1; // Generates a number between 1-6.
CCLOG(#"Random Number %d", randNumber);
_randNumLabel.string = [NSString stringWithFormat:#"Number: %d", randNumber];
}
}
- (void)update:(CCTime)delta {
}
#end
You need to declare your instance variables using curly brackets, i.e.:
#implementation Gameplay
{
CCLabelTTF *_randNumLabel;
}
- (void)play {
// rest of your code
...
As a personal preference I would use a private property instead of an instance variable, e.g.
#interface Gameplay ()
#property (nonatomic, strong) CCLabelTTF *randNumLabel;
#end
#implementation Gameplay
- (void)play {
// rest of your code
...
I'm building a game with 3 different level difficulties (Easy, Medium, & Delete) I have 2 ViewControllers and 1 NSObject class that I'm using to set and get the game difficulty value (0=easy, 1=Medium, 2=Delete(hard). Within context of how I'm doing it, I've research and tried many variations with no success. NOTE: I have tried to code this, but have since stripped out that part of the code because it wasn't working. I'm somewhat familiar with NSUserDefaults, so if that's a better solution, please advise. I may need a little direction on setting that up, though.
- ViewController 1 (RewindSettingsViewController): I have 3 buttons: Easy, Medium, Delete. User taps button, they go to ViewController 2
- ViewController 2 (RewindGameViewController): This is the actual game with a Start button. This is where I'm hung up; getting the difficulty passed into this view.
CODE:
.h - GameManager : NSObject
+(void) setDifficulty:(int)difficulty;
+(int) getDifficulty;
.m - GameManager
static int _difficulty;
+(void)setDifficulty:(int)difficulty {
_difficulty = difficulty;
}
+(int)getDifficulty {
return _difficulty;
}
(VC1) .h - RewindSettingsViewController : UIViewController
#property (nonatomic, strong) IBOutlet UIButton *easyModeButton;
#property (nonatomic, strong) IBOutlet UIButton *mediumModeButton;
#property (nonatomic, strong) IBOutlet UIButton *hardModeButton;
-(IBAction)easyMode;
-(IBAction)mediumMode;
-(IBAction)hardMode;
.m - RewindSettingsViewController
-(IBAction)easyMode {
[GameManager setDifficulty:0];
}
-(IBAction)mediumMode {
[GameManager setDifficulty:1];
}
-(IBAction)hardMode {
[GameManager setDifficulty:2];
}
(VC2) .h - RewindGameViewController : UIViewController
no code here for difficulty settings
.m - RewindGameViewController
At this point, _difficulty is of course still nil. My question marks below are my pain points
?: How to get the difficulty value stored in "setDifficulty" into "getDifficulty (_difficulty)" and properly call it in the IF statement?
?: I'm thinking _difficulty should be in the IF statements, but with everything I'm trying, syntax keeps barking at me.
-(void)placeObjectsRewind {
randomTopObjectRewind = arc4random() %350;
randomTopObjectRewind = randomTopObjectRewind - 228;
if (?) {
randomBottomObjectRewind = randomTopObjectRewind + 700; //LEVEL DIFFICULTY: EASY
objectTopRewind.center = CGPointMake(0, randomTopObjectRewind);
objectBottomRewind.center = CGPointMake(0, randomBottomObjectRewind);
} else if (?) {
randomBottomObjectRewind = randomTopObjectRewind + 665; //LEVEL DIFFICULTY: MEDIUM
objectTopRewind.center = CGPointMake(0, randomTopObjectRewind);
objectBottomRewind.center = CGPointMake(0, randomBottomObjectRewind);
} else if (?) {
randomBottomObjectRewind = randomTopObjectRewind + 635; //LEVEL DIFFICULTY: DELETE (HARD)
objectTopRewind.center = CGPointMake(0, randomTopObjectRewind);
objectBottomRewind.center = CGPointMake(0, randomBottomObjectRewind);
}
}
Instead of
static int _difficulty;
type this into your GameManager.h
#property int difficulty;
You don't have to write the Setter and Getter by yourself, the property-statement do this for you. The Getter/Setter is stored in the instance of GameManager you use. So get the instance of you GameManager and there you set like this:
[MyGameManagerInstance setDifficulty:0];
and get like
[MyGameManagerInstance difficulty];
Due to the fact that the difficulty-property is in the .h file, it's part of the Class's API which you can access from other ViewController or Classes.
EDIT:
Like you said, you could do this also with the NSUserDefaults. Just put it in like this:
[[NSUserDefaults standardUserDefaults] setInteger:1 forKey:#"Difficulty"];
and get it like this:
int myInt = [[NSUserDefaults standardUserDefaults] integerForKey:#"Difficulty"];
Since you've made the methods you want to use class methods, the syntax is going to be similar to if ([GameManager getDifficulty] == 0) etc.
(As a style point, you would usually drop the "get" in Objective-C and just name the method to return a value difficulty.)
Basiclly, I have a button which created value according to finish some math process like +, - , /, *. Question is when I get some value, I needed to use that value again when I press button second time. Let me explain Basicly,
When I clicked button process like,
int examplea = [txt_example.text intValue];
int exB = 5000;
int exC = exB - examplea;
This is the first time I push the button, my lastest value is exC When I input text field another value and clicked it same process will start but one difference:
int examplea = [txt_example.text intValue];
int exB = exC;
int exC ( this new value which will be calculated ) = exB - examplea;
How can I create process like this?
Something like this
in the .h file
#interface MyCalculator : NSObject
- (int) doCalculation;
#property (assign, nonatomic) int exB;
#end
in the .m file
#implementation MyCalculator
- (id) init
{
self = [super init];
if (self)
{
_exB = 5000;
}
return self;
}
- (int) doCalculation
{
int examplea = [txt_example.text intValue];
int exC = self.exB - examplea;
self.exB = exC;
return exC;
}
#end
....
MyCalculator* myCalculator = [[MyCalculator alloc] init];
...
[myCalculator doCalculation];
This isn't the full solution as there are several questions about what your code will do, but it will illustrate the principle of how to do it.
The last line is what gets called when the button is pressed.
I don't know where txt_example.text is coming from, but it might be better if that is passed to doCalculation as a parameter.
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