Switching out from unreliable disk caching to TMCache (damn threading issues).

This commit is contained in:
Samuel Clay 2014-02-24 18:56:51 -08:00
parent 2928f63868
commit 96b5e15718
11 changed files with 62 additions and 109 deletions

View file

@ -130,7 +130,7 @@
#pragma mark - Stories
- (void)refreshStories {
[[TMCache sharedCache] removeAllObjects:^(TMCache *cache) {
[appDelegate.cachedStoryImages removeAllObjects:^(TMCache *cache) {
[appDelegate loadRiverFeedDetailView:self.storiesModule withFolder:@"everything"];
}];
}

View file

@ -142,7 +142,7 @@ static UIFont *indicatorFont = nil;
r.size.height, r.size.height);
UIImageView *storyImageView = [[UIImageView alloc] initWithFrame:imageFrame];
UIImage *cachedImage = (UIImage *)[[TMCache sharedCache] objectForKey:cell.storyImageUrl];
UIImage *cachedImage = (UIImage *)[appDelegate.cachedStoryImages objectForKey:cell.storyImageUrl];
if (cachedImage && ![cachedImage isKindOfClass:[NSNull class]]) {
// NSLog(@"Found cached image: %@", cell.storyTitle);
storyImageView.image = cachedImage;

View file

@ -161,7 +161,7 @@
if (storiesCollection.isSocialView) {
spacerBarButton.width = -6;
NSString *feedIdStr = [NSString stringWithFormat:@"%@", [storiesCollection.activeFeed objectForKey:@"id"]];
UIImage *titleImage = [Utilities getImage:feedIdStr isSocial:YES];
UIImage *titleImage = [appDelegate getFavicon:feedIdStr isSocial:YES];
titleImage = [Utilities roundCorneredImage:titleImage radius:6];
[((UIButton *)titleImageBarButton.customView).imageView removeFromSuperview];
titleImageBarButton = [UIBarButtonItem barItemWithImage:titleImage
@ -360,15 +360,15 @@
UIImage *image = requestOperation.responseImage;
if (!image || image.size.height < 50 || image.size.width < 50) {
[[TMCache sharedCache] setObject:[NSNull null]
forKey:storyImageUrl];
[appDelegate.cachedStoryImages setObject:[NSNull null]
forKey:storyImageUrl];
continue;
}
CGSize maxImageSize = CGSizeMake(300, 300);
image = [image imageByScalingAndCroppingForSize:maxImageSize];
[[TMCache sharedCache] setObject:image
forKey:storyImageUrl];
[appDelegate.cachedStoryImages setObject:image
forKey:storyImageUrl];
if (self.isDashboardModule) {
[appDelegate.dashboardViewController.storiesModule
showStoryImage:storyImageUrl];
@ -1064,7 +1064,7 @@
cell.feedColorBarTopBorder = UIColorFromRGB(colorBorder);
// favicon
cell.siteFavicon = [Utilities getImage:feedIdStr];
cell.siteFavicon = [appDelegate getFavicon:feedIdStr];
// undread indicator
@ -1963,10 +1963,9 @@ didEndSwipingSwipingWithState:(MCSwipeTableViewCellState)state
if ((NSNull *)favicon != [NSNull null] && [favicon length] > 0) {
NSData *imageData = [NSData dataWithBase64EncodedString:favicon];
UIImage *faviconImage = [UIImage imageWithData:imageData];
[Utilities saveImage:faviconImage feedId:feed_id];
[appDelegate saveFavicon:faviconImage feedId:feed_id];
}
}
[Utilities saveimagesToDisk];
dispatch_async(dispatch_get_main_queue(), ^{
[self.storyTitlesTable reloadData];

View file

@ -44,6 +44,7 @@
@class IASKAppSettingsViewController;
@class UnreadCounts;
@class StoriesCollection;
@class TMCache;
@interface NewsBlurAppDelegate : BaseViewController
<UIApplicationDelegate, UIAlertViewDelegate, UINavigationControllerDelegate, OSKActivityCustomizations> {
@ -134,6 +135,9 @@
NSDictionary *categoryFeeds;
UIImageView *splashView;
NSMutableDictionary *activeCachedImages;
TMCache *cachedFavicons;
TMCache *cachedStoryImages;
}
@property (nonatomic) IBOutlet UIWindow *window;
@ -170,6 +174,9 @@
@property (nonatomic) IBOutlet FirstTimeUserAddNewsBlurViewController *firstTimeUserAddNewsBlurViewController;
@property (nonatomic, readwrite) StoriesCollection *storiesCollection;
@property (nonatomic, readwrite) TMCache *cachedFavicons;
@property (nonatomic, readwrite) TMCache *cachedStoryImages;
@property (readwrite) NSString * activeUsername;
@property (readwrite) NSString * activeUserProfileId;
@property (readwrite) NSString * activeUserProfileName;
@ -309,6 +316,9 @@
+ (UIView *)makeGradientView:(CGRect)rect startColor:(NSString *)start endColor:(NSString *)end;
- (UIView *)makeFeedTitleGradient:(NSDictionary *)feed withRect:(CGRect)rect;
- (UIView *)makeFeedTitle:(NSDictionary *)feed;
- (void)saveFavicon:(UIImage *)image feedId:(NSString *)filename;
- (UIImage *)getFavicon:(NSString *)filename;
- (UIImage *)getFavicon:(NSString *)filename isSocial:(BOOL)isSocial;
- (void)toggleAuthorClassifier:(NSString *)author feedId:(NSString *)feedId;
- (void)toggleTagClassifier:(NSString *)tag feedId:(NSString *)feedId;

View file

@ -94,6 +94,8 @@
@synthesize firstTimeUserAddNewsBlurViewController;
@synthesize feedDetailPortraitYCoordinate;
@synthesize cachedFavicons;
@synthesize cachedStoryImages;
@synthesize activeUsername;
@synthesize activeUserProfileId;
@synthesize activeUserProfileName;
@ -189,7 +191,7 @@
[self createDatabaseConnection];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
(unsigned long)NULL), ^(void) {
[[TMCache sharedCache] removeAllObjects:^(TMCache *cache) {}];
[self.cachedStoryImages removeAllObjects:^(TMCache *cache) {}];
[self.feedsViewController loadOfflineFeeds:NO];
// [self setupReachability];
cacheImagesOperationQueue = [NSOperationQueue new];
@ -202,6 +204,9 @@
[[PocketAPI sharedAPI] setConsumerKey:@"16638-05adf4465390446398e53b8b"];
// [self showFirstTimeUser];
cachedFavicons = [[TMCache alloc] initWithName:@"NBFavicons" rootPath:@"favicons"];
cachedStoryImages = [[TMCache alloc] initWithName:@"NBStoryImages" rootPath:@"story_images"];
return YES;
}
@ -264,7 +269,7 @@
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
[[TMCache sharedCache] removeAllObjects];
[cachedStoryImages removeAllObjects];
}
- (void)setupReachability {
@ -2041,7 +2046,7 @@
titleLabel.frame = CGRectMake(32, 1, rect.size.width-32, 20);
NSString *feedIdStr = [NSString stringWithFormat:@"%@", [feed objectForKey:@"id"]];
UIImage *titleImage = [Utilities getImage:feedIdStr];
UIImage *titleImage = [self getFavicon:feedIdStr];
UIImageView *titleImageView = [[UIImageView alloc] initWithImage:titleImage];
titleImageView.frame = CGRectMake(8, 3, 16.0, 16.0);
[titleLabel addSubview:titleImageView];
@ -2110,7 +2115,7 @@
} else if (storiesCollection.isRiverView) {
titleImage = [UIImage imageNamed:@"g_icn_folder.png"];
} else {
titleImage = [Utilities getImage:feedIdStr];
titleImage = [self getFavicon:feedIdStr];
}
UIImageView *titleImageView = [[UIImageView alloc] initWithImage:titleImage];
titleImageView.frame = CGRectMake(0.0, 2.0, 16.0, 16.0);
@ -2121,6 +2126,32 @@
return titleLabel;
}
- (void)saveFavicon:(UIImage *)image feedId:(NSString *)filename {
if (image && filename && ![image isKindOfClass:[NSNull class]] &&
[filename class] != [NSNull class]) {
[self.cachedFavicons setObject:image forKey:filename];
}
}
- (UIImage *)getFavicon:(NSString *)filename {
return [self getFavicon:filename isSocial:NO];
}
- (UIImage *)getFavicon:(NSString *)filename isSocial:(BOOL)isSocial {
UIImage *image = [self.cachedFavicons objectForKey:filename];
if (image) {
return image;
} else {
if (isSocial) {
// return [UIImage imageNamed:@"user_light.png"];
return nil;
} else {
return [UIImage imageNamed:@"world.png"];
}
}
}
#pragma mark -
#pragma mark Classifiers

View file

@ -1011,7 +1011,7 @@ static UIFont *userLabelFont;
[appDelegate.dictSocialFeeds objectForKey:feedIdStr] :
[appDelegate.dictFeeds objectForKey:feedIdStr];
NSDictionary *unreadCounts = [appDelegate.dictUnreadCounts objectForKey:feedIdStr];
cell.feedFavicon = [Utilities getImage:feedIdStr isSocial:isSocial];
cell.feedFavicon = [appDelegate getFavicon:feedIdStr isSocial:isSocial];
cell.feedTitle = [feed objectForKey:@"feed_title"];
cell.positiveCount = [[unreadCounts objectForKey:@"ps"] intValue];
cell.neutralCount = [[unreadCounts objectForKey:@"nt"] intValue];
@ -1520,11 +1520,9 @@ heightForHeaderInSection:(NSInteger)section {
if (!faviconImage) continue;
faviconImage = [Utilities roundCorneredImage:faviconImage radius:6];
[Utilities saveImage:faviconImage feedId:feed_id];
[appDelegate saveFavicon:faviconImage feedId:feed_id];
}
[Utilities saveimagesToDisk];
dispatch_async(dispatch_get_main_queue(), ^{
[self.feedTitlesTable reloadData];
});
@ -1553,10 +1551,9 @@ heightForHeaderInSection:(NSInteger)section {
if ((NSNull *)favicon != [NSNull null] && [favicon length] > 0) {
NSData *imageData = [NSData dataWithBase64EncodedString:favicon];
UIImage *faviconImage = [UIImage imageWithData:imageData];
[Utilities saveImage:faviconImage feedId:feed_id];
[appDelegate saveFavicon:faviconImage feedId:feed_id];
}
}
[Utilities saveimagesToDisk];
dispatch_async(dispatch_get_main_queue(), ^{
[self.feedTitlesTable reloadData];

View file

@ -113,7 +113,7 @@
NSString *feedIdStr = [NSString stringWithFormat:@"%@",
[appDelegate.activeStory objectForKey:@"story_feed_id"]];
UIImage *titleImage = [Utilities getImage:feedIdStr];
UIImage *titleImage = [appDelegate getFavicon:feedIdStr];
UIImageView *titleImageView = [[UIImageView alloc] initWithImage:titleImage];
titleImageView.frame = CGRectMake(0.0, 2.0, 16.0, 16.0);
titleImageView.hidden = YES;

View file

@ -212,7 +212,7 @@
} else {
NSString *feedIdStr = [NSString stringWithFormat:@"%@",
[appDelegate.activeStory objectForKey:@"story_feed_id"]];
titleImage = [Utilities getImage:feedIdStr];
titleImage = [appDelegate getFavicon:feedIdStr];
}
UIImageView *titleImageView = [[UIImageView alloc] initWithImage:titleImage];
@ -230,7 +230,7 @@
} else {
NSString *feedIdStr = [NSString stringWithFormat:@"%@",
[appDelegate.storiesCollection.activeFeed objectForKey:@"id"]];
UIImage *titleImage = [Utilities getImage:feedIdStr];
UIImage *titleImage = [appDelegate getFavicon:feedIdStr];
titleImage = [Utilities roundCorneredImage:titleImage radius:6];
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectZero];

View file

@ -366,7 +366,7 @@
int publisherScore = [[[[appDelegate.storiesCollection.activeClassifiers objectForKey:feedId]
objectForKey:@"feeds"] objectForKey:feedId] intValue];
UIImage *favicon = [Utilities getImage:feedId];
UIImage *favicon = [appDelegate getFavicon:feedId];
NSData *faviconData = UIImagePNGRepresentation(favicon);
NSString *feedImageUrl = [NSString stringWithFormat:@"data:image/png;charset=utf-8;base64,%@",
[faviconData base64Encoding]];

View file

@ -15,11 +15,7 @@ void drawLinearGradient(CGContextRef context, CGRect rect, CGColorRef startColor
NSCache *imageCache;
}
+ (void)saveImage:(UIImage *)image feedId:(NSString *)filename;
+ (UIImage *)getImage:(NSString *)filename;
+ (UIImage *)getImage:(NSString *)filename isSocial:(BOOL)isSocial;
+ (void)drawLinearGradientWithRect:(CGRect)rect startColor:(CGColorRef)startColor endColor:(CGColorRef)endColor;
+ (void)saveimagesToDisk;
+ (UIImage *)roundCorneredImage:(UIImage *)orig radius:(CGFloat)r;
+ (UIImage *)imageWithImage:(UIImage *)image convertToSize:(CGSize)size;
+ (NSString *)md5:(NSString *)string;

View file

@ -8,6 +8,7 @@
#import "Utilities.h"
#import <CommonCrypto/CommonCrypto.h>
#import "TMCache.h"
void drawLinearGradient(CGContextRef context, CGRect rect, CGColorRef startColor,
CGColorRef endColor) {
@ -34,59 +35,6 @@ void drawLinearGradient(CGContextRef context, CGRect rect, CGColorRef startColor
@implementation Utilities
static NSMutableDictionary *imageCache;
+ (void)saveImage:(UIImage *)image feedId:(NSString *)filename {
if (!imageCache) {
imageCache = [NSMutableDictionary dictionary];
}
// Save image to memory-based cache, for performance when reading.
// NSLog(@"Saving %@", [imageCache allKeys]);
if (image && filename && ![image isKindOfClass:[NSNull class]] &&
[filename class] != [NSNull class]) {
[imageCache setObject:image forKey:filename];
} else {
// NSLog(@"%@ has no image!!!", filename);
}
}
+ (UIImage *)getImage:(NSString *)filename {
return [self getImage:filename isSocial:NO];
}
+ (UIImage *)getImage:(NSString *)filename isSocial:(BOOL)isSocial {
UIImage *image;
if (filename && [imageCache objectForKey:filename]) {
image = [imageCache objectForKey:filename];
}
if (!image || [image class] == [NSNull class]) {
// Image not in cache, search on disk.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cacheDirectory = [paths objectAtIndex:0];
if (isSocial) {
cacheDirectory = [cacheDirectory stringByAppendingPathComponent:@"avatars"];
} else {
cacheDirectory = [cacheDirectory stringByAppendingPathComponent:@"favicons"];
}
NSString *path = [cacheDirectory stringByAppendingPathComponent:filename];
image = [UIImage imageWithContentsOfFile:path];
}
if (image) {
return image;
} else {
if (isSocial) {
// return [UIImage imageNamed:@"user_light.png"];
return nil;
} else {
return [UIImage imageNamed:@"world.png"];
}
}
}
+ (void)drawLinearGradientWithRect:(CGRect)rect startColor:(CGColorRef)startColor endColor:(CGColorRef)endColor {
CGContextRef context = UIGraphicsGetCurrentContext();
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
@ -111,34 +59,6 @@ static NSMutableDictionary *imageCache;
CGColorSpaceRelease(colorSpace);
}
+ (void)saveimagesToDisk {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW,0);
dispatch_async(queue, ^{
for (NSString *filename in [imageCache allKeys]) {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cacheDirectory = [paths objectAtIndex:0];
if ([filename hasPrefix:@"social"]) {
cacheDirectory = [cacheDirectory stringByAppendingPathComponent:@"avatars"];
} else {
cacheDirectory = [cacheDirectory stringByAppendingPathComponent:@"favicons"];
}
NSString *path = [cacheDirectory stringByAppendingPathComponent:filename];
// Save image to disk
UIImage *image;
@try {
image = [imageCache objectForKey:filename];
}
@catch (NSException *exception) {
NSLog(@"Warning: imageCache EXC_BAD_ACCESS!!! %@", imageCache);
return;
}
[UIImagePNGRepresentation(image) writeToFile:path atomically:YES];
}
});
}
+ (UIImage *)roundCorneredImage: (UIImage*) orig radius:(CGFloat) r {
if (!orig) return nil;
UIGraphicsBeginImageContextWithOptions(orig.size, NO, 0);