Merge pull request #2728 from dreampiggy/bugfix_sdanimatedimageview_init_image

Fix that SDAnimatedImageView initWithImage will skip the initialize logic and crash
This commit is contained in:
DreamPiggy 2019-05-16 15:12:21 +08:00 committed by GitHub
commit e9eb60e506
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 56 additions and 9 deletions

View File

@ -40,7 +40,10 @@ static NSUInteger SDDeviceFreeMemory() {
return vm_stat.free_count * page_size;
}
@interface SDAnimatedImageView () <CALayerDelegate>
@interface SDAnimatedImageView () <CALayerDelegate> {
NSRunLoopMode _runLoopMode;
BOOL _initFinished; // Extra flag to mark the `commonInit` is called
}
@property (nonatomic, strong, readwrite) UIImage *currentFrame;
@property (nonatomic, assign, readwrite) NSUInteger currentFrameIndex;
@ -123,9 +126,10 @@ static NSUInteger SDDeviceFreeMemory() {
- (void)commonInit
{
// Pay attention that UIKit's `initWithImage:` will trigger a `setImage:` during initialization before this `commonInit`.
// So the properties which rely on this order, should using lazy-evaluation or do extra check in `setImage:`.
self.shouldCustomLoopCount = NO;
self.shouldIncrementalLoad = YES;
self.lock = dispatch_semaphore_create(1);
#if SD_MAC
self.wantsLayer = YES;
// Default value from `NSImageView`
@ -134,9 +138,10 @@ static NSUInteger SDDeviceFreeMemory() {
self.imageAlignment = NSImageAlignCenter;
#endif
#if SD_UIKIT
self.runLoopMode = [[self class] defaultRunLoopMode];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveMemoryWarning:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
#endif
// Mark commonInit finished
_initFinished = YES;
}
- (void)resetAnimatedImage
@ -240,17 +245,38 @@ static NSUInteger SDDeviceFreeMemory() {
}
#if SD_UIKIT
- (void)setRunLoopMode:(NSString *)runLoopMode
- (void)setRunLoopMode:(NSRunLoopMode)runLoopMode
{
if (![@[NSDefaultRunLoopMode, NSRunLoopCommonModes] containsObject:runLoopMode]) {
NSAssert(NO, @"Invalid run loop mode: %@", runLoopMode);
_runLoopMode = [[self class] defaultRunLoopMode];
} else {
_runLoopMode = runLoopMode;
if ([_runLoopMode isEqual:runLoopMode]) {
return;
}
if (_displayLink) {
if (_runLoopMode) {
[_displayLink removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:_runLoopMode];
}
if (runLoopMode.length > 0) {
[_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:runLoopMode];
}
}
_runLoopMode = [runLoopMode copy];
}
- (NSRunLoopMode)runLoopMode
{
if (!_runLoopMode) {
_runLoopMode = [[self class] defaultRunLoopMode];
}
return _runLoopMode;
}
#endif
- (BOOL)shouldIncrementalLoad {
if (!_initFinished) {
return YES; // Defaults to YES
}
return _initFinished;
}
#pragma mark - Private
- (NSOperationQueue *)fetchQueue
{
@ -269,6 +295,13 @@ static NSUInteger SDDeviceFreeMemory() {
return _frameBuffer;
}
- (dispatch_semaphore_t)lock {
if (!_lock) {
_lock = dispatch_semaphore_create(1);
}
return _lock;
}
#if SD_MAC
- (CVDisplayLinkRef)displayLink
{

View File

@ -156,6 +156,20 @@ static const NSUInteger kTestGIFFrameCount = 5; // local TestImage.gif loop coun
#endif
}
- (void)test13AnimatedImageViewInitWithImage {
// Test that -[SDAnimatedImageView initWithImage:] this convenience initializer not crash
SDAnimatedImage *image = [SDAnimatedImage imageWithData:[self testAPNGPData]];
SDAnimatedImageView *imageView;
#if SD_UIKIT
imageView = [[SDAnimatedImageView alloc] initWithImage:image];
#else
if (@available(macOS 10.12, *)) {
imageView = [SDAnimatedImageView imageViewWithImage:image];
}
#endif
expect(imageView.image).equal(image);
}
- (void)test20AnimatedImageViewRendering {
XCTestExpectation *expectation = [self expectationWithDescription:@"test SDAnimatedImageView rendering"];
SDAnimatedImageView *imageView = [[SDAnimatedImageView alloc] init];