iOS網絡層設計git
一、網絡層和業務層的對接設計github
使用哪一種交互模式來和業務層對接 : 使用Delegate爲主,目的是爲了(1)減小代碼的分散度(2)減小業務層和網絡層的耦合,網絡層對於業務層應該是抽象的,隱藏了實現細節的 (3)只採用一種是限制了靈活性,方便進行維護面試
在網絡層不要濫用block :(1)block會延長對象的生命期,delegate則不會
(2)block適合於在每次回調的任務都不同的狀況下,若是同樣則應使用delegate,蘋果內部的網絡層封裝爲delegate(離散型),AF的網絡層封裝爲block(集約型)算法
使用一個reformer對象來封裝數據轉化邏輯,從而節省了業務層進行字典轉模型這樣相似的繁瑣操做,同時爲了解決直接使用字典的可讀性差的問題,採用KPropertyStudentID這樣的const變量來做爲字典的key。數據庫
使用離散型(delegate)的方式作網絡層封裝須要使用到繼承,使用一個BaseAPIManager做爲父類,來處理全部須要集約化的操做(例如一些公用信息),而後讓不少子類來作離散化的操做編程
二、網絡層的安全防範windows
防止競爭對手使用本身的API,爲本身的API設計一個簽名,服務端給出一個密鑰,在每次使用API的時候進行一個hash算法的操做,將hash出來的值和服務端hash出來的值進行一個對比,若是同樣,則代表是本身在使用API緩存
防止中間人攻擊,使用較爲安全的HTTPS協議,防止運營商在請求中加入廣告安全
MVC模式和MVVM模式的區別微信
一、MVC模式存在Controller中代碼臃腫的問題
之因此會出現MVC模式,是由於發如今開發中會有不少代碼能夠進行復用,同時事實也正是如此,MVC三個沒款中,Model和View的代碼確實能夠由於MVC模式而進行復用,在github上也有不少開源的項目中封裝了不少View,咱們能夠很方便得使用這些view,model類做爲一個數據轉化邏輯的類也能夠在同一個項目中進行屢次複用,可是Controller卻很難在一個項目中進行復用,因此咱們在寫代碼的時候儘可能在Controller中只寫那些沒法複用的代碼,例如將view添加到controller上,將model的數據傳給view等等,可是實際上很難作到這一點,每每有不少代碼咱們都不知道放在哪裏,到了最後便放在了controller裏面,致使controller變得十分臃腫。
二、對MVC模式中的Controller進行瘦身
咱們能夠從下面幾點對Controller進行瘦身:
三、MVVM模式的認知
四、總結
應該結合MVC和MVVM的各自的優勢去讓Controller進行瘦身,而不該該盲目地去追求新技術,亦或是過於保守,不肯意向前發展。
iOS中如何設置圓角
一、常規的設置方式帶來的性能損耗
使用cornerRadius屬性設置圓角是不會產生離屏渲染的,同時也不會真正在UI上產生圓角,這時候咱們須要將masksToBounds設置爲YES,纔可以產生在UI上的圓角效果,可是同時,這樣也會致使離屏渲染。產生離屏渲染對於性能上有很大的消耗,將會下降FPS幀數,緣由是由於離屏渲染須要將圖像的處理放在屏幕以外的內存緩存區進行處理,處理結束以後才把獲得的圖像放到主屏幕上。在這個過程當中產生最大消耗的是兩次上下文的交互,將處理放到屏幕以外的緩存區,而後把獲得的圖像放到主屏幕上。
二、使用不產生離屏渲染的方式來創造圓角
使用貝塞爾曲線UIBezierPath和Core Graphics框架畫出一個圓角
UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(100, 100, 100, 100)]; imageView.image = [UIImage imageNamed:@"1"]; //開始對imageView進行畫圖 UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, NO, [UIScreen mainScreen].scale); //使用貝塞爾曲線畫出一個圓形圖 [[UIBezierPath bezierPathWithRoundedRect:imageView.bounds cornerRadius:imageView.frame.size.width] addClip]; [imageView drawRect:imageView.bounds]; imageView.image = UIGraphicsGetImageFromCurrentImageContext(); //結束畫圖 UIGraphicsEndImageContext(); [self.view addSubview:imageView];
三、總結
微信中點擊頭像放大動畫的思路
建立一個背景和新的UIImageView,UIImageView是位於背景之上的,先把背景的透明度改成0,而後進行動畫,動畫的效果是將新的UIImageView從原始的位置(這個位置是原來的UIImageView在新的背景上對應的frame)變化到放大的位置,而後監聽背景的點擊事件,點擊的時候進行透明度和frame的相反變化便可。具體過程我封裝好了上傳到Github了,點擊這裏查看。
線上項目出現bug怎麼解決
這裏將會涉及到JSPatch這個框架的使用,這個框架的做用就是對bug進行熱修復..後續更新
iOS開發中有哪些狀況會產生循環引用
一、block
二、delegate
三、NSTimer
解決辦法:使用一個NSTimer的Catagory,而後重寫初始化方法,在實現中利用block,從而在調用的時候可使用weakSelf在block執行任務
autoreleasePool(加入到autoreleasePool中的對象)在何時釋放?
iOS中的深淺複製
請查看這篇文章,講得很深刻:iOS剖析深淺複製
iOS中的屬性修飾符
列舉的順序就是修飾符在聲明的時候的順序
一、原子性修飾符
二、讀寫權限修飾符
三、內存管理修飾符
四、讀寫方法名修飾符
iOS中屬性內存管理修飾符中的那些CP
strong vs copy
self.name = anotherName;
例如上面的代碼,使用strong表示的是self.name和anotherName這兩個指針同時指向了一個對象,過程是self.name指向了anotherName指向的對象,而若是使用copy的話,self.name和anotherName這兩個指針同時指向了不一樣的對象,過程是copy會將anotherName所指向的對象拷貝一份出來(淺拷貝),而後讓self.name指向這個被拷貝出來的對象。
strong vs weak
只要存在strong類型修飾的屬性(指針)指向了一個對象,那麼這個對象就不會被ARC銷燬,可是對於weak類型修飾的屬性(指針)指向了一個對象,若是這個對象被銷燬了,那麼這個屬性(指針)就會被自動設置爲nil。能夠說weak類型的指針是沒有約束做用的,只是簡單弱弱地表示了一下關係。
這裏還須要分析在聲明控件到底應該使用strong仍是weak
若是是使用storyboard:
若是是使用純代碼:
綜上,都應該使用weak去聲明控件,純代碼中若是使用了strong去聲明控件,那麼有一種狀況:若是將控件remove了,那麼controller中的view裏面的subviews所引用的那條線將會被切斷,可是strong屬性(指針)所引用的這條線依然存在,因爲採用的是強引用,因此控件將不會被ARC給銷燬,那麼就會一直佔用內存,直到控制器銷燬。
weak vs assign
weak只能用於對象類型,assign能夠用於基本類型,weak比起assign有一點更好,若是weak修飾的屬性指向的一個對象被銷燬了,那麼這個屬性將會自動被設置爲nil指針,若是assign修飾的屬性指向的一個對象被銷燬了,那麼這個屬性不會被自動設置爲nil,同時他也不知道所指向的對象已經被銷燬了,這樣就引起了野指針。
weak vs unsafe_unretained
若是weak修飾的屬性指向的一個對象被銷燬了,那麼這個屬性將會自動被設置爲nil指針,若是unsafe_unretained修飾的屬性指向的一個對象被銷燬了,那麼這個屬性不會被自動設置爲nil,同時他也不知道所指向的對象已經被銷燬了,這樣就引起了野指針。
assign vs unsafe_unretained
assign能修飾基本類型,unsafe_unretained只能修飾對象類型
iOS中的多線程
一、pthread
基於C語言,不經常使用
二、NSThread
須要本身手管理線程的生命週期,偶爾使用,例如獲取當前線程
[NSThread currentThread];
三、GCD(Grand Central Dispatch)
GCD是蘋果開發出來的多核編程的解決方案,雖然是基於C語言的,可是採用了block進行封裝,使用起來也很方便,同時也很重要,推薦使用GCD進行多線程編程
四、NSOperation
是蘋果對於GCD的封裝,效率不及GCD
iOS中的GCD
主隊列:是一個特殊的串行隊列,在主線程中運行,用於刷新UI,是一個串行隊列
//串行隊列 dispatch_queue_t queue = dispatch_get_main_queue;
自定義建立隊列: 既能夠建立串行隊列也能夠建立並行隊列。
//串行隊列 dispatch_queue_t queue = dispatch_queue_create("nineteen", NULL); dispatch_queue_t queue = dispatch_queue_create("nineteen", DISPATCH_QUEUE_SERIAL); //並行隊列 dispatch_queue_t queue = dispatch_queue_create("nineteen", DISPATCH_QUEUE_CONCURRENT);
全局並行隊列:系統提供的並行隊列
//並行隊列 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0); dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
循環執行任務:dispatch_apply相似一個for循環,併發地執行每一項。全部任務結束後,dispatch_apply纔會返回,會阻塞當前線程(相似同步執行)。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); /* *count: 循環執行次數 *queue: 隊列,能夠是串行隊列或者是並行隊列(使用串行隊列可能致使死鎖) *block: 任務 */ dispatch_apply(count, queue, ^(size_t i) { NSLog(@"%zu %@", i, [NSThread currentThread]); });
隊列組:隊列組將不少隊列添加到一個組裏,當組裏全部任務都執行完後,它會經過一個方法通知咱們。基本流程是首先建立一個隊列組,而後把任務添加到組中,最後等待隊列組的執行結果。
//建立隊列組 dispatch_group_t group = dispatch_group_create(); //建立隊列 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //並行隊列執行3次循環 (隊列組只能用異步方法執行) dispatch_group_async(group, queue, ^{ for (NSInteger i = 0; i < 3; i++) { NSLog(@"group-01 - %@", [NSThread currentThread]); } }); //主隊列執行5次循環 dispatch_group_async(group, dispatch_get_main_queue(), ^{ for (NSInteger i = 0; i < 5; i++) { NSLog(@"group-02 - %@", [NSThread currentThread]); } }); //都完成後會自動通知 dispatch_group_notify(group, dispatch_get_main_queue(), ^{ NSLog(@"完成 - %@", [NSThread currentThread]); });
實現單例模式
static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ //dispatch_once中的代碼只執行一次,經常使用來實現單例 });
GCD延遲操做
//建立隊列 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //設置延時,單位秒 double delay = 3; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), queue, ^{ //3秒後須要執行的任務 });
GCD中的死鎖場景
五個案例瞭解GCD的死鎖
一、
案例:
NSLog(@"1"); // 任務1 dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"2"); // 任務2 }); NSLog(@"3"); // 任務3
結果:
1
二、
案例:
NSLog(@"1"); // 任務1 dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ NSLog(@"2"); // 任務2 }); NSLog(@"3"); // 任務3
結果:
1 2 3
三、
案例:
dispatch_queue_t queue = dispatch_queue_create("com.demo.serialQueue", DISPATCH_QUEUE_SERIAL); NSLog(@"1"); // 任務1 dispatch_async(queue, ^{ NSLog(@"2"); // 任務2 dispatch_sync(queue, ^{ NSLog(@"3"); // 任務3 }); NSLog(@"4"); // 任務4 }); NSLog(@"5"); // 任務5
結果:
1 5 2 // 5和2的順序不必定
四、
案例:
NSLog(@"1"); // 任務1 dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"2"); // 任務2 dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"3"); // 任務3 }); NSLog(@"4"); // 任務4 }); NSLog(@"5"); // 任務5
結果:
1 2 5 3 4 // 5和2的順序不必定
五、
案例:
dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"1"); // 任務1 dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"2"); // 任務2 }); NSLog(@"3"); // 任務3 }); NSLog(@"4"); // 任務4 while (1) { } NSLog(@"5"); // 任務5
結果:
1 4 // 1和4的順序不必定
iOS中的遞歸鎖
若是加鎖操做處於一個循環或者遞歸中,在第一次加鎖尚未解鎖的時候,就進行了第二次加鎖,因此就形成死鎖現象,這時候應該使用遞歸鎖來防止死鎖的發生。
iOS中的ARC是怎麼解決內存管理問題的
ARC會自動處理對象的聲明週期,編譯的時候在合適的地方插入內存管理代碼
ARC中autorelease的使用場景
iOS中的RunLoop
通常主線程會自動運行RunLoop,咱們通常狀況下不會去管。在其餘子線程中,若是須要咱們須要去管理。使用RunLoop後,能夠把線程想象成進入了一個循環;若是沒有這個循環,子線程完成任務後,這個線程就結束了。因此若是須要一個線程處理各類事件而不讓它結束,就須要運行RunLoop。
SDWebImage是怎麼使用RunLoop的
- (void)start{ ... self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO]; ... if(self.connection){ ... CFRunLoopRun( ) ... } }
- (void)cancelInternalAndStop { if (self.isFinished) return; [self cancelInternal]; CFRunLoopStop(CFRunLoopGetCurrent()); }
在建立self.connection成功後,執行了CFRunLoopRun(),開啓了runloop。在failed或finished的時候會調用CFRunLoopStop中止runloop。若是不開啓runloop的話,在執行完start ()後任務就完成了,NSURLConnection的代理就不會執行了。runloop至關於子線程的循環,能夠靈活控制子線程的生命週期。
AFNetworking是怎麼使用RunLoop的
AFNetworking解決這個問題採用了另外一種方法:單獨起一個global thread,內置一個runloop,全部的connection都由這個runloop發起,回調也都由它接收。這是個不錯的想法,既不佔用主線程,又不耗CPU資源:
iOS中的響應鏈
NSRunloop、runloop、autoreleasePool、thread
NSRunloop:NSRunloop是一個消息循環,它會檢測輸入元和定時源,而後作回調處理。NSRunloop封裝了windows中的消息處理,將SendMessage、PostMessage、GetMessage等細節封裝了起來。關於NSRunloop須要着重瞭解這幾點內容:
runloop:新建iOS項目的時候會看到在main方法中會手動建立一個autoreleasePool,程序開始時建立,結束時銷燬,若是隻是從表面上來看的話,那麼這樣和內存泄露是沒有什麼區別的。其實,對於每個runloop,系統會隱式地建立一個autoreleasePool,這樣全部的autoreleasePool構成一個棧式的結構,在每個runloop結束的時候,當前棧頂的autoreleasePool就會被彈出,同時銷燬,其中的全部對象也一樣被銷燬。這裏所指的runloop不是NSRunloop,這裏的runloop多是一個UI事件,一個timer等等,具體來講指的是從接受到消息,處處理完這個消息的一個完整過程。
autoreleasePool和thread:
thread是不會自動建立autoreleasePool的
drawRect的做用
layoutSubviews的做用
自定義控件
數據持久化的幾種方式的對比
Plist文件(屬性列表):
plist文件是將某些特定的類,經過XML文件的方式保存在目錄中,這些類包括(若是存在對應的可變類也包括可變類)
NSArray
NSDictionary
NSData
NSString
NSNumber
NSDate
Preference(偏好設置):
NSKeyedArchiver(歸檔):
歸檔在iOS中是另外一種形式的序列化,只要遵循了NSCoding協議的對象均可以經過它實現序列化
app的狀態
UIView和CALayer的區別
KVO的實現原理