NSString alloc or not! - objective-c

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.

Related

ARC deallocate my NSmutablearray before NSTableview reloaddata

My NSMutableArray lOfSegments, declared as IVAR, get populated correctly. During the debug it shows 4 object in the array.
for (int x=0; [arrayOfSegmentsTcIn count]>x; x++) {
NSDictionary *segmentDic=[[NSDictionary alloc] initWithObjectsAndKeys: [arrayOfSegmentsNumbers objectAtIndex:x],#"segment",[arrayOfSegmentsTcIn objectAtIndex:x],#"tc_in",[arrayOfSegmentsTcOut objectAtIndex:x],#"tc_out", nil];
[lOfSegments addObject:segmentDic];
[myDMXML.segments addObject:segmentDic];
}
[self.xmlTCLable setStringValue:[myDMXML startTimeCode]];
[self.xmlDurationLable setStringValue:[myDMXML duration]];
[self xmlValidationCheck];
NSLog(#"arrayController:%#",[lOfSegments valueForKey:#"segment"]);
[self.tableViewOutlet reloadData];
NSLog list the array correctly but when reloadData is executed the code jumps to
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
return [lOfSegments count];
}
The array is null.
The Object is initialised in viewDidLoad as
lOfSegments = [[NSMutableArray alloc]init];
Please Help!
First, I recommend making your code more clear here by using self.lOfSegments rather than directly accessing an ivar. (The fact that the ivar lacks a leading _ is very suspicious as well, and raises the question of whether this is even the variable you think it is.)
On the assumption that this is the variable you think it is, and that you have overridden the standard behavior to make the ivar match the property or created explicit ivars (neither of which you should do), there are several common causes for this kind of problem:
The most likely cause is that you called your initialization code prior to viewDidLoad and then viewDidLoad blew away the array. Many things can run prior to viewDidLoad, and viewDidLoad can run more than once (at least this used to be true; I'd have to study whether the view-loading changes in iOS 6 made it guaranteed to be run once.)
You have some other way reset lOfSegments between the time your initialization code ran and the time reloadData ran. If you would reliably use self. then you could override setLOfSegments: so you could log this. Or you could mark this property readonly so you could prevent it. Thats one of many reasons that you should use properties, not ivars.
The setting code failed to run before reloadData. Ensure that the log statement actually printed prior to getting to reloadData and is on the same queue (the queue identifier will be part of the NSLog output in brackets). I don't think this is likely given your description, but it is a common problem.
There are two instances of this object. Make sure that the object that ran your initialization code is the same object that ran reloadData. This is a more common mistake then you may think. Log self and make sure the memory address is the same in both cases.
looks like you have variable with same name lOfSegments in method viewDidLoad. So in viewDidLoad you use stack variable, in numberOfRowsInTableView - instance variable.
Or maybe tableViewOutlete.dataSource pointing on different viewController

Why does this ternary operation causes memory growth

The following line causes memory growth (no releases, only one malloc line in instruments) when testing with mark generation feature of allocations instrument
- (instancetype)initWithTitle:(NSString *)title andImageNamed:(NSString *)imageName andButtonProperties:(NOZSKButtonNodeProperties *)buttonProperties
{
...
textNode.text = buttonProperties.buttonTitleIsUppercase ?
[title uppercaseStringWithLocale:[NSLocale currentLocale] ] : title;
...
}
Here is the code that calls it
NOZSKButtonNodeProperties *props = [self getThreeButtonProps];
...
props.buttonTitleIsUppercase = YES;
...
// this initializer is calling the above initializer by passing nil for imageName arg
NOZSKButtonNode *btn = [[NOZSKButtonNode alloc] initWithTitle:#"Play Again" andButtonProperties:props];
-uppercaseStringWithLocale: makes an upper-case copy of the title string. This requires allocation. And although you didn't show this, I assume textNode both retains its text property and has a wider scope than the -initWithTitle:andImageNamed:andButtonProperties: method and therefore continues to live afterwards.
Without more complete code it is not possible to be sure, but here is a guess in case it helps.
It sounds like you might be chasing a ghost. Are you seeing increasing memory due to this code across different iterations of your run loop?
Explanation: Even with ARC some memory is placed in the "autorelease pool" rather than being immediately deallocated when no longer required. This is an unfortunate legacy of MRC. ARC is able to avoid some uses of the autorelease pool and deallocate memory quicker, but not all uses.
The autorelease pool is typically emptied once per iteration of your main run loop. If you allocate and release a lot of memory in response to a single event, say in a long loop, it can be worth while wrapping the offending code in an #autorelease { ... } block which creates, and empties on exit, a local autorelease pool. This helps keep the peak memory usage down - it doesn't deallocate any more memory overall, it just does it cleanup sooner.
When you altered your code and saw an apparent improvement you may just have reorganised your code in a way more amenable to ARC's optimisations which reduce use of the autorelease pool, and so memory is deallocated sooner.
You only need to be concerned if (a) memory is increasing across multiple events or (b) you are hitting a too high peak memory use. Under ARC (a) should be rare, while (b) requires locating the source and wrapping it in an #autorelease { ... } block.
HTH
textNode.text = buttonProperties.buttonTitleIsUppercase ?
[title uppercaseStringWithLocale:[NSLocale currentLocale] ] : title;
When you write [title uppercaseStringWithLocale:], this method has to create a new instance in order to change the given string to upper case. This is going to increase the memory usage of your program because this new string has to be allocated.
Also, it would have helped to know whether or not you are using ARC, because I don't think this should be an issue with ARC.

Override setter in Objective-c. Why check is equal parameter

Often find custom setter sintaxis like this:
- (void)setParentCatalog:(Catalog *)parentCatalog {
if (_parentCatalog != parentCatalog) { //???
_parentCatalog = parentCatalog;
[self refresh];
}
}
Why i should check?
if (_parentCatalog != parentCatalog)
This checks if both _parentCatalog and parentCatalog are pointing to the same memory location.
If both are same object then no need to set the objectValue.
The reason for checking if the two are equal is to avoid executing code when it's not necessary. If the method is called very often, this could have a performance benefit. Under non-ARC, your code might look more like this:
- (void)setParentCatalog:(Catalog *)parentCatalog {
if (_parentCatalog != parentCatalog) {
[_parentCatalog release];
[parentCatalog retain];
_parentCatalog = parentCatalog;
[self refresh];
}
}
So, by checking that what you received is actually a new value, you avoid those retain and release calls happening (which are still there with ARC). You've also got [self refresh] in there, which probably doesn't need to happen unless the value has actually changed.
The idea here is that if the parameter passed in to the setter is the same object already stored in the property, then there is no need to call [self refresh] again.
A refresh method often reads in data, works on it and then re-displays it in the app's views. No need to do all this work again if the data in the property haven't really changed.
It's a decision that is use case dependant. The idea behind this guard is to prevent doing unnecessary work.
If you imagine that your [self refresh] kicked off a very expensive operation then you would be reluctant to do it every time. So if you only do it when the object actually changes you save yourself some work.
Of course this may well be the behaviour you are looking for in which case you would need to stick the [self refresh] call outside of the guard.
Like all code examples you find it's worth weighing up the trade offs of the implementation and then you can better decide what you need in your case.

Optimize tests in Cocoa

In this simple test, after being sure that the index is valid, does it worth to assign a variable instead of calling two times objectAtIndex: method ?
NSString *s = [myArray objectAtIndex:2];
if (s) {
Test *t = [Test initFromString:s];
}
instead of
if ([myArray objectAtIndex:2]) {
Test *t = [Test initFromString:[myArray objectAtIndex:2]];
}
From the performance point of view it’s not worth it, unless the code lies on a really hot path (and you would know that). Sending a message is practically free and looking up an object on a given index is also too fast to care in most situations.
The change makes the code more readable, though: First, you can name the thing that you pull from the container (like testName). Second, when reading the two repeated calls to objectAtIndex you have to make sure that it’s really the same code. After you introduce the separate variable it’s obvious, there’s less cognitive load.

NSTimer in a for loop

I want to call the getData method after waiting 2 seconds each time this loop...loops. I've written out the NSTimer a number of times inside and outside the loop but can't get the correct usage for it to do what I want.
for (TwitterPerson *person in [tempDict allValues]) {
[self getDataFromTwitterUserAPIandArchiveFor:person.handle];
}
Could you set it up differently and call the [getData... method every time the timer fires? You could keep your dictionary keys in an array and pop the next key each time the timer fired.
I think the NSTimer is designed to not block the main thread whereas the for-loop definitely blocks the thread.
As in this related question Using NSTimer in a Loop, you might consider NSRunLoop.