【问题标题】:Huge memory usage when displaying gifs Swift iOS显示 GIF Swift iOS 时占用大量内存
【发布时间】:2016-06-12 07:39:50
【问题描述】:

我正在制作一个在 UIImageView 中显示 gif 的应用程序,该应用程序从解析加载 gif。我偶然发现的问题是,每当我加载 Gif 时,它都会使用大约 20 mb 的内存,并且在我执行 segue 时没有分配这个内存。我从 UIImageView 中删除了图像本身,但它仍然没有释放内存。

这是我用来显示 gif 的代码:

extension UIImage {

public class func gifWithData(data: NSData) -> UIImage? {
    guard let source = CGImageSourceCreateWithData(data, nil) else {
        print("SwiftGif: Source for the image does not exist")
        return nil
    }
    return UIImage.animatedImageWithSource(source)
}

public class func gifWithName(name: String) -> UIImage? {
    guard let bundleURL = NSBundle.mainBundle().URLForResource(name, withExtension: "gif") else {
        print("SwiftGif: This image named \"\(name)\" does not exist")
        return nil
    }
    guard let imageData = NSData(contentsOfURL: bundleURL) else {
        print("SwiftGif: Cannot turn image named \"\(name)\" into NSData")
        return nil
    }
    return gifWithData(imageData)
}

class func delayForImageAtIndex(index: Int, source: CGImageSource!) -> Double {
    var delay = 0.1

    // Get dictionaries
    let cfProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil)
    let gifProperties: CFDictionaryRef = unsafeBitCast(
        CFDictionaryGetValue(cfProperties,
            unsafeAddressOf(kCGImagePropertyGIFDictionary)),
        CFDictionary.self)

    // Get delay time
    var delayObject: AnyObject = unsafeBitCast(
        CFDictionaryGetValue(gifProperties,
            unsafeAddressOf(kCGImagePropertyGIFUnclampedDelayTime)),
        AnyObject.self)
    if delayObject.doubleValue == 0 {
        delayObject = unsafeBitCast(CFDictionaryGetValue(gifProperties,
            unsafeAddressOf(kCGImagePropertyGIFDelayTime)), AnyObject.self)
    }

    delay = delayObject as! Double

    if delay < 0.1 {
        delay = 0.1 // Make sure they're not too fast
    }

    return delay
}

class func gcdForPair(var a: Int?, var _ b: Int?) -> Int {
    // Check if one of them is nil
    if b == nil || a == nil {
        if b != nil {
            return b!
        } else if a != nil {
            return a!
        } else {
            return 0
        }
    }

    // Swap for modulo
    if a < b {
        let c = a
        a = b
        b = c
    }

    // Get greatest common divisor
    var rest: Int
    while true {
        rest = a! % b!

        if rest == 0 {
            return b! // Found it
        } else {
            a = b
            b = rest
        }
    }
}

class func gcdForArray(array: Array<Int>) -> Int {
    if array.isEmpty {
        return 1
    }

    var gcd = array[0]

    for val in array {
        gcd = UIImage.gcdForPair(val, gcd)
    }

    return gcd
}

class func animatedImageWithSource(source: CGImageSource) -> UIImage? {
    let count = CGImageSourceGetCount(source)
    var images = [CGImageRef]()
    var delays = [Int]()

    // Fill arrays
    for i in 0..<count {
        // Add image
        if let image = CGImageSourceCreateImageAtIndex(source, i, nil) {
            images.append(image)
        }

        // At it's delay in cs
        let delaySeconds = UIImage.delayForImageAtIndex(Int(i),
            source: source)
        delays.append(Int(delaySeconds * 1000.0)) // Seconds to ms
    }

    // Calculate full duration
    let duration: Int = {
        var sum = 0

        for val: Int in delays {
            sum += val
        }

        return sum
        }()

    // Get frames
    let gcd = gcdForArray(delays)
    var frames = [UIImage]()

    var frame: UIImage
    var frameCount: Int
    for i in 0..<count {
        frame = UIImage(CGImage: images[Int(i)])
        frameCount = Int(delays[Int(i)] / gcd)

        for _ in 0..<frameCount {
            frames.append(frame)
        }
    }

    // Heyhey
    let animation = UIImage.animatedImageWithImages(frames,
        duration: Double(duration) / 1000.0)

    return animation
}

class func FinalFrame(source: CGImageSource) -> UIImage? {
    let count = CGImageSourceGetCount(source)
    var images = [CGImageRef]()
    var delays = [Int]()
    var finalFrame = UIImage()

    // Fill arrays
    for i in 0..<count {
        // Add image
        if let image = CGImageSourceCreateImageAtIndex(source, i, nil) {
            images.append(image)
        }

        // At it's delay in cs
        let delaySeconds = UIImage.delayForImageAtIndex(Int(i),
            source: source)
        delays.append(Int(delaySeconds * 1000.0)) // Seconds to ms
    }

    // Get frames
    let gcd = gcdForArray(delays)
    var frames = [UIImage]()

    var frame: UIImage
    var frameCount: Int
    for i in 0..<count {
        frame = UIImage(CGImage: images[Int(i)])
        frameCount = Int(delays[Int(i)] / gcd)

        for _ in 0..<frameCount {
            frames.append(frame)
        }
    }
    finalFrame = frames[frames.count-1]

    return finalFrame
}
}

这可能是我遇到内存问题的原因吗? 感谢任何帮助!

【问题讨论】:

标签: ios swift memory gif


【解决方案1】:

