IOS開發系列——異步繪製專題

異步繪製專題

 

1    圖片處理

1.1    編輯圖片的幾個方法

第一種

先用UIImage對象加載一張圖片

然後轉化成CGImageRef放到CGContext中去編輯

第二種 

CGImageCreate函數創建CGImageRef

然後把CGImageRef放到CGContext中去編輯

第三種

CGImageCreateCopy或者CGImageCreateCopyWithColorSpace

函數拷貝

 

CGImageRef CGImageCreate (

   size_t width, //圖片的寬度

   size_t height, //圖片的高度

   size_t bitsPerComponent,  //圖片每個顏色的bits,比如rgb顏色空間,有可能是5或者 8 ==

   size_t bitsPerPixel,  //每一個像素佔用的buts1524 32位等等

   size_t bytesPerRow, //每一行佔用多少bytes注意是bytes不是bits  1byte 8bit

   CGColorSpaceRef colorspace,  //顏色空間,比如rgb

   CGBitmapInfo bitmapInfo,  //layout,像素中bit的佈局,rgba還是 argb,==

   CGDataProviderRefprovider,  //數據源提供者,url或者內存==

   const CGFloat decode[],  //一個解碼數組

   bool shouldInterpolate,  //抗鋸齒參數

   CGColorRenderingIntent intent

//圖片渲染相關參數

);

1.2    示例代碼

CGImageRef CGImageCreate(size_t width,size_t height,

   size_tbitsPerComponent,size_t bitsPerPixel,size_t bytesPerRow,

   CGColorSpaceRef space, CGBitmapInfo bitmapInfo, CGDataProviderRefprovider,

   constCGFloat decode[],bool shouldInterpolate,

   CGColorRenderingIntent intent);

通過這個方法,我們可以創建出一個CGImageRef類型的對象,下面分別對參數進行解釋:

sizt_t是定義的一個可移植性的單位,在64位機器中爲8字節,32位位4字節。

width:圖片寬度像素

height:圖片高度像素

bitsPerComponent:每個顏色的比特數,例如在rgba-32模式下爲8

bitsPerPixel:每個像素的總比特數

bytesPerRow:每一行佔用的字節數,注意這裏的單位是字節

space:顏色空間模式,例如constCFStringRef kCGColorSpaceGenericRGB這個函數可以返回一個顏色空間對象。

bitmapInfo:位圖像素佈局,枚舉如下:

?

1

2

3

4

5

6

7

8

9

10

11

typedef CF_OPTIONS(uint32_t, CGBitmapInfo) {

  kCGBitmapAlphaInfoMask = 0x1F,

  kCGBitmapFloatComponents = (1 << 8),

     

  kCGBitmapByteOrderMask = 0x7000,

  kCGBitmapByteOrderDefault = (0 << 12),

  kCGBitmapByteOrder16Little = (1 << 12),

  kCGBitmapByteOrder32Little = (2 << 12),

  kCGBitmapByteOrder16Big = (3 << 12),

  kCGBitmapByteOrder32Big = (4 << 12)

}

 

provider:數據源提供者

decode[]:解碼渲染數組

shouldInterpolate:是否抗鋸齒

intent:圖片相關參數

 

CGImageRef CGImageMaskCreate(size_t width,size_t height,

    size_t bitsPerComponent,size_t bitsPerPixel,size_t bytesPerRow,

    CGDataProviderRef provider,constCGFloat decode[],bool shouldInterpolate)

這個方法用於創建mask圖片圖層,可以設置其顯示部分與不顯示部分達到特殊的效果,參數意義同上。

 

CGImageRef CGImageCreateCopy(CGImageRef image)

這個方法可以複製一個CGImageRef對象

 

CGImageRef CGImageCreateWithJPEGDataProvider(CGDataProviderRef

    source, constCGFloat decode[],bool shouldInterpolate,

    CGColorRenderingIntent intent)

通過JPEG數據源獲取圖像

 

CGImageRef CGImageCreateWithPNGDataProvider(CGDataProviderRef source,

    constCGFloat decode[],bool shouldInterpolate,

    CGColorRenderingIntent intent)

通過PNG數據源獲取圖像

 

CGImageRef CGImageCreateWithImageInRect(CGImageRef image,

    CGRect rect)

截取圖像的一個區域重繪圖像

 

CGImageRef CGImageCreateWithMask(CGImageRef image,CGImageRef mask)

截取mask圖像的某一區域重繪

 

CGImageRef CGImageCreateWithMaskingColors(CGImageRef image,

    constCGFloat components[])

通過顏色分量數組創建位圖

 

CGImageRef CGImageCreateCopyWithColorSpace(CGImageRef image,

    CGColorSpaceRef space)

通過顏色空間模式複製位圖

 

CGImageRef CGImageRetain(CGImageRef image)

引用+1

 

void CGImageRelease(CGImageRef image)

引用-1

 

bool CGImageIsMask(CGImageRef image)

