iOS知識點總結

# 運行時

```

1、程序中任何代碼都會被轉化成runtime的C代碼執(zhí)行,如[target doSomeThing]會轉化成objc_msgSend(tagert,@selector(doSomeThing))

2、OC中一切都是對象,實例,類本質也是對象,在runtime中結構體表示。

3、相關定義

* typedef struct objc_method *Method; 方法

* typedef struct objc_ivar *Ivar;實例變量

* typedef struct objc_category *Category;實例Category

* typedef struct objc_property *objc_property_t;類中聲明的屬性

4、基于以上,可以實時獲取當前類中所有屬性,方法,變量

* 獲取屬性:objc_property_t *propertyList = class_copyPropertyList([self class], &count);

* 獲取方法:Method *methodList = class_copyMethodList([self class], &count);

* 獲取變量:Ivar *ivarList = class_copyIvarList([self class], &count);

* 獲取協(xié)議:__unsafe_unretained Protocol **protocolList = class_copyProtocolList([self class], &count);

5、方法調用

* 如果用實例對象調用實例方法,會到實例的isa指針指向的對象(類對象)中操作;

* 如果調用的是類方法,會到類對象的isa指針指向的對象(元類對象)中操作。

* 尋找過程:

* 首先在操作對象中的緩存方法列表中尋找

* 若無,在操作對象中的方法列表中尋找

* 若無,去父類指針指向對象中執(zhí)行1,2

* 依次遍歷,若遍歷到根類還沒有,轉向攔截調用(若沒有實現(xiàn),程序報錯)

* 攔截調用:

* + (BOOL)resolveClassMethod:(SEL)sel;當調用一個不存在的類方法的時候,會調用此方法,默認返回NO

* + (BOOL)resolveInstanceMethod:(SEL)sel;當調用不存在的實例方法時,會調用次方法。

* - (id)forwardingTargetForSelector:(SEL)aSelector;將你調用的不存在的方法重定向到一個其他聲明了這個方法的類,只需要你返回一個有這個方法的target。

* - (void)forwardInvocation:(NSInvocation *)anInvocation; 將你調用的不存在的方法打包成NSInvocation傳給你。做完你自己的處理后,調用invokeWithTarget:方法讓某個target觸發(fā)這個方法。

6、關聯(lián)對象

//首先定義一個全局變量,用它的地址作為關聯(lián)對象的key

static char associatedObjectKey;

//設置關聯(lián)對象

objc_setAssociatedObject(target, &associatedObjectKey, @"添加的字符串屬性", OBJC_ASSOCIATION_RETAIN_NONATOMIC);

//獲取關聯(lián)對象

NSString *string = objc_getAssociatedObject(target, &associatedObjectKey); NSLog(@"AssociatedObject = %@", string);

7、方法交換

* 通常寫在load方法中,保證執(zhí)行時間靠前,且只執(zhí)行一次

* // swizzing method for instance

#define swizzing_sel_instance(_cls_, _sel_1, _sel_2) {\

Method method1 = class_getInstanceMethod(_cls_, _sel_1);\

Method method2 = class_getInstanceMethod(_cls_, _sel_2);\

ASSERT(method1 != NULL);\

ASSERT(method2 != NULL);\

LOG(@"swizzing (%@), -%@(0x%08lx) ==> -%@(0x%08lx), ", NSStringFromClass(_cls_), NSStringFromSelector(_sel_1), (long)method1, NSStringFromSelector(_sel_2), (long)method2);\

if (method1 && method2) {\

method_exchangeImplementations(method1, method2);\

}\

}

```

# 內存管理

