OC語法篇
面向?qū)ο?/h3>
1. 一個(gè)NSObject對(duì)象占用多少內(nèi)存?
系統(tǒng)分配了16個(gè)字節(jié)給NSobject對(duì)象(通過malloc_size函數(shù)獲得)灶挟,但NSobject對(duì)象內(nèi)部只是用了8個(gè)字節(jié)的空間(64bit環(huán)境下,可以通過class_getInstanceSize函數(shù)獲得)
2. 對(duì)象的isa指針指向哪里亚斋?
instance對(duì)象的isa指針指向class對(duì)象扎酷;class對(duì)象的isa指針指向meta-class對(duì)象;meta-class對(duì)象的isa指針指向基類的meta-class對(duì)象
3. OC的類信息存放在哪里?
對(duì)象方法酪捡、屬性、成員變量纳账、協(xié)議信息逛薇,存放在class對(duì)象中;類方法存放在meta-class對(duì)象中疏虫;成員變量的具體值永罚,存放在instance對(duì)象中啤呼。
OC對(duì)象分為三種對(duì)象
instance對(duì)象:實(shí)例對(duì)象(包含isa指針、成員變量)
class對(duì)象:類對(duì)象(包含isa指針呢袱、superclass官扣、屬性、對(duì)象方法羞福、協(xié)議惕蹄、成員變量···)
meta-class對(duì)象:元類對(duì)象(包含isa指針、superclass治专、類方法)
4. isa焊唬、superclass總結(jié)
instance的isa指向class
class的isa指向meta-class
meta-class的isa指向基類的meta-class
class的superclass指向父類的class
如果沒有父類,superclass指針為nil
meta-class的superclass指向父類的meta-class
基類的meta-class的superclass指向基類的class
instance調(diào)用對(duì)象方法的軌跡
isa找到class看靠,方法不存在赶促,就通過superclass找父類
class調(diào)用類方法的軌跡
isa找meta-class,方法不存在挟炬,就通過superclass找父類
KVO and KVC
1. iOS用什么方式實(shí)現(xiàn)對(duì)一個(gè)對(duì)象的KVO鸥滨?(KVO的本質(zhì)是什么?)
利用RuntimeAPI動(dòng)態(tài)生成一個(gè)子類谤祖,并且讓instance對(duì)象的isa指向這個(gè)全新的子類
當(dāng)修改instance對(duì)象的屬性時(shí)婿滓,會(huì)調(diào)用Foundation的_NSSetXXXValueAndNotify函數(shù)
willChangeValueForKey:
父類原來的setter
didChangeValueForKey:
內(nèi)部會(huì)觸發(fā)監(jiān)聽器(Oberser)的監(jiān)聽方法( observeValueForKeyPath:ofObject:change:context:)
2. 如何手動(dòng)觸發(fā)KVO?
手動(dòng)調(diào)用willChangeValueForKey:和didChangeValueForKey:
3. 通過KVC修改屬性會(huì)觸發(fā)KVO么粥喜?
會(huì)觸發(fā)KVO
4. KVC的賦值和取值過程是怎樣的凸主?原理是什么?
KVC的全稱是Key-Value Coding额湘,俗稱“鍵值編碼”卿吐,可以通過一個(gè)key來訪問某個(gè)屬性
常見的API有
- (void)setValue:(id)value forKeyPath:(NSString *)keyPath;
- (void)setValue:(id)value forKey:(NSString *)key;
- (id)valueForKeyPath:(NSString *)keyPath;
- (id)valueForKey:(NSString *)key;
賦值過程:
按照setKey:、_setKey順序查找方法锋华,找到了方法傳遞參數(shù)嗡官,調(diào)用方法;若沒找到毯焕,查看accessInstanceVariablesDirectly方法的返回值衍腥,若為NO則調(diào)用setValue:forUndefinedKey:并拋出異常NSUnknownKeyException,若返回YES則按照_key纳猫、_isKey婆咸、key、isKey順序查找成員變量芜辕,找到直接賦值尚骄,找不到則調(diào)用setValue:forUndefinedKey:并拋出異常NSUnknownKeyException
取值過程
按照getKey:、key物遇、_isKey乖仇、key順序查找方法憾儒,找到了方法询兴,調(diào)用方法乃沙;若沒找到,查看accessInstanceVariablesDirectly方法的返回值诗舰,若為NO則調(diào)用valueForUndefinedKey:并拋出異常NSUnknownKeyException警儒,若返回YES則按照_key、_isKey眶根、key蜀铲、isKey順序查找成員變量,找到直接取值属百,找不到則調(diào)用valueForUndefinedKey:并拋出異常NSUnknownKeyException
Category
1. Category的使用場合是什么记劝?
2. Category的實(shí)現(xiàn)原理
Category編譯之后的底層結(jié)構(gòu)是struct category_t,里面存儲(chǔ)著分類的對(duì)象方法族扰、類方法厌丑、屬性、協(xié)議信息
在程序運(yùn)行的時(shí)候渔呵,runtime會(huì)將Category的數(shù)據(jù)怒竿,合并到類信息中(類對(duì)象、元類對(duì)象中)
3. Category和Class Extension的區(qū)別是什么扩氢?
Class Extension在編譯的時(shí)候耕驰,它的數(shù)據(jù)就已經(jīng)包含在類信息中
Category是在運(yùn)行時(shí),才會(huì)將數(shù)據(jù)合并到類信息中
4. Category中有l(wèi)oad方法嗎录豺?load方法是什么時(shí)候調(diào)用的朦肘?load 方法能繼承嗎?
有l(wèi)oad方法
load方法在runtime加載類双饥、分類的時(shí)候調(diào)用
load方法可以繼承厚骗,但是一般情況下不會(huì)主動(dòng)去調(diào)用load方法,都是讓系統(tǒng)自動(dòng)調(diào)用
5. load兢哭、initialize方法的區(qū)別什么领舰?它們?cè)赾ategory中的調(diào)用的順序?以及出現(xiàn)繼承時(shí)他們之間的調(diào)用過程迟螺?
+load方法會(huì)在runtime加載類冲秽、分類時(shí)調(diào)用
每個(gè)類、分類的+load矩父,在程序運(yùn)行過程中只調(diào)用一次
調(diào)用順序:先調(diào)用類的+load锉桑,按照編譯先后順序調(diào)用(先編譯,先調(diào)用)窍株,調(diào)用子類的+load之前會(huì)先調(diào)用父類的+load民轴;再調(diào)用分類的+load攻柠,按照編譯先后順序調(diào)用(先編譯,先調(diào)用)
+initialize方法會(huì)在類第一次接收到消息時(shí)調(diào)用后裸,調(diào)用順序:先調(diào)用父類的+initialize瑰钮,再調(diào)用子類的+initialize,(先初始化父類微驶,再初始化子類浪谴,每個(gè)類只會(huì)初始化1次)
+initialize和+load的很大區(qū)別是,+initialize是通過objc_msgSend進(jìn)行調(diào)用的因苹,所以有以下特點(diǎn):如果子類沒有實(shí)現(xiàn)+initialize苟耻,會(huì)調(diào)用父類的+initialize(所以父類的+initialize可能會(huì)被調(diào)用多次);如果分類實(shí)現(xiàn)了+initialize扶檐,就覆蓋類本身的+initialize調(diào)用
6. Category能否添加成員變量凶杖?如果可以,如何給Category添加成員變量款筑?
不能直接給Category添加成員變量智蝠,但是可以間接實(shí)現(xiàn)Category有成員變量的效果
添加關(guān)聯(lián)對(duì)象:void objc_setAssociatedObject(id object, const void * key,
id value, objc_AssociationPolicy policy)
獲得關(guān)聯(lián)對(duì)象:id objc_getAssociatedObject(id object, const void * key)
移除所有的關(guān)聯(lián)對(duì)象:void objc_removeAssociatedObjects(id object)
block
1. block的原理是怎樣的?本質(zhì)是什么醋虏?
block本質(zhì)上也是一個(gè)OC對(duì)象寻咒,它內(nèi)部也有個(gè)isa指針,封裝了函數(shù)調(diào)用以及調(diào)用環(huán)境的OC對(duì)象
block有3種類型颈嚼,可以通過調(diào)用class方法或者isa指針查看具體類型毛秘,最終都是繼承自NSBlock類型
__NSGlobalBlock__(_NSConcreteGlobalBlock):存儲(chǔ)在數(shù)據(jù)區(qū),沒有訪問auto變量
__NSStackBlock__(_NSConcreteStackBlock):存儲(chǔ)在棧區(qū),訪問auto變量
__NSMallocBlock__(_NSConcreteMallocBlock):存儲(chǔ)在堆區(qū),__NSStackBlock__調(diào)用了copy
2. __block的作用是什么阻课?有什么使用注意點(diǎn)叫挟?
__block可以用于解決block內(nèi)部無法修改auto變量值的問題
__block不能修飾全局變量、靜態(tài)變量(static)
編譯器會(huì)將__block變量包裝成一個(gè)對(duì)象
3. block的屬性修飾詞為什么是copy限煞?使用block有哪些使用注意抹恳?
block一旦沒有進(jìn)行copy操作,就不會(huì)在堆上
使用注意:循環(huán)引用問題
__weak typeof(self)weakSelf = self;
__unsafe _unretained id weakSelf = self;
__block id weakSelf = self;
Runtime
1. 講一下 OC 的消息機(jī)制
OC中的方法調(diào)用其實(shí)都是轉(zhuǎn)成了objc_msgSend函數(shù)的調(diào)用署驻,給receiver(方法調(diào)用者)發(fā)送了一條消息(selector方法名)
objc_msgSend底層有3大階段
消息發(fā)送(當(dāng)前類奋献、父類中查找)、動(dòng)態(tài)方法解析旺上、消息轉(zhuǎn)發(fā)
2. 什么是Runtime瓶蚂?平時(shí)項(xiàng)目中有用過么?
OC是一門動(dòng)態(tài)性比較強(qiáng)的編程語言宣吱,允許很多操作推遲到程序運(yùn)行時(shí)再進(jìn)行
OC的動(dòng)態(tài)性就是由Runtime來支撐和實(shí)現(xiàn)的窃这,Runtime是一套C語言的API,封裝了很多動(dòng)態(tài)性相關(guān)的函數(shù)
平時(shí)編寫的OC代碼征候,底層都是轉(zhuǎn)換成了Runtime API進(jìn)行調(diào)用
具體應(yīng)用
利用關(guān)聯(lián)對(duì)象(AssociatedObject)給分類添加屬性杭攻;遍歷類的所有成員變量(修改textfield的占位文字顏色祟敛、字典轉(zhuǎn)模型、自動(dòng)歸檔解檔)兆解; 交換方法實(shí)現(xiàn)(交換系統(tǒng)的方法)馆铁;利用消息轉(zhuǎn)發(fā)機(jī)制解決方法找不到的異常問題
RunLoop
1. 講講 RunLoop,項(xiàng)目中有用到嗎痪宰?
運(yùn)行循環(huán)叼架,在程序運(yùn)行過程中循環(huán)做一些事情
應(yīng)用范疇:定時(shí)器(Timer)畔裕、PerformSelector衣撬;GCD Async Main Queue;事件響應(yīng)扮饶、手勢(shì)識(shí)別具练、界面刷新;網(wǎng)絡(luò)請(qǐng)求甜无;AutoreleasePool
RunLoop的基本作用:保持程序的持續(xù)運(yùn)行扛点;處理App中的各種事件(比如觸摸事件、定時(shí)器事件等)岂丘;節(jié)省CPU資源陵究,提高程序性能:該做事時(shí)做事,該休息時(shí)休息
2. runloop內(nèi)部實(shí)現(xiàn)邏輯奥帘?
3. runloop和線程的關(guān)系铜邮?
每條線程都有唯一的一個(gè)與之對(duì)應(yīng)的RunLoop對(duì)象
RunLoop保存在一個(gè)全局的Dictionary里,線程作為key寨蹋,RunLoop作為value
線程剛創(chuàng)建時(shí)并沒有RunLoop對(duì)象松蒜,RunLoop會(huì)在第一次獲取它時(shí)創(chuàng)建
RunLoop會(huì)在線程結(jié)束時(shí)銷毀
主線程的RunLoop已經(jīng)自動(dòng)獲取(創(chuàng)建)已旧,子線程默認(rèn)沒有開啟RunLoop
4. timer 與 runloop 的關(guān)系秸苗?
5. 程序中添加每3秒響應(yīng)一次的NSTimer,當(dāng)拖動(dòng)tableview時(shí)timer可能無法響應(yīng)要怎么解決运褪?
6. runloop 是怎么響應(yīng)用戶操作的惊楼, 具體流程是什么樣的?
7. 說說runLoop的幾種狀態(tài)
eg:添加Observer監(jiān)聽RunLoop的所有狀態(tài)
CFRunLoopObserverRef observe = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, yearMask, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
switch (activity) {
case kCFRunLoopEntry:
NSLog(@"kCFRunLoopEntry");//即將進(jìn)入Loop
break;
case kCFRunLoopBeforeTimers:
NSLog(@"kCFRunLoopBeforeTimers");//即將處理Timers
break;
case kCFRunLoopBeforeSources:
NSLog(@"kCFRunLoopBeforeSources");//即將處理Sources
break;
case kCFRunLoopBeforeWaiting:
NSLog(@"kCFRunLoopBeforeWaiting");//即將進(jìn)入休眠
break;
case kCFRunLoopAfterWaiting:
NSLog(@"kCFRunLoopAfterWaiting");//剛從休眠中喚醒
break;
case kCFRunLoopExit:
NSLog(@"kCFRunLoopExit");//即將退出Loop
break;
default:
break;
}
});
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observe, kCFRunLoopCommonModes);
CFRelease(observe);
8. runloop的mode作用是什么秸讹?
CFRunLoopModeRef代表RunLoop的運(yùn)行模式
一個(gè)RunLoop包含若干個(gè)Mode檀咙,每個(gè)Mode又包含若干個(gè)Source0/Source1/Timer/Observer
RunLoop啟動(dòng)時(shí)只能選擇其中一個(gè)Mode,作為currentMode
如果需要切換Mode嗦枢,只能退出當(dāng)前Loop攀芯,再重新選擇一個(gè)Mode進(jìn)入
不同組的Source0/Source1/Timer/Observer能分隔開來,互不影響
如果Mode里沒有任何Source0/Source1/Timer/Observer文虏,RunLoop會(huì)立馬退出
常見的2種Mode
kCFRunLoopDefaultMode(NSDefaultRunLoopMode):App的默認(rèn)Mode侣诺,通常主線程是在這個(gè)Mode下運(yùn)行
UITrackingRunLoopMode:界面跟蹤 Mode殖演,用于 ScrollView 追蹤觸摸滑動(dòng),保證界面滑動(dòng)時(shí)不受其他 Mode 影響
多線程
1. iOS中的線程同步方案
性能從高到低排序
OSSpinLock
OSSpinLock叫做”自旋鎖”年鸳,等待鎖的線程會(huì)處于忙等(busy-wait)狀態(tài)趴久,一直占用著CPU資源
目前已經(jīng)不再安全,可能會(huì)出現(xiàn)優(yōu)先級(jí)反轉(zhuǎn)問題
如果等待鎖的線程優(yōu)先級(jí)較高搔确,它會(huì)一直占用著CPU資源彼棍,優(yōu)先級(jí)低的線程就無法釋放鎖
需要導(dǎo)入頭文件#import <libkern/OSAtomic.h>
//初始化
OSSpinLock lock = OS_SPINLOCK_INIT;
//嘗試加鎖(如果需要等待就不加鎖,直接返回false;如果不需要等待就加鎖,返回true)
bool result = OSSpinLockTry(&lock);
//加鎖
OSSpinLockLock(&lock);
//解鎖
OSSpinLockUnLock(&lock);
os_unfair_lock
os_unfair_lock
os_unfair_lock用于取代不安全的OSSpinLock ,從iOS10開始才支持
從底層調(diào)用看膳算,等待os_unfair_lock鎖的線程會(huì)處于休眠狀態(tài)座硕,并非忙等
需要導(dǎo)入頭文件#import <os/lock.h>
//初始化
os_unfair_lock lock = OS_UNFAIR_LOCK_INIT;
//嘗試加鎖
os_unfair_lock_trylock(&lock);
//加鎖
os_unfair_lock_lock(&lock);
//解鎖
os_unfair_lock_unlock(&lock);
pthread_mutex
mutex叫做”互斥鎖”,等待鎖的線程會(huì)處于休眠狀態(tài)
需要導(dǎo)入頭文件#import <pthread.h>
dispatch_semaphore:
semaphore叫做”信號(hào)量”
信號(hào)量的初始值涕蜂,可以用來控制線程并發(fā)訪問的最大數(shù)量
信號(hào)量的初始值為1华匾,代表同時(shí)只允許1條線程訪問資源,保證線程同步
//信號(hào)量的初始值
int value = 1;
//初始化信號(hào)量
dispatch_semaphore_t semaphore = dispatch_semaphore_create(value);
//如果信號(hào)量的值<=0,當(dāng)前線程就會(huì)進(jìn)入休眠等待(直到信號(hào)量的值>0)
//如果信號(hào)量的值>0,就減1机隙,然后往下執(zhí)行后面的代碼
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
//讓信號(hào)量的值加1
dispatch_semaphore_signal(semaphore);
dispatch_queue(DISPATCH_QUEUE_SERIAL)
直接使用GCD的串行隊(duì)列蜘拉,也是可以實(shí)現(xiàn)線程同步的
dispatch_queue_t queue = dispatch_queue_create("lock_queue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
//任務(wù)
});
NSLock:對(duì)mutex普通鎖的封裝
NSRecursiveLock
NSCondition:對(duì)mutex和cond的封裝
NSConditionLock:對(duì)NSCondition的進(jìn)一步封裝,可以設(shè)置具體的條件值
@synchronized:是對(duì)mutex遞歸鎖的封裝
什么情況使用自旋鎖比較劃算有鹿?
預(yù)計(jì)線程等待鎖的時(shí)間很短
加鎖的代碼(臨界區(qū))經(jīng)常被調(diào)用旭旭,但競爭情況很少發(fā)生
CPU資源不緊張
多核處理器
什么情況使用互斥鎖比較劃算?
預(yù)計(jì)線程等待鎖的時(shí)間較長
單核處理器
臨界區(qū)有IO操作
臨界區(qū)代碼復(fù)雜或者循環(huán)量大
臨界區(qū)競爭非常激烈
內(nèi)存管理
1. 使用CADisplayLink葱跋、NSTimer有什么注意點(diǎn)持寄?
CADisplayLink、NSTimer會(huì)對(duì)target產(chǎn)生強(qiáng)引用年局,如果target又對(duì)它們產(chǎn)生強(qiáng)引用际看,那么就會(huì)引發(fā)循環(huán)引用
解決方案
使用block 弱引用weakSelf
使用代理對(duì)象(NSProxy)
NSTimer依賴于RunLoop,如果RunLoop的任務(wù)過于繁重矢否,可能會(huì)導(dǎo)致NSTimer不準(zhǔn)時(shí)仲闽,而GCD的定時(shí)器會(huì)更加準(zhǔn)時(shí)
//創(chuàng)建一個(gè)定時(shí)器
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
//設(shè)置時(shí)間(start是幾秒后開始執(zhí)行,interval是時(shí)間間隔)
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, (int64_t)(start * NSEC_PER_SEC)), (uint64_t)(interval ) * NSEC_PER_SEC);
//設(shè)置回調(diào)
dispatch_source_set_event_handler(timer, ^{
});
//啟動(dòng)定時(shí)器
dispatch_resume(timer);
2. 介紹下內(nèi)存的幾大區(qū)域
代碼段:編譯之后的代碼
數(shù)據(jù)段
字符串常量:比如NSString *str = @"123"
已初始化數(shù)據(jù):已初始化的全局變量、靜態(tài)變量等
未初始化數(shù)據(jù):未初始化的全局變量僵朗、靜態(tài)變量等
棧:函數(shù)調(diào)用開銷赖欣,比如局部變量。分配的內(nèi)存空間地址越來越小
堆:通過alloc验庙、malloc顶吮、calloc等動(dòng)態(tài)分配的空間,分配的內(nèi)存空間地址越來越大
3. 講一下你對(duì) iOS 內(nèi)存管理的理解
在iOS中粪薛,使用引用計(jì)數(shù)來管理OC對(duì)象的內(nèi)存
一個(gè)新創(chuàng)建的OC對(duì)象引用計(jì)數(shù)默認(rèn)是1悴了,當(dāng)引用計(jì)數(shù)減為0,OC對(duì)象就會(huì)銷毀,釋放其占用的內(nèi)存空間
調(diào)用retain會(huì)讓OC對(duì)象的引用計(jì)數(shù)+1湃交,調(diào)用release會(huì)讓OC對(duì)象的引用計(jì)數(shù)-1
內(nèi)存管理的經(jīng)驗(yàn)總結(jié)
當(dāng)調(diào)用alloc熟空、new、copy搞莺、mutableCopy方法返回了一個(gè)對(duì)象息罗,在不需要這個(gè)對(duì)象時(shí),要調(diào)用release或者autorelease來釋放它
想擁有某個(gè)對(duì)象才沧,就讓它的引用計(jì)數(shù)+1迈喉;不想再擁有某個(gè)對(duì)象,就讓它的引用計(jì)數(shù)-1
可以通過以下私有函數(shù)來查看自動(dòng)釋放池的情況
extern void _objc_autoreleasePoolPrint(void);
性能優(yōu)化
1. 列表卡頓的原因可能有哪些温圆?你平時(shí)是怎么優(yōu)化的挨摸?
CPU
- 盡量用輕量級(jí)的對(duì)象,比如用不到事件處理的地方捌木,可以考慮使用CALayer取代UIView
- 不要頻繁地調(diào)用UIView的相關(guān)屬性油坝,比如frame嫉戚、bounds刨裆、transform等屬性,盡量減少不必要的修改
- 盡量提前計(jì)算好布局彬檀,在有需要時(shí)一次性調(diào)整對(duì)應(yīng)的屬性帆啃,不要多次修改屬性
- Autolayout會(huì)比直接設(shè)置frame消耗更多的CPU資源
- 圖片的size最好剛好跟UIImageView的size保持一致
- 控制一下線程的最大并發(fā)數(shù)量
- 盡量把耗時(shí)的操作放到子線程: 文本處理(尺寸計(jì)算、繪制);圖片處理(解碼窍帝、繪制)
GPU
- 盡量避免短時(shí)間內(nèi)大量圖片的顯示努潘,盡可能將多張圖片合成一張進(jìn)行顯示
- GPU能處理的最大紋理尺寸是4096x4096,一旦超過這個(gè)尺寸坤学,就會(huì)占用CPU資源進(jìn)行處理疯坤,所以紋理盡量不要超過這個(gè)尺寸
- 盡量減少視圖數(shù)量和層次
- 減少透明的視圖(alpha<1),不透明的就設(shè)置opaque為YES
- 盡量避免出現(xiàn)離屏渲染
2. 什么是離屏渲染深浮?
在OpenGL中压怠,GPU有2種渲染方式
On-Screen Rendering:當(dāng)前屏幕渲染,在當(dāng)前用于顯示的屏幕緩沖區(qū)進(jìn)行渲染操作
Off-Screen Rendering:離屏渲染飞苇,在當(dāng)前屏幕緩沖區(qū)以外新開辟一個(gè)緩沖區(qū)進(jìn)行渲染操作
3. 離屏渲染消耗性能的原因
需要?jiǎng)?chuàng)建新的緩沖區(qū)
離屏渲染的整個(gè)過程菌瘫,需要多次切換上下文環(huán)境,先是從當(dāng)前屏幕(On-Screen)切換到離屏(Off-Screen)布卡;等到離屏渲染結(jié)束以后雨让,將離屏緩沖區(qū)的渲染結(jié)果顯示到屏幕上,又需要將上下文環(huán)境從離屏切換到當(dāng)前屏幕
4. 哪些操作會(huì)觸發(fā)離屏渲染忿等?
- 光柵化栖忠,layer.shouldRasterize = YES
- 遮罩,layer.mask
- 圓角,同時(shí)設(shè)置layer.masksToBounds = YES庵寞、layer.cornerRadius大于0(考慮通過CoreGraphics繪制裁剪圓角虚汛,或者叫美工提供圓角圖片)
- 陰影,layer.shadowXXX(如果設(shè)置了layer.shadowPath就不會(huì)產(chǎn)生離屏渲染)
5. 怎么檢測卡頓皇帮?
平時(shí)所說的“卡頓”主要是因?yàn)樵谥骶€程執(zhí)行了比較耗時(shí)的操作
可以添加Observer到主線程RunLoop中卷哩,通過監(jiān)聽RunLoop狀態(tài)切換的耗時(shí),以達(dá)到監(jiān)控卡頓的目的
暫時(shí)先整理這些属拾,如果有錯(cuò)誤将谊,感謝各位大佬指正。