返回是否爲Mask圖層

 

size_t CGImageGetWidth(CGImageRef image)

獲取寬度像素

 

size_t CGImageGetHeight(CGImageRef image)

獲取高度像素

 

下面這些方法分別獲取相應屬性

size_t CGImageGetBitsPerComponent(CGImageRef image)

size_t CGImageGetBitsPerPixel(CGImageRef image)

size_t CGImageGetBytesPerRow(CGImageRef image)

CGColorSpaceRef CGImageGetColorSpace(CGImageRef image)CG_EXTERNCGImageAlphaInfo CGImageGetAlphaInfo(CGImageRef image)

CGDataProviderRef CGImageGetDataProvider(CGImageRef image)

constCGFloat *CGImageGetDecode(CGImageRef image)

bool CGImageGetShouldInterpolate(CGImageRef image)

CGColorRenderingIntent CGImageGetRenderingIntent(CGImageRef image)

CGBitmapInfo CGImageGetBitmapInfo(CGImageRef image)

 

1.3    PNG與JPEG優劣比較

存儲速度:JPG更快

壓縮比:JPG更大;

圖片質量:JPG更好

JPG不支持透明效果;

 

UIImageJPEGRepresentation方法在耗時上比較少 而UIImagePNGRepresentation耗時操作時間比較長

UIImageJPEGRepresentation函數需要兩個參數:圖片的引用和壓縮係數.而UIImagePNGRepresentation只需要圖片引用作爲參數.通過在實際使用過程中,比較發現:UIImagePNGRepresentation(UIImage* image) 要比UIImageJPEGRepresentation(UIImage*image, 1.0) 返回的圖片數據量大很多.譬如,同樣是讀取攝像頭拍攝的同樣景色的照片, UIImagePNGRepresentation()返回的數據量大小爲199K ,而 UIImageJPEGRepresentation(UIImage* image, 1.0)返回的數據量大小隻爲140KB,比前者少了50多KB.如果對圖片的清晰度要求不高,還可以通過設置 UIImageJPEGRepresentation函數的第二個參數,大幅度降低圖片數據量.譬如,剛纔拍攝的圖片, 通過調用UIImageJPEGRepresentation(UIImage*image, 1.0)讀取數據時,返回的數據大小爲140KB,但更改壓縮係數後,通過調用UIImageJPEGRepresentation(UIImage* image, 0.5)讀取數據時,返回的數據大小隻有11KB多,大大壓縮了圖片的數據量 ,而且從視角角度看,圖片的質量並沒有明顯的降低.因此,在讀取圖片數據內容時,建議優先使用UIImageJPEGRepresentation,並可根據自己的實際使用場景,設置壓縮係數,進一步降低圖片數據量大小.

 

1.4    圖片縮放

圖片縮放的三個函數

http://www.cnblogs.com/pengyingh/articles/2355052.html

 

程序中一個界面用到了好多張大圖,內存報警告了,所以做了一下圖片縮放,在網上找了別人寫的代碼

//把圖片做等比縮放,生成一個新圖片

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

 2

 3    UIGraphicsBeginImageContext(targetSize);

 4         [sourceImage drawInRect:CGRectMake(0,0,targetSize.width, targetSize.height)];

 5        UIImage* scaledImage =UIGraphicsGetImageFromCurrentImageContext();

 6         UIGraphicsEndImageContext();

 7         returnscaledImage;

 8

 9    UIImage *newImage = nil;

10     CGSize imageSize = sourceImage.size;

11     CGFloat width = imageSize.width;

12     CGFloat height = imageSize.height;

13     CGFloat targetWidth = targetSize.width;

14     CGFloat targetHeight = targetSize.height;

15     CGFloat scaleFactor = 0.0;

16     CGFloat scaledWidth = targetWidth;

17     CGFloat scaledHeight = targetHeight;

18     CGPoint thumbnailPoint = CGPointMake(0.0,0.0);

19

20     UIGraphicsBeginImageContext(targetSize);// this will crop

21

22     CGRect thumbnailRect = CGRectZero;

23     thumbnailRect.origin = thumbnailPoint;

24     thumbnailRect.size.width  = scaledWidth;

25     thumbnailRect.size.height = scaledHeight;

26

27     [sourceImage drawInRect:thumbnailRect];

28

29     newImage =UIGraphicsGetImageFromCurrentImageContext();

30     if(newImage== nil)

31         NSLog(@"couldnot scale image");

32

33     //pop thecontext to get back to the default

34     UIGraphicsEndImageContext();

35     returnnewImage;

36 }

 

//把圖片按照新大小進行裁剪,生成一個新圖片