```

1、只有OC對象才需要管理,非OC對象不需要內存管理(OC對象放在堆內存里,非OC對象放在棧內存里,棧內存里的東西系統(tǒng)會自動管理)

2、自動釋放池底層實現(xiàn):

3、string的內存管理

* NSString *str1 = @"abcde";字符串“abcde”在常量區(qū)(有且只有一份),str1指針在棧區(qū)。

* NSString *str2 = [NSString stringWithFormat:@"hijkl"];

* 通過stringWithFormat創(chuàng)建的字符串放在堆中(即使字符串內容相同,還是會再次開辟新空間),str2在棧中。

* 通過stringWithFormat創(chuàng)建的字符串,會返回一個autorelease的實例,會在自動釋放池自動釋放。

* 通過initWithFormt創(chuàng)建的字符串,則需要自己進行手動管理內存

4、copy和Mutablecopy

* copy返回immutable對象,mutableCopy返回mutable對象(所以不可以對mutabe對象使用copy操作)

* NSmutableArray *mArray = [array mutableCopy];(此處的內容拷貝,僅僅是拷貝array這個對象,array集合內部的元素仍然是指針拷貝)

* [immutableObject copy] // 淺復制

* [immutableObject mutableCopy] //單層深復制

* [mutableObject copy] //單層深復制

* [mutableObject mutableCopy] //單層深復制

* 使用總結:

* 修飾可變類型的屬性時,如NSMutableArray、NSMutableDictionary、NSMutableString,用strong

* 修飾不可變類型的屬性時,如NSArray、NSDictionary、NSString,用copy。

5、Block內存管理

* 默認情況下,Block的內存是在棧中(程序自動管理)

* 如果對block做了copy操作,block的內存會搬到堆中,對引用對象做一次+1操作

* block內部可以一直引用__block修飾的變量,static修飾的變量,全局變量(Block不可以修改外部變量的值,指的是棧中指針的內存地址,__block修飾的變量之所以可以被修改,是因為__block會將棧中的內存地址放到堆中,所以可以修改【static和全局變量在全局初始化區(qū)】)

* unsafe_unretained和weak的區(qū)別:兩者都不持有對象,當引用計數(shù)為0時,weak會被置為nil,unsafe_unretained不置為nil(有野指針風險)

```

# 多線程# 動畫

```

1、GCD

* 同步:阻塞當前線程

* 異步:不會阻塞當前線程

* 串行:FIFO 依次取出,一個一個執(zhí)行(主隊列)DISPATCH_QUEUE_SERIAL

* 并行:也是FIFO,但是放到不同線程去執(zhí)行(全局隊列)DISPATCH_QUEUE_CONCURRENT

* dispatch_barrier_async:當你傳入的 queue 是通過 DISPATCH_QUEUE_CONCURRENT 參數(shù)自己創(chuàng)建的 queue 時,這個方法會阻塞這個 queue(注意是阻塞 queue ,而不是阻塞當前線程),一直等到這個 queue 中排在它前面的任務都執(zhí)行完成后才會開始執(zhí)行自己

* dispatch_barrier_sync:自定義的并發(fā)隊列(DISPATCH_QUEUE_CONCURRENT),它和上一個方法一樣的阻塞 queue,不同的是 這個方法還會 阻塞當前線程

2、NSOperation

* NSOperation 只是一個抽象類,所以不能封裝任務。但它有 2 個子類用于封裝任務。分別是:NSInvocationOperation 和 NSBlockOperation ( 默認在當前隊列同步執(zhí)行)

* NSOperationQueue可以設置maxConcurrentOperationCount,可以添加依賴

3、線程同步:防止多個線程搶奪同一塊資源

* 互斥鎖:@synchronized(self)

* NSLock

取消GCD任務 ?

```

# 常見設計模式

# Runloop

```

1、Runloop的寄生于線程:一個線程只能有唯一對應的runloop;但這個根runloop里可以嵌套子runloops;

2、同一時間一個runloop只能在一個mode,切換mode只能退出runloop,再重進指定mode(隔離modeItems使之互不干擾);

3、timerWithTimeInterval:需要手動加到runloop的mode中

scheduledTimerWithTimeInterval:默認已經添加到主線程的runLoop的DefaultMode中

4、mode類型

* kCFRunLoopDefaultMode: 默認 mode,通常主線程在這個 Mode 下運行。

* UITrackingRunLoopMode: 追蹤mode,保證Scrollview滑動順暢不受其他 mode 影響。

* UIInitializationRunLoopMode: 啟動程序后的過渡mode,啟動完成后就不再使用。

* GSEventReceiveRunLoopMode: Graphic相關事件的mode,通常用不到。

* kCFRunLoopCommonModes: 占位mode,作為標記DefaultMode和CommonMode用。

5、應用

* 當tableview的cell上有需要從網絡獲取的圖片的時候,滾動tableView,異步線程會去加載圖片,加載完成后主線程就會設置cell的圖片,但是會造成卡頓??梢宰屧O置圖片的任務在CFRunLoopDefaultMode下進行,當滾動tableView的時候,RunLoop是在 UITrackingRunLoopMode 下進行,不去設置圖片,而是當停止的時候,再去設置圖片。

[self.myImageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@""] afterDelay:ti inModes:@[NSDefaultRunLoopMode]];

* 常駐子線程,保持子線程一直處理事件

為了保證線程長期運轉,可以在子線程中加入RunLoop,并且給Runloop設置item,防止Runloop自動退出。

self.downloadRunloop = CFRunLoopGetCurrent();

*? 當調用 NSObject 的 performSelecter:afterDelay:后,實際上其內部會創(chuàng)建一個 Timer 并添加到當前線程的 RunLoop 中。所以如果當前線程沒有 RunLoop,則這個方法會失效。

```

