深入剖析IOS性能優(yōu)化_第1頁(yè)
深入剖析IOS性能優(yōu)化_第2頁(yè)
深入剖析IOS性能優(yōu)化_第3頁(yè)
深入剖析IOS性能優(yōu)化_第4頁(yè)
深入剖析IOS性能優(yōu)化_第5頁(yè)
已閱讀5頁(yè),還剩68頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶(hù)提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

深入剖析IOS性能優(yōu)化技術(shù)創(chuàng)新,變革未來(lái)什么影晌性能?女可僉則?時(shí)間復(fù)雜度數(shù)組O(n)

會(huì)遍歷的接口:containsObject:,indexOfObject*,removeObject:O(1)

棧頂棧底操作:objectAtIndex:,firstObject:,lastObject:,addObject:,removeLastObject:O(logn)二分查找:indexOfObject:inSortedRange:options:usingComparator:Dictionary和

Set無(wú)序沒(méi)有重復(fù)元素,通過(guò)

hash

table

進(jìn)行快速的操作添加刪除和查找都是

O(1)

的containsObject不同實(shí)現(xiàn)containsObject

in

數(shù)組-(BOOL)containsObject:

(id)anObject{return([selfindexOfObject:anObject]!=

NSNotFound);}-(NSUInteger)indexOfObject:

(id)anObject{unsigned c=[self

count];if(c>0&&anObject!=

nil){unsigned i;IMPget=[selfmethodForSelector:

oaiSel];BOOL (*eq)(id,SEL,id)=(BOOL(*)(id,SEL,id))[anObjectmethodForSelector:

eqSel];for(i=0;i<c;

i++)if((*eq)(anObject,eqSel,(*get)(self,oaiSel,i))==

YES)return

i;}return

NSNotFound;}containsObjectin

Set-(BOOL)containsObject:

(id)anObject{return(([selfmember:anObject])?YES:

NO);}-(id)member:

(id)anObject{if(anObject!=

nil){GSIMapNodenode=GSIMapNodeForKey(&map,

(GSIMapKey)anObject);if(node!=

0){return

node->key.obj;}}return

nil;}GCD通過(guò)

dispatch_block_create_with_qos_class

方法指定隊(duì)列的QoS

QOS_CLASS_UTILITY(>=

iOS

8)。這個(gè)

QoS

系統(tǒng)會(huì)針對(duì)大的計(jì)算,I/O,網(wǎng)絡(luò)以及復(fù)雜數(shù)據(jù)處理做電量?jī)?yōu)化。/\、-一睿爆炸和死鎖//線(xiàn)程爆炸for(inti=0;i<999;i++){dispatch_async(q,

^{...});}//會(huì)造成死鎖dispatch_barrier_sync(q,^{});避免方式—使用串行隊(duì)列

NSOperationQueuesNSOperationQueue.maxConcurrentOperationCount

dispatch_apply(999,q,^(size_t

i){...});四#defineCONCURRENT_TASKS

4sema=

dispatch_semaphore_create(CONCURRENT_TASKS);for(inti=0;i<999;

i++){dispatch_async(q,

^{dispatch_semaphore_signal(sema);});dispatch_semaphore_wait(sema,

DISPATCH_TIME_FOREVER);}I/O將零碎的內(nèi)容作為—個(gè)整體進(jìn)行寫(xiě)入使用合適的

I/O

操作

API使用合適的線(xiàn)程使用

NSCache

做緩存能夠減少

I/O-l

UO勺為什么不直接用字典?自動(dòng)清理系統(tǒng)占用內(nèi)存NSCache

是線(xiàn)程安全-(void)cache:(NSCache

*)cachewillEvictObject:(id)obj; 緩存對(duì)象將被清理時(shí)的回調(diào)evictsObjectsWithDiscardedContent

可以控制是否清理NSCache

里有個(gè)NSMutableDictionary@implementation

NSCache-(id)