1 - (UIImage*)imageByScalingAndCroppingForSize:(CGSize)targetSize image:(UIImage *)sourceImage

 2 {

 3    //   UIImage *sourceImage = self;

 4    UIImage *newImage = nil;

 5    CGSize imageSize = sourceImage.size;

 6    CGFloat width = imageSize.width;

 7    CGFloat height = imageSize.height;

 8    CGFloat targetWidth = targetSize.width;

 9    CGFloat targetHeight = targetSize.height;

10     CGFloat scaleFactor = 0.0;

11     CGFloat scaledWidth = targetWidth;

12     CGFloat scaledHeight = targetHeight;

13     CGPoint thumbnailPoint = CGPointMake(0.0,0.0);

14

15     if(CGSizeEqualToSize(imageSize, targetSize) == NO)

16     {

17         CGFloat widthFactor = targetWidth /width;

18         CGFloat heightFactor = targetHeight / height;

19

20         if(widthFactor > heightFactor)

21             scaleFactor = widthFactor;// scale to fit height

22         else

23             scaleFactor = heightFactor;// scale to fit width

24         scaledWidth  = width * scaleFactor;

25         scaledHeight = height * scaleFactor;

26

27         // centerthe image

28         if(widthFactor > heightFactor)

29         {

30             thumbnailPoint.y = (targetHeight -scaledHeight) *0.5;

31         }

32         else

33             if(widthFactor < heightFactor)

34             {

35                 thumbnailPoint.x =(targetWidth - scaledWidth) *0.5;

36             }

37     }      

38

39     UIGraphicsBeginImageContext(targetSize);// this will crop

40

41     CGRect thumbnailRect = CGRectZero;

42     thumbnailRect.origin = thumbnailPoint;

43     thumbnailRect.size.width  = scaledWidth;

44     thumbnailRect.size.height = scaledHeight;

45

46     [sourceImage drawInRect:thumbnailRect];

47

48     newImage =UIGraphicsGetImageFromCurrentImageContext();

49     if(newImage== nil)

50         NSLog(@"couldnot scale image");

51

52     //pop thecontext to get back to the default

53     UIGraphicsEndImageContext();

54     returnnewImage;

55 }

 

1 - (UIImage*)generatePhotoThumbnail:(UIImage *)image

 2 {

 3    // Create a thumbnail version of the imagefor the event object.

 4    CGSize size = image.size;

 5    CGSize croppedSize;

 6    CGFloat ratio = 64.0;//這個是設置轉換後圖片的尺寸大小

 7    CGFloat offsetX = 0.0;

 8    CGFloat offsetY = 0.0;

 9    

10     // check thesize of the image, we want to make it

11 // a square with sides the size of the smallest dimension

12     if(size.width > size.height) {

13         offsetX = (size.height - size.width) /2;

14         croppedSize = CGSizeMake(size.height,size.height);

15     } else {

16         offsetY = (size.width - size.height) /2;

17         croppedSize = CGSizeMake(size.width,size.width);

18     }

19    

20     // Crop theimage before resize

21     CGRect clippedRect = CGRectMake(offsetX *-1, offsetY * -1,croppedSize.width, croppedSize.height);

22 //裁剪圖片

CGImageRef imageRef =CGImageCreateWithImageInRect([image CGImage], clippedRect);

23     // Donecropping

24     

25 // Resize the image

26     CGRect rect = CGRectMake(0.0,0.0,ratio, ratio);

27    

28     UIGraphicsBeginImageContext(rect.size);

29     [[UIImage imageWithCGImage:imageRef]drawInRect:rect];

30     UIImage *thumbnail =UIGraphicsGetImageFromCurrentImageContext();

31     UIGraphicsEndImageContext();

32     // DoneResizing

33    

34     returnthumbnail;

35 }

 

實際應用簡化

- (UIImage *)generatePhotoThumbnail:(UIImage*)image

{

   CGRect rect=CGRectMake(0, 0, 60, 78);

//裁剪圖片

   CGImageRef imageRef=CGImageCreateWithImageInRect([image CGImage],CGRectMake(0,0,140,182));

 

   UIGraphicsBeginImageContext(rect.size);

   [[UIImage imageWithCGImage:imageRef]drawInRect:rect];

//如果不裁剪圖片可以直接畫

   //[image drawInRect:CGRectMake(0, 0, theSize.width, theSize.height)];

   UIImage *thumbnail=UIGraphicsGetImageFromCurrentImageContext();

   UIGraphicsEndImageContext();

   return thumbnail;

}

 

附:

UIImage類並沒有提供縮放圖片需要用到的API,是不是覺得很吃驚?沒關係,我們自己來添加一個。

 

定義縮放圖片的Category

//  UIImage+Scale.h

@interface UIImage (scale)

-(UIImage*)scaleToSize:(CGSize)size;

@end

 

實現這個Category的定義

// UIImage+Scale.m 

#import "UIImage+Scale.h"

@implementation UIImage (scale)

 

-(UIImage*)scaleToSize:(CGSize)size

