Created
June 2, 2016 08:59
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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