init{if(nil==(self=[super

init])){return

nil;}_objects=[NSMutableDictionary

new];_accesses=[NSMutableArray

new];return

self;}還有些額外信息@interface_GSCachedObject:NSObject{@publicid

object;

//cache

的值NSString

*key;

//設(shè)置

cache

keyint

accessCount;

//保存訪(fǎng)問(wèn)次數(shù),用于自動(dòng)清理NSUIntegercost;//setObject:forKey:cost:BOOL

isEvictable;

//線(xiàn)程安全}@endCache

讀取-(id)objectForKey:

(id)key{_GSCachedObject*obj=[_objectsobjectForKey:

key];if(nil==

obj){return

nil;}if

(obj->isEvictable)

//保證添加刪除操作線(xiàn)程安全{//

obj

移到

access

list

末端[_accessesremoveObjectIdenticalTo:

obj];[_accessesaddObject:

obj];}obj->accessCount++;_totalAccesses++;return

obj->object;}Cache

添加-(void)setObject:(id)objforKey:(id)keycost:

(NSUInteger)num{_GSCachedObject*oldObject=[_objectsobjectForKey:

key];_GSCachedObject

*newObject;if(nil!=

oldObject){[selfremoveObjectForKey:

oldObject->key];}[self_evictObjectsToMakeSpaceForObjectWithCost:

num];newObject=[_GSCachedObject

new];//Retainedhere,releasedwhenobjis

dealloc'dnewObject->object=

RETAIN(obj);newObject->key=

RETAIN(key);newObject->cost=

num;if([objconformsToProtocol:

@protocol(NSDiscardableContent)]){newObject->isEvictable=YES;[_accessesaddObject:

newObject];}[_objectssetObject:newObjectforKey:key];RELEASE(newObject);_totalCost+=

num;}Cache

自動(dòng)清理方法可時(shí)自動(dòng)清理的判斷//

cost

在添加新

cache

值時(shí)指定的

cost//

_costLimit

totalCostLimit

屬性值if(_costLimit>0&&_totalCost+cost>

_costLimit){spaceNeeded=_totalCost+cost-

_costLimit;}//

只有當(dāng)

cost

大于人工限制時(shí)才會(huì)清理//

或者

cost

設(shè)置為0不進(jìn)行人工干預(yù)if(count>0&&(spaceNeeded>0||count>=

_countLimit))不要清理經(jīng)常訪(fǎng)問(wèn)的

objects//_totalAccesses

所有的值的訪(fǎng)問(wèn)都會(huì)

+1NSUIntegeraverageAccesses=(_totalAccesses/count*0.2)+

1;//accessCount

每次

obj

取值時(shí)會(huì)

+1if(obj->accessCount<averageAccesses&&

obj->isEvictable)清理前準(zhǔn)備工作NSUIntegercost=

obj->cost;obj->cost=

0;//

不會(huì)被再次清除obj->isEvictable=

NO;//

添加到

remove

list

里if

(_evictsObjectsWithDiscardedContent){[evictedKeysaddObject:

obj->key];}_totalCost-=

cost;//

女果已經(jīng)釋放了足夠空間就不用后面操作了if(cost>spaceNeeded){break;}spaceNeeded-=

cost;清理時(shí)執(zhí)行回調(diào)-(void)removeObjectForKey:

(id)key{_GSCachedObject*obj=[_objectsobjectForKey:

key];if(nil!=

obj){[_delegatecache:selfwillEvictObject:

obj->object];_totalAccesses-=obj->accessCount;[_objectsremoveObjectForKey:key];[_accessesremoveObjectIdenticalTo:

obj];}}NSCache

SDWebImage

的運(yùn)用(UIImage*)imageFromMemoryCacheForKey:(NSString*)key

{return[self.memCache

objectForKey:key];}(UIImage*)imageFromDiskCacheForKey:(NSString*)key

{//

僉查

NSCache

里是否有UIImage*image=[self

imageFromMemoryCacheForKey:key];if(image)

{returnimage;}//

從磁盤(pán)里讀UIImage*diskImage=[self

diskImageForKey:key];if(diskImage&&self.shouldCacheImagesInMemory){NSUIntegercost=SDCacheCostForImage(diskImage);[self.memCachesetObject:diskImageforKey:key

cost:cost];}return

diskImage;}控制

App

Wake

