Smoothness of NSProgressIndicator: incrementBy vs setDoubleValue - objective-c

I suspect that NSProgressIndicator runs smoother when its value is updated through incrementBy as compared to setDoubleValue.
Does anyone know if there's any real difference between the two methods of updating its value?

I don't have any knowledge of the internals, but I would be very surprised if incrementBy were not the equivalent of
[self setDoubleValue:[self doubleValue] + delta];
Of course, since it has access to internal state, it could presumably skip all the method calls.But the act of actually rendering pixels to the screen almost certainly overpowers any minor performance difference between the two methods.

I suspect that NSProgressIndicator runs smoother when its value is
updated through incrementBy as compared to setDoubleValue.
Makes no difference on macOS 10.14. When I set the maxValue to 1000 and then have a timer call a method every 100 ms, it looks identical, whether the method always calls [self.progress incrementBy:1] or [self.progress setDoubleValue:self.step++], with step being an instance variable of type NSUInteger that is initially zero.

Related

SKScene's update method gets a "currentTime" that is different from CFAbsoluteTimeGetCurrent()

The problem can be reproduced in an freshly created SpriteKit project.
After creating a new project, I add two lines to the update: method of the MyScene class:
-(void)update:(CFTimeInterval)currentTime {
NSLog(#" currentTime = %f", currentTime);
NSLog(#"CFAbsoluteTimeGetCurrent() = %f", CFAbsoluteTimeGetCurrent());
}
Running this shows that currentTime is several orders of magnitude smaller than the time returned by CFAbsoluteTimeGetCurrent(). I searched for possible reasons, but found none. The only possibility I could think of is that currentTime is the time frame since some other date than 00:00:00 01.01.2000, but I have no idea what other reference point it would be using, and found nothing when searching online either.
What can I do to get the current time, calculated by the same rules that are used to calculate currentTime?
Nothing, unless you're willing to disassemble and understand the Sprite Kit framework code.
Though the problem is most likely explained by Sprite Kit taking CFAbsoluteTimeGetCurrent at an earlier point in time, which is then passed into the update: method. Simply the overhead of sending that message to the scene means that the two times can't be the same. Or the currentTime may not be obtained by CFAbsoluteTimeGetCurrent but some other means.
What you can do is to get the currentTime of the very first time update: runs and store it in an ivar. You can then use this time as the reference time for the scene.
currentTime is a CFTimeInterval, so I expect it is the interval between some point in time and the current time - probably the interval since a scene was started. If you let your scene running for a minute, do the values get close to 60.0 (60 seconds)?
CFAbsoluteTimeGetCurrent is a CFAbsoluteTime, that is the number of seconds since Jan 1st 2001, so that should always be around 500 million or so.

Bundle declarations into one statement or not?

Given the following Objective-C example, is it simply a matter of style and ease of reading to keep separate statements or to bundle them into one? Are there any actual benefits of either? Is it a waste of memory to declare individual variables?
NSDictionary *theDict = [anObject methodToCreateDictionary];
NSArray *theValues = [theDict allValues];
NSString *theResult = [theArray componentsJoinedByString:#" "];
or
NSString *theResult = [[[anObject methodToCreateDictionary] theValues] componentsJoinedByString:#" "];
I take the following into consideration when I declare a separate variable:
If I might want to see its value in the debugger.
If I am accessing the variable more than once.
If the line is too long.
There is no practical difference between the two approaches, however.
Also, you haven't asked directly about this, but be aware, when you access objects using dot notation, for example:
myObject.myObjectProperty1.myObjectProperty1Property;
If you are going to access myObjectProperty1Property more than once, it can be advisable to assign it to a local named variable. If you don't, the look-up will be executed more than once.
Now I can't emphasise enough, for many if not most situations this time saving is so infinitesimal as to seriously call into question whether it is worth even spending the time doing extra typing for the assignation! So why am I raising this? Because having said that - stylistic "anality" apart (I just made up a new word) - if the section of code you are writing is running in a tight loop, it can be worth taking the extra care. An example would be when writing the code which populates the cells in a UICollectionView that contains a large number of cells. Additionally, if you are using Core Data and you are using the dot notation to refer to the properties of NSManagedObject properties, then there is far greater overhead with each and every look-up, in which case it is much more surely worth taking the time to assign any values referred to by "nested" dot notation calls to a local variable first.

Why is the row property of NSIndexPath a signed integer?

Why is the row property of NSIndexPath a signed integer?
Could it ever take on a "valid" negative value?
edit
I haven't thought about this until today when I set LLVM to check sign comparison. This made the compiler spew out warnings whenever there was indexPath.row <= [someArray count] or similar.
What happens if you use negative numbers?
It isn't wise to use negative values, if you do, you'll get crazy results
NSIndexPath* path = [NSIndexPath indexPathForRow:-2 inSection:0];
The above results in a section of 0, and a row of 4294967294 (which looks like underflow of an NSUInteger to me!) Be safe in the knowledge that this only occurs within the UIKit Additions category, and not within NSIndexPath itself. Looking at the concept behind NSIndexPath, it really doesn't make sense to hold negative values. So why?
(Possible) Reason for why it is so
The core object NSIndexPath from OS X uses NSUIntegers for its indices, but the UIKit Addition uses NSInteger. The category only builds on top of the core object, but the use of NSInteger over NSUInteger doesn't provide any extra capabilities.
Why it works this way, I have no idea. My guess (and I stipulate guess), is it was a naive API slipup when first launching iOS. When UITableView was released in iOS 2, it used NSIntegers for a variety of things (such as numberOfSections). Think about it: This conceptually doesn't make sense, you can't have a negative number of sections. Now even in iOS 6, it still uses NSInteger, so not to break previous application compatibility with table views.
Alongside UITableView, we have the additions to NSIndexPath, which are used in conjunction with the table view for accessing it's rows and such. Because they have to work together, they need compatible types (in this case NSInteger).
To change the type to NSUInteger across the board would break a lot of things, and for safe API design, everything would need to be renamed so that the NSInteger and NSUInteger counterparts could work safely side by side. Apple probably don't want this hassle (and neither do the developers!), and as such they have kept it to NSInteger.
One possible reason is that unsigned types underflow very easily. As an example, I had an NSUInteger variable for stroke width in my code. I needed to create an “envelope” around a point painted with this stroke, hence this code:
NSUInteger width = 3;
CGRect envelope = CGRectInset(CGRectZero, -width, -width);
NSLog(#"%#", NSStringFromCGRect(envelope));
With an unsigned type this outputs {{inf, inf}, {0, 0}}, with a signed integer you get {{-3, -3}, {6, 6}}. The reason is that the unary minus before the width variable creates an underflow. This might be obvious to somebody, but will surprise a lot of programmers:
NSUInteger a = -1;
NSUInteger b = 1;
NSLog(#"a: %u, b: %u", a, -b); // a: 4294967295, b: 4294967295
So even in situations where it doesn’t make sense to use a negative value (stroke width can’t be negative) it makes sense to use the value in a negative context, causing an underflow. Switching to a signed type leads to less surprises, while still keeping the range reasonably high. Sounds like a nice compromise.
I think UIKit Additions on NSIndexPath use NSInteger type intentionally. If for some reason negative row would be passed as parameter to any method (I see none at the moment, though...), autocast to NSIntegerMax + parameter value would not happen and any possible object would not look for a ridiculously large parameter that does not exist. Still, there are other ways to prevent this, so it might be just a matter of taste.
I, for example, would not take NSUInteger as parameters in NSIndexPath class, but rather NSInteger and checked for a sign and wouldn't create NSIndexPath at all, if any parameter was negative.

Pauses/computation between function execution?

I'm working on a game and the speed of the game is 60fps, giving me about 16ms to execute what I need to execute. What I'm finding tho is that certain code paths are taking a while to execute. At first I thought this must be an issue with memory allocation/deallocation. It could possibly be, but I have implemented object pooling for the more heavyweight objects, and this seems to have had little effect.
I have stripped out the offendingly large objects, and replaced them with a much simpler object, yet it still takes about 3ms to make this object. The simple object consists of two custom objects, 2 arrays, and 2 dictionaries. The large object is of an arbitrary complexity. Making the more complex larger objects seem to take linearly more time, taking up to 20-30ms. In many instances in my game I can make a much more complicated object in a fraction of the time. But somehow these specific locations in code cause a serious slowdown.
I am using the following code to profile my functions (from SO originally):
NSDate* methodStart = [NSDate date];
// My code here.
NSDate *methodFinish = [NSDate date];
NSTimeInterval executionTime = [methodFinish timeIntervalSinceDate:methodStart];
NSLog(#"[CH] executionTime to create %# = %f", myObject , executionTime );
As best as I can confirm if I do something like the above in an inner and outer function, it looks like there can be a serious slowdown:
Inner function call completed: 4ms
...no code between inner and outer code except timer...
Outer function call completed: 8ms
My current theory is there is 'in-between' computation that occurs between the functions that is causing the serious slowdowns. As I'm new to Obj-C/C I'm not entirely sure what this slowdown could be. It could be threading, it could be because of memory alloc/autorelease/dealloc, or some other kind of unrelated issue. I have attempted to get more info from Instruments with little benefit.
So my question is: Does this sound logical? Is it possible that some kind of computation could be happening between my functions that would take up 2-4ms of cpu on an ipad2? And if my theory isn't possible, any ideas what it could be?
The best way to track down this type of problem is to use Instruments to profile the execution of the code. Often, you can run this against the code on the Mac under the simulator to see where the time is going, however, in this case you may need to profile it on your iPad to be certain.
Remove your NSDate-based profiling code, as NSDate can be a pretty heavy-weight object, especially when you put the results into the autorelease pool, which you are doing here.
If you feel you must do NSDate-based profiling, you should only need to create a single NSDate object and the use:
[methodStart timeIntervalSinceNow]
to calculate the time delta without having to create a separate object and perform a date subtraction operation.

NSString alloc or not!

I am running this code from the scrollViewDidScroll method (so it runs when you scroll!):
NSString *yearCount = [[NSString alloc] initWithFormat:#"%0.1f", theScroller.contentOffset.y];
years.text = yearCount;
[yearCount release];
which works fine, however it hits performance on the scroll (causing it to judder as it slows down)
My question is, do I need to keep using alloc and release or is there a way to get some numbers using initWithFormat onto some text without it?
years.text = [NSString stringWithFormat:#"%0.1f", theScroller.contentOffset.y];
will avoid the need to explicitly release the string, since it is autoreleased.
However, if you are trying to avoid slowdown, consider updating the field less frequently. For example, each time scrollViewDidScroll is called, set a timer to update the field in say 0.1 seconds from now, but not if the timer is already running from a previous call. This reduces the number of calls while keeping the UI updated.
Here is an example how you could do it. Declare an NSTimer in the interface declaration of your scroll view delegate:
NSTimer *timer;
And methods:
- (void)updateYear:(NSTimer*)theTimer
{
timer=nil;
UIScrollView *theScroller=[theTimer userInfo];
years.text=[NSString stringWithFormat:#"%0.1f", theScroller.contentOffset.y];
}
- (void)scrollViewDidScroll:(UIScrollView *)theScroller
{
if (!timer) {
timer=[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(updateYear:) userInfo:theScroller repeats:NO];
}
}
Obviously, you don't have to use 0.1 as the time interval, you can try making it faster or slower and see what works best.
Note that this example is complete as far as memory management is concerned, You should not try to retain or release the timer object yourself. Its lifetime is handled internally by the runloop.
Consider using scrollViewDidEndDecelerating method to avoid the frequent updates. Alloc-init is not responsible for the performance decrease, setting the text frequently is. Unless you really need to change it continuously (in which case solution with a timer might be an option), you should be looking for a different hook method.
You've got poor performance absolutely not because of the string formatting or alloc-release. You can use some shorter form like:
years.text = [NSString stringWithFormat:#"%0.1f", theScroller.contentOffset.y];
which is equivalent to
years.text = [[[NSString alloc] initWithFormat:#"%0.1f", theScroller.contentOffset.y] autorelease];
However this won't help to improve your performance at all.