我决定添加完整的代码以节省内存,如果您使用的是 GIF 文件,请修改 UIImage 缩放方法(在这里找到它,一个 Stackoverflow)。正如 SD Image 中的 GangstaGraham 所说,存在方法 sd_animatedImageByScalingAndCroppingToSize

@interface UIImage (Scaling)

-(UIImage *)imageByScalingProportionallyToSize:(CGSize)targetSize;
-(UIImage*) croppedImageWithRect: (CGRect) rect;

@end

@implementation UIImage (Scaling)

- (UIImage *)imageByScalingProportionallyToSize:(CGSize)targetSize {

    if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
        if ([[UIScreen mainScreen] scale] == 2.0) {
            targetSize.height *= 2.0f;
            targetSize.width *= 2.0f;
        }
    }

    NSUInteger width = targetSize.width;
    NSUInteger height = targetSize.height;
    UIImage *newImage = [self resizedImageWithMinimumSize: CGSizeMake (width, height)];
    return [newImage croppedImageWithRect: CGRectMake ((newImage.size.width - width) / 2, (newImage.size.height - height) / 2, width, height)];
}

-(CGImageRef)CGImageWithCorrectOrientation
{
    if (self.imageOrientation == UIImageOrientationDown) {
        //retaining because caller expects to own the reference
        CGImageRetain([self CGImage]);
        return [self CGImage];
    }

    UIGraphicsBeginImageContext(self.size);

    CGContextRef context = UIGraphicsGetCurrentContext();

    if (self.imageOrientation == UIImageOrientationRight) {
        CGContextRotateCTM (context, 90 * M_PI/180);
    } else if (self.imageOrientation == UIImageOrientationLeft) {
        CGContextRotateCTM (context, -90 * M_PI/180);
    } else if (self.imageOrientation == UIImageOrientationUp) {
        CGContextRotateCTM (context, 180 * M_PI/180);
    }

    [self drawAtPoint:CGPointMake(0, 0)];

    CGImageRef cgImage = CGBitmapContextCreateImage(context);
    UIGraphicsEndImageContext();

    return cgImage;
}

-(UIImage*)resizedImageWithMinimumSize:(CGSize)size
{
    CGImageRef imgRef = [self CGImageWithCorrectOrientation];
    CGFloat original_width  = CGImageGetWidth(imgRef);
    CGFloat original_height = CGImageGetHeight(imgRef);
    CGFloat width_ratio = size.width / original_width;
    CGFloat height_ratio = size.height / original_height;
    CGFloat scale_ratio = width_ratio > height_ratio ? width_ratio : height_ratio;
    CGImageRelease(imgRef);
    return [self drawImageInBounds: CGRectMake(0, 0, round(original_width * scale_ratio), round(original_height * scale_ratio))];
}

-(UIImage*)drawImageInBounds:(CGRect)bounds
{
    UIGraphicsBeginImageContext(bounds.size);
    [self drawInRect: bounds];
    UIImage *resizedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return resizedImage;
}

-(UIImage*)croppedImageWithRect:(CGRect)rect
{

    UIGraphicsBeginImageContext(rect.size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGRect drawRect = CGRectMake(-rect.origin.x, -rect.origin.y, self.size.width, self.size.height);
    CGContextClipToRect(context, CGRectMake(0, 0, rect.size.width, rect.size.height));
    [self drawInRect:drawRect];
    UIImage* subImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return subImage;
}

-(UIImage *) resizableImageWithCapInsets2: (UIEdgeInsets) inset
{
    if ([self respondsToSelector:@selector(resizableImageWithCapInsets:resizingMode:)])
    {
        return [self resizableImageWithCapInsets:inset resizingMode:UIImageResizingModeStretch];
    }
    else
    {
        float left = (self.size.width-2)/2;//The middle points rarely vary anyway
        float top = (self.size.height-2)/2;
        return [self stretchableImageWithLeftCapWidth:left topCapHeight:top];
    }
}

@end

还有 UIImageView:

#import <SDWebImage/SDImageCache.h>

@implementation UIImageView (Scaling)

-(void)setImageWithURL:(NSURL*)url scaleToSize:(BOOL)scale
{
    if(url.absoluteString.length < 10) return;
    if(!scale){
        [self setImageWithURL:url];
        return;
    }
    __block UIImageView* selfimg = self;
    __block NSString* prevKey = SPRINTF(@"%@_%ix%i", url.absoluteString, (int)self.frame.size.width, (int)self.frame.size.height);
    __block UIImage* prevImage = nil;

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queue, ^ {

        prevImage = [[SDImageCache sharedImageCache] imageFromDiskCacheForKey:prevKey];
        if(prevImage){
            dispatch_async(dispatch_get_main_queue(), ^ {
                [self setImage:prevImage];
            });
        }else{

            [[SDWebImageDownloader sharedDownloader] downloadImageWithURL:url options:SDWebImageDownloaderFILOQueueMode progress:nil completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {
                if(error){
                    [selfimg setImageWithURL:url scaleToSize:scale];
                }else{
                    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
                    dispatch_async(queue, ^ {
                        prevImage = [image imageByScalingProportionallyToSize:self.frame.size];
                        if(finished)
                            [[SDImageCache sharedImageCache] storeImage:prevImage forKey:prevKey];
                        dispatch_async(dispatch_get_main_queue(), ^ {
                            [self setImage:prevImage];
                        });
                    });
                }
            }];
        }
    });

    return;
}

-(void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder scaleToSize:(BOOL)scale
{
    [self setImage:placeholder];
    [self setImageWithURL:url scaleToSize:scale];
}


@end

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-05-11
    • 2011-02-27
    • 2013-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多