Reputation: 8279
THE SCENARIO I need a method to fire off every second. I also need to be able to stop the firing of the method at any time. At the moment I am using an NSTimer
:
THE CODE
self.controlTimer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(updatePlayer) userInfo:nil repeats:YES];
THE ISSUE I am certain I can achieve this functionality using an NSTimer
and call invalidate
when I want it to stop, however I am concerned about the performance overhead of placing an NSTimer
in a UITableViewCell.
THE QUESTION Does anyone know of a more light-weight alternative to calling a method every second?
Upvotes: 0
Views: 1173
Reputation: 4678
I have used NSTimer
instances inside of UITableViewCell
and UICollectionViewCell
custom subclasses to do what you are doing, but I created a protocol PLMMonitor
to provide -startMonitoring
and -stopMonitoring
contracts on my cells to start/stop (see: invalidate
) any timing mechanisms.
The Protocol
(Obviously the protocol name prefix can be easily changed)
@protocol PLMMonitor <NSObject>
@required
- (void)startMonitoring;
- (void)stopMonitoring;
@end
Using Cell Visibility to Control the Timers
I could then utilize -[UITableViewDataSource tableView:cellForRowAtIndexPath:]
or -[UICollectionViewDelegate collectionView:willDisplayCell:forItemAtIndexPath:]
to call -startMonitoring
on the cell if it conforms to the protocol (allows for mixed cells in the UITableView/UICollectionView
):
- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath
{
if ([cell conformsToProtocol:@protocol(PLMMonitor)])
{
[(UICollectionViewCell<PLMMonitor> *)cell startMonitoring];
}
}
Then I utilized the -[UITableViewDelegate tableView:didEndDisplayingCell:forRowAtIndexPath:]
or -[UICollectionViewDelegate collectionView:didEndDisplayingCell:forItemAtIndexPath:]
to call -stopMonitoring
on the cell if it conformed to the protocol (again allowing for mixed cells in the UITableView/UICollectionView
):
- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath
{
if ([cell conformsToProtocol:@protocol(PLMMonitor)])
{
[(UICollectionViewCell<PLMMonitor> *)cell stopMonitoring];
}
}
Using View Controller Visibility to Control the Timers
You should also add code to -viewWillAppear
and -viewWillDisappear
to -startMonitoring
and -stopMonitoring
on the visible cells that conform to the protocol to ensure the timers get started/stopped appropriately when they are no longer visible:
- (void)viewWillAppear
{
for (UICollectionViewCell *aCell in [self.collectionView visibleCells])
{
if ([aCell conformsToProtocol:@protocol(PLMMonitor)])
{
[(UICollectionViewCell<PLMMonitor> *)aCell startMonitoring];
}
}
}
- (void)viewWillDisappear
{
for (UICollectionViewCell *aCell in [self.collectionView visibleCells])
{
if ([aCell conformsToProtocol:@protocol(PLMMonitor)])
{
[(UICollectionViewCell<PLMMonitor> *)aCell stopMonitoring];
}
}
}
Performance Implications / Energy Usage of NSTimers
One way you can reduce the impact NSTimer
instances have on battery life, etc is the make use of their tolerance
property which allows iOS to do some power savings magic with them while sacrificing a strict firing interval.
Alternative Timer/Trigger Mechanisms
You can utilize Grand Central Dispatch's (GCD) dispatch_after()
mechanism, but you will lose the ability to cancel the invocation.
Another option is to utilize -[NSObject
performSelector:withObject:afterDelay:]
methods and the
accompanying +[NSObject
cancelPreviousPerformRequestsWithTarget:selector:object:]
method to schedule a selector to be invoked and cancel an invocation
respectively.
Upvotes: 3
Reputation: 17
NSTimer is pretty lightweight. You just need to make sure you properly handle the Cell's timer when the cell is reused.
Upvotes: 2