次數(shù)喚起這個(gè)過(guò)程會(huì)有比較大的消耗通知,VoIP,定位,藍(lán)牙都會(huì)使設(shè)備從

Standby

狀態(tài)喚起定位方法的選擇連續(xù)的位置更新:startUpdatingLocation延時(shí)有效定位:allowDeferredLocationUpdatesUntilTraveled:

timeout:重大位置變化:startMonitoringSignificantLocationChanges區(qū)域監(jiān)測(cè):startMonitoringForRegion:(CLRegion

*)女可預(yù)防這些性能問(wèn)題需要刻意預(yù)防么?堅(jiān)持幾個(gè)原則優(yōu)化計(jì)算的復(fù)雜度從而減少

CPU

的使用在應(yīng)用響應(yīng)交互的時(shí)候停止沒(méi)必要的任務(wù)處理設(shè)置合適的

QoS將定時(shí)器任務(wù)合并,讓

CPU

更多時(shí)候處于

idle

狀態(tài)女可僉查?監(jiān)聽(tīng)主線(xiàn)程用

CFRunLoopObserverCreate

創(chuàng)建—個(gè)觀察者接受

CFRunLoopActivity

的回調(diào)用

CFRunLoopAddObserver

將觀察者添加到CFRunLoopGetMain()

主線(xiàn)程

Runloop

的kCFRunLoopCommonModes

模式下進(jìn)行觀察創(chuàng)建—個(gè)子線(xiàn)程來(lái)進(jìn)行監(jiān)控根據(jù)兩個(gè)

Runloop

的狀態(tài)

BeforeSources

AfterWaiting在區(qū)間時(shí)間是否能檢測(cè)到來(lái)判斷是否卡頓女可打印堆棧信息保存現(xiàn)場(chǎng)task_threads

取到所有的線(xiàn)程thread_act_array_tthreads;//int組成的數(shù)組比女thread[1]=5635mach_msg_type_number_t

thread_count

=0;//mach_msg_type_number_t

int

類(lèi)型consttask_tthis_task=mach_task_self();

//int//根據(jù)當(dāng)前

task

獲取所有線(xiàn)程kern_return_tkr=task_threads(this_task,&threads,

&thread_count);thread_info獲取線(xiàn)程詳細(xì)信息if(thread_info((thread_act_t)thread,THREAD_BASIC_INFO,(thread_info_t)threadInfo,&threadInfoCount)==KERN_SUCCESS)

{threadBasicInfo=

(thread_basic_info_t)threadInfo;if(!(threadBasicInfo->flags&TH_FLAGS_IDLE))

{threadInfoSt.cpuUsage=threadBasicInfo->cpu_usage/

10;threadInfoSt.userTime=

threadBasicInfo->system_time.microseconds;}}thread_get_state獲取線(xiàn)程里所有棧的信息_STRUCT_MCONTEXT

machineContext;

//線(xiàn)程棧里所有的棧指針//通過(guò)

thread_get_state

獲取完整的

machineContext

信息,包含

thread

狀態(tài)信息mach_msg_type_number_tstate_count=smThreadStateCountByCPU();kern_return_tkr=thread_get_state(thread,smThreadStateByCPU(),(thread_state_t)&machineContext.

ss,

&state_count);棧結(jié)構(gòu)體保存棧數(shù)據(jù)//為通用回溯設(shè)計(jì)結(jié)構(gòu)支持棧地址由小到大,地址里存儲(chǔ)上個(gè)棧指針的地址typedefstructSMStackFrame

{conststructSMStackFrame*const

previous;constuintptr_t

return_address;}

SMStackFrame;SMStackFramestackFrame=

{0};//通過(guò)?;分羔槴@取當(dāng)前棧幀地址constuintptr_tframePointer=

smMachStackBasePointerByCPU(&machineContext);if(framePointer==0||smMemCopySafely((void*)framePointer,&stackFrame,sizeof(stackFrame))!=KERN_SUCCESS)

