I got a little stuck and I'm hoping someone can point me in the right direction. I have an NSMutableArray that stores a sequence. I created an enumerator so that a while loop can get the content of the array one by one.
Everything works fine however I want the methods to be called with a 10 second gap in between each call. Right now it plays all at once (or in very quick order). What should I look at to create a delay in between method calls?
Below is what I got so far. Thanks!
NSEnumerator * enumerator = [gameSequenceArray objectEnumerator];
id element;
while(element = [enumerator nextObject])
{
NSLog(element);
int elementInt = [element intValue];
[self.view showButton:elementInt];
}
You almost certainly don't want to stick a "delay" in your loop, which will block the thread until it's over (and, unless your app is multi-threaded, block the entire thing). You could use multiple threads, but instead I'd probably split the loop out over repeated timed calls of another selector. Store the enumerator (or current index), and then look at NSObject's performSelector:awithObject:afterDelay:
So something like
[NSObject performSelector:#selector(some:selector:name:) withObject:objInstance afterDelay: 10]
where the selector will pickup the current enumerator, use it, advance it and schedule another call. Make sure you don't allow changes to the collection whilst this set of timed methods is executing.
This is what NSTimer is for. Use NSTimer to get each element in the array sequentially.
As an aside: you might want to take a look at Objective-C 2.0's Fast Enumeration
if gameSequenceArray is an array, then you don't need to use an enumerator:
NSTimeInterval time = 10;
for (id elementId in gameSequenceArray) {
[self.view performSelector:#selector(showButton:) withObject:elementID afterDelay:time];
}
and then you declare showButton:
- (void)showButton:(id)anElement {
...
}
If you end up passing your object enumerator around with a timer, know that you are not allowed to modify your array's contents until you are finished enumerating it.
So here was the solution that I came up with based on everyones input.
NSEnumerator * enumerator = [gameSequenceArray objectEnumerator];
NSTimeInterval time = 5;
for (NSString *element in enumerator) {
id elementId = element;
time++;
[self.view performSelector:#selector(showButton:) withObject:elementId afterDelay:time];
}
Thanks again for pointing me in the right direction everyone.
Related
I am having trouble getting objects added to my NSMutableArray to log properly (which definitely means they won't process any of the appropriate functions correctly) with Spritebuilder [version 1.4.9, from the Apple App Store]. I am creating several objects using the same class, but each new one is overriding the older objects which exist. I thought an array would help keep things in order (and then on collision, I could call the array to check for which object was collided with), but it simply is not working that way - at all. Here is the relevant code.
Main.h
#property Coven *coven;
#property Nellie *nellie;
#property NSMutableArray *array;
//Physics, other things
Main.m
/Adding other things...
-(void) addCovenMember{
//This function is called on a RANDOM time interval
_array = [[NSMutableArray] alloc]init];
for (i = 0, i < 15, i++){
_coven = (Coven*) [CCBReader load:#"CovenMember"];
[_array addChild:_coven];
}
[_physicNode addChild:_coven];
}
-(BOOL)ccPhysicsCollisionBegin:(CCPhysicsCollisionPair *)pair nellie:(Nellie*)nellie coven:(Coven*)coven{
for (_coven in _array){
NSLog(#"%#",_coven.name)
if (CGRectIntersectsRect(_nellie.boundingBox, _coven.boundingBox){
NSLog(#"We're intersecting!");
}
}
Coven. h
//Nothing Important Here
Coven.m
-(void)didLoadFromCCB{
self.physicsBody.CollisionType = #"coven";
}
Nellie.h
//Nothing Here
Nellie.m
-(void) didLoadFromCCB{
self.physicsBody.CollisionType = #"nellie";
}
The collision is logging with every collision - but only as the name of the LATEST _coven member to be generated, no matter what I am colliding with. This also means that the _coven.boundingBox is solely on the latest _coven member and interaction only occurs when I hit the new member as soon as it generates on to the screen.
Any ideas? Any help?
Note: This is also posted on the Spritebuilder website - I decided to post it here as well because answers can be a little slow on those forums.
The -(void) addCovenMember overwrites (creates a new instance) of _array every time it's called. Thus, when you try to iterate in -ccPhysicsCollisionBegin: you'll only ever see 1 coven.
Add a nil check around your array creation:
if(_array == nil) {
_array = [[NSMutableArray] alloc]init];
}
The for loop in the -addCovenMember method looks broken (at least not a c loop). Reaplace the , with ;:
for (i = 0; i < 15 i++){
Also, using for(_coven in _array) seems wrong, you already have a property self.coven (presumably) with a backing _coven ivar. Try changing it to for(Coven * c in self.array) and use the local c in the loop:
for (Coven * c in _array){
NSLog(#"%#",c.name)
if (CGRectIntersectsRect(_nellie.boundingBox, c.boundingBox){
NSLog(#"We're intersecting!");
}
}
To everyone out in the world struggling with their ccPhysicsCollisions, arrays may not be the answer - this was a simple fix that left me incapacitated for days.
Using the basic ccPhysicsCollisionsBegan that ships with spritebuilder, try this without arrays first:
Scene.m
-(BOOL)ccPhysicsCollisionBegin:(CCPhysicsCollisionPair *)pair nellie:(Nellie*)nellie coven:(Coven*)coven{
[_coven stopAction:coven.path];
}
I initially created the method with:
[_coven stopAction:_coven.path];
Yes, that (underscore) set me back three weeks. Be sure you refer to the object interacting through the physics delegate, and not the object itself, which in my case, was constantly being overwritten by the new ones being generated.
Check your underscores.
Solved! :D
Thanks to #thelaws for your help! I'll get better at Obj C... eventually.
Being a ReactiveCocoa newbie, I'm hoping for some advice with this:
I'm trying to create a dynamic form that contains multiple Field objects parsed from an XML file. Each Field can have muliple validation rules that will run against the Field's NSString *value param.
For the RAC part of the question-
inside each Field object, I want to bind BOOL completed to a signal that checks the Field's *value param against an array of rules. So far I've gotten here with my thinking:
#implementation Field
self = [super init];
if (self) {
RAC(self, completed) = [RACObserve(self, value) filter:^BOOL(NSString *fieldValue) {
NSLog(#"%s::self.completed = %d\n", sel_getName(_cmd), self.completed); // trying to watch the values here, with no luck
NSLog(#"%s::fieldValue = %#\n", sel_getName(_cmd), fieldValue); // same here, I'd like to be able to view the `*value` here but so far no luck
return [self validateCurrentValue]; // currently this method just checks value.length > 5
}];
}
return self;
The *value param has already been bound to my view model (successfully) and it gets updated each time a textfield changes.
What I'm looking for is a basic example or best-practice, the code above crashes when run so I know I'm missing something fundamental.
Thanks all
-filter: is simply passing values from RACObserve(self, value) through unchanged, but only if the block returns YES. So that means you're trying to set completed to values of whatever type value is. That's Probably Bad®.
But the good news is that you're really close!
Instead of filtering, you want to transform. You want to take every value and map it to something other thing. Namely whether that value passes validation. To do that, we use -map::
RAC(self, completed) = [RACObserve(self, value) map:^(NSString *fieldValue) {
return #([self validateCurrentValue]);
}];
I have an array full of NSObjects I created called "Questions".
One property of each Question is which level it belongs to.
If the user has chosen to play level 2, I want to get all the Questions that have a .level property of 2. Right now I am looping through all the questions to find the matches, but this is taking ~2 seconds on an iPad 3 / new iPad device. Is there a faster way of dealing with a situation like this?
int goThrough = 0;
do {
Question *currentQuestion = [allQs objectAtIndex:(goThrough)];
if (currentQuestion.level == levelChosen) {
[questions addObject:currentQuestion];
}
goThrough++;
} while (goThrough < [allQs count]);
Your help is greatly appreciated!
If you have to organize the questions by level on a regular basis, then why not keep all of the questions organized by level. Create a dictionary of arrays. Each key if the level and each array is the list of questions for that level. You do this once and it becomes trivial to get the questions for a level.
I dont have access to a mac at the moment but you can give a try to this:
[allQs enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id obj, NSUInteger index, BOOL *stop) {
Question *currentQuestion = [allQs objectAtIndex:index];
if (currentQuestion.level == levelChosen) {
[questions addObject:currentQuestion];
}
}
This will use all the cores of your device so it can be twice as fast
You could always use fast enumeration (which, unless you intend on mutating the objects is the fastest way to enumerate a collection). Something like this:
for (Question *thisQuestion in allQs) {
if (thisQuestion.level == levelChosen)
[questions addObject:thisQuestion];
}
}
Since you are not mutating the collection you are iterating through (allQs), this would work fine and be faster than using enumerateObjectsUsingBlock. If you need the index of the array you are iterating through (allQs), then use enumerateObjectsUsingBlock.
I would suggest using the NSArray method enumerateObjectsUsingBlock or one of it's variants. There are even variants that will loop through the array elements concurrently. You'd probably need to use a lock to add elements to your questions array however, since I doubt if NSMutableArray's addObject method is thread-safe.
You should probably test a non-concurrent version against a concurrent version with locking to see which is faster. Which approach is faster would depend on how many of the objects in the allQs array belong to the current level. If only a few belong, the code that asserts a lock won't fire very often, and the benefit of concurrency will outweigh the time penalty of asserting a lock. If most of the objects in the allQs array match the chosen level, code will end up spending a lot of time asserting locks, and the concurrent threads will still waiting for other threads to release a lock.
Modified code might look something like this:
single-threaded version:
[allQs enumerateObjectsUsingBlock:
^(Question *currentQuestion, NSUInteger index, BOOL *stop)
{
if (currentQuestion.level == levelChosen)
[questions addObject:currentQuestion];
}
];
Concurrent version:
[allQs enumerateObjectsWithOptions:
NSEnumerationConcurrent
usingBlock:
^(Question *currentQuestion, NSUInteger index, BOOL *stop)
{
if (currentQuestion.level == levelChosen)
#synchronized
{
[questions addObject:currentQuestion];
}
}
];
Actually, now that I think about it, you would likely get still faster performance by first doing a concurrent pass on the array using indexesOfObjectsWithOptions:passingTest. In that pass you'd build an NSIndexSet of all the objects that match the current level. Then in one pass you'd extract those elements into another array:
NSIndexSet *questionIndexes = [allQs indexesOfObjectsWithOptions: NSEnumerationConcurrent
usingBlock:
^(Question *currentQuestion, NSUInteger index, BOOL *stop)
{
return (currentQuestion.level == levelChosen)
}
];
questions = [allQs objectsAtIndexes: questionIndexes];
Another poster pointed out that you are better off breaking up your arrays of questions up by level in advance. If that works with your program flow it's better, since not filtering your array at all will always be faster than the most highly optimized filtering code.
There is a simple answer that seems to be missing. If you want to filter the objects of an array to only have certain ones left, -filteredArrayUsingPredicate: is what you would want. It can be done exceptionally simply.
NSPredicate *p = [NSPredicate predicateWithBlock:^(Question *aQuestion, NSDictionary *bindings){
return (aQuestion.level==2);
}];
NSArray *filteredArray = [originalArray filteredArrayUsingPredicate:p];
I have the following code, intended to select a random string from the array.
NSArray *popupMessages = [NSArray arrayWithObjects:
#"Shoulda' been bobbin' and weaving! Need anything from the shop?",
#"Don't forget you can use old boss's guns! Available in the shop!",
#"Hey Chaz, you Bojo! You need more POWER! Come by the shop for some better weapons!",
#"Aw… lame. Maybe I got something that can help you out here at my shop!",
nil];
int pmCount = popupMessages.count; // Breakpoint Here - pmCount = 971056545
int messageIndex = arc4random() % pmCount; // Breakpoint Here - same as above
I am using ARC with cocos2d. Any ideas as to why the array's count returns such a huge number? Thanks!
Your problem just looks like it's a debugger artifact. It could be optimization-related, for example. Sometimes compilers can generate code that confuses debuggers pretty seriously. Add a log statement to make sure the debugger isn't just telling you lies.
"count" is not a property, AFAIK.
The way I usually get the count for an array is:
[popupMessages count];
Try:
NSInteger pmCount = [popupMessages count];
I'm trying to initialize a dict variable but I don't understand why one way works, while the other does not.
In case 1 everything is alright and I can use dict later.
In case 2 it will be released very soon (It will becomes a zombie) and If I try to use it later (outside a block) the program crashes.
Here's some code from my class (c++ mixed with objective-c) written for ios.
Inside the block i tried to initialize variable dict in two different ways.
class Data
{
public:
NSMutableDictionary *dict;
void DoSomeStuff()
{
[NSSomeFrameworkTool doSomeStuffWithCompletionHandler:^(NSError *err) {
// case 1 - OK
dict = [[NSMutableDictionary alloc] initWithDictionary:[NSKeyedUnarchiver unarchiveObjectWithFile:#"dict.dat"]];
// case 2 - will crash later if i try to use dict
dict = [NSKeyedUnarchiver unarchiveObjectWithFile:#"dict.dat"]; }];
}
}
This class has class variable dict, which is initialized in the DoSomeStuff() method.
That method calls a method from the ios framework that uses block (as a callback) to inform me that some task is done.
I was wondering why case 1 and case 2 work different. Maybe it is forbidden to use references outside the block, that was initialized inside this block?
What's wrong with doing this the way shown in case2?
In first case you don't release your dict, and in second case it is autoreleased so you should retain it.
dict = [[NSKeyedUnarchiver unarchiveObjectWithFile:#"dict.dat"] retain];
I think you can use a block variable here.
__block NSMutableDictionary *dict;
Variables are immutable inside of the block. They are a constant copy, a snapshot of the variable at the time of "block creation" so it can not be modified inside the block. The block variable will move the variable to the 'Heap' from the 'Stack' allowing you to change it's state. I'm by no means an expert on blocks, being that they are relatively new to Objective c.But there are some good articles if you google around to learn from.
http://pragmaticstudio.com/blog/2010/7/28/ios4-blocks-1