若是你不知道你接下來須要補充學習哪一部分知識,作點面試題吧。javascript
進程是一個程序執行的實例,是資源分配的最小單位
線程是進程中的一個實例,是操做系統能夠識別的最小執行和調度單位
複製代碼
那麼,線程和進程與堆、棧之間的關係?php
棧是線程獨有的,保存其運行狀態和局部自動變量,棧空間是線程安全的,棧被自動分配到進程的內存空間,棧內存無需開發管理,系統自動管理
堆在操做系統初始化進程的時候分配,運行過程能夠要求更多額外的堆內存,可是須要返回,否則呢就是內存泄露
複製代碼
例如在多線程併發條件下,爲了讓線程之間能夠更方便的共同完成一個任務,須要一些協調通訊,採起的通訊方式就是 等待、喚起。
也就是 wait() 和 notify()、 notifyAll()
複製代碼
表面上看多是同樣快,由於字典底層都用了哈希表,查找的時間複雜度爲 O(1),(最差的時候是O(n))都是同樣的,可是可能會因爲兩個哈希表的負載因子不一樣,卻是查找的時間也是不一樣的。
複製代碼
指針常量是 常量,指針修飾它,這個常量的值是一個指針 int a; int *const b = &a;
常量指針本質是指針,常量修飾它 const int *p;
複製代碼
算術運算
int a,b;
a=10;b=12;
a=b-a; //a=2;b=12
b=b-a; //a=2;b=10
a=b+a; //a=12;b=10
位運算 異或
int a=10,b=12; //a=1010^b=1100;
a=a^b; //a=0110^b=1100;
b=a^b; //a=0110^b=1010;
a=a^b; //a=1100=12;b=1010;
棧實現
int exchange(int x,int y) {
stack S;
push(S,x);
push(S,y);
x=pop(S);
y=pop(S);
}
複製代碼
func add(n: Int) -> Int {
var sum = 0
if n > 0 {
sum = n + add(n: n - 1)
} else {
sum = 0
}
return sum
}
複製代碼
須要一輪遍歷 O(n)
複製代碼
GET請求的數據會附在URL以後
POST把提交的數據則放置在是HTTP包的包體中
GET請求URL受瀏覽器影響 因此有長度限制
POST沒有,通常服務器會作POST數據長度的限制
POST的數據傳輸不是直接拼接URL 因此相對安全一些
複製代碼
http + ssl/tls = https
主要介紹一下,ssl的驗證過程 保證安全和數據完整性
複製代碼
web端:
Connection:keep-alive
服務器在閒置時候會向客戶端發生偵測包,默認閒置時間是2個小時
移動端:
基於tcp的長鏈接,socket編程技術
複製代碼
application/x-www-form-urlencoded:窗體數據被編碼爲名稱/值對。這是標準的編碼格式。
multipart/form-data:窗體數據被編碼爲一條消息,頁上的每一個控件對應消息中的一個部分
複製代碼
OSI採用了分層的結構化技術,共分七層:
物理層:爲設備間的數據通訊提供傳輸媒體和互連設備,光纖、無線信道等等
數據鏈路層:爲網絡層提供數據傳送服務的,包括鏈路鏈接的創建、拆除和分離;對幀的收發順序控制
網絡層:數據傳送的單位是分組或者包,網絡層在給兩個不一樣地理位置的主機之間提供
傳輸層:定義了一些傳輸數據的協議和端口號,TCP, UDP;主要從下層接收的數據進行分段和傳輸,到達目的地後再重組
會話層:經過傳輸層創建數據傳輸通道,主要在你的系統之間發起會話或者接受會話請求(IP、MAC、主機名稱)
表示層:可確保一個系統的應用層所發送的信息能夠被另外一個系統的應用層讀取,主要作的就是把應用層提供的信息變換爲可以共同理解的形式,提供字符代碼,數據格式,控制信息格式,加密等的統一表示。
應用層:爲用戶的應用程序提供網絡服務
TCP/IP 採用四層結構:
網絡接口層:硬件、幀頭幀尾的添加
網絡互聯層:肯定目標計算機的IP地址
傳輸層:TCP,肯定如何傳輸
應用層:app
複製代碼
XMPP是一種以XML爲基礎的開放式實時通訊協議。
XMPP 是一種很相似於http協議的一種數據傳輸協議,它的過程就如同「解包裝–〉包裝」的過程,用戶只須要明白它接受的類型,並理解它返回的類型,就能夠很好的利用xmpp來進行數據通信。基於可擴展標記語言(XML)的協議
XMPP基本結構:客戶端 服務器 網關
通訊可以在這三者的任意兩個之間雙向發生。服務器同時承擔了客戶端信息記錄,鏈接管理和信息的路由功能。網關承擔着與異構即時通訊系統的互聯互通,異構系統能夠包括SMS(短信),MSN,ICQ等。基本的網絡形式是單客戶端經過TCP/IP鏈接到單服務器,而後在之上傳輸XML。
XMPP核心協議通訊的基本模式就是先創建一個stream,而後協商一堆安全之類的東西,中間通訊過程就是客戶端發送XML Stanza,一個接一個的。服務器根據客戶端發送的信息以及程序的邏輯,發送XML Stanza給客戶端。可是這個過程並非一問一答的,任什麼時候候都有可能從一方發信給另一方。通訊的最後階段是關閉流,關閉TCP/IP鏈接。
客戶端1 <--> XMPP服務器 <--> 客戶端2
兩個客戶端能夠分別和服務器通訊,可是客戶端之間的通訊必須通過服務器
用於一些即時通訊
複製代碼
保障通訊雙方的可靠性,通訊的安全和數據的完整性
複製代碼
https和ssl在握手方向有什麼區別?css
一個是鏈接握手,一個是安全校驗握手,描述一下二者握手過程
複製代碼
具體原理見參考中的 網絡知識整理。html
Http是基於Tcp的,而Socket是一套編程接口讓咱們更方便的使用Tcp/Ip協議;
Http是應用層協議,在Tcp/Udp上一層。
一、Http是基於"請求-響應"的,服務器不能主動向客戶端推送數據,只能藉助客戶端請求到後向客戶端推送數據,而Sokcet雙方隨時能夠互發數據;
二、Http不是持久鏈接的,Socket用Tcp是持久鏈接;
三、Http基於Tcp,Socket能夠基於Tcp/Udp;
四、Http鏈接是經過Socket實現的;
五、Http鏈接後發送的數據必須知足Http協議規定的格式:請求頭、請求頭和請求體,而Socket鏈接後發送的數據沒有格式要求。
複製代碼
Socket的實現原理及 Socket之間是如何通訊的java
網絡上的兩個程序經過一個雙向的通訊鏈接實現數據的交換,這個鏈接的一端稱爲一個socket。
創建網絡通訊鏈接至少要一對端口號(socket)。
socket本質是編程接口(API),對TCP/IP的封裝,TCP/IP也要提供可供程序員作網絡開發所用的接口,這就是Socket編程接口;
HTTP是轎車,提供了封裝或者顯示數據的具體形式;Socket是發動機,提供了網絡通訊的能力。
socket分爲客戶端和服務端,客戶端發送鏈接請求,服務端等待鏈接請求
當服務端socket監聽到客戶端socket的請求時,就響應客戶端套接字的請求,創建一個新的線程,把服務端套接字描述發送給客戶端,一旦客戶端確認了此描述,雙方正式創建鏈接,而服務端socket繼續處於監聽狀態,等待其餘鏈接請求
複製代碼
一些常見的狀態代碼爲:
200 - 服務器成功返回網頁
300 - 重定向之類
404 - 請求的網頁不存在
503 - 服務器暫時不可用
複製代碼
不要一開始就發送大量的數據,先探測一下網絡的擁塞程度,也就是說由小到大逐漸增長擁塞窗口的大小。
簡單來講 擁塞控制就是防止過多的數據注入網絡中,這樣能夠使網絡中的路由器或鏈路不致過載。
原理:
請求發送,每次按窗口數發送數據,收到一個確認就把窗口值加一,逐漸遞增,這就是慢開始算法
當網絡擁塞,窗口從新回 1 最大慢開始門限變爲出現問題的網絡擁塞窗口值的一半 這就是擁塞避免算法
而後再次循環。
複製代碼
由於TCP鏈接的時候,最後一次握手錶示收到服務器確認的請求能夠攜帶須要發給服務器的數據,三次是最短可能
四次揮手是確保客戶端 沒有消息要發給服務端,服務端也沒有消息要發給客戶端了,也能夠不用四次,可是就會增長空等待的資源浪費
複製代碼
複製代碼
網絡知識整理ios
leetCodegit
劍指offer程序員
告訴編譯器,這個全局變量在本文件找不到就去其餘文件去找。若有必要須要使用#import "x.h"這樣編譯器才知道到哪裏去找。使用extern前要保證對應變量被編譯過,想要訪問全局變量能夠使用extern關鍵字(全局變量定義不能有static修飾)。github
好比 A文件中 我聲明的全局變量 NSInteger age = 10; 可是屬性也不能直接獲取。 以下在B文件中能夠獲取到 :
extern NSInteger age;
age ++;
NSLog(@"%d",age); // 11
若是不想讓age被找到,聲明爲static
複製代碼
常量定義,修飾一個常量
int a = 1;
int b = 2;
int const *p = &a
// 若是const修飾的是*p,那麼*p的值是不能改變的,也就是p中存放的a的地址中的值沒法改變,可是p的值是能夠改變的(也就是p此時能夠改變指向)
p = &b;
printf("---");
printf("%p",&b);
printf("---");
printf("%p",p);
printf("---");
printf("%d",*p);
//輸出 ---0x7ffeea7e89f8---0x7ffeea7e89f8---2
int *const p = &a;
// 若是const修飾的是p,那麼p的值是不能改變的,也就是p中存放的a的地址沒法改變(p是int類型的指針變量)。可是*p是能夠變化的,咱們並無用const去修飾*p,因此能夠經過*p去改變a的值
*p = b;
複製代碼
static NSInteger staticValue = 0;
static關鍵字修飾局部變量:
當static關鍵字修飾局部變量時,只會初始化一次且在程序中只有一分內存
關鍵字static不能夠改變局部變量的做用域,但可延長局部變量的生命週期(直到程序結束才銷燬)
static關鍵字修飾全局變量:
當static關鍵字修飾全局變量時,做用域僅限於當前文件,外部類是不能夠訪問到該全局變量的(即便在外部使用extern關鍵字也沒法訪問)
若是須要直接訪問 須要引用頭文件
複製代碼
宏定義屬於預編譯指令,在程序運行以前已經編譯好了的
#define M_PI 3.14159265358979323846264338327950288
#define SELF(x) x //NSLog(@"Hello %@",SELF(name));
#define PLUS(x,y) x + y //printf("%d",PLUS(3,2));
#define MIN(A,B) A < B ? A : B // int a = MIN(1,2);
#define NSLog(format, ...) do { \ fprintf(stderr, "<%s : %d> %s\n", \ [[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String], \ __LINE__, __func__); \ (NSLog)((format), ##__VA_ARGS__); \ fprintf(stderr, "-------\n"); \ } while (0)
複製代碼
在內存角度來看,block分爲 全局 、棧 和 堆 三種類型,
有強引用的block就屬於堆內存block,
只用到外部局部變量、成員屬性變量、沒有強指針引用的block屬於棧block
只引用全局變量或靜態變量的block,生命週期和程序生命週期同樣的block就是全局block
block的實質是一個對象,一個結構體
複製代碼
__block修飾符標記後,block就會訪問標記變量自己內存地址,而未標記對象則訪問截獲拷貝後的變量的內存地址
複製代碼
block 使用 copy 是從 MRC 遺留下來的「傳統」
在 MRC 中,方法內部的 block 是在棧區的,使用 copy 能夠把它放到堆區。
在 ARC 中寫不寫都行
對於 block 使用 copy 仍是 strong 效果是同樣的,但寫上 copy 也無傷大雅,還能時刻提醒咱們:編譯器自動對 block 進行了 copy 操做。若是不寫 copy ,該類的調用者有可能會忘記或者根本不知道「編譯器會自動對 block 進行了 copy 操做」,他們有可能會在調用以前自行拷貝屬性值。這種操做多餘而低效。
複製代碼
@property = ivar + getter + setter;
「屬性」 (property)有兩大概念:ivar(實例變量)、getter+setter(存取方法)
「屬性」 (property)做爲 Objective-C 的一項特性,主要的做用就在於封裝對象中的數據。 Objective-C 對象一般會把其所須要的數據保存爲各類實例變量。實例變量通常經過「存取方法」(access method)來訪問。其中,「獲取方法」 (getter)用於讀取變量值,而「設置方法」 (setter)用於寫入變量值。
複製代碼
引伸一個問題:@synthesize 和 @dynamic 分別有什麼做用?web
完成屬性(@property)定義後,編譯器會自動編寫訪問這些屬性所需的方法,此過程叫作「自動合成」(autosynthesis)。
咱們也能夠在類的實現代碼裏經過 @synthesize 語法來指定實例變量的名字。
@synthesize lastName = _myLastName;
或者經過 @dynamic 告訴編譯器:屬性的 setter 與 getter 方法由用戶本身實現,不自動生成。
@property有兩個對應的詞,
一個是@synthesize(合成實例變量),一個是@dynamic。
若是@synthesize和@dynamic都沒有寫,那麼默認的就是 @synthesize var = _var;
// 在類的實現代碼裏經過 @synthesize 語法能夠來指定實例變量的名字。(@synthesize var = _newVar;)
1. @synthesize 的語義是若是你沒有手動實現setter方法和getter方法,那麼編譯器會自動爲你加上這兩個方法。
2. @dynamic 告訴編譯器,屬性的setter與getter方法由用戶本身實現,不自動生成(如,@dynamic var)。
複製代碼
用 @property 聲明 NSString、NSArray、NSDictionary 常用 copy 關鍵字,是由於他們有對應的可變類型:NSMutableString、NSMutableArray、NSMutableDictionary,他們之間可能進行賦值操做(就是把可變的賦值給不可變的),爲確保對象中的字符串值不會無心間變更,應該在設置新屬性值時拷貝一份。
1. 由於父類指針能夠指向子類對象,使用 copy 的目的是爲了讓本對象的屬性不受外界影響,使用 copy 不管給我傳入是一個可變對象仍是不可對象,我自己持有的就是一個不可變的副本。
2. 若是咱們使用是 strong ,那麼這個屬性就有可能指向一個可變對象,若是這個可變對象在外部被修改了,那麼會影響該屬性。
//總結:使用copy的目的是,防止把可變類型的對象賦值給不可變類型的對象時,可變類型對象的值發送變化會無心間篡改不可變類型對象原來的值。
複製代碼
這裏還有一個引伸問題:
NSMutableArray 若是用 copy修飾了會出現什麼問題?
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArray0 addObject:]: unrecognized selector sent to instance 0x600000a100c0'
因爲使用的是copy屬性,自己的可變屬性默認有一個不可變的拷貝 NSArray ,因此咱們用這個可變數組去添加元素的時候,找不到對應方法而發生crash。
複製代碼
淺拷貝(copy):只複製指向對象的指針,而不復制引用對象自己。
深拷貝(mutableCopy):複製引用對象自己。內存中存在了兩份獨立對象自己,當修改A時,A_copy不變。
只有對不可變對象進行copy操做是指針複製(淺複製),其它狀況都是內容複製(深複製)
複製代碼
若想令本身所寫的對象具備拷貝功能,則需實現 NSCopying 協議。若是自定義的對象分爲可變版本與不可變版本,那麼就要同時實現 NSCopying 與 NSMutableCopying 協議。
具體步驟:
1. 需聲明該類聽從 NSCopying 協議
2. 實現 NSCopying 協議的方法。
// 該協議只有一個方法:
- (id)copyWithZone:(NSZone *)zone;
// 注意:使用 copy 修飾符,調用的是copy方法,其實真正須要實現的是 「copyWithZone」 方法。
複製代碼
按照執行順序排列:
1. initWithCoder:經過nib文件初始化時觸發。
2. awakeFromNib:nib文件被加載的時候,會發生一個awakeFromNib的消息到nib文件中的每一個對象。
//若是不是nib初始化 上面兩個換成 initWithNibName:bundle:
3. loadView:開始加載視圖控制器自帶的view。
4. viewDidLoad:視圖控制器的view被加載完成。
5. viewWillAppear:視圖控制器的view將要顯示在window上。
6. updateViewConstraints:視圖控制器的view開始更新AutoLayout約束。
7. viewWillLayoutSubviews:視圖控制器的view將要更新內容視圖的位置。
8. viewDidLayoutSubviews:視圖控制器的view已經更新視圖的位置。
9. viewDidAppear:視圖控制器的view已經展現到window上。
10. viewWillDisappear:視圖控制器的view將要從window上消失。
11. viewDidDisappear:視圖控制器的view已經從window上消失。
複製代碼
1). class反射
經過類名的字符串形式實例化對象。
Class class = NSClassFromString(@"student");
Student *stu = [[class alloc] init];
將類名變爲字符串。
Class class =[Student class];
NSString *className = NSStringFromClass(class);
2). SEL的反射
經過方法的字符串形式實例化方法。
SEL selector = NSSelectorFromString(@"setName");
[stu performSelector:selector withObject:@"Mike"];
將方法變成字符串。
NSStringFromSelector(@selector*(setName:));
複製代碼
self 是類的隱藏參數,指向當前調用方法的這個類的實例。
super是一個Magic Keyword,它本質是一個編譯器標示符,和self是指向的同一個消息接收者。
不一樣的是:super會告訴編譯器,調用class這個方法時,要去父類的方法,而不是本類裏的。
複製代碼
id是一個 objc_object 結構體指針,定義是
typedef struct objc_object *id
id能夠理解爲指向對象的指針。全部oc的對象 id均可以指向,編譯器不會作類型檢查,id調用任何存在的方法都不會在編譯階段報錯,固然若是這個id指向的對象沒有這個方法,該崩潰仍是會崩潰的。
NSObject *指向的必須是NSObject的子類,調用的也只能是NSObjec裏面的方法不然就要作強制類型轉換。
不是全部的OC對象都是NSObject的子類,還有一些繼承自NSProxy。NSObject *可指向的類型是id的子集。
複製代碼
引伸: id 和 instancetype 的區別
instancetype的做用,就是使那些非關聯返回類型的方法返回所在類的類型!
相同點:
均可以做爲方法的返回類型
不一樣點:
instancetype能夠返回和方法所在類相同類型的對象,id只能返回未知類型的對象
instancetype只能做爲返回值,不能像id那樣做爲參數
複製代碼
一:字典原理
NSDictionary(字典)是使用hash表來實現key和value之間的映射和存儲的
方法:- (void)setObject:(id)anObject forKey:(id)aKey;
Objective-C中的字典NSDictionary底層實際上是一個哈希表
複製代碼
引伸:字典的查詢工做原理
字典的工做原理 ?怎100w箇中是怎麼快速去取value?
複製代碼
本地儘可能不存儲用戶隱私數據、敏感信息
使用如AES256加密算法對數據進行安全加密後再存入SQLite中
或者數據庫總體加密
存放在keychain裏面
向Keychain中存儲數據時,不要使用kSecAttrAccessibleAlways,而是使用更安全的kSecAttrAccessibleWhenUnlocked或kSecAttrAccessibleWhenUnlockedThisDeviceOnly選項。
AES DES
複製代碼
90%的錯誤來源在於對一個已經釋放的對象進行release操做, 或者說對一個訪問不到的地址進行訪問,多是因爲些變量已經被回收了,亦多是因爲使用棧內存的基本類型的數據賦值給了id類型的變量。
例如:
id x_id = [self performSelector:@selector(returnInt)];
- (int)returnInt { return 5; }
上面經過id去接受int返回值,int是存放在棧裏面的,堆內存地址如何找獲得,天然就是 EXC_BAD_ACCESS。
複製代碼
處理方法
一、xcode能夠用殭屍模式打印出對象 而後經過對象查找對應的代碼位置
一、Edit Scheme - Diagnositics - Memory Management 勾選 Zombie Objects 和 Malloc Stack
二、會打印出
cyuyan[7756:17601127] *** -[UIViewController respondsToSelector:]: message sent to deallocated instance 0x7fe71240d390
這句開啓殭屍模式後打出來的輸出,包含了咱們須要的 進程pid、崩潰地址,終端經過下面命令查看堆棧日誌來找到崩潰代碼
三、查找日誌
sudo malloc_history 7756 0x7fe71240d390
複製代碼
二、在 other c flags中加入-D FOR_DEBUG(記住請只在Debug Configuration下加入此標記)。這樣當你程序崩潰時,Xcode的console上就會準確地記錄了最後運行的object的方法。重寫一個object的respondsToSelector方法,打印報錯前的
#ifdef _FOR_DEBUG_
-(BOOL) respondsToSelector:(SEL)aSelector {
printf("SELECTOR: %s\n", [NSStringFromSelector(aSelector) UTF8String]);
return [super respondsToSelector:aSelector];
}
#endif
複製代碼
三、經過instruments的Zombies
引伸:怎麼定位到野指針的地方。若是還沒定位到,這個對象被提早釋放了,怎麼知道該對象在什麼地方釋放的
一種是多線程,一種是野指針。這兩種Crash都帶隨機性,咱們要讓隨機crash變成不隨機
把這一隨機的過程變成不隨機的過程。對象釋放後在內存上填上不可訪問的數據,其實這種技術其實一直都有,xcode的Enable Scribble就是這個做用。
一、Edit Scheme - Diagnositics - Memory Management 勾選 Malloc Scribble
暫時未解決
複製代碼
單例設計一個NotificationCenter,NSPointerArray 保存 observer,對象銷燬 observer自動變null
複製代碼
KVC( 鍵值編碼 )實現
1.KVC是基於runtime機制實現的
2、能夠訪問私有成員變量、能夠間接修改私有變量的值
[object setValue:@"134567" forKey:@"uid"];
就會被編譯器處理成:
// 首先找到對應sel
SEL sel = sel_get_uid("setValue:forKey:");
// 根據object->isa找到sel對應的IMP實現指針
IMP method = objc_msg_lookup (object->isa,sel);
// 調用指針完成KVC賦值
method(object, sel, @"134567", @"uid");
KVC鍵值查找原理
setValue:forKey:搜索方式
1、首先搜索setKey:方法.(key指成員變量名, 首字母大寫)
2、上面的setter方法沒找到, 若是類方法accessInstanceVariablesDirectly返回YES. 那麼按 _key, _isKey,key, iskey的順序搜索成員名。(這個類方法是NSKeyValueCodingCatogery中實現的類方法, 默認實現爲返回YES)
3、若是沒有找到成員變量, 調用setValue:forUnderfinedKey:
valueForKey:的搜索方式
1、首先按getKey, key, isKey的順序查找getter方法, 找到直接調用. 若是是BOOL、int等內建值類型, 會作NSNumber的轉換.
2、上面的getter沒找到, 查找countOfKey, objectInKeyAtindex, KeyAtindexes格式的方法. 若是countOfKey和另外兩個方法中的一個找到, 那麼就會返回一個能夠響應NSArray全部方法的代理集合的NSArray消息方法.
3、還沒找到, 查找countOfKey, enumeratorOfKey, memberOfKey格式的方法. 若是這三個方法都找到, 那麼就返回一個能夠響應NSSet全部方法的代理集合.
4、仍是沒找到, 若是類方法accessInstanceVariablesDirectly返回YES. 那麼按 _key, _isKey, key, iskey的順序搜索成員名.
5、再沒找到, 調用valueForUndefinedKey.
複製代碼
KVO實現 鍵值觀察、觀察者模式的一種應用
簡答
1.KVO是基於runtime機制實現的
2.當某個類的屬性對象第一次被觀察時,系統就會在運行期動態地建立該類的一個派生類,在這個派生類中重寫基類中任何被觀察屬性的setter 方法。派生類在被重寫的setter方法內實現真正的通知機制
3.若是原類爲Person,那麼生成的派生類名爲NSKVONotifying_Person
4.每一個類對象中都有一個isa指針指向當前類,當一個類對象的第一次被觀察,那麼系統會偷偷將isa指針指向動態生成的派生類,從而在給被監控屬性賦值時執行的是派生類的setter方法
5.鍵值觀察通知依賴於NSObject 的兩個方法: willChangeValueForKey: 和 didChangevlueForKey:;在一個被觀察屬性發生改變以前, willChangeValueForKey:必定會被調用,這就 會記錄舊的值。而當改變發生後,didChangeValueForKey:會被調用,繼而 observeValueForKey:ofObject:change:context: 也會被調用。
深刻
1.Apple 使用了 isa 混寫(isa-swizzling)來實現 KVO 。當觀察對象A時,KVO機制動態建立一個新的名爲:?NSKVONotifying_A的新類,該類繼承自對象A的本類,且KVO爲NSKVONotifying_A重寫觀察屬性的setter?方法,setter?方法會負責在調用原?setter?方法以前和以後,通知全部觀察對象屬性值的更改狀況。
2.NSKVONotifying_A類剖析:在這個過程,被觀察對象的 isa 指針從指向原來的A類,被KVO機制修改成指向系統新建立的子類 NSKVONotifying_A類,來實現當前類屬性值改變的監聽;
3.因此當咱們從應用層面上看來,徹底沒有意識到有新的類出現,這是系統「隱瞞」了對KVO的底層實現過程,讓咱們誤覺得仍是原來的類。可是此時若是咱們建立一個新的名爲「NSKVONotifying_A」的類(),就會發現系統運行到註冊KVO的那段代碼時程序就崩潰,由於系統在註冊監聽的時候動態建立了名爲NSKVONotifying_A的中間類,並指向這個中間類了。
4.(isa 指針的做用:每一個對象都有isa 指針,指向該對象的類,它告訴 Runtime 系統這個對象的類是什麼。因此對象註冊爲觀察者時,isa指針指向新子類,那麼這個被觀察的對象就神奇地變成新子類的對象(或實例)了。)?於是在該對象上對 setter 的調用就會調用已重寫的 setter,從而激活鍵值通知機制。
5.子類setter方法剖析:KVO的鍵值觀察通知依賴於 NSObject 的兩個方法:willChangeValueForKey:和 didChangevlueForKey:,在存取數值的先後分別調用2個方法: 被觀察屬性發生改變以前,willChangeValueForKey:被調用,通知系統該 keyPath?的屬性值即將變動;當改變發生後, didChangeValueForKey: 被調用,通知系統該 keyPath?的屬性值已經變動;以後,?observeValueForKey:ofObject:change:context: 也會被調用。且重寫觀察屬性的setter?方法這種繼承方式的注入是在運行時而不是編譯時實現的。
複製代碼
category 它是在運行期決議的,由於在運行期,對象的內存佈局已經肯定,若是添加實例變量就會破壞類的內部佈局,這對編譯型語言來講是災難性的。
extension看起來很像一個匿名的category,可是extension和有名字的category幾乎徹底是兩個東西。 extension在編譯期決議,它就是類的一部分,在編譯期和頭文件裏的@interface以及實現文件裏的@implement一塊兒造成一個完整的類,它伴隨類的產生而產生,亦隨之一塊兒消亡。extension通常用來隱藏類的私有信息,你必須有一個類的源碼才能爲一個類添加extension,因此你沒法爲系統的類好比NSString添加extension。
可是category則徹底不同,它是在運行期決議的。
就category和extension的區別來看,咱們能夠推導出一個明顯的事實,extension能夠添加實例變量,而category是沒法添加實例變量的。
那爲何 使用Runtime技術中的關聯對象能夠爲類別添加屬性。
其緣由是:關聯對象都由AssociationsManager管理,AssociationsManager裏面是由一個靜態AssociationsHashMap來存儲全部的關聯對象的。這至關於把全部對象的關聯對象都存在一個全局map裏面。而map的的key是這個對象的指針地址(任意兩個不一樣對象的指針地址必定是不一樣的),而這個map的value又是另一個AssociationsHashMap,裏面保存了關聯對象的kv對。
如合清理關聯對象?
runtime的銷燬對象函數objc_destructInstance裏面會判斷這個對象有沒有關聯對象,若是有,會調用_object_remove_assocations作關聯對象的清理工做。(詳見Runtime的源碼)
複製代碼
Objective-C Associated Objects 的實現原理
runloop與線程是一一對應的
runloop是來管理線程的
線程和 RunLoop 之間是一一對應的,其關係是保存在一個全局的 Dictionary 裏。線程剛建立時並無 RunLoop,若是你不主動獲取,那它一直都不會有。RunLoop 的建立是發生在第一次獲取時,RunLoop 的銷燬是發生在線程結束時。你只能在一個線程的內部獲取其 RunLoop(主線程除外)
複製代碼
autoreleasePool是一個延時release的機制, 在自動釋放池被銷燬或耗盡時,會向池中的全部對象發送release消息,釋放全部autorelease對象。
ARC下,咱們使用@autoreleasepool{}來使用一個自動釋放池
AutoreleasePool並無單獨的結構,而是由若干個AutoreleasePoolPage做爲結點以雙向鏈表的形式組合而成。整個鏈表以堆棧的形式運做。
一、每個指針表明一個加入到釋放池的對象 或者是哨兵對象,哨兵對象是在 @autoreleasepool{} 構建的時候插入的
二、當自動釋放池 pop的時候,全部哨兵對象以後的對象都會release
三、鏈表會在一個Page空間佔滿時進行增長,一個AutoreleasePoolPage的空間被佔滿時,會新建一個AutoreleasePoolPage對象,鏈接鏈表,後來的autorelease對象在新的page加入。
主線程:
主線程runloop中註冊了兩個Observer,回調都是 _wrapRunLoopWithAutoreleasePoolHandler()。
第一個oberver監聽 當從休眠狀態即將進入loop的時候 ,這個時候,構建自動釋放池
第二個oberver監聽 當準備進入休眠狀態的時候,調用 objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 釋放舊的池並建立新池
子線程:
runloop默認不開啓,不會自動建立自動釋放池,在須要使用自動釋放池的時候,須要咱們手動建立、添加自動釋放池,此時若是全部的異步代碼都寫在自動釋放池中,也能夠理解爲當子線程銷燬的時候,自動釋放池釋放
複製代碼
簡單工廠模式:根據外部信息就能夠決定建立對象,全部產品都經過工廠判斷就建立,體系結構很明顯,缺點就是集中了全部的產品建立邏輯,耦合過重。
工廠模式:產品的各自建立邏輯下發到各自的工廠類中,必定程度達到解耦合。 多態性,產品構建邏輯能夠具體到對應的產品工廠類中,更加清晰。 當我須要新產品的時候,只須要添加一個新的產品工廠,實現抽象工廠的產品產出方法,產出對應的產品。不影響客戶邏輯。
抽象工廠模式:當有多個產品線,須要多個工廠分別生產不一樣的產品線產品,這個時候咱們抽象出工廠邏輯,產品也抽象出產品類型,工廠抽象類只須要構建返回抽象產品的方法便可,更深程度的解耦。具體的什麼工廠產什麼產品邏輯下發到實際工廠實現。 即便添加新產品也不影響抽象工廠和抽象產品的邏輯。
複製代碼
網絡請求庫須要的功能:
一、在任意位置發起請求
二、請求表單的建立 (url拼接、參數填充、http請求方法確認)
三、UI-Loading
四、數據解析
五、異常處理
六、結果提示
本身分裝的 一個 API 網絡請求庫
複製代碼
經常使用的有 GCD 和 NSOperation 、NSThread
NSThread 用於獲取當前線程等操做
GCD 和 NSOperation 實現多線程操做不須要本身管理線程,操做簡單
GCD block的使用方式比NSOperation 適合簡單操做,NSOperation 對象級操做方法更多,更復雜操做適用
複製代碼
通常簡單的UITableViewCell都不會卡頓,TableView自己有Cell重用機制,但一些複雜的自適應高度的cell比較容易產生卡頓。
1、避免cell的過多從新佈局,差異太大的cell之間不要選擇重用。
2、提早計算並緩存cell的高度,內容
3、儘可能減小動態添加View的操做
4、減小全部對主線程有影響的無心義操做
5、cell中的圖片加載用異步加載,緩存等
6、局部更新cell
7、減小沒必要要的渲染時間,好比少用透明色之類的
複製代碼
ARC全稱是 Automatic Reference Counting,是Objective-C的內存管理機制。簡單地來講,就是代碼中自動加入了retain/release,原先須要手動添加的用來處理內存管理的引用計數的代碼能夠自動地由編譯器完成了。
ARC的使用是爲了解決對象retain和release匹配的問題。之前手動管理形成內存泄漏或者重複釋放的問題將不復存在。
之前須要手動的經過retain去爲對象獲取內存,並用release釋放內存。因此之前的操做稱爲MRC (Manual Reference Counting)。
複製代碼
weak和assign都是引用計數不變,兩個的差異在於,weak用於object type,就是指針類型,而assign用於簡單的數據類型,如int BOOL 等。
assign看起來跟weak同樣,其實不能混用的,assign的變量在釋放後並不設置爲nil(和weak不一樣),當你再去引用時候就會發生錯誤,崩潰,EXC_BAD_ACCESS.
assign 能夠修飾對象麼? 能夠修飾,編譯器不會報錯,可是訪問過程當中對象容易野指針
__block 用於標記須要在block內部修改的變量,__weak 用於防止引用循環
複製代碼
atomic只能保證操做也就是存取屬性的時候的存取方法是線程安全的,並不能保證整個對象就是線程安全的。
好比NSMutableArray 設置值得時候是線程安全的,可是經過objectAtIndex訪問的時候就再也不是線程安全的了。仍是須要鎖來保證線程的安全。
複製代碼
VC中一個強引用block裏面使用self
代理使用強引用
sqllite多線程搶寫入操做
複製代碼
+(void)load;
當類對象被引入項目時, runtime 會向每個類對象發送 load 消息。
load 方法會在每個類甚至分類被引入時僅調用一次,調用的順序:父類優先於子類, 子類優先於分類。
因爲 load 方法會在類被 import 時調用一次,而這時每每是改變類的行爲的最佳時機,在這裏能夠使用例如 method swizlling 來修改原有的方法。
load 方法不會被類自動繼承。
+(void)initialize;
也是在第一次使用這個類的時候會調用這個方法,也就是說 initialize 也是懶加載
總結:
在 Objective-C 中,runtime 會自動調用每一個類的這兩個方法
1.+load 會在類初始加載時調用
2.+initialize 會在第一次調用類的類方法或實例方法以前被調用
這兩個方法是可選的,且只有在實現了它們時纔會被調用
二者的共同點:兩個方法都只會被調用一次
複製代碼
runtime是 oc 語言特性,方法調用採用消息發送的方式,直到項目運行階段才能最終肯定,而且還能夠動態添加成員變量與方法。
項目中用的多的runtime應該是方法實現的替換,動態屬性的添加,KVO,performSelector,消息轉發之類
複製代碼
如何高性能的給 UIImageView 加個圓角?
很差的解決方案:使用下面的方式會強制Core Animation提早渲染屏幕的離屏繪製, 而離屏繪製就會給性能帶來負面影響,會有卡頓的現象出現。
self.view.layer.cornerRadius = 5.0f;
self.view.layer.masksToBounds = YES;
正確的解決方案:使用繪圖技術
- (UIImage *)circleImage {
// NO表明透明
UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0);
// 得到上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 添加一個圓
CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);
CGContextAddEllipseInRect(ctx, rect);
// 裁剪
CGContextClip(ctx);
// 將圖片畫上去
[self drawInRect:rect];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// 關閉上下文
UIGraphicsEndImageContext();
return image;
}
還有一種方案:使用了貝塞爾曲線"切割"個這個圖片, 給UIImageView 添加了的圓角,其實也是經過繪圖技術來實現的。
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
imageView.center = CGPointMake(200, 300);
UIImage *anotherImage = [UIImage imageNamed:@"image"];
UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, NO, 1.0);
[[UIBezierPath bezierPathWithRoundedRect:imageView.bounds
cornerRadius:50] addClip];
[anotherImage drawInRect:imageView.bounds];
imageView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[self.view addSubview:imageView];
複製代碼
卡頓的緣由就是耗時長,設計一個檢測主線程方法執行時間過長的方案
複製代碼
多線程同時操做同一個數據源的時候
AFNetworking 對於session的構建等都是線程安全的
複製代碼
經常使用的鎖有 NSLock、@synchronized代碼塊、信號量 dispatch_semaphore_t
信號量性能最高
@synchronized代碼塊 最方便
複製代碼
靜態庫
.a 、.framework 結尾
是一個已經編譯好了的集合,使用的時候鏈接器會把靜態庫合併到可執行文件中。
動態庫
.tbd 或 .framework結尾
編譯過程不會被連接到目標代碼中, 只會將動態庫頭文件添加到目標app的可執行文件,程序運行的時候被添加在獨立於app的內存區域。
複製代碼
設計一個架構 須要考慮多個層次
1、代碼風格、例如 代碼整齊,一個類不能幹兩個事情,目錄設定要清晰一眼就知道是幹什麼的,不要設置什麼common module之類的目錄,面向協議開發,瘦Controller啊等
2、規範業務塊的分層,例如 MVC 或者 MVVM,統一的業務處理分層,讓業務代碼更清晰,耦合性也低
3、基礎層的定義, 開發幫助庫,例如 網絡庫,數據持久化庫,路由庫,要求易於擴展、易於測試,易於理解,讓開發小夥伴上手快,接口方法設定要靈活,減小開發小夥伴的使用成本
4、組件化,一個架構自己也須要良好的封裝,合理的組件化可讓功能更清晰,耦合性也更低,
大的組件化就是項目層級,把不常改動的基礎庫沉底,好比放pod中,常常擴展的內容放在工程裏面,獨立的業務塊能夠經過工程的方式依賴
小的組件化就是UI方面,統一封裝管理UI輪子,避免一個東西出現不少份的狀況
複製代碼
UIKit 不是線程安全的,容易產生UI更新上的混亂
複製代碼
core animation的使用
time profiler 的使用
複製代碼
self.view的初始化,根據xib初始化或者init初始化
複製代碼
controller layout觸發的時候,開發者有機會去從新layout本身的各個subview。說UI熟悉的必定要知道。
當子View發生frame的變更的時候會觸發layoutsubView,咱們能夠在這個方法中提早作一些預處理
複製代碼
兩種queue,串行和並行。
main queue是串行,global queue是並行。
有些開發者爲了在工做線程串行的處理任務會本身創建一個serial queue。背後是蘋果維護的線程池,各類queue要用線程都是這個池子裏取的。
複製代碼
sqlite 一個線程A操做寫入、一個線程B操做讀取,在第一個線程等待寫入的過程當中也發起寫入,寫入操做在普通的事務操做 begin trancaction commit transaction ,這種狀況就會死鎖
兩個線程都爭取寫入操做,由於在A線程等待變成排他鎖的過程當中處於待定鎖狀態,並不會拒絕B線程的保留鎖的獲取,致使B線程一直不釋放共享鎖,A就一直得不到排他鎖,形成死鎖。
單個線程能夠死鎖(main thread裏dispatch_sync到main queue),
多個線程直接也能夠死鎖(A,B線程互相持有對方須要的資源且互相等待)。
複製代碼
- (int)myStrLength:(NSString *)str {
int length = 0;
char * p_str = [str cStringUsingEncoding:NSUTF8StringEncoding];
for (int i = 0; i < [str lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; i++) {
if (*p_str) {
p_str++;
length++;
} else {
p_str++;
}
}
return length;
}
複製代碼
簽名密鑰對用於數據的完整性檢測,保證防僞造與防抵賴,簽名私鑰的遺失,並不會影響對之前簽名數據的驗證,所以,簽名私鑰無須備份,所以,簽名密鑰不須要也不該該須要第三方來管理,徹底由持有者本身產生;
加密密鑰對用於數據的加密保護,若加密私鑰遺失,將致使之前的加密數據沒法解密,這在實際應用中是沒法接受的,加密私鑰應該由可信的第三方(即一般所說的CA)來備份,以保證加密數據的可用性,所以,加密密鑰對能夠由第三方來產生,並備份。
一個加密 一個保證完整性
複製代碼
能夠大大加快數據的檢索速度,這也是建立索引的最主要的緣由。
經過建立惟一性索引,能夠保證數據庫表中每一行數據的惟一性。
複製代碼
使用信號量
GCD的信號量機制(dispatch_semaphore)
信號量是一個整型值,有初始計數值;能夠接收通知信號和等待信號。當信號量收到通知信號時,計數+1;當信號量收到等待信號時,計數-1;若是信號量爲0,線程會阻塞,直到線程信號量大於0,纔會繼續下去。
使用信號量機制能夠實現線程的同步,也能夠控制最大併發數。如下是控制最大併發數的代碼。
dispatch_queue_t workConcurrentQueue = dispatch_queue_create("cccccccc", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t serialQueue = dispatch_queue_create("sssssssss",DISPATCH_QUEUE_SERIAL);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(3);
for (NSInteger i = 0; i < 10; i++) {
dispatch_async(serialQueue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_async(workConcurrentQueue, ^{
NSLog(@"thread-info:%@開始執行任務%d",[NSThread currentThread],(int)i);
sleep(1);
NSLog(@"thread-info:%@結束執行任務%d",[NSThread currentThread],(int)i);
dispatch_semaphore_signal(semaphore);});
});
}
NSLog(@"主線程...!");
說明:從執行結果中能夠看出,雖然將10個任務都異步加入了併發隊列,但信號量機制控制了最大線程併發數,始終是3個線程在執行任務。此外,這些線程也沒有阻塞線程。
複製代碼
函數是一等公民
函數能像參數那樣被傳遞到另外一個函數、從另外一個函數那像值同樣被返回出來、函數能夠賦值給變量或者存在數據結構中。
複製代碼
可能形成tableView卡頓的緣由有:
1.最經常使用的就是cell的重用, 註冊重用標識符
若是不重用cell時,每當一個cell顯示到屏幕上時,就會從新建立一個新的cell
若是有不少數據的時候,就會堆積不少cell。
若是重用cell,爲cell建立一個ID,每當須要顯示cell 的時候,都會先去緩衝池中尋找可循環利用的cell,若是沒有再從新建立cell
2.避免cell的從新佈局
cell的佈局填充等操做 比較耗時,通常建立時就佈局好
如能夠將cell單獨放到一個自定義類,初始化時就佈局好
3.提早計算並緩存cell的屬性及內容
當咱們建立cell的數據源方法時,編譯器並非先建立cell 再定cell的高度
而是先根據內容一次肯定每個cell的高度,高度肯定後,再建立要顯示的cell,滾動時,每當cell進入憑虛都會計算高度,提早估算高度告訴編譯器,編譯器知道高度後,緊接着就會建立cell,這時再調用高度的具體計算方法,這樣能夠方式浪費時間去計算顯示之外的cell
4.減小cell中控件的數量
儘可能使cell得佈局大體相同,不一樣風格的cell能夠使用不用的重用標識符,初始化時添加控件,
不適用的能夠先隱藏
5.不要使用ClearColor,無背景色,透明度也不要設置爲0
渲染耗時比較長
6.使用局部更新
若是隻是更新某組的話,使用reloadSection進行局部更
7.加載網絡數據,下載圖片,使用異步加載,並緩存
8.少使用addView 給cell動態添加view
9.按需加載cell,cell滾動很快時,只加載範圍內的cell
10.不要實現無用的代理方法,tableView只遵照兩個協議
11.緩存行高:estimatedHeightForRow不能和HeightForRow裏面的layoutIfNeed同時存在,這二者同時存在纔會出現「竄動」的bug。因此個人建議是:只要是固定行高就寫預估行高來減小行高調用次數提高性能。若是是動態行高就不要寫預估方法了,用一個行高的緩存字典來減小代碼的調用次數便可
12.不要作多餘的繪製工做。在實現drawRect:的時候,它的rect參數就是須要繪製的區域,這個區域以外的不須要進行繪製。例如上例中,就能夠用CGRectIntersectsRect、CGRectIntersection或CGRectContainsRect判斷是否須要繪製image和text,而後再調用繪製方法。
13.預渲染圖像。當新的圖像出現時,仍然會有短暫的停頓現象。解決的辦法就是在bitmap context裏先將其畫一遍,導出成UIImage對象,而後再繪製到屏幕;
14.使用正確的數據結構來存儲數據。
複製代碼
swizzle NavigationController 的 push 和 pop方法
pop了控制器後過幾秒鐘進行一遍判斷,若是爲nil表示已銷燬,沒有則表示內存泄露
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[weakSelf assertNotDealloc];
});
複製代碼
這種圖片加載方式帶有圖片緩存的功能,使用這種方式加載圖片後,圖片會自動加入系統緩存中,並不會當即釋放到內存。一些資源使程序中常用的圖片資源,
使用這種方式會加快程序的運行減小IO操做,但對於項目中只用到一次的圖片,若是採用這種方案加載,會增致使程序的內存使用增長。
非緩存的加載方式
(UIImage *)imageWithContentsOfFile:(NSString *)path
(UIImage *)
:(NSData *)data
複製代碼
後臺進入前臺
通知中心回來
正常啓動app
複製代碼
block 中 return;
[thread cancle]
複製代碼
plist
preference NSUserDefault
NSKeyedArchiver
SQLite3
coreData
沙盒
複製代碼
一、儘可能使用https
https能夠過濾掉大部分的安全問題。https在證書申請,服務器配置,性能優化,客戶端配置上都須要投入精力,因此缺少安全意識的開發人員容易跳過https,或者拖到之後遇到問題再優化。https除了性能優化麻煩一些之外其餘都比想象中的簡單,若是沒精力優化性能,至少在註冊登陸模塊須要啓用https,這部分業務對性能要求比較低。
二、不要傳輸明文密碼
不知道如今還有多少app後臺是明文存儲密碼的。不管客戶端,server仍是網絡傳輸都要避免明文密碼,要使用hash值。客戶端不要作任何密碼相關的存儲,hash值也不行。存儲token進行下一次的認證,並且token須要設置有效期,使用refresh token去申請新的token。
三、Post並不比Get安全
事實上,Post和Get同樣不安全,都是明文。參數放在QueryString或者Body沒任何安全上的差異。在Http的環境下,使用Post或者Get都須要作加密和簽名處理。
四、不要使用301跳轉
301跳轉很容易被Http劫持攻擊。移動端http使用301比桌面端更危險,用戶看不到瀏覽器地址,沒法察覺到被重定向到了其餘地址。若是必定要使用,確保跳轉發生在https的環境下,並且https作了證書綁定校驗。
五、http請求都帶上MAC
全部客戶端發出的請求,不管是查詢仍是寫操做,都帶上MAC(Message Authentication
Code)。MAC不但能保證請求沒有被篡改(Integrity),還能保證請求確實來自你的合法客戶端(Signing)。固然前提是你客戶端的key沒有被泄漏,如何保證客戶端key的安全是另外一個話題。MAC值的計算能夠簡單的處理爲hash(request
params+key)。帶上MAC以後,服務器就能夠過濾掉絕大部分的非法請求。MAC雖然帶有簽名的功能,和RSA證書的電子簽名方式卻不同,緣由是MAC簽名和簽名驗證使用的是同一個key,而RSA是使用私鑰簽名,公鑰驗證,MAC的簽名並不具有法律效應。
六、http請求使用臨時密鑰
高延遲的網絡環境下,不經優化https的體驗確實會明顯不如http。在不具有https條件或對網絡性能要求較高且缺少https優化經驗的場景下,http的流量也應該使用AES進行加密。AES的密鑰能夠由客戶端來臨時生成,不過這個臨時的AES
key須要使用服務器的公鑰進行加密,確保只有本身的服務器才能解開這個請求的信息,固然服務器的response也須要使用一樣的AES
key進行加密。因爲http的應用場景都是由客戶端發起,服務器響應,因此這種由客戶端單方生成密鑰的方式能夠必定程度上便捷的保證通訊安全。
七、AES使用CBC模式
不要使用ECB模式,記得設置初始化向量,每一個block加密以前要和上個block的祕文進行運算。
複製代碼
1.將網絡請求抽象到單獨的類中
方便在基類中處理公共邏輯;
方便在基類中處理緩存邏輯,以及其它一些公共邏輯;
方便作對象的持久化。
2.將界面的封裝抽象到專門的類中
構造專門的 UIView 的子類,來負責這些控件的拼裝。這是最完全和優雅的方式,不過稍微麻煩一些的是,你須要把這些控件的事件回調先接管,再都一一暴露回 Controller。
3.構造 ViewModel
借鑑MVVM。具體作法就是將 ViewController 給 View 傳遞數據這個過程,抽象成構造 ViewModel 的過程。
4.專門構造存儲類
專門來處理本地數據的存取。
5.整合常量
複製代碼
MVC 是一種設計思想,一種框架模式,是一種把應用中全部類組織起來的策略,它把你的程序分爲三塊,分別是:
M(Model):實際上考慮的是「什麼」問題,你的程序本質上是什麼,獨立於 UI 工做。是程序中用於處理應用程序邏輯的部分,一般負責存取數據。
C(Controller):控制你 Model 如何呈如今屏幕上,當它須要數據的時候就告訴 Model,你幫我獲取某某數據;當它須要 UI 展現和更新的時候就告訴 View,你幫我生成一個 UI 顯示某某數據,是 Model 和 View 溝通的橋樑。
V(View):Controller 的手下,是 Controller 要使用的類,用於構建視圖,一般是根據 Model 來建立視圖的。
要了解 MVC 如何工做,首先須要瞭解這三個模塊間如何通訊。
MVC通訊規則
http://cc.cocimg.com/api/uploads//20171127/1511752329535960.jpg
Controller to Model
能夠直接單向通訊。Controller 須要將 Model 呈現給用戶,所以須要知道模型的一切,還須要有同 Model 徹底通訊的能力,而且能任意使用 Model 的公共 API。
Controller to View
能夠直接單向通訊。Controller 經過 View 來佈局用戶界面。
Model to View
永遠不要直接通訊。Model 是獨立於 UI 的,並不須要和 View 直接通訊,View 經過 Controller 獲取 Model 數據
View to Controller
View 不能對 Controller 知道的太多,所以要經過間接的方式通訊。
Target
action。首先 Controller 會給本身留一個 target,再把配套的 action 交給 View 做爲聯繫方式。那麼 View
接收到某些變化時,View 就會發送 action 給 target 從而達到通知的目的。這裏 View 只須要發送
action,並不須要知道 Controller 如何去執行方法。
代理。有時候 View 沒有足夠的邏輯去判斷用戶操做是否符合規範,他會把判斷這些問題的權力委託給其餘對象,他只需得到答案就好了,並不會管是誰給的答案。
DataSoure。View 沒有擁有他們所顯示數據的權力,View 只能向 Controller 請求數據進行顯示,Controller 則獲取 Model 的數據整理排版後提供給 View。
Model 訪問 Controller
一樣的 Model 是獨立於 UI 存在的,所以沒法直接與 Controller 通訊,可是當 Model 自己信息發生了改變的時候,會經過下面的方式進行間接通訊。
Notification & KVO一種相似電臺的方法,Model 信息改變時會廣播消息給感興趣的人 ,只要 Controller 接收到了這個廣播的時候就會主動聯繫 Model,獲取新的數據並提供給 View。
從上面的簡單介紹中咱們來簡單歸納一下 MVC 模式的優勢。
1.低耦合性
2.有利於開發分工
3.有利於組件重用
4.可維護性
複製代碼
M + V + VM , VM的做用主要用於簡化Controller的負擔,可是VM的設計中不能夠沒有C,其實應該是 M + V + C +VM , C 做爲 關聯 V 和 VM 的紐帶, 最好不要直接關聯VM。
複製代碼
專題(持續更新)