{return@"Failframe

pointer";}for(;i<32;i++)

{buffer[i]=

stackFrame.return_address;if(buffer[i]==0||stackFrame.previous==0||smMemCopySafely(stackFrame.previous,&stackFrame,sizeof(stackFrame))!=KERN_SUCCESS)

{break;}}棧信息符號(hào)化獲取

mach_header

slide計(jì)算

ASLR

偏移量//根據(jù)

image

的序號(hào)獲取

mach_headerconststructmach_header*machHeader=

_dyld_get_image_header(idx);//返回

image_index

索引的

image

的虛擬內(nèi)存地址

slide

的數(shù)量,女果

image_index

超出范圍返回0//動(dòng)態(tài)鏈接器加載

image

時(shí),image

必須映射到未占用地址的進(jìn)程的虛擬地址空間。動(dòng)態(tài)鏈接器通過(guò)添加—個(gè)值到

image

的基地址來(lái)實(shí)現(xiàn),這個(gè)值是虛擬內(nèi)存

slide

數(shù)量constuintptr_timageVMAddressSlide=

(uintptr_t)_dyld_get_image_vmaddr_slide(idx);///wiki/Address_space_layout_randomizationconstuintptr_taddressWithSlide=address-

imageVMAddressSlide;獲取符號(hào)表的虛擬內(nèi)存偏移量//LC_SYMTAB

描述了

LINKEDIT

segment

內(nèi)查找字符串和符號(hào)表的位置if(loadCmd->cmd==LC_SYMTAB)

{//獲取字符串和符號(hào)表的虛擬內(nèi)存偏移量。conststructsymtab_command*symtabCmd=(structsymtab_command*)cmdPointer;constnlistByCPU*symbolTable=(nlistByCPU*)(segmentBase+

symtabCmd->symo?);constuintptr_tstringTable=segmentBase+

symtabCmd->stro?;找到最匹配的符號(hào)地址//給定的偏移量是文件偏移量,減去

LINKEDIT

segment

的文件偏移量獲得字符串和符號(hào)表的虛擬內(nèi)存偏移量uintptr_tsymbolBase=

symbolTable[iSym].n_value;uintptr_tcurrentDistance=addressWithSlide-

symbolBase;//尋找最小的距離

bestDistance,因?yàn)?/p>

addressWithSlide

是某個(gè)方法的指令地址,要大于這個(gè)方法的入口。//離

addressWithSlide

越近的函數(shù)入口越匹配if((addressWithSlide>=symbolBase)&&(currentDistance<=bestDistance)){bestMatch=symbolTable+

iSym;bestDistance=

currentDistance;}獲取更多信息更細(xì)化的測(cè)星時(shí)間消耗,找到耗時(shí)方法給優(yōu)化定個(gè)目標(biāo),比如某場(chǎng)景響應(yīng)操作在

100ms

內(nèi)完成女可獲取到更多信息呢?—張圖片hook

objc_msgSend

方法能夠獲取所有被調(diào)用的方法,facebook的

fishhook/facebook/fishhook記錄深度就能夠得到方法調(diào)用的樹(shù)狀結(jié)構(gòu),InspectiveC/DavidGoldman/InspectiveC通過(guò)執(zhí)行前后時(shí)間的記錄能夠得到每個(gè)方法的耗時(shí)獲取方法調(diào)用樹(shù)結(jié)構(gòu)設(shè)計(jì)兩個(gè)結(jié)構(gòu)體CallRecordtypedefstructCallRecord_

{id

obj; //object_getClass

得到

ClassNSStringFromClass

得類(lèi)名SEL

_cmd;

//通過(guò)

NSStringFromSelector

方法能夠得到方法名uintptr_t

lr;int

prevHitIndex;char

isWatchHit;}

CallRecord;ThreadCallStacktypedefstructThreadCallStack_

{CallRecord

*stack;int

allocatedLength;int

index;

//index

記錄方法樹(shù)的深度…}

ThreadCallStack;存儲(chǔ)讀取ThreadCallStackstaticinlineThreadCallStack

*getThreadCallStack()

