__block 和 __weak 的區(qū)別:
- __block不管是ARC還是MRC模式下都可以使用,可以修飾對象,還可以修飾基本數(shù)據(jù)類型。
- __weak只能在ARC模式下使用,也只能修飾對象(NSString),不能修飾基本數(shù)據(jù)類型(int)。
- __block對象可以在block中被重新賦值, __weak不可以.
- __block對象在ARC下可能會導(dǎo)致循環(huán)引用, 非ARC下會避免循環(huán)引用, __weak只在ARC下使用, 可以避免循環(huán)引用.
assign 和 weak 的區(qū)別
assign 平時只是用在基本類型的的修飾,比如NSInteger,float等
weak主要用在對象上,其中尤以代理用的多,幾乎都是用weak; 自身已經(jīng)對它進行一次強引用,沒有必要再強引用一次時也會使用weak。比如:自定義 IBOutlet控件屬性一般也使用weak,當然也可以使用strong...
weak修飾的對象不增加引用計數(shù),被釋放后自動置為nil,而assign修飾的對象屬于指針賦值,也不會增加引用計數(shù),但是在對象被釋放后,assign修飾的這個變量并不會自動置為nil, 指針的地址還是存在的, 所以會導(dǎo)致野指針 (對象一般分配在堆上的某塊內(nèi)存,如果在后續(xù)的內(nèi)存分配中,剛好分到了這塊地址,程序就會崩潰掉。)
那為什么可以用assign修飾基本數(shù)據(jù)類型?因為基礎(chǔ)數(shù)據(jù)類型一般分配在棧上,棧的內(nèi)存會由系統(tǒng)自己自動處理,不會造成野指針。
copy 與 深. 淺拷貝
- copy拷貝出來的對象類型總是不可變類型(例如, NSString, NSDictionary, NSArray等等)
- mutableCopy拷貝出來的對象類型總是可變類型(例如, NSMutableString, NSMutableDictionary, NSMutableArray等等)
深拷貝 與 淺拷貝
- 深拷貝 : 拷貝出來的對象與源對象地址不一致! 這意味著我修改拷貝對象的值對源對象的值沒有任何影響.
-
淺拷貝 : 拷貝出來的對象與源對象地址一致! 這意味著我修改拷貝對象的值會直接影響到源對象.
image.png
為什么block要用 copy
block是一個對象, 所以block理論上是可以retain/release的. 但是block在創(chuàng)建的時候它的內(nèi)存是默認是分配在棧(stack)上, 而不是堆(heap)上的. 所以它的作用域僅限創(chuàng)建時候的當前上下文(函數(shù), 方法...), 當你在該作用域外調(diào)用該block時, 程序就會崩潰.
RunLoop
當定時器 Timer 加到 mode 為 kCFRunLoopDefaultMode 時,拖拽 UITextview 時候 Timer 就不起作用了;當 mode 為 kCFRunLoopCommonModes 時,拖拽 UITextview 不影響 Timer 的使用...
一個線程對應(yīng)一個RunLoop,RunLoop里面有若干個mode,每個mode都有自己的內(nèi)容,Source/Timer/Observer等等。而RunLoop有了mode才會有效果。注意:mode里面必須要有內(nèi)容!??!
要讓RunLoop跑起來,既要有有內(nèi)容的mode,也需要 [[NSRunLoop currentRunLoop] run]; 。兩個都不可少。
RunLoop跑起來后相當于是一個while的死循環(huán),后面的代碼不會執(zhí)行。
GCD
- GCD的好處
- GCD 可用于多核的并行運算
- GCD 會自動利用更多的 CPU 內(nèi)核(比如雙核、四核)
- GCD 會自動管理線程的生命周期(創(chuàng)建線程、調(diào)度任務(wù)、銷毀線程)
- 程序員只需要告訴 GCD 想要執(zhí)行什么任務(wù),不需要編寫任何線程管理代碼
- GCD的幾種情況:
- 同步任務(wù) + 并發(fā)隊列 : 沒有開啟新線程,串行執(zhí)行任務(wù)
- 異步任務(wù) + 并發(fā)隊列 : 有開啟新線程,并發(fā)執(zhí)行任務(wù)
- 同步任務(wù) + 串行隊列 : 沒有開啟新線程,串行執(zhí)行任務(wù)
- 異步任務(wù) + 串行隊列 : 有開啟新線程(1條),串行執(zhí)行任務(wù)
- 同步任務(wù) + 主隊列 : 主線程調(diào)用:死鎖卡住不執(zhí)行; 其他線程調(diào)用:沒有開啟新線程,串行執(zhí)行任務(wù)
- 異步任務(wù) + 主隊列 : 沒有開啟新線程,串行執(zhí)行任務(wù)
- GCD 柵欄方法:dispatch_barrier _async
在執(zhí)行完柵欄前面的操作之后,才執(zhí)行柵欄操作,最后再執(zhí)行柵欄后邊的操作。
特別注意: 不能用全局隊列: dispatch _get _global _queue , 只能用: dispatch _queue _create 來創(chuàng)建
GCD 延時執(zhí)行方法:dispatch_after
GCD 一次性代碼(只執(zhí)行一次):dispatch_once
GCD 快速迭代方法:dispatch_apply : 按照指定的次數(shù)將指定的任務(wù)追加到指定的隊列中,并等待全部隊列執(zhí)行結(jié)束。
GCD 隊列組:dispatch_group
- dispatch _group _notify : 當所有任務(wù)都執(zhí)行完成之后,才執(zhí)行dispatch _group _notify block 中的任務(wù)。
- dispatch _group _wait : 暫停當前線程(阻塞當前線程),等待指定的 group 中的任務(wù)執(zhí)行完成后,才會往下繼續(xù)執(zhí)行。
- dispatch _group _enter、dispatch _group _leave :
- GCD 信號量:dispatch_semaphore
作用:
- 保持線程同步,將異步執(zhí)行任務(wù)轉(zhuǎn)換為同步執(zhí)行任務(wù)
- 保證線程安全,為線程加鎖
進程與線程
程序是一個可以運行的文件, 一個程序至少有一個進程, 一個進程至少有一個線程, 即主線程.
進程
- 正在進行的程序被稱為進程, 負責程序運行的內(nèi)存分配, 每個進程都有自己的獨立虛擬內(nèi)存空間, 一個程序的一次運行, 在執(zhí)行過程中擁有獨立的內(nèi)存單元, 而多個線程共享一個內(nèi)存.
線程
- 線程是進程中的基本單元(可執(zhí)行的代碼段), 線程可以并發(fā)運行, 提高執(zhí)行效率
- 創(chuàng)建線程的目的: 就是為了開啟一條新的可執(zhí)行的代碼段, 與主線程中的代碼實現(xiàn)同時運行, 防止界面假死, 是實現(xiàn)異步的技術(shù)的主要手段, 比如網(wǎng)絡(luò)異步下載...
多線程開發(fā)的優(yōu)缺點:
- 好處:
- 使用多線程可以把程序中占據(jù)時間長的任務(wù)或耗時操作放到后臺去處理, 如圖片 視頻的下載;
- 發(fā)揮多核處理器的優(yōu)勢, 并發(fā)執(zhí)行讓系統(tǒng)運行的更快, 更流暢, 用戶體驗更好;
- 缺點:
- 資源競爭, 線程安全的問題: 多個線程會對同一資源出現(xiàn)爭奪;
- 內(nèi)存消耗: 更多的線程需要更多的內(nèi)存空間;
- 大量的線程降低代碼的可讀性;
- 死鎖;
空指針和野指針
空指針: 沒有存儲任何內(nèi)存地址的指針;
野指針: 存儲不可用的內(nèi)存地址(或不存在的內(nèi)存地址, 如已被收回釋放) 的指針, 指向的內(nèi)存地址不可用會報錯;
僵尸指針: 野指針的一種情況, 即該指針指向的對象已經(jīng)被釋放, 但是卻沒有對當前指針賦值為 nil ;
僵尸對象: 使用已經(jīng)被釋放了的對象, 若使用了一般會報錯: unrecognized selector sent to instance ;
宏與const的區(qū)別:
宏(define) : 宏只是進行簡單的文本替換, 沒有類型, 也不會作類型檢查, 不會報編譯錯誤, 宏是預(yù)編譯(編譯之前處理), 若大量使用宏, 容易造成編譯時間久; 宏還能定義一些函數(shù), 方法, const不能;
const: 定義常量, 會有類型檢查; 它是在編譯運行階段使用, 會報編譯錯誤;
static 的作用:
static 聲明局部變量: 則改變變量的存儲方式(生命周期), 使變量成為表態(tài)的局部變量, 即編譯時就為變量分配內(nèi)存, 直到程序退出才釋放存儲單元. 這樣 使得該局部變量有記憶功能, 可以記憶上次的數(shù)據(jù), 不過由于仍是局部變量, 因而只能在代碼內(nèi)部使用(作用域不變).
static 聲明外部變量: 為了限制某些外部變量的作用域 (外部變量指在所有代碼塊 {} 之外定義的變量, 它本身是靜態(tài)變量, 編譯時分配內(nèi)存, 程序結(jié)束時釋放內(nèi)存單元. 同時, 其作用域很廣, 整個文件都有效, 甚至別的文件也能引用它 ) , 使其只在本文件中有效, 而不能被其他文件引用, 可以用 static 關(guān)鍵字對其作出聲明.
swfit 和 oc 的區(qū)別
- Swift對數(shù)據(jù)要求嚴格,不存在隱式轉(zhuǎn)換,強制轉(zhuǎn)換格式:int(a);
- swift 使用 private 修飾后, 是用運行時也不能獲取其屬性或函數(shù), 而 oc 則可能...
- nil 指針的使用: swift 中 nil 也是一種類型(可選類型值會 nil), 莫名調(diào)用會報錯, OC則不會...
- swift 還有命名空間, 泛型, 可選類型 的概念;
@synthesize和@dynamic 的區(qū)別
- @property有兩個對應(yīng)的詞,一個是@synthesize,一個是@dynamic。如果@synthesize和@dynamic都沒寫,那么默認的就是@syntheszie var = _var;
- @synthesize的語義是如果你沒有手動實現(xiàn)setter方法和getter方法,那么編譯器會自動為你加上這兩個方法。
- @dynamic告訴編譯器,屬性的setter與getter方法由用戶自己實現(xiàn),不自動生成。(當然對于readonly的屬性只需提供getter即可)。假如一個屬性被聲明為@dynamic var,然后你沒有提供@setter方法和@getter方法,編譯的時候沒問題,但是當程序運行到instance.var =someVar,由于缺setter方法會導(dǎo)致程序崩潰;或者當運行到 someVar = var時,由于缺getter方法同樣會導(dǎo)致崩潰。編譯時沒問題,運行時才執(zhí)行相應(yīng)的方法,這就是所謂的動態(tài)綁定
- @dynamic其主要的作用就是用在NSManageObject對象的屬性聲明上,由于此類對象的屬性一般是從Core Data的屬性中生成的,Core Data框架會在程序運行的時候為此類屬性生成getter和Setter方法
動態(tài)庫 和 靜態(tài)庫
- 動態(tài)庫: .dylib 和 .framework
- 靜態(tài)庫: .a 和 .framework
系統(tǒng)的.framework是動態(tài)庫,我們自己建立的.framework是靜態(tài)庫。
instancetype 和 id 的區(qū)別
- 相同點:
都可以作為方法的返回類型
- 不同點:
- instancetype可以返回和方法所在類相同類型的對象,id只能返回未知類型的對象;
- instancetype : 編譯時判斷數(shù)據(jù)類型; - id : 編譯器不檢查類型, 運行時檢查類型
- id可以作為方法的參數(shù),但instancetype不可以, instancetype只適用于初始化方法和便利構(gòu)造器的返回值類型
Smalltalk : 被稱為 "面向?qū)ο缶幊讨?
imageNamed 和 imageWithContentOfFile的區(qū)別
- 使用imageNamed:加載圖片
- 加載到內(nèi)存后, 會一直停留在內(nèi)存中, 不會隨著對象銷毀而銷毀;
- 加載進圖片后, 戰(zhàn)勝的內(nèi)存歸系統(tǒng)管理, 我們無法管理;
- 相同的圖片, 圖片不會重新加載;
- 加載到內(nèi)存后, 占據(jù)內(nèi)存空間較大;
- 使用 imageWithContentOfFile 加載圖片
- 加載到內(nèi)存中后, 占據(jù)內(nèi)存空間比較小;
- 相同的圖片會被重新加載到內(nèi)存中;
- 對象銷毀的時候, 加載到內(nèi)存中的圖片會被一起銷毀
結(jié)論:
-
如果圖片較小,并且頻繁使用的圖片,使用imageName:來加載圖片(按鈕圖片/主頁圖片/占位圖)
-
如果圖片較大,并且使用次數(shù)較少,使用 imageWithContentOfFile:來加載(相冊/版本新特性)
NSCache
- NSCache 的特點:
- NSCache是線程安全的
- 在內(nèi)存不足時NSCache會自動釋放存儲的對象
- NSCache的鍵key不會被復(fù)制,所以key不需要實現(xiàn)NSCopying協(xié)議
- NSCache緩存對象自身被釋放
- 手動調(diào)用removeObjectForKey:方法
- 手動調(diào)用removeAllObjects
- 緩存中對象的個數(shù)大于countLimit,或,緩存中對象的總cost值大于totalCostLimit
- 程序進入后臺后
- 收到系統(tǒng)的內(nèi)存警告
幾種常用的鎖 ( iOS 中幾種常用的鎖總結(jié) )
@synchronized
NSLock 對象鎖
NSRecursiveLock 遞歸鎖
NSConditionLock 條件鎖
pthread_mutex 互斥鎖(C語言)
dispatch_semaphore 信號量實現(xiàn)加鎖(GCD)
UIView 的 setNeedsDisplay 與 setNeedsLayout 方法
兩個方法都是異步執(zhí)行的;
setNeedsDisplay:
setNeedsDisplay 會自動調(diào)用drawRect方法,這樣可以拿到 UIGraphicsGetCurrentContext
setNeedsLayout:
setNeedsLayout會默認調(diào)用layoutSubViews,可以調(diào)整子視圖的尺寸和位置;
一些UIView方法的調(diào)用情況
NSNumber 與NSValue
- 由于集合里只能存放對象,不可以存放基本數(shù)據(jù)類型,所以我們有時候需要講一些對象比如基本數(shù)據(jù)類型,結(jié)構(gòu)體等存到NSDictionary NSArray中,我們就需要將這些數(shù)據(jù)類型或結(jié)構(gòu)體包裝成OC對象,以便集合能訪問到。常用的用來包裝這些類型的有NSNumber NSValue
- NSNumber只能包裝基本數(shù)據(jù)類型,比如int, float,char,BOOL等
- NSValue可以包裝任意一個對象,包括系統(tǒng)自定義的數(shù)據(jù)結(jié)構(gòu),結(jié)構(gòu)體等等
- NSNumber是NSValue的一個子類
nil / Nil / NULL / NSNull的區(qū)別 ( 參考 )
- nil : 用于表示指向Objective-C中對象的指針為空
NSString *string = nil;
- Nil : 用于表示Objective-C類(Class)類型的變量值為空
Class anyClass = Nil;
- NULL: NULL表示C指針為空
char * c = NULL;
- NSNull: 一個Objective-C類
NSArray *arr = [NSArray arrayWithObjects:@"wang",@"zz",[NSNull null],@"foogry"];
內(nèi)存分區(qū)情況
- 代碼區(qū): 用來存放函數(shù)、二進制代碼及最靜態(tài)的東西;
- 數(shù)據(jù)區(qū): 系統(tǒng)運行時申請內(nèi)存并初始化, 系統(tǒng)退出時由系統(tǒng)釋放; 存放全局變量, 靜態(tài)變量, 常量;
- 堆區(qū): 通過malloc 等函數(shù) 或 new 等操作符動態(tài)申請得到, 需要程序員手動申請和釋放;
- 棧區(qū): 函數(shù)模塊內(nèi)申請, 函數(shù)結(jié)束時由系統(tǒng)自動釋放; 存放局部變量, 函數(shù)參數(shù), 結(jié)構(gòu)體中創(chuàng)建的變量也在棧中。
- 內(nèi)存泄漏:
用動態(tài)存儲分配函數(shù)動態(tài)開辟的空間,在使用完畢后未釋放,結(jié)果導(dǎo)致一直占據(jù)該內(nèi)存單元,不能被任何程序再次使用,直到程序結(jié)束。即所謂內(nèi)存泄漏。簡單的說就是申請了一塊內(nèi)存空間,使用完畢后沒有釋放掉。
它的一般表現(xiàn)方式是程序運行時間越長,占用內(nèi)存越多,最終用盡全部內(nèi)存,整個系統(tǒng)崩潰。由程序申請的一塊內(nèi)存,且沒有任何一個指針指向它,那么這塊內(nèi)存就泄露了。注意:內(nèi)存泄漏是指堆內(nèi)存的泄漏。
- 內(nèi)存溢出:
內(nèi)存溢出就是在程序運行的過程中如果一次需要讀取的數(shù)據(jù)超過這個棧內(nèi)存大小的話就會出現(xiàn)溢出.所以一般內(nèi)存管理中,需要生成一個對象釋放一個對象,你想對這個對象執(zhí)行的操作寫在生成和釋放之間.
- 野指針:
對象內(nèi)存空間已經(jīng)被系統(tǒng)回收,仍然使?指針操作這塊內(nèi)存。野指針異常是程序crash的主要原因。
iOS系統(tǒng)架構(gòu) 參考鏈接
iOS的系統(tǒng)架構(gòu)分為四層,由上到下一次為:可觸摸層(Cocoa Touch layer)、媒體層(Media layer)、核心服務(wù)層(Core Services layer)、核心操作系統(tǒng)層(Core OS layer)
影響app性能的幾個方面 參考鏈接
- 網(wǎng)絡(luò)性能:
- 內(nèi)存問題:
- 主線程阻塞:
- Offscreen rendering(離屏渲染):
- 圖片的處理:
self 和 super 的區(qū)別: 參考文章
self : 關(guān)鍵字, 代表當前方法的調(diào)用者
- 如果是類方法, 代表當前類
- 如果是實例方法, 代表當前類的對象
super :編譯器指令
- 經(jīng)典題目:
@implementation Son : Father
- (id)init
{
self = [super init];
if (self)
{
NSLog(@"%@", NSStringFromClass([self class])); // 返回 Son
NSLog(@"%@", NSStringFromClass([super class])); // 返回 Son
}
return self;
}
@end
TabelView的性能優(yōu)化: 參考鏈接
- 行高一定要緩存! ( 因為 heightForRowAtIndexPath: 是調(diào)用最頻繁的方法 )
- 不要動態(tài)創(chuàng)建子視圖,所有的子視圖都預(yù)先創(chuàng)建,如果不需要顯示可以設(shè)置Hidden;
- 所有的子視圖都應(yīng)該添加到 contentView上;
- 所有的子視圖都必須指定背景顏色, 且所有的顏色都不要 alpha;
- 異步加載圖片;
- 離屏渲染的問題
UIView動畫與核心動畫的區(qū)別?
- 核心動畫只作用在 layer 上;
- 核心動畫修改的值都是假像, 它的真實位置沒有發(fā)生變化
- 什么時候用 UIView動畫 ?
當需要與用戶交互時用 UIView, 不需要與用戶交互時兩個都可以...
- 什么情況用核心動畫 ?
- 轉(zhuǎn)場動畫.
- 幀動畫.
- 動畫組.
