I am trying to call a method that checks whether the player is bigger or smaller than the enemy. (this is a fish game)
In EnemyFish.m I am using this method
-(void) compareSize:(Player*)player{
if (self.fSize > player.pSize){
isBigger = true;
}
else{
isBigger = false;
}
}
Then I want to call this method during the update so I am doing this:
-(void) update {
[self compareSize];
//Check to see if bigger than player fish
if( isBigger == true){
//code for if bigger
}else{ //etc. }
I am getting an exception: sharedlibrary apply-load-rules all
not sure what the best way to set up this method would be, and the best way to call it, since [self compareSize] is definately not working.
Any help would be greatly appreciated, Thanks!
------UPDATE----------
What about if I use this
update:(Player *)player{
The problem I was running into here, is how to call this correctly, I wasn't sure how to change this to correctly call the new update method:
[self schedule:#selector(update) interval:1.0f/60.0f];
It is unclear what you are asking, but let's look at your code and see if it helps.
Your first method can be written more concisely as:
- (void) compareSize:(Player *)player
{
isBigger = self.fSize > player.pSize;
}
There is no point in using an if/else to assign a true/false (or YES/NO) value.
Looking at this method raises the obvious question of whether it would be better returning a value rather than assigning to an instance variable. This would look like:
- (BOOL) compareSize:(Player *)player
{
return self.fSize > player.pSize;
}
and now you can use a call to compareSize in an if.
Assuming the second version of compareSize your second method is:
-(void) update
{
//Check to see if bigger than player fish
if ([self compareSize]) // OOPS, no Player
{
//code for if bigger
}
else
{
//etc.
}
}
But this doesn't work as you need an instance of Player to pass to compareSize:, e.g. [self compareSize:somePlayerInstance]. So you now have to ask yourself where you expect the Player to be found; it could be an argument to update (e.g. - (void) update:(Player *)somePlayerInstance), or you might have a method to call which returns a whole collection of players and you need to test against each one, etc., etc. I can't give an answer as I've no idea of your game and algorithm!
Following comment
You must store a reference to your Player object somewhere in your application. If there is only a single player is Player designed as a singleton with a sharedInstance, or similarly named, class method that returns the single instance? If so then your update will contain:
if ([self compareSize:[Player sharedInstance]])
etc.
Another design pattern is to have your application delegate store the reference and to provide a method (or property) for accessing it. Following this pattern (and making up a class MyDelegateApp and property player names) your code might look like:
if ([self compareSize:((MyAppDelegate *)[NSApp delegate]).player])
Yet another model is to create the single player in the application's main XIB/NIB file - etc., etc., there are many application models!
You "simply" (its not simple of course) need to design your application model so that your single player is accessible, one way or another, where you need it...
Your compareSize: method (note the colon) requires a player parameter.
So you need to call it like this:
[someEnemyFish compareSize:somePlayer]
If self is the instance of EnemyFish you want, you can do [self compareSize:somePlayer], but your title seems to indicate self isn't an EnemyFish?
Related
What I want to do is cause another save anytime saveInBackgroundWithBlock is called in my app. Im doing this to create a "log" of everything that changes and who changed it. I looked into the PFObject.h but I am not sure how to properly write this in Obj-C or where else/how I could do this other than going through and adding it to every single instance. Is this a good way to do it? Or should I stick it out and use the harder way?
- (void)saveInBackgroundWithBlock:(nullable PFBooleanResultBlock)block;
you have multiple ways in order to achieve this kind of functionality:
Create Util class which receive PFObject and PFBooleanResultBlock as parameters this Util class will execute a call to saveInBackgroundWithBlock inside the callback you can implement the additional save that you need. at the end your Util class should look like the following:
#interface ParseUtils : NSObject
+ (void)saveParseObject:(PFObject *)object block:(PFBooleanResultBlock)block;
#end
#implementation ParseUtils
+ (void)saveParseObject:(PFObject *)object block:(PFBooleanResultBlock)block {
// if the object is nil or it was not changed return
if (!object || !object.isDirty) return;
[object saveInBackgroundWithBlock:^(BOOL succeeded, NSError * _Nullable error) {
// if no error occured
if (!error){
// HERE YOU SHOULD CALL YOUR ADDITIONAL SAVE...
}
// handle the callback to the calling class
if (block){
block(succeeded,error);
}
}];
}
#end
Another option is to do the same but with singleton (if you created some REST Client singleton for your project)
Another nice option is to create category on top of the PFObject and there to create a method which do exactly the same like the method that of the util
There are more options but i think that it's enough..
if you want to do it as quick as possible use the utilities. if you want to do it with best architecture then go with the category.
working on xcode I realized that if I create a non-void method, I call it from a class / method, the result is processed optimally only if the action is immediate. I tried to do a test by inserting a delay and I realized that it no longer works.
I will write down here the example that I created:
Class A
//--------------------CLASS A
- (void)viewDidLoad {
[super viewDidLoad];
i = 0;
Class *classB = [[Class alloc] init];
i = [classB method1];
[self performSelector:#selector(method3) withObject:NULL afterDelay:1.8];
}
-(void)method3 {
NSLog(#"i = %i",i); // i = 0
}
Class B
//--------------------CLASS B
-(int)method1 {
[self performSelector:#selector(method2) withObject:NULL afterDelay:1];
return a;
}
-(void)method2 {
a = 800;
}
Obviously my problem is not something so trivial but I tried to make it easy to get an answer as thoroughly as possible, I was advised to use modal methods but I don't think that's the solution I was looking for.
What could I do to solve this?!
What you really need is a better understanding of asynchronous methods. In your method1, the variable a is never altered -- all you are doing is scheduling method2 to be called in the future and then returning the current state of variable a.
In Objective-C, there are a few different ways you can solve this problem. People most commonly use protocols and delegates to solve this issue. Here is a basic intro to protocols and delegates. Basically, you would want your class A object to be a delegate of your class B object. You could also use NSNotifications or blocks, although you should probably understand the usage of protocols and delegates (they are very important in Objective-C) before moving on to notifications and blocks.
What could I do to solve this?!
Where do you want to return the value to? In your example, method1 will complete long before method2 is ever invoked. If you want to preserve the value calculated by method2, you'll typically have that method store the value in one of ClassB's instance variables and possibly call some other method to continue processing.
If you really need method1 to return the result from method2, you'll need to call it synchronously (i.e. without -performSelector:withObject:afterDelay:). In this case, consider a) why you need the delay at all; and b) if you should perhaps be calling method1 after a delay instead of method2.
We'll be able to provide much better help if you can explain what the real-world problem that you're trying to solve is.
My problem is that I'm using dispatch_async(dispatch_get_main_queue(), ^(void) { ... }); to call a method asynchronously, in this method depending on some conditions i set a boolean to YES. When this boolean is read in this method, it's always read by it's old value which is NO.
The weird thing is that when i made a breakpoint on the line where the bool is checked, everything went fine and as intended !
EDIT:
Here is the code where the threads are spawned
dispatch_async(dispatch_get_main_queue(), ^(void) {
[self drawFaceBoxesForFeatures:features forVideoBox:claporientation:curDeviceOrientation image:img];
});
The method itself
- (void)drawFaceBoxesForFeatures:(NSArray *)features forVideoBox:(CGRect)clap orientation: (UIDeviceOrientation)orientation image:(UIImage *)image;
{
if (![self getSendingRequestStatus]) {
NSLog(#"Sending req");
// send async request
dispatch_async(dispatch_get_main_queue(),^ {
sendingRequest = YES;
} );
}
}
It looks like you are modifying an ivar that was created outside of a block inside of the block. In order to do this and have the ivar hold the correct value, you are going to need to use the __block keyword like so:
#interface MyCoolClass : NSObject {
#private
__block int sendingRequest_;
}
As Jack Lawrence said in the commend above, "[the runtime] takes a snapshot of all of the relevant objects/variables at that point in time". The __block identifier will tell the runtime that it should not copy that ivar to the heap and will allow you to assign values to sendingRequest_ inside of a block, even if that block is simply being run on the main thread.
A lot of good information to start with (including the above) can be found in the Blocks Programming Guide.
When primitives are passed into a block they are copied. So if you put a primitive local or instance variable in a block and then later change it either in the same method that created the block (after the block creation) or another method it won't have any effect on the variable in the block. In the case of a local variable, just make sure you make any necessary changes before block creation. In the case of instance variables you could try accessing the instance variable by using some C: self->iVar or declare it as a property and access it through the property accessor: self.iVar.
Hey, right now I have implemented my own listener pattern.
I will send an update to the listeners using fast enumeration.
the code will look like this
- (void) updateListeners {
for (id<AProtocol>listener in _listeners)
{
[listener update];
}
and in listener, i implement method for AProtocol, which is update.
suppose there are n object in _listeners, and m number of listener such that m < n want to remove it self from listen when listener's update method is called.
The problem with this is that I can't remove when the fast enumeration is ongoing, I will get an error.
In order to make the listener more dynamic so that we can remove listener from _listeners when update method is called, what would be the solution?( I don't want to use NSNotificationCenter)
It sounds like what you have now is the listener itself deciding whether it should be removed, and removing itself. That's problematic because (a) as you say, it breaks your enumeration, but (b) because it's a tricky abstraction-- if the object that runs "update" doesn't also control ownership in the listener list directly, your design pattern might run into problems anyways. I might suggest that you redefine update listeners like this:
- (BOOL)update
and return a BOOL indicating whether the listener should be removed (or kept, depending on your semantics). Then you could write the loop like this:
NSMutableSet * listenersToBeRemoved = [NSMutableSet set];
for (id<AProtocol> listener in _listeners) {
BOOL shouldRemove = [listener update];
if (shouldRemove) {
[listenersToBeRemoved addObject:listener];
}
}
// Do this if _listeners is a Set, or whatever the equivalent is.
[_listeners minusSet:listenersToBeRemoved];
As others have suggested, if you do want to allow the listeners to remove themselves during the update process, it's simple enough to just iterate through a local copy of the collection, instead of the collection itself. The syntax for that depends on whether _listeners is an array, a set, or something else, but see other answers or the docs.
Why not operate the enumeration on a copy of the array?
for (id<AProtocol>listener in [NSArray arrayWithArray:_listeners])
{
[listener update];
}
Then _listeners can safely be modified during the loop. It's safer than Davids solution since it's immune against any listener removals not only the ones that happen in -update.
Replace fast iteration by usual iteration and start from the last.
// must iterate from the last in case the current listener removes itself from the list
for (int i = [_listeners count] - 1; i > -1; i--) {
id<AProtocol> listener = [_listeners objectAtIndex:i];
[listener update];
}
I've got a function called updateTheValue() that I have called using [self updateTheValue] for a while now. Two things happened recently; I added the method calling in the viewDidLoad() method and it spit out a warning saying my class may not respond to this. Second, I want to pass objects to updateTheValue() like strings, but mostly ints, so I declared an NSObject to pass into the method. Can an int fit into an NSObject slot, or what should I use instead?
I would have posted these seperately but they seem to be related since after updating updateTheValue() to accept an NSObject every reference to this function turns out the error that my class "May not respond to -updateTheValue"
You could make your method like this:
-(void)updateTheValue:(NSObject *)anObject
// or use -(void)updateTheValue:(id)anObject
{
if ([anObject isKindOfClass:[NSString class]]) {
// Do your string handling here
}
else if ([anObject isKindOfClass:[NSNumber class]]) {
// Do your number handling here
}
}
Use it like this:
[self updateTheValue:[NSNumber numberWithInt:42]];
I'd suggest doing two different methods though, i.e. updateTheValueWithInt: and updateTheValueWithString: making it easier to read and understand.
Make sure you make the method signature visible before using them, so that the compiler knows what this does.
If you use separate methods you can use int directly without wrapping them into NSNumber objects.
First problem:
updateTheValue() must be declared before you try to call it.
You can either move the definition of function before the calls to it, or add a prototype at the top - eg, add:
(void) updateTheValue;
near the top.
Second problem:
Use an NSNumber, eg [NSNumber numberWithInt:45];