# 事件響應鏈條

```

UIApplication-->UIWindow-->遞歸找到最合適處理的控件-->控件調用touches方法-->判斷是否實現(xiàn)touches方法-->沒有實現(xiàn)默認會將事件傳遞給上一個響應者-->找到上一個響應者-->找不到方法作廢

1、根據main函數(shù)的參數(shù)加載UIApplication->AppDelegate->UIWindow->UIViewController->superView->subViews

關系為:UIApplication.keyWindow.rootViewController.view.subView

事件傳遞機制:

1.當iOS程序中發(fā)生觸摸事件后,系統(tǒng)會將事件加入到UIApplication管理的一個任務隊列中

2.UIApplication將處于任務隊列最前端的事件向下分發(fā)。即UIWindow。

3.UIWindow將事件向下分發(fā),即UIView。

4.UIView首先看自己是否能處理事件,觸摸點是否在自己身上。如果能,那么繼續(xù)尋找子視圖。

5.遍歷子控件,重復以上兩步。

6.如果沒有找到,那么自己就是事件處理者。如果

7.如果自己不能處理,那么不做任何處理。

其中 UIView不接受事件處理的情況主要有以下三種

1)alpha <0.01

2)userInteractionEnabled = NO

3.hidden = YES

2、hittest

UIWindow實例對象會首先在它的內容視圖上調用hitTest:withEvent:,此方法會在其視圖層級結構中的每個視圖上調用pointInside:withEvent:(該方法用來判斷點擊事件發(fā)生的位置是否處于當前視圖范圍內,以確定用戶是不是點擊了當前視圖),如果pointInside:withEvent:返回YES,則繼續(xù)逐級調用,直到找到touch操作發(fā)生的位置,這個視圖也就是要找的hit-test view。

```

# 性能優(yōu)化 (內存占用,耗電,UI渲染)

1、例如一個黑色半透明的可以設置為一個灰色不透明的View替代.原因是這會使系統(tǒng)用一個最優(yōu)的方式渲染這些views:如果一個圖層是完全不透明的,則系統(tǒng)直接顯示該圖層的顏色即可。而如果圖層是帶透明效果的,則會引入更多的計算,因為需要把下面的圖層也包括進來,進行混合后顏色的計算

2、永遠不要使主線程承擔過多。因為UIKit在主線程上做所有工作,渲染,管理觸摸反應,回應輸入等都需要在它上面完成

3、如果要在UIImageView中顯示一個來自bundle的圖片,你應保證圖片的大小和UIImageView的大小相同,盡可能的先在子線程把圖片縮放后再進行賦值。

4、盡量使用懶加載,不要一次性創(chuàng)建所有subview,等用到的時候再進行加載。(NSDateFormatter和NSCalendar初始化就比較慢)

5、處理內存警告

在app delegate中使用applicationDidReceiveMemoryWarning: 的方法

在你的自定義UIViewController的子類(subclass)中覆蓋didReceiveMemoryWarning

注冊并接收 UIApplicationDidReceiveMemoryWarningNotification 的通知

6、如果你用小圖平鋪來創(chuàng)建背景,你就需要用UIColor的colorWithPatternImage來做了,它會更快地渲染也不會花費很多內存:

self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"background"]];

7、如果你要加載一個大圖片而且是一次性使用,那么就沒必要緩存這個圖片,用imageWithContentsOfFile足矣,這樣不會浪費內存來緩存它。

然而,在圖片反復重用的情況下imageNamed是一個好得多的選擇。


# tableView CollectionView 性能調優(yōu)

提前計算并緩存好高度(布局),因為heightForRowAtIndexPath:是調用最頻繁的方法;

