Fix retain cycle in basic, cache-less ASNetworkImageNodes

This should not directly affect more complex apps that don't use the ASBasicImageDownloader.

Also disables the default-on text placeholders, as they churn memory during the measurement pass.
These were intended to be written with pure layers (without using backing stores), so I don't
think it is a reasonable default-on behavior until that is fixed.
This commit is contained in:
Scott Goodson 2015-10-30 22:42:05 -07:00
parent ed63577370
commit 3175ce2fe7
7 changed files with 31 additions and 11 deletions

View File

@ -23,10 +23,10 @@
@interface ASNetworkImageNode : ASImageNode @interface ASNetworkImageNode : ASImageNode
/** /**
* The designated initializer. * The designated initializer. Cache and Downloader are WEAK references.
* *
* @param cache The object that implements a cache of images for the image node. * @param cache The object that implements a cache of images for the image node. Weak reference.
* @param downloader The object that implements image downloading for the image node. Must not be nil. * @param downloader The object that implements image downloading for the image node. Must not be nil. Weak reference.
* *
* @discussion If `cache` is nil, the receiver will not attempt to retrieve images from a cache before downloading them. * @discussion If `cache` is nil, the receiver will not attempt to retrieve images from a cache before downloading them.
* *

View File

@ -16,8 +16,8 @@
@interface ASNetworkImageNode () @interface ASNetworkImageNode ()
{ {
ASDN::RecursiveMutex _lock; ASDN::RecursiveMutex _lock;
id<ASImageCacheProtocol> _cache; __weak id<ASImageCacheProtocol> _cache;
id<ASImageDownloaderProtocol> _downloader; __weak id<ASImageDownloaderProtocol> _downloader;
// Only access any of these with _lock. // Only access any of these with _lock.
__weak id<ASNetworkImageNodeDelegate> _delegate; __weak id<ASNetworkImageNodeDelegate> _delegate;
@ -51,7 +51,7 @@
- (instancetype)init - (instancetype)init
{ {
return [self initWithCache:nil downloader:[[ASBasicImageDownloader alloc] init]]; return [self initWithCache:nil downloader:[ASBasicImageDownloader sharedImageDownloader]];
} }
- (void)dealloc - (void)dealloc

View File

@ -79,6 +79,15 @@ typedef NS_ENUM(NSUInteger, ASTextNodeHighlightStyle) {
#pragma mark - Placeholders #pragma mark - Placeholders
/**
* @abstract ASTextNode has a special placeholder behavior when placeholderEnabled is YES.
*
* @discussion Defaults to NO. When YES, it draws rectangles for each line of text,
* following the true shape of the text's wrapping. This visually mirrors the overall
* shape and weight of paragraphs, making the appearance of the finished text less jarring.
*/
@property (nonatomic, assign) BOOL placeholderEnabled;
/** /**
@abstract The placeholder color. @abstract The placeholder color.
*/ */

View File

@ -131,7 +131,8 @@ static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncation
_constrainedSize = CGSizeMake(-INFINITY, -INFINITY); _constrainedSize = CGSizeMake(-INFINITY, -INFINITY);
// Placeholders // Placeholders
self.placeholderEnabled = YES; // Disabled by default in ASDisplayNode, but add a few options for those who toggle
// on the special placeholder behavior of ASTextNode.
_placeholderColor = ASDisplayNodeDefaultPlaceholderColor(); _placeholderColor = ASDisplayNodeDefaultPlaceholderColor();
_placeholderInsets = UIEdgeInsetsMake(1.0, 0.0, 1.0, 0.0); _placeholderInsets = UIEdgeInsetsMake(1.0, 0.0, 1.0, 0.0);
} }
@ -747,6 +748,8 @@ static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncation
- (UIImage *)placeholderImage - (UIImage *)placeholderImage
{ {
// FIXME: Replace this implementation with reusable CALayers that have .backgroundColor set.
// This would completely eliminate the memory and performance cost of the backing store.
CGSize size = self.calculatedSize; CGSize size = self.calculatedSize;
UIGraphicsBeginImageContext(size); UIGraphicsBeginImageContext(size);
[self.placeholderColor setFill]; [self.placeholderColor setFill];

View File

@ -14,4 +14,6 @@
*/ */
@interface ASBasicImageDownloader : NSObject <ASImageDownloaderProtocol> @interface ASBasicImageDownloader : NSObject <ASImageDownloaderProtocol>
+ (instancetype)sharedImageDownloader;
@end @end

View File

@ -200,6 +200,16 @@ static const char *kContextKey = NSStringFromClass(ASBasicImageDownloaderContext
@implementation ASBasicImageDownloader @implementation ASBasicImageDownloader
+ (instancetype)sharedImageDownloader
{
static ASBasicImageDownloader *sharedImageDownloader = nil;
static dispatch_once_t once = 0;
dispatch_once(&once, ^{
sharedImageDownloader = [[ASBasicImageDownloader alloc] init];
});
return sharedImageDownloader;
}
#pragma mark Lifecycle. #pragma mark Lifecycle.
- (instancetype)init - (instancetype)init

View File

@ -166,8 +166,6 @@ static const NSInteger kMaxLitterSize = 100; // max number of kitten cell
- (void)tableView:(UITableView *)tableView willBeginBatchFetchWithContext:(ASBatchContext *)context - (void)tableView:(UITableView *)tableView willBeginBatchFetchWithContext:(ASBatchContext *)context
{ {
NSLog(@"adding kitties");
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1); sleep(1);
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
@ -189,8 +187,6 @@ static const NSInteger kMaxLitterSize = 100; // max number of kitten cell
[tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationFade]; [tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationFade];
[context completeBatchFetching:YES]; [context completeBatchFetching:YES];
NSLog(@"kittens added");
}); });
}); });
} }