{// 創建一個bitmapcontext//並把它設置成爲當前正在使用的context

 UIGraphicsBeginImageContext(size);

 // 繪製改變大小的圖片

[self drawInRect:CGRectMake(0,0,size.width, size.height)];

 // 從當前context中創建一個改變大小後的圖片

 UIImage* scaledImage = UIGraphicsGetImageFromCurrentImageContext();

 // 使當前的context出堆棧

 UIGraphicsEndImageContext();

 // 返回新的改變大小後的圖片

return scaledImage;

}

 

@end

 

如何使用

// 創建圖片

UIImage *image =[UIImage imageNamed:@"myImage.png"];

 

// 更改圖片大小

UIImage *scaledImage =[image scaleToSize:CGSizeMake(25.0f,35.0f)]

 

1.5    參考鏈接

IOS-圖片操作集合

http://blog.csdn.net/ch_soft/article/details/7685753

UIImagePNGRepresentation 存在緩慢問題

http://blog.sina.com.cn/s/blog_95a3991f010162ws.html

 

<iOS>UIImage變爲NSData並進行壓縮

http://www.cnblogs.com/robinkey/archive/2013/01/21/2869930.html

 

UIImageJPEGRepresentation和UIImagePNGRepresentation

http://blog.csdn.net/mideveloper/article/details/11473627

 

png有透明通道,JPEG無

http://blog.163.com/chentong1115@126/blog/static/45314732200972323921819/

 

透明PNG圖片有黑邊的解決方法

http://www.minroad.com/?p=9

 

用UIImage和UIButton畫出的按鈕,使用透明的png圖片,爲什麼會出現白邊

http://segmentfault.com/q/1010000000095447

 

JPG、PNG和GIF圖片的基本原理及優化方法

http://www.mahaixiang.cn/Photoshop/400.html

 

JPEG 原理詳細

http://blog.chinaunix.net/uid-27002868-id-3220554.html

 

IOS開發中圖片資源使用png還是jpg格式

http://www.cnblogs.com/wengzilin/p/3485298.html

 

(good)ios開發圖片格式的選擇:png和jpg

http://m.blog.csdn.net/blog/awaylin113/22712317

 

IOS開發之保存圖片到Documents目錄及PNG,JPEG格式相互轉換

http://blog.csdn.net/sanpintian/article/details/7418755

 

iOS過濾png圖片透明部分點擊事件

http://www.cocoachina.com/industry/20121127/5192.html

 

JPEG壓縮原理

http://blog.csdn.net/xfortius/article/details/8904012

 

png壓縮原理

http://blog.csdn.net/zykun/article/details/1825086

 

iOS開發,圖片使用png好還是jpg好?

http://www.cocoachina.com/bbs/read.php?tid=110115

  

2    繪製文本 

2.1    NSMutableAttributedString繪製

CGRect textViewRect = CGRectMake(ICON_SPACE*1-2,_imageHeight +ICON_SPACE,_postContentTextView.frame.size.width,_labelSize);//ceilf(_labelSize)

//            [_postContentTextViewdrawRect:textViewRect];

 

           NSMutableAttributedString *str = [[NSMutableAttributedStringalloc]initWithString:_vm.contentText];

           

           [str addAttribute:NSForegroundColorAttributeNamevalue:kContentTextColorrange:NSMakeRange(0,[_vm.contentTextlength])];

           [str addAttribute:NSFontAttributeNamevalue:kContentTextFontrange:NSMakeRange(0,[_vm.contentTextlength])];

           [str addAttribute:NSBackgroundColorDocumentAttributevalue:[UIColorwhiteColor]range:NSMakeRange(0,[_vm.contentTextlength])];

           

//            NSLog(@"ContentText:%@, Frame:%@",_vm.contentText, NSStringFromCGRect(_postContentTextView.frame));

//           NSLog(@"TextViewRect:%@",NSStringFromCGRect(textViewRect));

            [str drawInRect:textViewRect]; 

 

2.2    參考資料

IOS開發(78)之繪製文本

http://www.2cto.com/kf/201305/212045.html

 

iOS 界面上繪製不同字體 顏色 大小的字符串

http://blog.csdn.net/wsk_123_123/article/details/23277457

 

初探NSAttributedString和NSMutableAttributedString的使用 -LiuWJ

http://www.tuicool.com/articles/Fvqia2

 

iOS 字符屬性NSAttributedString描述

http://my.oschina.net/lanrenbar/blog/395909

 

NSAttributedString 詳解

http://www.cnblogs.com/zhw511006/archive/2012/09/21/2696700.html

 

3    異步繪製

