I have the following code:
CharacterMoving = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:#selector(CharacterMoving) userInfo:nil repeats:YES];
This basically starts the timer, and runs the CharacterMoving method every 0.01 seconds. That all works nice and smooth.
Then, I have this code:
-(void)CharacterMoving{
Character.center = CGPointMake(Character.center.x - 1, Character.center.y);
}
This makes the character image move to the left, and this works smoothly. So when the image reaches the edge of the screen, I want the image to stop moving, and run a different method. I tried doing this with the following code:
-(void)CharacterMoving{
Character.center = CGPointMake(Character.center.x - 1, Character.center.y);
if (Character.center.x == 0) {
[Character invalidate];
[self DifferentVoid];
}
}
-(void)DifferentMethod{
//do something here
}
Unfortunately, the DifferentMethod doesn't run, and the image doesn't stop moving when it hits the edge of the screen. Thanks in advance for helping me out!
EDIT:
I've found this solution. Changing the '==' into '<' solves the problem.
You've got this:
[Character invalidate];
[self DifferentVoid];
so it's no surprise that a method called -DifferentMethod doesn't run -- you're not calling it. Also, you're sending -invalidate to Character, but the timer that you started is (judging by the preceding code) called CharacterMoving.
Aside: I see that you also have a method called -CharacterMoving. It'd be a good idea to avoid using the same names for your variables and your methods. Also, you'll help yourself and avoid confusing others if you follow the normal Objective-C naming conventions.
Related
I've noticed a significant memory usage having the following function executed by a timer:
_timer = [NSTimer scheduledTimerWithTimeInterval:0.01
target:self
selector:#selector(test)
userInfo:nil
repeats:YES];
- (void)test {
NSRunningApplication *app = [NSWorkspace sharedWorkspace].frontmostApplication;
app.processIdentifier;
}
The code basically does nothing.
Accessing almost any property (bundleIdentifier, bundleURL, description...) of a NSRunningApplication instance results into memory usage increasing at ~1MB/sec (considering the specified time interval). And the worst thing is that it never stops. I haven't tried to put it to the limit yet though...
I've tried to profile it using Instruments (Leaks template), but it finds no memory leaks.
Any clue?
Edit #1:
I've performed a simple experiment, creating a console application with a single swift file:
import Cocoa
while true {
guard let app = NSRunningApplication(processIdentifier: 315) else {
break
}
}
Put any pid you have running.
It takes a gig in a few seconds...
Edit #2:
My latest finding is that Process Type directly affects the behavior.
Consider:
TransformProcessType(&psn, UInt32(processType))
If processType = kProcessTransformToBackgroundApplication or kProcessTransformToUIElementApplication, I face the issue.
If process type = kProcessTransformToForegroundApplication (default value), everything works perfectly fine.
I'm a little stumped by this weird occurrence:
I have a UIButton, which once tapped either sets a loop for an audio player, or resets it to 0 (no loop). Here is the method -
-(void)changeLoopValueForPlay:(int)tag toValue:(bool)value{
AVAudioPlayer *av = [self.playerArray objectAtIndex:tag];
if(value){
[av setNumberOfLoops:100];
[av prepareToPlay];
}
else{
[av setNumberOfLoops:0];
}
}
Now for some reason, the loop will only take effect after the player plays through the audio one time, meaning that the looping value doesn't take affect immediately, but the "numberOfLoops" value of the player is in fact set to 100 when I check its value before playing. I'm assuming this has something to do with the initialization or loading of the player, but I don't re-initialize it between those two plays (one without loop, the other with). Any idea why this is happening? If you want to see any other code please let me know.
This fixed the problem, however I feel as if this is a work-around instead of a direct solution. What I did is just create a new AVAudioPlayer with the numberOfLoops value set to whatever it is I wanted and replace that player with the existing player, instead of changing the value of the already existing player.
I workaround the issue by abandoning numberOfLoops altogether and doing my own logic instead.
First, set the delegate of the AVAudioPlayer:
self.audioPlayer.delegate = self;
Next, implement -audioPlayerDidFinishPlaying:successfully: of the delegate:
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer*)player successfully:(BOOL)flag
{
if(flag && <#(bool)i_want_to_repeat_playing#>)
{
[self.audioPlayer play];
}
}
Just replace <#(bool)i_want_to_repeat_playing#> with your desired logic, e.g., check if a counter has reached a certain threshold.
I need to do a possibly long series of calls that must occur on the main thread (because otherwise UIKit will balk). By "long" I mean 10,000 operations lasting .1 second each on an iPad 3.
Obviously, It's probably not the best idea to just loop through all of them at once.
I don't know how to execute all these on the main thread while leaving enough breathing room to keep UIKit responsive and the watchdog asleep (ie. not get terminated for hogging the run loop).
Does anybody have an idea? I will be targeting iOS 5.
Specifically what I'm trying to do is cache UITextPositions, because a UITextView is apparently taking a non-cached, iterative approach at getting UITextPositions, which means it is very, very slow at doing positionFromPosition:textview.beginningOfDocument offset:600011, but much faster at getting positionFromPosition:aPositionAt600000 offset:11. In fact, in my test case, the former takes over 100 seconds (on the main thread!), while the latter is virtually instantaneous.
Why do you want to do it on the main thread? The typical answer is to do these operations on a background thread, and send UI updates back to the main thread. For example, you could use Grand Central Dispatch:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// do my time consuming task and everytime it wants to update the UI,
// it should dispatch that back to the main queue, e.g.
for (NSInteger i = 0; i < 10000; i++)
{
// do my background work
// now update the UI
dispatch_async(dispatch_get_main_queue(), ^{
// update the UI accordingly
});
}
});
Update:
It sounds like you have to do this in the foreground, so perhaps using a NSTimer might be better. I'm not a big NSTimer guy, but it might look something like the following.
First, make sure you have a class instance variable for it:
NSTimer *_timer;
Next, you can initialize it with:
- (void)startTimer
{
_timer = [NSTimer timerWithTimeInterval:0.0 target:self selector:#selector(timerCallback:) userInfo:nil repeats:YES];
NSRunLoop *runloop = [NSRunLoop currentRunLoop];
[runloop addTimer:_timer forMode:NSDefaultRunLoopMode];
}
This will then invoke the timerCallback, perhaps processing a single UITextPosition on each invocation:
- (void)timerCallback:(NSTimer*)theTimer
{
BOOL moreTextPositionsToCalculate = ...;
if (moreTextPositionsToCalculate)
{
// calculate the next UITextPosition
}
else
{
[self stopTimer];
}
}
and when you're done, you could stop your timer like so:
- (void)stopTimer
{
[_timer invalidate];
_timer = nil;
}
I have the following code:
CCProgressTimer *aTimer;
-(void) generateDungeon {
srand (time(NULL));
[self initDungeonArray];
int numRooms = RNDM(10,100);
for (int a=0; a< numRooms; a++) {
[self makeRandomRoom];
aTimer.percentage += 100/numRooms;
}
[self connectTheRooms];
[self placeStairs];
}
The problem is that during the loop the timer does not get updated on screen, then suddenly (after the loop finishes I think) fills up to nearly full. I don't understand why this is happening. I thought that when you change the percentage, the image would update.
Can anyone help me understand what I should be understanding?
Thanks.
Your image(in this case, you progress timer), updates on the screen in draw() and visit() methods. They are called every tick. Here you change it's value in one tick. So during previous drawing the timer value was 0 percent, and on the next drawing it's value will be 100
you make it in the single thread. so your timer will not be update in this case. to create updatable timer, you can try to move your long working code to the separate thread. then you will be able to update timer.
Bear with me, this one is hard to explain. I hope some hero out there knows what’s going on here. Some history needed;
One of my cocoa objects, “Ball” represents a small graphic. It only makes sense within a view. In some of the Ball’s methods, it asks the view to redraw. Most importantly, it asks the view to redraw whenever the Ball’s position parameter is set. This is achieved in the setter.
Here’s the mouthful, as suggested:
In View.m
- (void)mouseUp:(NSEvent *)theEvent {
if (![runnerPath isEmpty]) {
[walkPath removeAllPoints];
[walkPath appendBezierPath:runnerPath];
[runnerPath removeAllPoints];
[[self held] setStep:0];
[[self held] setPath:walkPath];
[NSTimer scheduledTimerWithTimeInterval:.01 target:[self held] selector:#selector(pace) userInfo:nil repeats:YES];
}
}
In Ball.m
- (void)pace {
CGFloat juice = 10;
BOOL loop = YES;
while (loop) {
if ([self step] == [[self path] elementCount]) {
if ([[self timer] isValid]) {
[[self timer] invalidate];
}
[[self path] removeAllPoints];
// #throw([NSException exceptionWithName:#"test" reason:#"reason" userInfo:nil]);
}
if (loop) {
CGFloat distance;
NSPoint stepPoint;
if ([[self path] elementCount] > 0) {
NSPoint returnPoints[2];
[[self path] elementAtIndex:[self step] associatedPoints:returnPoints];
stepPoint = returnPoints[0];
distance = pixelDistance([self position], stepPoint);
}
if (distance <= juice) {
[self setPosition:stepPoint];
if (distance < juice) {
juice -= distance;
loop = YES;
[self setStep:[self step]+1];
} else {
loop = NO;
}
} else {
NSPoint cutPoint = moveAlongBetween([self position], stepPoint, juice);
[self setPosition:cutPoint];
loop = NO;
}
}
}
}
could you also tell how you handle exceptions? since normally an unrecognized selector will end your program. Maybe you need an exception rather than an unrecognized selector. Try:
#throw([NSException exceptionWithName:#"test" reason:#"reason" userInfo:nil]);
If this would fix it as well, you're doing something after this code which freezes the app.
edit: thanks for the code update.
There's some weird stuff going on here! I'm not going to rewrite the whole thing, so here's some pointers:
first of all: you're looping inside some routine that is called from a timer loop. Is that intended? There is no way to pause execution within that while() loop, so it will happen in a blink anyway. You would need to keep some state information in the class. E.g. adding a loop counter every time pace is called.
second: if you start a timer, it will call your selector with the timer as an argument. So define the function as -(void)pace:(NSTimer*)timer, and use timer, not [self timer] (the latter will not be your timer anyway, if you don't assign it!)
third: you're firing 100 times a second. That is a lot, and presumably higher than the refresh rate of any device you're writing this for. I think 20/sec is enough.
fourth: to be sure, if you change it to -(void)pace:(NSTimer*)timer, don't forget to use #selector(pace:) (i.e. don't forget the :)
fix those things, and if it's still broken, update your question again and put in comment so we will know. Good luck!
Try calling
for (NSView *each in [self views]) {
...
}
I'm assuming that views is an array, so fast enumeration applies to it directly and there is no need to call allObjects.
A couple of other points.
Have you set a Global breakpoint of objc_exception_throw? This will apply to all Xcode projects and is so useful I'm surprised it isn't set by default.
You say you looked at the Console for errors. I take it, then, that you didn't set a breakpoint on the code and step into it to see exactly what is happening when your execution reaches that point? Have a look at the Xcode Debugging Guide