異步繪制,遇到復雜界面,遇到性能瓶頸時,可能就是突破口;

滑動時按需加載[快速滑動只展示cell,不加載圖片],這個在大量圖片展示,網絡加載的時候很管用?。⊿DWebImage已經實現(xiàn)異步加載,配合這條性能杠杠的)。

除了上面最主要的三個方面外,還有很多幾乎大伙都很熟知的優(yōu)化點:

正確使用reuseIdentifier來重用Cells

盡量使所有的view opaque,包括Cell自身

盡量少用或不用透明圖層

如果Cell內現(xiàn)實的內容來自web,使用異步加載,緩存請求結果

減少subviews的數(shù)量

在heightForRowAtIndexPath:中盡量不使用cellForRowAtIndexPath:,如果你需要用到它,只用一次然后緩存結果

盡量少用addView給Cell動態(tài)添加View,可以初始化時就添加,然后通過hide來控制是否顯示

NSLog向??輸出添加時間戳和標識符,而println則不會;

NSLog同步日志語句,以便如果您同時發(fā)出來自不同線程的日志,則它們將不會彼此重疊; println可能會導致混亂的輸出,如果同時從單獨的線程執(zhí)行,而不做一些同步

iOS啟動做了哪些事?

1.main 函數(shù)

2.UIApplicationMain

創(chuàng)建UIApplication對象

創(chuàng)建UIApplication的delegate對象

delegate對象開始處理(監(jiān)聽)系統(tǒng)事件(沒有storyboard)

程序啟動完畢的時候, 就會調用代理的application:didFinishLaunchingWithOptions:方法

在application:didFinishLaunchingWithOptions:中創(chuàng)建UIWindow

創(chuàng)建和設置UIWindow的rootViewController

顯示窗口

3.根據Info.plist獲得最主要storyboard的文件名,加載最主要的storyboard(有storyboard)

創(chuàng)建UIWindow

創(chuàng)建和設置UIWindow的rootViewController

顯示窗口

mas_equalTo有自動包裝的功能,equalTo沒有自動包裝功能。

用mas_equalTo可以把基本數(shù)據類型轉換為對象類型,這個過程叫裝箱,比如自動將1包裝成@1。

load和initialize

load和initialize方法都會在實例化對象之前調用,以main函數(shù)為分水嶺,前者在main函數(shù)之前調用,后者在之后調用。這兩個方法會被自動調用,不能手動調用它們。

load和initialize方法都不用顯示的調用父類的方法而是自動調用,即使子類沒有initialize方法也會調用父類的方法,而load方法則不會調用父類。

load方法通常用來進行Method Swizzle,initialize方法一般用于初始化全局變量或靜態(tài)變量。

load和initialize方法內部使用了鎖,因此它們是線程安全的。實現(xiàn)時要盡可能保持簡單,避免阻塞線程,不要再使用鎖。

autoreleasePool

自動釋放池可以延長對象的聲明周期,如果一個事件周期很長,比如有一個很長的循環(huán)邏輯,那么一個臨時變量可能很長時間都不會被釋放,一直在內存中保留,那么內存的峰值就會一直增加,但是其實這個臨時變量是我們不再需要的。這個時候就通過創(chuàng)建新的自動釋放池來縮短臨時變量的生命周期來降低內存的峰值。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 內容均轉自標哥的技術博客 只是按照自己的習慣進行簡單的整理 1、對數(shù)組中的元素去重復 1.第一種方法:開辟新的內存...
    Kk太陽閱讀 5,702評論 0 21
  • runtime 和 runloop 作為一個程序員進階是必須的,也是非常重要的, 在面試過程中是經常會被問到的, ...
    made_China閱讀 1,273評論 0 7
  • 轉至元數(shù)據結尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉至元數(shù)據起始第一章:isa和Class一....
    40c0490e5268閱讀 2,067評論 0 9
  • 接《iOS知識點大總結一》 三十四、主線程操作UI(對UI進行更新只能在主線程進行) 解釋:所謂的在主線程更新UI...
    RunningMan_Fly閱讀 1,555評論 0 0
  • 一、每一個人要有自己的定位 說簡單點,就是要清楚自己要做的事情是什么,并為之努力。 每一位來鋼琴中心...
    SEER思閱讀 459評論 0 0

友情鏈接更多精彩內容