{ThreadCallStack*cs=

(ThreadCallStack*)pthread_getspecific(threadKey);

//讀取if(cs==NULL)

{cs=

(ThreadCallStack*)malloc(sizeof(ThreadCallStack));...cs->stack=

(CallRecord*)calloc(DEFAULT_CALLSTACK_DEPTH,sizeof(CallRecord));

//分配

CallRecord

默認(rèn)空間...pthread_setspecific(threadKey,

cs);

//保存數(shù)據(jù)}return

cs;}記錄方法調(diào)用深度//開(kāi)始時(shí)staticinlinevoidpushCallRecord(idobj,uintptr_tlr,SEL_cmd,ThreadCallStack*cs)

{intnextIndex=

(++cs->index);

//增加深度if(nextIndex>=cs->allocatedLength)

{cs->allocatedLength+=

CALLSTACK_DEPTH_INCREMENT;cs->stack=(CallRecord*)realloc(cs->stack,cs->allocatedLength*

sizeof(CallRecord));cs->spacesStr=(char*)realloc(cs->spacesStr,cs->allocatedLength+

1);memset(cs->spacesStr,'',

cs->allocatedLength);cs->spacesStr[cs->allocatedLength]=

'\0';}CallRecord*newRecord=

&cs->stack[nextIndex];newRecord->obj=

obj;newRecord->_cmd=_cmd;newRecord->lr=lr;newRecord->isWatchHit=

0;}//結(jié)束時(shí)staticinlineCallRecord*popCallRecord(ThreadCallStack*cs)

{return

&cs->stack[cs->index--];

//減少深度}objc_msgSend前后插入執(zhí)行方法目的是在調(diào)用前和調(diào)用后分別加入

pushCallRecord

和popCallRecord不可能編寫(xiě)—個(gè)保留未知參數(shù)并跳轉(zhuǎn)到

c

中任意函數(shù)指針的函數(shù),那么這就需要用到匯編來(lái)做到主要思路就是先入棧參數(shù),x0

第—個(gè)參數(shù)是傳入對(duì)象,x1

第二個(gè)參數(shù)是選擇器

_cmd然后交換寄存器中,將用于返回的寄存器

lr

移到

x1

里。先讓pushCallRecord

能夠執(zhí)行,再執(zhí)行原始的

objc_msgSend,保存返回值,最后讓

popCallRecord

能執(zhí)行Hook

msgsend

方法dyld

是通過(guò)更新

Mach-O

二進(jìn)制的

DATA

segment

特定的部分中的指針來(lái)邦定

lazy

non-lazy

符號(hào)通過(guò)確認(rèn)傳遞給

rebind_symbol

里每個(gè)符號(hào)名稱(chēng)更新的位置就可以找出對(duì)應(yīng)替換來(lái)重新綁定這些符號(hào)遍歷

dyld//首先是遍歷

dyld

里的所有的

image,取出

image

header

slide。注第—次調(diào)用時(shí)主要注冊(cè)

callback。if(!_rebindings_head->next)

{_dyld_register_func_for_add_image(_rebind_symbols_for_image);}else

{uint32_tc=

_dyld_image_count();for(uint32_ti=0;i<c;i++)

{_rebind_symbols_for_image(_dyld_get_image_header(i),_dyld_get_image_vmaddr_slide(i));}}找出符號(hào)表相關(guān)

Commandsegment_command_t*cur_seg_cmd;segment_command_t*linkedit_segment=NULL;structsymtab_command*symtab_cmd=NULL;structdysymtab_command*dysymtab_cmd=

NULL;uintptr_tcur=(uintptr_t)header+

sizeof(mach_header_t);for(uinti=0;i<header->ncmds;i++,cur+=cur_seg_cmd->cmdsize)

{cur_seg_cmd=(segment_command_t

*)cur;if(cur_seg_cmd->cmd==LC_SEGMENT_ARCH_DEPENDENT)

{if(strcmp(cur_seg_cmd->segname,SEG_LINKEDIT)==0)

{linkedit_segment=

cur_seg_cmd;}}elseif(cur_seg_cmd->cmd==LC_SYMTAB)

{symtab_cmd=(struct

symtab_command*)cur_seg_cmd;}elseif(cur_seg_cmd->cmd==LC_DYSYMTAB)

{dysymtab_cmd=(struct

dysymtab_command*)cur_seg_cmd;}}

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶(hù)所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶(hù)上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶(hù)上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶(hù)因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論