問題:
如果項目中使用了xib來創建視圖,并且xib中包含了一些iOS系統中不支持的字體,如圖:
那么在低系統版本中,這個視圖的加載就會異常緩慢,使用iPhone4S iOS8.1.2測試結果顯示單獨加載這3個Label就需要大概3秒多鐘的時間!但是第二次進入時間正常。
分析:
iOS8
當我們在xib中把字體改為system的時候,加載速度變得正常起來,那么有理由相信是字體導致了整個界面的加載變慢。
使用Time Profiler來驗證一下:
發現時間都花在了CoreText的一些底層操作上,猜想就是系統在尋找支持的字體。
把修改字體的過程寫在代碼里:
- (void)viewDidLoad {
[super viewDidLoad];
self.lblTest.font = [UIFont fontWithName:@"STHeitiSC-Light" size:18];
NSLog(@"%@",_lblTest.font);
// Do any additional setup after loading the view from its nib.
}
此時Time Profiler:
可以看到現在相同的方法,執行效率立馬高了很多(因為沒花時間找)。
這里是加載字體的主要方法。
iOS9
當換成iOS9系統進行相同Time Profiler時,我試著去找相同加載字體的方法,但是貌似兩者的call tree也有較大的不同,是蘋果修改了這個字體加載致緩的Bug還是其他原因就不得而知了,總之在iOS9上,對于找不到的字體,系統會很快做出反應。
還有一點比較奇怪的是,我在xib中使用的是STHeitiSC-Light,實際最后加載出來Label的字體卻被替換成了另外一種:PingFangSC-Light
我以為是因為字體不存在造成了替換,后來發現就算你的系統下載了STHeitiSC-Light,也會在iOS9中被替換為PingFangSC-Light。
最后看了些資料,最后發現是因為iOS9更新之后對于“黑體”字的改進,蘋果推出了自己的黑體:蘋方
為了界面統一的關系,估計蘋果會把所有的黑體改成自家的蘋方字體來以示統一。
但是TMD又奇怪了,如果在代碼中規定字體:
- (void)viewDidLoad {
[super viewDidLoad];
self.lblTest.font = [UIFont fontWithName:@"STHeitiSC-Light" size:18];
NSLog(@"%@",_lblTest.font);
// Do any additional setup after loading the view from its nib.
}
輸出如下:
所以這個字體到底是什么規律加載的,真的是有點懵逼。
使用xib的call tree:
紅框部分為主要加載字體的方法,對比iOS8發現兩者的加載方式幾乎完全不一樣了。
iOS9舍棄了之前那種又臭又長的加載方式,使用新庫:libFontParser.dylib來解析字體,提高了速度。
使用代碼的call tree:
紅框部分為主要的兩個字體加載方法。
與iOS8類似,但是仔細觀察發現iOS9中也引入了新庫:libFontParser.dylib來幫助解析字體。
小結一下:
系統差異:
9使用了libFontParser.dylib,使得加載速度和方式上有了改變,不會造成8上的異常卡頓。
并且libFontParser.dylib在xib和UIFont方法中都發揮了作用,猜測是一個字體加載的統一庫。
xib和代碼的差異:
兩系統下xib方式的加載確實存在很大的差異,光看9下的表現,字體更像是“畫”出來的,而8下xib字體的尋找仍然走的是fontWithDescriptor:這樣的UIFont方法。
代碼方法的話,都走UIFont方法,只不過9使用了libFontParser.dylib
疑惑:
- 1 不管是iOS8還是9,兩者使用xib都無法加載到STHeitiSC-Light,雖然兩者系統的字體庫都支持該字體,并且字體存在。
- 2 iOS9下xib不能加載到STHeitiSC-Light,代碼法卻仍然生效。
對于以上兩個問題,做的分析還不夠,自己學的也不多,還不知道原因。
希望有了解的可以多多指教。
字體下載:
蘋果開放了字體下載的API,可以對設備支持的字體但又不在本地的字體進行下載,直接上代碼:
NSMutableDictionary *fontAttrs = [NSMutableDictionary dictionaryWithObjectsAndKeys:@"STHeitiSC-Light", kCTFontNameAttribute, nil];
CTFontDescriptorRef desc = CTFontDescriptorCreateWithAttributes((__bridge CFDictionaryRef)fontAttrs);
NSMutableArray *descArray = [NSMutableArray new];
[descArray addObject:(__bridge id)desc];
CFRelease(desc);
//封裝成一個CF的字體對象
//將對象傳入,進行下載
CTFontDescriptorMatchFontDescriptorsWithProgressHandler((__bridge CFArrayRef)descArray, NULL, ^bool(CTFontDescriptorMatchingState state, CFDictionaryRef _Nonnull progressParameter) {
NSDictionary *progressDic = (__bridge NSDictionary *)progressParameter;
NSLog(@"state: %u",state);
NSLog(@"progress: %@",progressDic);
return YES;
});
return YES;
//
注意的一點是:CTFontDescriptorMatchingState,它描述了當前查找字體的“狀態”
kCTFontDescriptorMatchingDidBegin, // 開始匹配。
kCTFontDescriptorMatchingDidFinish, // 結束匹配。
kCTFontDescriptorMatchingWillBeginQuerying, // 與服務器連接前會是此狀態
kCTFontDescriptorMatchingStalled, // 掛起,等待服務器回調時是此狀態
kCTFontDescriptorMatchingWillBeginDownloading, // 將要開始下載
kCTFontDescriptorMatchingDownloading, // 正在下載
kCTFontDescriptorMatchingDidFinishDownloading, // 完成下載
kCTFontDescriptorMatchingDidMatch, // 已經匹配
kCTFontDescriptorMatchingDidFailWithError // 發生錯誤,可能會多次發生
官方文檔:
In iOS 6.0 and later, apps can download on demand available fonts that are not installed using the CTFontDescriptorMatchFontDescriptorsWithProgressHandler
function. Fonts downloaded this way are not installed permanently, and the system may remove them under certain circumstances. Fonts available for downloading are listed in as “Additional Information” in iOS 6: Font list and iOS 7: Font list. DownloadFont (in the iOS Developer Library) demonstrates the download technique. Downloading fonts on demand is unnecessary in OS X because all available fonts are installed with the system.
大致意思就是,以這種方式下載的字體并不是永久保存的,在特定條件下可能會被系統刪除,可支持下載的字體可以在
iOS 6: Font list
iOS 7: Font list.
DownloadFont
進行查找。