I have a problem with one property in one class. I am coding with iOS 6.1 if it makes any difference.
The class is UIViewController and the property is the declared in the header file like so:
// Keeps track of time in seconds
#property (nonatomic, strong) NSNumber *timeInSeconds;
In my implementation file I use the property during 3 occasions:
one is to add time with the method - (void)addTime
one is to subtract time with the method - (void)subtractTime
Those two methods use the property like so:
- (void)addTime
{
CGFloat timeFloat = [self.timeInSeconds floatValue];
// Here I set the value of the property timeInSeconds, but I can't access that value later on in the code!
self.timeInSeconds = [NSNumber numberWithFloat:timeFloat +5];
NSLog(#"Total Time:%#", self.timeInSeconds);
}
The two methods addTime and subtractTime do what they are supposed to do, and they keep a good track of the property timeInSeconds value as I add then subtract then add...
The problem is when I call in the same implementation file the third method which is:
- (void)updateLabelTime
{
self.label.attributedText = [[NSAttributedString alloc]initWithString:[self.timeInSeconds stringValue]];
[self.label setNeedsDisplay];
[NSTimer scheduledTimerWithTimeInterval:0.8 target:self selector:#selector(updateLabelTime) userInfo:nil repeats:YES];
}
I also tried to create a the NSAttributedString with stringWithFormat instead of initWithString but the problem persists which is that instead of returning the value of the property timeInSeconds which i previously set using addTime and subtractTime, it calls the getter which creates a new instance of timeInSeconds since in my getter I have lazy instantiation.
I tried to not write the getter/setter for the property (since I am using iOS 6.1) but it makes no difference.
If I just set the label to some random string, it would work. The problem is that if I know the value of timeInSeconds is 55, it would still create a new _timeInSeconds.
I tried my best with my English since I am French, please don't answer if the question was already asked by a beginner iOS developer and just redirect me. I couldn't find an answer though, thanks!
EDIT: Here is the custom getter
- (float)timeInSeconds
{
if (!_timeInSeconds) {
_timeInSeconds = 0;
}
return _timeInSeconds;
}
SECOND EDIT:
The stupid beginner mistake that I made was that addTime and subtractTime are actually implementing a protocol and they set the property which "lives" in another class which is why I could not access it! That other class that needs the protocol was creating a new instance of the class where addTime and subtractTime is written.
What needs to be done is to set the controller as the delegate for the protocol. I did this in the viewDidLoad method with something like:
self.view.delegate = self;
Thanks for all the help.
In your header file, declare this property:
#property (assign) float timeInSeconds;
In the implementation file:
#synthesize timeInSeconds = _timeInSeconds;
- (void)viewDidLoad
{
[super viewDidLoad];
_timeInSeconds = 0.0f;
}
- (void)addTime
{
_timeInSeconds += 5.0f;
}
This should initialize timeInSeconds to zero and then increment its value by 5 each time you call addTime. To use its value in the label:
- (void)updateLabelTime
{
self.label.text = [NSString stringWithFormat:#"%f", _timeInSeconds];
}
In your custom getter you are assigning a scalar value to an object property. In fact assigning zero to an object property is the equivalent of setting the object to nil.
What you need to do is this:
- (float)timeInSeconds
{
if (!_timeInSeconds) {
_timeInSeconds = [NSNumber numberWithFloat:0.0f];
// or alternatively with the latest version of objective c
// you can more simply use:
// _timeInSeconds = #(0.0f);
}
return _timeInSeconds;
}
Related
I currently have a SpriteKit game with the objective of shooting down enemies. I've implemented collision detection for it, and it works just fine. But I need to implement health for enemies. Enemies are constantly generated and keep moving, so you never know what node that should bebeSo I tried to declare my custom class node in didBeginContact method, then assigning it to bodyA, then changing it's health value, but this seems useless since I just create a new node (same shows the NSLog). I tried typecasting the declaration, but still with no luck. Did some research on this topic, but didn't find anything that suits me. Currently I can't provide source code for what I did, but I hope what I have requested is possible to explain. Please push me in the right direction.
Every SKSpriteNode has a userData NSMutableDictionary which can be used to store data (objects).
You first have initialize the dictionary like this:
myNode.userData = [NSMutableDictionary dictionary];
Then you can assign data to it like this:
float myHealth = 100.0;
NSString *myX = [NSString stringWithFormat:#"%f",myHealth];
[myNode.userData setValue:myX forKey:#"health"];
To read data you do this:
float myHealth = [[myNode.userData objectForKey:#"health"] floatValue];
I used float in my example but you can use whatever you want. Just remember that you cannot store primitives like float, int, long, etc... directly. Those need to be converted to NSNumber, NSString and so on.
That being said, Stephen J is right with his suggestion. You should subclass SKSpriteNode for your enemies and have health as a class property. Subclassing is much easier to work with in the long run and gives you greater flexibility compared to using the userData.
To illustrate some Object oriented concepts Stephen J and sangony are referring to, I have added some code for you.
Subclassing SKNode will define a new object class which inherits all functionality from SKNode. The main advantage here is that you can implement custom properties (such as health) and custom logic (such as lowering that health).
#interface EnemyNode : SKSpriteNode
- (void)getHit;
- (BOOL)isDead;
#property (nonatomic) CGFloat health;
#end
#implementation EnemyNode
- (instancetype)initWithColor:(UIColor *)color size:(CGSize)size {
self = [super initWithColor:color size:size];
if (self) {
self.health = 100.f;
}
}
- (void)getHit {
self.health -= 25.f;
}
- (BOOL)isDead {
return self.health <= 0;
}
#end
In your scene, you would use it as such:
EnemyNode *newEnemy = [[EnemyNode alloc] initWithColor:[UIColor blueColor] size:CGSizeMake(50,50)];
[self addChild:newEnemy];
...
[newEnemy getHit];
if ([newEnemy isDead]) {
[newEnemy removeFromParent];
}
For further illustration, you could take a look at my answer to a similar question.
To my understand self refers to the current class and when i use a dot after self is to use one of its properties. In the code here there's a use in self.popOperand that i don't understand if popOpernad is not a property. Another thing i don't understand is why
[self pushOperand:result]; works and [self.pushOperand:result]; doesn't.
#import "Calcbrain.h"
#interface Calcbrain()
#property (nonatomic,strong) NSMutableArray *operandStack;
#end
#implementation Calcbrain
#synthesize operandStack = _operandStack;
-(NSMutableArray *) operandStack
{
if(_operandStack == nil) _operandStack = [[NSMutableArray alloc]init];
return _operandStack;
}
-(double)popOperand
{
NSNumber *objectNum = [self.operandStack lastObject];
if (objectNum)[self.operandStack removeLastObject];
return [objectNum doubleValue];
}
/*-(void) setOperandStack:(NSMutableArray *)operandStack
{
_operandStack = operandStack;
}*/
-(void)pushOperand:(double)opernand
{
[self.operandStack addObject:[NSNumber numberWithDouble:opernand]];
}
-(double)performOperation:(NSString *)operation
{
double result=0;
if([operation isEqualToString:#"+"])
{
result = self.popOperand + self.popOperand;
}
else if ([#"*" isEqualToString:operation])
{
result = self.popOperand * self.popOperand;
}
[self pushOperand:result];
return result;
}
#end
Whilst the . notation is primarily used for properties, it can be used for paramaterless methods that return a value. Why? Because the synthesised getter for a property is in the same form.
-(double)calcValue {
....
return value;
}
Is equivalent to the property declaration:
#property (nonatomic, readonly) double calcValue;
Whilst there may be no property declaration, it doesn't mean the . notation cannot be used. The compiler will effectively change . notation to a method call when compiling, as . is a form of syntactic sugar. As so:
self.popOperand
// Translates to
[self popOperand];
This leads on to part 2, why does [self.pushOperand:result]; not work? The reason being is that . does not support the passing of parameters directly.
The only way to assign/push a parameter to a property is via self.pushOperand = result, but this wouldn't work, because there isn't a corresponding - (void)setPushOperand:(double)pushOperand; that the . notation assignment maps to.
[self pushOperand:result]; works because you're being explicit in calling a particular method, called pushOperand:.
Overall, keep . notation for properties only, and if you're using a method that isn't designed to be a 'property', be explicit.
Update: self is a reserved keyword, that represents a pointer to the instance we're working within at that time.
For example, I can create two instances of Calcbrain outside of Calcbrain, for example BrainViewController:
Calcbrain* instance1;
Calcbrain* instance2;
Now, Calcbrain has methods declared within it, let's use -(double)performOperation:(NSString *)operation as an example. Now, if I wanted to call that from BrainViewController, I would do:
[instance1 performOperation:#"+"];
[instance2 performOperation:#"+"];
Because we are calling a method which is part of another class, I have to determine the correct instance I've created to refer to it (i.e. instance1 and instance2). But how would I call that from within the class itself, and make sure it applies to the correct instance? The instance I've created is unaware of the other instances I've created. Use self. self allows you to reference yourself within methods. So if I wanted to performOperation within Calcbrain itself, I would need to use self:
[self performOperation:#"+"];
The "dot" is just a syntactic sugar, it can be used even if there isn't a declared property. The expression
a.someProperty
is equivalent to
[a someProperty]
and the expression
a.someProperty = c
is equivalent to
[a setSomeProperty:c]
Therefore, self.popOperand is just the same as [self popOperand]. And one could also use the "dot" in some absurd cases like [NSMutableArray alloc].init.
Using the "dot" syntax for non-properties are highly discouraged. If I were the maintainer of this code I would change all self.popOperand back to [self popOperand] to avoid confusion.
(BTW, it is not defined which side of the + will get evaluated first. Better change
result = [self popOperand] + [self popOperand]
to
double operand1 = [self popOperand]
double operand2 = [self popOperand]
result = operand1 + operand2;
This will be a trouble when you define - and /.)
Dot is used to access properties of the class. You can also access properties and methods without dots:
[classInstance someMethodOrProperty];
This code: [classInstance someProperty]; equals this code classInstance.someProperty; You cannot call methods with dots like properties.
I am trying to remove a pointer in an NSMutableArray that points to an object in another array without deleting the object itself. E.g.:
// In ViewController.m – Code abridged and somewhat simplified
#interface ViewController ()
#property (nonatomic, strong) NSMutableArray *objectPool;
#property (nonatomic, strong) NSMutableArray *objectsOwnedByFriend;
#property (nonatomic, strong) NSMutableArray *objectsOwnedByMe;
- (void)transferPointerToObjectFromFriendToMe;
- (void)copyPointerToObjectFromFriendToMe;
#end
#implementation ViewController
#synthesize objectPool = _objectPool;
#synthesize objectsOwnedByFriend = _objectsOwnedByFriend;
#synthesize objectsOwnedByMe = _objectsOwnedByMe;
- (void)setObjectPool:(NSMutableArray *)objectPool
{
_objectPool = objectPool;
}
- (NSMutableArray *)objectPool
{
if (!_objectPool) _objectPool = [[NSMutableArray alloc] initWithArray:self.objects]; // self.objects is a mutable array containing multiple NSObjects
return _objectPool;
}
- (void)setObjectsOwnedByFriend:(NSMutableArray *)objectsOwnedByFriend
{
_objectsOwnedByFriend = objectsOwnedByFriend;
}
- (NSMutableArray *)objectsOwnedByFriend
{
if (!_objectsOwnedByFriend)
{
_objectsOwnedByFriend = [[NSMutableArray alloc] init];
[_objectsOwnedByFriend addObjectsFromArray:self.objectPool];
}
return _objectsOwnedByFriend;
}
- (void)setObjectsOwnedByMe:(NSMutableArray *)objectsOwnedByMe
{
_objectsOwnedByMe = objectsOwnedByMe;
}
- (NSMutableArray *)objectsOwnedByMe
{
if (!_objectsOwnedByMe) _objectsOwnedByMe = [[NSMutableArray alloc] init];
return _objectsOwnedByMe;
}
- (void)transferPointerToObjectFromFriendToMe
{
[self.objectsOwnedByMe addObject:[self.objectsOwnedByFriend lastObject]];
[self.objectsOwnedByFriend removeLastObject];
}
- (void)copyPointerToObjectFromFriendToMe
{
[self.objectsOwnedByMe addObject:[self.objectsOwnedByFriend lastObject]];
}
#end
In the above code, when I use transferPointerToObjectFromFriendToMe, removing the last object removes both the pointer to it in self.objectsOwnedByFriend (as I want) and also the object itself in self.objectPool (which I don't want to happen).
What I would like is an array (self.objectPool) that contains all of the actual objects and then two mutable arrays (self.objectsOwnedByFriend and self.objectsOwnedByMe) that contains pointers to objects in self.objectPool and the ability to add and remove more pointers referencing objects in self.objectPool to self.objectsOwnedByFriend and self.objectsOwnedByMe.
Also, when I use either transferPointerToObjectFromFriendToMe or copyPointerToObjectFromFriendToMe, the object doesn't seem to be added properly, as a subsequent check via self.objectsOwnedByMe.count results in 0 instead of 1.SOLUTION = My lazy instantiation for self.objectsOwnedByMe was missing in my original code :SI was able to check whether self.objectsOwnedByMe was properly created via:
NSLog(#"self.objectsOwnedByMe = %#", self.objectsOwnedByMe);
** My first StackOverflow question! ** I hope I was clear...couldn't find a a similar question so apologies if I missed an old thread. Let me know if you need more info to diagnose. (I am trying to learn Obj-C.)
Typo :P Sorry peeps. In my actual code in Xcode I had:
- (void)setObjectPool:(NSMutableArray *)objectPool
{
_objectPool = objectPool;
}
- (NSMutableArray *)objectPool
{
if (!_objectPool) _objectPool = [[NSMutableArray alloc] initWithArray:self.objects];
return _objectsOwnedByFriend;
}
I think my mistake is super obvious (and if not, the mistake was that my getter for objectPool was returning _objectsOwnedByFriend...copy/paste error that I somehow missed).
Everything works now!
This is very peculiar and confusing code. I suspect the problem is that something is calling one of the setters, -setObjectPool: or -setObjectsOwnedByFriend:, with the array of the other object. Those setters simply make the ivar refer to the object that was passed in. Because of that, they are very prone to lead to objects being shared.
Typically, a property like that would be declared and implemented with copy semantics.
It looks like self.objectsOwnedByMe is never initialized and you are therefore always working with nil instead of an actual NSMutableArray.
Somewhere (perhaps in a custom getter for objectsOwnedByMe as below?) you need to create an array before you start using it:
- (NSMutableArray *)objectsOwnedByMe {
if (_objectsOwnedByMe == nil) {
_objectsOwnedByMe = [[NSMutableArray alloc] init];
}
return _objectsOwnedByMe;
}
This would explain both problems: Since it is nil it never retains the objects and therefore they go away when removed from the other array, and also why they are never added to the _objectsOwnedByMe array.
I have problem that im trying to get solve for like week.
My goal is to get variable out of my IBAction, to use for example in -(void)viewDidLoad..
But as far as I am now I can use my variable only in my IBAction..
- (IBAction) changeLat:(NSNumber *)str {
longi = str;
double lop = longi.doubleValue;
NSLog(#"%f",lop);
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog (#"%#",lop);
}
It NSLog shows everything fine in action, but in view did load it doesn't even recorganize it.
If you create a variable inside of -IBAction, the scope of that variable is only that method, so you cannot access to that variable outside it.
If you want your variable to be global to your class, you have to create it in the declaration of your class, like this:
#interface MainViewController () {
#private
double lop;
}
Put this at the beginning of your .m file, and then lop would be accesible in all your class.
You can read more about the scope of the variables here:
http://www.techotopia.com/index.php/Objective-C_Variable_Scope_and_Storage_Class
Actually, IBAction is converted to void by the preprocessor. It's used by Interface Builder as a label that identifies this method as an action able to be related from an IB Object.
There's no way (AFAIK) to use two return types in a function (for example `(IBAction double)´, equivalent to ´(void double)´), but a good practice could be something like this:
- (IBAction)changeLatAction:(id)sender {
NSNumber *str = <get the NSNumber from a valid place>;
[self changeLat:str];
}
- (double) changeLat:(NSNumber *)str {
longi = str;
double lop = longi.doubleValue;
NSLog(#"%f",lop);
return ????;
}
Your first declaration of changeLat seems to be wrong, because as a first parameter you'll always get the "sender" or "caller" object, related from IB (when called from an action, of course), so, you need to get the str value from a valid place.
Cheers.
I'm getting this warning when I'm calling a local routine.
My code is this:
-(void)nextLetter {
// NSLog(#"%s", __FUNCTION__);
currentLetter ++;
if(currentLetter > (letters.count - 1))
{
currentLetter = 0;
}
self.fetchLetter;
}
I'm getting the warning on the self.fetchLetter statement.
That routine looks like this:
- (void)fetchLetter {
// NSLog(#"%s", __FUNCTION__);
NSString *wantedLetter = [[letters objectAtIndex: currentLetter] objectForKey: #"langLetter"];
NSString *wantedUpperCase = [[letters objectAtIndex: currentLetter] objectForKey: #"upperCase"];
.....
}
I prefer to fix warning messages, is there a better way to write this?
Thanks!
The dot notation (i.e. self.fetchLetter) is meant for properties, not for arbitrary methods. The self.fetchLetter is being interpreted as "get the 'fetchLetter' property of 'self'," which isn't what you intend.
Just use [self fetchLetter] instead.
In newer Xcode versions, even the [object method]; may trigger the warning. But sometimes we actually do need to call a property and discard the result, for example when dealing with view controllers and we need to make sure the view is actually loaded.
So we were doing:
// Ensure view is loaded and all outlets are connected.
[self view];
This now also triggers the “Property access results unused - getters should not be used for side effects” warning. The solution is to let the compiler know it's done intentionally by casting the result type to void:
(void)[self view];
You're declaring fetchLetter using syntax like this?
#property (retain) id fetchLetter;
That looks wrong for what you're doing. Properties are intended to be variable accessors that (in the case of getters) don't have any side effects.
You should declare fetchLetter as a method, like so:
- (void) fetchLetter;
and access it using:
[self fetchLetter]
I just got my problem resolved, in my case a CoreLocation Project, using both answers from Tom and Chris -
I declare:
#property (strong, nonatomic)CLLocationManager *locationManager;
And implemented like:
#synthesize locationManager = _locationManager;
....
- (void) dealloc {
[self locationManager];
}