3.1    異步繪製示例

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{

       CGRect drawRect = _bgImageView.frame;

       

       UIGraphicsBeginImageContextWithOptions(drawRect.size,YES,0);

       CGContextRef context = UIGraphicsGetCurrentContext();

       

       if (!context) {

           return;

       }

       

       [[UIColor whiteColor] set];

       CGContextFillRect(context, drawRect);

       

       CGRect imgRect = CGRectZero;

       if ([_vm.contentImgPathlength] >0) {

           imgRect = CGRectMake(0,0,BODY_HEIGTH,_imageHeight);//_postContentImageView.frame;

           [_vm.contentImagedrawInRect:_postContentImageView.frameblendMode:kCGBlendModeNormalalpha:1];

       }

       

       CGRect textViewRect = CGRectZero;

        if ([_vm.contentTextlength] >0) {

            NSMutableAttributedString*str;

            if (!_isContentDisplayCompletly) {

                if (_vm.digestText) {

                    str = [[NSMutableAttributedStringalloc]initWithString:_vm.digestTextattributes:_postContentTextView.typingAttributes];

                } else

                    str = [[NSMutableAttributedStringalloc]initWithString:_vm.contentTextattributes:_postContentTextView.typingAttributes];

            } else

                str = [[NSMutableAttributedStringalloc]initWithString:_vm.contentTextattributes:_postContentTextView.typingAttributes];

            [str drawInRect:_postContentTextView.frame];

        }

       

        if (_subjectTitleHeight>0) {

           

            CGRectsubjectIconFrame =CGRectMake(_subjectButton.frame.origin.x,_subjectButton.frame.origin.y,10,12);//kContentToSubjectSpace

            UIImage*iconImg = [UIImageimageNamed:@"PostChannelFlageIcon"];

            subjectIconFrame.size =iconImg.size;

            [iconImg drawInRect:subjectIconFrameblendMode:kCGBlendModeNormalalpha:1];

           

            CGRectsubjectTitleFrame =CGRectMake(subjectIconFrame.origin.x +subjectIconFrame.size.width +2,subjectIconFrame.origin.y-1,200,_subjectTitleHeight);

           

            [_subjectButton.titleLabel.attributedTextdrawInRect:subjectTitleFrame];

//            [_subjectButtondrawRect:_subjectButton.frame];

        }

       

        UIImage*temp = UIGraphicsGetImageFromCurrentImageContext();

       

        UIGraphicsEndImageContext();

       

        dispatch_async(dispatch_get_main_queue(),^{

            _bgImageView.image =nil;

            _bgImageView.image =temp;

            [self setHidden:NO];

        });

    });

 

3.2    DrawRect之後注意用hitTest:withEvent:方法處理事件接收

//用戶觸摸時第一時間加載內容

- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event{

    UIView*result = [superhitTest:pointwithEvent:event];

    CGPointbuttonPoint = [_subjectButtonconvertPoint:pointfromView:self];

    if ([_subjectButtonpointInside:buttonPointwithEvent:event]){

        return _subjectButton;

    }

    returnresult;

}

 

3.3    參考鏈接

[iOS Animation]-CALayer 繪圖效率-異步繪製

http://my.oschina.net/u/2438875/blog/507545?fromerr=R4LnEaJ5

CGDataProviderCreateWithData對內存數據的釋放

http://www.taofengping.com/2012/11/04/cgdataprovidercreatewithdata_memory_release/#.VnJQ6jaitZF

 

IOS中使用像素位圖(CGImageRef)對圖片進行處理

http://my.oschina.net/u/2340880/blog/406437?p={{currentPage-1}}

 

 

4    Asyncdisplaykit

 


4.1    參考鏈接

Asyncdisplaykit 指南(一)

http://www.th7.cn/Program/IOS/201410/302413.shtml

 

AsyncDisplayKit 教程:達到 60FPS 的滾動幀率

http://www.cocoachina.com/swift/20141124/10298.html

 

http://asyncdisplaykit.org/guide/

 

AsyncDisplayKit入門指南

http://www.cocoachina.com/ios/20141020/9975.html

 

5    開發技巧

5.1    常見問題

5.1.1 CGBitmapContextCreateImage繪製後內存泄露導致內存告警

1、        CGBitmapContextCreateImage繪製的圖片會造成內存無法釋放,應該換用CGDataProviderCreateWithCFData。

 

5.1.1.1 方案一:修改源代碼,入緩存前壓縮

http://my.oschina.net/u/1244672/blog/510379

 

SDWebImage有一個SDWebImageDownloaderOperation類來執行下載操作的。裏面有個下載完成的方法:

 

- (void)connectionDidFinishLoading:(NSURLConnection*)aConnection {

SDWebImageDownloaderCompletedBlockcompletionBlock = self.completedBlock;

@synchronized(self) {

CFRunLoopStop(CFRunLoopGetCurrent());

self.thread= nil;

self.connection= nil;

[[NSNotificationCenterdefaultCenter] postNotificationName:SDWebImageDownloadStopNotificationobject:nil];

}

 

if(![[NSURLCache sharedURLCache] cachedResponseForRequest:_request]) {

responseFromCached= NO;

}

 

if(completionBlock)

{

if(self.options & SDWebImageDownloaderIgnoreCachedResponse &&responseFromCached) {

completionBlock(nil,nil, nil, YES);

}

else {

UIImage *image= [UIImage sd_imageWithData:self.imageData];

NSString *key= [[SDWebImageManager sharedManager] cacheKeyForURL:self.request.URL];

image = [selfscaledImageForKey:key image:image];

 

// Do notforce decoding animated GIFs

if(!image.images) {

image = [UIImagedecodedImageWithImage:image];

}


if(CGSizeEqualToSize(image.size, CGSizeZero)) {

completionBlock(nil,nil, [NSError errorWithDomain:@"SDWebImageErrorDomain" code:0userInfo:@{NSLocalizedDescriptionKey : @"Downloaded image has 0pixels"}], YES);

}

else {

completionBlock(image,self.imageData, nil, YES);

}

}

}


self.completionBlock= nil;

[self done];

}

 

其中,UIImage*image = [UIImage sd_imageWithData:self.imageData];就是將data轉換成image。

 

再看看sd_imageWithData:這個方法:

 

+ (UIImage*)sd_imageWithData:(NSData *)data {

UIImage*image;

NSString*imageContentType = [NSData sd_contentTypeForImageData:data];

if([imageContentType isEqualToString:@"image/gif"]) {

image =[UIImage sd_animatedGIFWithData:data];

}


#ifdef SD_WEBP

else if([imageContentType isEqualToString:@"image/webp"])

{

image =[UIImage sd_imageWithWebPData:data];

}

#endif

else {

image =[[UIImage alloc] initWithData:data];

UIImageOrientationorientation = [self sd_imageOrientationFromImageData:data];

if(orientation != UIImageOrientationUp) {

image =[UIImage imageWithCGImage:image.CGImage

scale:image.scale

orientation:orientation];

}

}

 

return image;

}

  

這個方法在UIImage+MultiFormat裏面,是UIImage的一個類別處理。這句話很重要image =[[UIImage alloc] initWithData:data]; SDWebImage把下載下來的data直接轉成image,然後沒做等比縮放直接存起來使用。所以,我們只需要在這邊做處理即可:

 

UIImage+MultiFormat添加一個方法:

 

+(UIImage*)compressImageWith:(UIImage *)image

{

float imageWidth= image.size.width;

float imageHeight = image.size.height;

float width =640;

float height =image.size.height/(image.size.width/width);

float widthScale = imageWidth /width;

float heightScale = imageHeight /height;

 

// 創建一個bitmapcontext

// 並把它設置成爲當前正在使用的context

UIGraphicsBeginImageContext(CGSizeMake(width,height));

 

if (widthScale> heightScale) {

[imagedrawInRect:CGRectMake(0, 0, imageWidth /heightScale , height)];

}

else {

[imagedrawInRect:CGRectMake(0, 0, width , imageHeight /widthScale)];

}

 

// 從當前context中創建一個改變大小後的圖片

UIImage*newImage = UIGraphicsGetImageFromCurrentImageContext();

// 使當前的context堆棧

UIGraphicsEndImageContext();

 

returnnewImage;

}

 

然後在:image =[[UIImage alloc] initWithData:data];下面調用以下:

if(data.length/1024 > 1024) {

image = [selfcompressImageWith:image];

}

 

data大於1M的時候做壓縮處理。革命尚未成功,還需要一步處理。在SDWebImageDownloaderOperation的connectionDidFinishLoading方法裏面的:

 

UIImage *image= [UIImage sd_imageWithData:self.imageData];

 

//將等比壓縮過的image在賦在轉成data賦給self.imageData

NSData *data =UIImageJPEGRepresentation(image, 1);

self.imageData = [NSMutableDatadataWithData:data];

 

5.1.1.2 方案二:設置全局緩存大小

http://www.myexception.cn/swift/2033029.html

 

1、首先在appdelegate方法 didFinishLaunchingWithOptions 

SDImageCache.sharedImageCache().maxCacheSize=1024*1024*8設置一下最大的緩存大小。

2、在appdelegate applicationDidReceiveMemoryWarning里加入

SDImageCache.sharedImageCache().clearMemory()

SDWebImageManager.sharedManager().cancelAll()

 

5.1.1.3 方案三:定時清理內存緩存

http://www.bubuko.com/infodetail-956863.html

 

經過嘗試,發現了一個最簡單的完美解決該問題的方法

 

在使用SDWebImage加載較多圖片造成內存警告時,定期調用

 [[SDImageCache sharedImageCache] setValue:nilforKey:@"memCache"];

 

5.1.1.4 方案四(不推薦):修復SD庫代碼,不做解壓,直接返回壓縮的原圖

 

5.1.1.5           方案五(推薦):使用CGDataProviderRef進行圖形解壓重繪

iOS開發中界面展示大圖片時UIImage的性能有關問題

http://www.myexception.cn/operating-system/578931.html

 

#import "SDWebImageDecoder.h"

 

@implementation UIImage (ForceDecode)

 

