Skip to content

Instantly share code, notes, and snippets.

@akisute
Created June 2, 2016 08:59
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save akisute/84f36cc11fdbe3672461939d1e43cf44 to your computer and use it in GitHub Desktop.
Save akisute/84f36cc11fdbe3672461939d1e43cf44 to your computer and use it in GitHub Desktop.
A practice to improve performance of UITableView by "NOT" reusing cells as far as possible
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/**
UITableViewやUICollectionPoolなどで使用するIdentifierをプールしてユニークに採番するためのユーティリティです。
具体的には以下の様な挙動を示します。
@li identifier発番回数がnumberOfUniqueIdentifiers未満の時、uniqueKeyに対して常にユニークなidentifierを発番します。
@li もしidentifier発番回数がnumberOfUniqueIdentifiersを上回ったら、次の発番時に最も古いidentifierが使いまわされます。
これを利用すると、例えばUITableViewに100セルまでは各View ModelのID毎にユニークなセルを発番してView Model再セットによるパフォーマンス劣化を避け、
101セル目からはセルの使い回しを行うことで無尽蔵にメモリを無駄使いすることを避ける事が可能です。
*/
@interface AKIdentifierPool : NSObject
@property (nonatomic, copy, readonly) NSString *identifierPrefix;
@property (nonatomic, readonly) NSInteger numberOfUniqueIdentifiers;
- (instancetype)initWithIdentifierPrefix:(NSString *)identifierPrefix numberOfUniqueIdentifiers:(NSInteger)numberOfUniqueIdentifiers;
- (NSString *)identifierForUniqueKey:(NSString *)uniqueKey;
@end
NS_ASSUME_NONNULL_END
#import "AKIdentifierPool.h"
@interface AKIdentifierPool ()
@property (nonatomic, copy) NSString *identifierPrefix;
@property (nonatomic) NSInteger numberOfUniqueIdentifiers;
@property (nonatomic) NSArray<NSString *> *identifiers;
@property (nonatomic) NSMutableDictionary<NSString *, NSString *> *uniqueKeyDictionary;
@property (nonatomic) NSInteger nextIndex;
@end
@implementation AKIdentifierPool
- (instancetype)initWithIdentifierPrefix:(NSString *)identifierPrefix numberOfUniqueIdentifiers:(NSInteger)numberOfUniqueIdentifiers {
self = [super init];
if (self) {
self.identifierPrefix = identifierPrefix;
self.numberOfUniqueIdentifiers = numberOfUniqueIdentifiers;
NSMutableArray *buffer = [NSMutableArray arrayWithCapacity:numberOfUniqueIdentifiers];
for (NSInteger i=0; i<numberOfUniqueIdentifiers; i++) {
NSString *identifier = [NSString stringWithFormat:@"%@_%ld", identifierPrefix, (long)i];
[buffer addObject:identifier];
}
self.identifiers = [NSArray arrayWithArray:buffer];
self.uniqueKeyDictionary = [NSMutableDictionary dictionary];
self.nextIndex = 0;
}
return self;
}
- (NSString *)identifierForUniqueKey:(NSString *)uniqueKey {
/// XXX: This method is not thread safe.
/// This should not be a problem because this method is meant to be used for managing UI unique identifiers,
/// but if need be make a lock here to protect uniqueKeyDictionary and nextIndex.
NSString *identifier = self.uniqueKeyDictionary[uniqueKey];
if (identifier) {
return identifier;
} else {
identifier = self.identifiers[self.nextIndex];
self.uniqueKeyDictionary[uniqueKey] = identifier;
self.nextIndex = (self.nextIndex >= self.numberOfUniqueIdentifiers-1) ? 0 : self.nextIndex + 1;
return identifier;
}
}
@end
#pragma mark - UITableViewCell
- (void)prepareForReuse {
[super prepareForReuse];
// ここでは何もしない。
// このセルはパフォーマンス最適化のためにIdentifierPoolを使って可能な限り同じViewModelに対して同じCellを返すように使用される。
// だがprepareForReuseはdequeue時に(全く不要でも)見境なくUITableViewによって呼ばれてしまって無駄。
// そこで通常のprepareForReuseは無視して、必要なときだけprepareForRealReuseを呼び出す。
}
- (void)prepareForRealReuse {
[self.someView prepareForReuse];
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment