趁著剛剛上班還有空的時間繼續(xù)寫一寫博客,今天剛上班就被告知產(chǎn)品6.2已經(jīng)設(shè)計好了侥蒙,明天開發(fā)暗膜,然后看了下需求,瞬間又淚奔了鞭衩,又是一輪奮力地去干学搜。
好了,前話就說這么多论衍,我們繼續(xù)我們的圖形性能優(yōu)化了瑞佩,值得一說的就是上一篇文章編寫離屏渲染那段不太清晰,還是需要找個時間去修改一下坯台,讓各位小伙伴更明白炬丸。
6 使用后臺繪制
當(dāng)你的應(yīng)用存在的繪圖部分并不是那么流暢的時候,你可以去檢驗一下是否你的 drawRect: 就是影響你應(yīng)用性能的瓶頸蜒蕾,那么稠炬,你可以將這段繪制代碼放到后臺去做。但是在你這么做之前咪啡,檢查是不是有其他方法來解決首启,比如、考慮使用 core animation layers 或者預(yù)先渲染圖片而不去做 Core Graphics 繪制撤摸∶銎拢可以看看 Florian 對在真機(jī)上圖像性能測量的帖子,或者可以看看來自 UIKit 工程師 Andy Matuschak 對個各種方式的權(quán)衡的評論愁溜。
我們或多或少都聽過后臺繪制疾嗅,但是實質(zhì)上我們該怎么做呢?思路如下:
將原本打算繪制的視圖用一個 UIImageView 來代替冕象,當(dāng)所有繪制操作完成后渲染成一張 UIImage 代承,
然后來替換到 UIImageView 中去。在繪制的方法中渐扮,使用UIGraphicsBeginImageContextWithOptions
來取代 UIGraphicsGetCurrentContext论悴。
talk is cheap掖棉,show me the code。下面膀估,我就一步一步教小伙伴們?nèi)绾稳プ觥?/p>
1.首先創(chuàng)建一個 ASynchronousDrawView
類繼承 UIView
ASynchronousDrawView.h
@interface ASynchronousDrawView : UIView
@end
ASynchronousDrawView.m
@interface ASynchronousDrawView()
@property (nonatomic , weak) UIImageView *imageView;
@end
2.然后在 initWithFrame:
方法中去建立好 UIImageView
ASynchronousDrawView.m
@implementation ASynchronousDrawView
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
UIImageView *imageView = [[UIImageView alloc] init];
imageView.frame = self.bounds;
imageView.backgroundColor = [UIColor whiteColor];
[self addSubview:imageView];
self.imageView = imageView;
}
return self;
}
3.好了幔亥,接下來就是我們的繪制方法,我下面代碼只是一個演示察纯,并不是說這段代碼很耗時帕棉,小伙伴們要根據(jù)情況來用喔。
- (void)asynchronousDrawComplete:(void(^)(UIImage *))complete {
// 異步繪制饼记,在繪制方法中香伴,使用 UIGraphicsBeginImageContextWithOptions 來取代 UIGraphicsGetCurrentContext
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 第三個參數(shù)傳入0 ,設(shè)備的主屏幕的 scale 將被自動傳入具则,這將使圖片在普通設(shè)備和 retina 屏幕上都有良好的表現(xiàn)即纲。[UIScreen mainScreen].scale
UIGraphicsBeginImageContextWithOptions(CGSizeMake(self.frame.size.width, self.frame.size.height), true, 0);
// 實例畫圖代碼(耗時的繪制應(yīng)放在這里)
UIBezierPath *bezier = [UIBezierPath bezierPath];
[[UIColor redColor] setFill];
[bezier addArcWithCenter:CGPointMake(self.frame.size.width * 0.5, self.frame.size.height * 0.5) radius:40 startAngle:0 endAngle:M_PI * 2 clockwise:true];
[bezier fill];
// 將繪制轉(zhuǎn)換成圖片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// 關(guān)閉圖片上下文
UIGraphicsEndImageContext();
dispatch_async(dispatch_get_main_queue(), ^{
// 繪制操作完成,將圖片傳出去
complete(image);
});
});
}
4.調(diào)用后臺繪制方法來替換 UIImageView
的 UIImage
// 開始異步繪制
__weak typeof(self) weakSelf = self;
[self asynchronousDrawComplete:^(UIImage *image) {
weakSelf.iconView.image = image;
}];
當(dāng)然博肋,我們調(diào)用到 block 的小伙伴低斋,記得,防止循環(huán)引用喔匪凡。
以上就是后臺繪制的方法實現(xiàn)了拔稳,根據(jù)這個方法我們可以將 drawRect: 方法中耗時的繪制分離出來,讓我們的性能進(jìn)一步提高锹雏。除了在后臺自己調(diào)度繪制代碼,以也可以試試看使用 CALayer 的 drawsAsynchronously 屬性术奖。然而你需要精心衡量這樣做的效果礁遵,因為有時候它能使繪制加速,有時候卻適得其反采记。
7 使用 NSCache 來為緩存 UITableView 中的 SectionHeadView
我們在使用 UITableView 的時候可能要為每一組 Section 都得加一個一個 HeadView佣耐,然后就會用到
*-(UIView )tableView:viewForHeaderInSection: 這個方法,小伙伴們通常都會這么調(diào)用
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
UIView *secHeadView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
return secHeadView;
}
但是通過打下斷點的方法我們就知道唧龄,當(dāng)我們滾動 tableView兼砖,每當(dāng)滾動到每一組的 headView 的時候就會調(diào)用一次,上面方法就會產(chǎn)生一個 secHeadView
對象既棺,這樣做無疑是浪費(fèi)我們的內(nèi)存讽挟,能少創(chuàng)建對象才是皇道呀。
那么我們該怎么優(yōu)化這樣需求呢丸冕,沒錯耽梅,我們需要把視圖緩存,那么就不需要每次滾動的時候就去重新創(chuàng)建一次了胖烛,然后我們在適當(dāng)?shù)臅r候?qū)⒕彺驷尫诺粞劢悖迅嗟膬?nèi)存騰出來诅迷。但是,這里又衍生了一個問題众旗,緩存方法有很多罢杉,例如用 NSArray
,NSDictionary
等這些容器對象贡歧,或者用 NSPointerFunctions
這些指針容器類滩租。但是,我們在這里選擇 NSCache艘款。
為什么選擇NSCache
持际?
NSCache 用法跟 NSDictionary 一樣,可以調(diào)用 objectForKey:哗咆、setObject:forkey:和 removeObjectForkey蜘欲。
但是 NSCache 有一些特性是被低估了,比如其多線程的安全性晌柬,小伙伴們可以在任何線程不加鎖的情況下修改 NSCache姥份。
NSCache 還沒設(shè)計為能與符合<NSDiscardableContent>協(xié)議的對象整合,這不僅能在應(yīng)用運(yùn)行的時候提供
自動緩存管理年碘,甚至在應(yīng)用暫停的時候也有用澈歉。意思當(dāng)你的內(nèi)存吃緊的時候,系統(tǒng)會幫你釋放 NSCache 內(nèi)的對象屿衅,
而且埃难,這是相當(dāng)安全的行為。
還是來代碼讓小伙伴們精神精神吧涤久。
1.創(chuàng)建 NSCache
對象
self.scHeadcache = [[NSCache alloc] init];
2.在*-(UIView )tableView:viewForHeaderInSection: 檢驗一下涡尘。
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
UIView *view;
if (![self.scHeadcache objectForKey:@(section)]) {
view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
}
else {
view = [self.scHeadcache objectForKey:@(section)];
}
return view;
}
當(dāng)然這個 NSCache 對象也是會占內(nèi)存的,所以我們會在適當(dāng)?shù)臅r候把它釋放出去响迂,那什么是適當(dāng)?shù)臅r候呢考抄,當(dāng)然是用戶不再對著頁面感興趣的時候啦。
@end
好了蔗彤,今天先寫這么多川梅,還得認(rèn)認(rèn)真真的擼代碼,新的一年然遏,大家加油喔贫途。