+ (UIImage*)decodedImageWithImage:(UIImage *)image {

    if (image.images) {

       // Do notdecode animated images

       return image;

    }

   

    //僅僅作爲臨時應付方案

//    return image;

   

    UIImage*decompressedImage;

   

@autoreleasepool {

//核心代碼,可以解決內存未釋放問題

        NSData *data=UIImageJPEGRepresentation(image,1);

        CGDataProviderRef dataProvider =CGDataProviderCreateWithCFData((__bridgeCFDataRef)data);

        CGImageRefimageRef =CGImageCreateWithPNGDataProvider(dataProvider,

                                                              NULL,NO,

kCGRenderingIntentDefault);

       

//    CGImageRef imageRef = image.CGImage;

    CGSizeimageSize = CGSizeMake(CGImageGetWidth(imageRef),CGImageGetHeight(imageRef));

    CGRectimageRect = (CGRect){.origin =CGPointZero, .size =imageSize};

 

    CGColorSpaceRefcolorSpace =CGColorSpaceCreateDeviceRGB();

    CGBitmapInfobitmapInfo = CGImageGetBitmapInfo(imageRef);

 

    intinfoMask = (bitmapInfo &kCGBitmapAlphaInfoMask);

    BOOLanyNonAlpha = (infoMask ==kCGImageAlphaNone ||

            infoMask == kCGImageAlphaNoneSkipFirst ||

            infoMask == kCGImageAlphaNoneSkipLast);

 

    // CGBitmapContextCreatedoesn't support kCGImageAlphaNone with RGB.

    // https://developer.apple.com/library/mac/#qa/qa1037/_index.html

    if(infoMask == kCGImageAlphaNone&& CGColorSpaceGetNumberOfComponents(colorSpace)>1) {

        // Unset the old alpha info.

        bitmapInfo &= ~kCGBitmapAlphaInfoMask;

 

        // Set noneSkipFirst.

        bitmapInfo |= kCGImageAlphaNoneSkipFirst;

    }

            // Some PNGs tell us theyhave alpha but only 3 components. Odd.

    else if(!anyNonAlpha && CGColorSpaceGetNumberOfComponents(colorSpace)==3) {

        // Unset the old alphainfo.

        bitmapInfo &= ~kCGBitmapAlphaInfoMask;

        bitmapInfo |= kCGImageAlphaPremultipliedFirst;

    }

 

    // It calculates thebytes-per-row based on the bitsPerComponent and width arguments.

    CGContextRefcontext = CGBitmapContextCreate(NULL,

            imageSize.width,

            imageSize.height,

            CGImageGetBitsPerComponent(imageRef),

            0,

            colorSpace,

            bitmapInfo);

    CGColorSpaceRelease(colorSpace);

 

    // If failed, returnundecompressed image

    if(!context) return image;

 

    CGContextDrawImage(context,imageRect, imageRef);

    CGImageRefdecompressedImageRef =CGBitmapContextCreateImage(context);

       

    CGContextRelease(context);

 

    decompressedImage = [UIImageimageWithCGImage:decompressedImageRefscale:image.scaleorientation:image.imageOrientation];

 

    CGImageRelease(decompressedImageRef);

    }

   

//    CVPixelBufferRef pixelBuffer;

//   

//    CreateCGImageFromCVPixelBuffer(pixelBuffer,&decompressedImageRef);

   

//    CGImage *cgImage =CGBitmapContextCreateImage(context);

//    CFDataRef dataRef =CGDataProviderCopyData(CGImageGetDataProvider(cgImage));

//    CGImageRelease(cgImage);

//    image->imageRef = dataRef;

//    image->image =CFDataGetBytePtr(dataRef);

   

    returndecompressedImage;

}

 

5.1.2 繪製時底部出現高度不定的細微黑線


問題原因:

            將Text做寬高計算時,高度值容易得出小數數值,而頁面繪製均是基於整數像素點繪製,對於小數點部分,系統會做捨去處理(即便有縮放),固留下高度不定的未繪製區域(爲黑色)。

 

解決方案:

            將計算出來的高度值做向下取整處理即可。

CGRect labelFrame = [content boundingRectWithSize:size options: NSStringDrawingTruncatesLastVisibleLine | NSStringDrawingUsesLineFragmentOrigin |NSStringDrawingUsesFontLeadingattributes:_postContentTextView.typingAttributescontext:nil];

   

    labelSize = labelFrame.size;

 

labelSize.height = ceilf(labelSize.height);

 

6    參考鏈接

(GOOD)iOS開發中界面展示大圖片時UIImage的性能有關問題

http://www.myexception.cn/operating-system/578931.html

 

(Good)iPhone - UIImage Leak, CGBitmapContextCreateImage Leak

http://stackoverflow.com/questions/1427478/iphone-uiimage-leak-cgbitmapcontextcreateimage-leak

 

Another iPhone - CGBitmapContextCreateImage Leak

http://stackoverflow.com/questions/1434714/another-iphone-cgbitmapcontextcreateimage-leak

 

UIGraphicsBeginImageContext vs CGBitmapContextCreate

http://stackoverflow.com/questions/4683448/uigraphicsbeginimagecontext-vs-cgbitmapcontextcreate

 

iPhone - CGBitmapContextCreateImage Leak, Anyone else withthis problem?

http://stackoverflow.com/questions/1431566/iphone-cgbitmapcontextcreateimage-leak-anyone-else-with-this-problem

 

Build and Analyze false positive on leak detection?

http://stackoverflow.com/questions/8438249/build-and-analyze-false-positive-on-leak-detection

 

iPhone - Multiple CGBitmapContextCreateImage Calls -ObjectAlloc climbing

http://stackoverflow.com/questions/1436465/iphone-multiple-cgbitmapcontextcreateimage-calls-objectalloc-climbing

 

(Good)ios開發圖片處理,內存泄露

http://www.oschina.net/question/736524_69802

 

主題 : CGBitmapContextCreateImage(bitmap)內存泄露問題處理

http://www.cocoachina.com/bbs/read.php?tid=31835

 

iOS異步圖片加載優化與常用開源庫分析

http://luoyibu.com/2015/05/12/iOS異步圖片加載優化與常用開源庫分析/

 

主題 : 圖片處理開源函數ImageProcessing  CGDataProviderCreateWithData Bug修復

http://www.cocoachina.com/bbs/read.php?tid=116149

 

CGDataProviderCreateWithData對內存數據的釋放

http://www.taofengping.com/2012/11/04/cgdataprovidercreatewithdata_memory_release/#.VmpqgoSitZE

 

IOS7.x下UIGraphicsGetImageFromCurrentImageContext引發內存暴漲,導致應用被結束掉

http://blog.163.com/l1_jun/blog/static/1438638820155593641529/

 

在 iOS 中與CGContextRef 的內存泄漏

http://www.itstrike.cn/Question/55b86ce7-dfba-4548-a103-22dc5317420a.html

 

Quartz 2D (ProgrammingWithQuartz) note

http://renxiangzyq.iteye.com/blog/1188025

 

使用AFNetworking, SDWebimage和OHHTTPStubs

http://blog.shiqichan.com/using-afnetworking-sdwebimage-and-ohhttpstubs/

 

SDWebImage緩存圖片的機制(轉)

http://blog.csdn.net/zhun36/article/details/8900327

 

近來一個swift項目用uicollectionview 用sdwebimage 加載圖片,發生內存猛增,直接閃退的情況,簡單說一下解決方案

http://www.myexception.cn/swift/2033029.html

 

關於SDWebImage加載高清圖片導致app崩潰的問題

http://www.bubuko.com/infodetail-956863.html

 

SDWebImage加載大圖導致的內存警告問題

http://blog.csdn.net/richer1997/article/details/43481959

 

解決MWPhotoBrowser中的SDWebImage加載大圖導致的內存警告問題

http://my.oschina.net/u/1244672/blog/510379

 

使用SDWebImage加載大量圖片後造成內存泄露的解決辦法

http://www.bubuko.com/infodetail-985746.html

 

UIGraphicsBeginImageContext系列知識

http://blog.sina.com.cn/s/blog_5fb39f9101017n1v.html

 

iOS繪圖教程

http://blog.csdn.net/nogodoss/article/details/18660153

 

Quartz 2D 參考-文本

http://blog.csdn.net/kmyhy/article/details/7258338

 

CGBitmapContextCreate函數

http://blog.csdn.net/thanklife/article/details/25790433

 

UIGraphicsBeginImageContext創建的映像停留在內存中永恆

http://codego.net/589714/

 

多次在cell中加載網絡圖片後,內存增長,以前資源未釋放

http://bbs.csdn.net/topics/390891681

 

請問下面的代碼有潛在的內存泄漏?

http://codego.net/459077/

 

[ios]UIGraphicsGetImageFromCurrentImageContext()-內存泄漏

http://www.itstrike.cn/Question/88ada9bd-911c-44a7-874b-e04c1a1c2bca.html

 

[轉載]ios開發之View屬性hidden, opaque, alpha的區別

http://blog.sina.com.cn/s/blog_7da2c9030101ev8n.html

利用預渲染加速iOS設備的圖像顯示

http://www.keakon.net/2011/07/26/利用預渲染加速iOS設備的圖像顯示

 

iOS使用CGContextRef繪製各種圖形

http://www.devstore.cn/essay/essayInfo/116.html

 

iOS CGContextRef畫圖小結

http://blog.sina.com.cn/s/blog_9693f61a0101deko.html

 

IOS用CGContextRef畫各種圖形(文字、圓、直線、弧線、矩形、扇形、橢圓、三角形、圓角矩形、貝塞爾曲線、圖片)

http://blog.csdn.net/rhljiayou/article/details/9919713

 

iOS 畫圖 以及清空

http://blog.csdn.net/woshidaniu/article/details/46683409

 

iOS通過Quartz畫矩形、文字、線

http://blog.csdn.net/onlyou930/article/details/7726399


發佈了44 篇原創文章 · 獲贊 5 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章