離職找工做中,刷一刷網上的面試題。原文連接ios
由於OC能夠在運行時能夠改變其結構:新的函數能夠被引進,已有的函數能夠被刪除等。因此,OC是一門動態語言。它具備至關多的動態特性,基本的,也是常常被提到和用到的有動態類型(Dynamic typing),動態綁定(Dynamic binding)和動態加載(Dynamic loading)面試
id obj = someInstance;
if ([obj isKindOfClass:someClass])
{
someClass *classSpecifiedInstance = (someClass *)obj;
// Do Something to classSpecifiedInstance which now is an instance of someClass
//...
}
複製代碼
動態綁定(Dynamic binding)
便是在實例所屬類肯定後,將某些屬性和相應的方法綁定到實例上。例如class_addMethod
這個方法就能夠動態的添加方法。編程
動態加載(Dynamic loading)
讓程序在運行時添加代碼模塊以及其餘資源。用戶能夠根據須要加載一些可執行代碼和資源,而不是在啓動時就加載全部組件。好比@2x,@3x資源,就是在運行的時候根據不一樣的設備加載不一樣的資源。安全
它們都是MVC的變種,結構劃分爲:bash
其中mvp和mvvm中的v是包含了ViewController的。服務器
看圖說話。網絡
viewDidLoad
、
viewWillAppear
這些view的生命週期都會在controller裏面來管理。再加上controller還要負責代理、數據源、網絡請求等,因而controller就變得愈來愈龐大,愈來愈混亂,很很差測試。
UIViewController
,它負責全部跟UI相關的東西,好比view的生命週期管理,佈局。因此P的責任更加單一,只是經過數據和狀態更新View。因爲V和P的分離,咱們會寫不少事件傳遞的代碼來鏈接V和P。好比:
import UIKit
struct Person { // Model
let firstName: String
let lastName: String
}
protocol GreetingView: class {
func setGreeting(greeting: String)
}
protocol GreetingViewPresenter {
init(view: GreetingView, person: Person)
func showGreeting()
}
class GreetingPresenter : GreetingViewPresenter {
unowned let view: GreetingView
let person: Person
required init(view: GreetingView, person: Person) {
self.view = view
self.person = person
}
func showGreeting() {
let greeting = "Hello" + " " + self.person.firstName + " " + self.person.lastName
self.view.setGreeting(greeting)
}
}
class GreetingViewController : UIViewController, GreetingView {
var presenter: GreetingViewPresenter!
let showGreetingButton = UIButton()
let greetingLabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
self.showGreetingButton.addTarget(self, action: "didTapButton:", forControlEvents: .TouchUpInside)
}
func didTapButton(button: UIButton) {
self.presenter.showGreeting()
}
func setGreeting(greeting: String) {
self.greetingLabel.text = greeting
}
// layout code goes here
}
// Assembling of MVP
let model = Person(firstName: "David", lastName: "Blaine")
let view = GreetingViewController()
let presenter = GreetingPresenter(view: view, person: model)
view.presenter = presenter
複製代碼
MVP帶給咱們更好的可測試性的同時又帶來了大量的代碼。像上面這個例子裏,由於V和P的分離,在須要傳遞事件時,View中的一個方法只調用Presenter的一個方法的狀況會時常發生多線程
func didTapButton(button: UIButton) {
self.presenter.showGreeting()
}
複製代碼
MVVM和MVP的區別主要在於數據綁定這一塊。經過響應式編程的框架好比ReactiveCocoa來把View和ViewModel綁定在一塊兒,這樣咱們就不用寫不少刷新頁面的代碼了。架構
最後說一下VIPER這個框架,它不屬於MV(X)架構,它更像是樂高積木同樣搭建你的應用。VIPER對職責劃分了5個模塊。併發
實際上 VIPER 模塊能夠只是一個頁面(screen),也能夠是你應用裏整個的用戶使用流程(the whole user story)- 好比說「驗證」這個功能,它能夠只是一個頁面,也能夠是連續相關的一組頁面。你的每一個「樂高積木」想要有多大,都是你本身來決定的。
這篇文章對幾個模式分析得很好。值得好好讀一讀。文中的幾個例子也是引自這篇文章。
避免循環引用。
@interface SubObj : NSObject
@property(nonatomic,strong) id delegate;
@end
@interface ViewController : UIViewController
@property(nonatomic,strong)SubObj * obj;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.obj = [[SubObj alloc] init];
self.obj.delegate = self;
}
@end
複製代碼
上面的代碼中就出現了ViewController
持有了SubObj
,同時由於Suobj
的delegate是強引用的,因此持有了ViewController
,出現了循環引用。
delegate主要是事件拋到給代理來作。dataSource主要是數據來源。
通常狀況下,簡單功能的回調用block,系列函數的回調選擇delegate。
nonatomic
,atomic
readonly
,readwrite
getter
,setter
strong
,assign
,weak
,copy
,unsafe_unretained
nullable
,nonnull
,null_resettable
,null_unspecified
class
基本數據類型默認關鍵字是 atomic
,readwrite
,assign
其餘類型默認關鍵字是 atomic
,readwrite
,strong
防止被修改。好比:
@interface ViewController : UIViewController
@property(nonatomic,strong)NSString * text;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableString *test = [NSMutableString stringWithFormat:@"123"];
self.text = test;
[test appendString:@"456"];
}
@end
複製代碼
這個時候打印self.text
則變成了被修改後的值123456
。
實現NSCopying
協議就讓對象有了拷貝功能。
簡單的說,copy
會生成一個不可變的對象,mutableCopy
會生成一個可變對象
NSArray *array = @[@1,@2];
NSArray *array2 = [array copy];//不可變
NSMutableArray *array3 = [array mutableCopy];//可變
NSArray *array4 = [array3 copy];//不可變
NSMutableArray *array5 = [array3 mutableCopy];//可變
複製代碼
集合裏面的元素並無內容拷貝。仍是原來的對象。
NSArray<NSMutableString *> *array = @[[NSMutableString stringWithString:@"123"]];
NSArray<NSMutableString *> *array2 = [array copy];
NSMutableString *item = array2[0];
[item appendString:@"456"];
NSLog(@"%@",array);
複製代碼
打印結果是
(
123456
)
複製代碼
由於view
被添加到superView
上面後,就被superView
持有了。咱們通常在IB裏面的拖的view
都是加在了根view或者它的子view上。而根view又被它的controller
持有,因此IBOutlet
能夠用weak
。若是,在IB裏面拖出來的view是一個單獨的view
沒有被加到任何其餘view
上,則須要用strong
。
nonatomic
表示非原子性,不安全,效率高。atomic
表示原子性,效率低。
atomic
不是絕對的線程安全的。
@property (atomic, assign) int intA;
//thread A
for (int i = 0; i < 10000; i ++) {
self.intA = self.intA + 1;
NSLog(@"Thread A: %d\n", self.intA);
}
//thread B
for (int i = 0; i < 10000; i ++) {
self.intA = self.intA + 1;
NSLog(@"Thread B: %d\n", self.intA);
}
複製代碼
即便我將intA聲明爲atomic,最後的結果也不必定會是20000。緣由就是由於self.intA = self.intA + 1
;不是原子操做,雖然intA的getter和setter是原子操做,但當咱們使用intA的時候,整個語句並非原子的,這行賦值的代碼至少包含讀取(load),+1(add),賦值(store)三步操做,當前線程store的時候可能其餘線程已經執行了若干次store了,致使最後的值小於預期值。這種場景咱們也能夠稱之爲多線程不安全。
@property (atomic, strong) NSString* stringA;
//thread A
for (int i = 0; i < 100000; i ++) {
if (i % 2 == 0) {
self.stringA = @"a very long string";
}
else {
self.stringA = @"string";
}
NSLog(@"Thread A: %@\n", self.stringA);
}
//thread B
for (int i = 0; i < 100000; i ++) {
if (self.stringA.length >= 10) {
NSString* subStr = [self.stringA substringWithRange:NSMakeRange(0, 10)];
}
NSLog(@"Thread B: %@\n", self.stringA);
}
複製代碼
雖然stringA是atomic的property,並且在取substring的時候作了length判斷,線程B仍是很容易crash,由於在前一刻讀length的時候self.stringA = @"a very long string";,下一刻取substring的時候線程A已經將self.stringA = @"string";,當即出現out of bounds的Exception,crash,多線程不安全。
這段例子引用於這篇文章
繼承UICollectionViewLayout
本身實現prepareLayout
,collectionViewContentSize
,layoutAttributesForElementsInRect:
這三個方法。
平日裏開發,複用較多的模塊用的xib來寫。單獨的模塊,如設置界面用的storyboard來開發。
關於StoryBoard的討論能夠參看喵神的這篇文章
進程(process)
狹義的定義:進程就是一段程序的執行過程。
廣義定義:進程是一個具備必定獨立功能的程序關於某次數據集合的一次運行活動,它是操做系統分配資源的基本單元。
簡單來說進程的概念主要有兩點:第一,進程是一個實體。每個進程都有它本身的地址空間,通常狀況下,包括文本區域(text region)、數據區域(data region)和堆棧(stack region)。文本區域存儲處理器執行的代碼;數據區域存儲變量和進程執行期間使用的動態分配的內存;堆棧區域存儲着活動過程當中調用的指令和本地變量。第二,進程是一個「執行中的程序」。程序是一個沒有生命的實體,只有處理器賦予程序生命時,它才能成爲一個活動的實體,咱們稱其爲進程。
進程狀態:進程有三個狀態,就緒,運行和阻塞。就緒狀態其實就是獲取了除cpu外的全部資源,只要處理器分配資源立刻就能夠運行。運行態就是獲取了處理器分配的資源,程序開始執行,阻塞態,當程序條件不夠時,須要等待條件知足時候才能執行,如等待I/O操做的時候,此刻的狀態就叫阻塞態。
說說程序,程序是指令和數據的有序集合,其自己沒有任何運動的含義,是一個靜態的概念,而進程則是在處理機上的一次執行過程,它是一個動態的概念。進程是包含程序的,進程的執行離不開程序,進程中的文本區域就是代碼區,也就是程序。
線程(thread)
一般在一個進程中能夠包含若干個線程,固然一個進程中至少有一個線程,否則沒有存在的意義。線程能夠利用進程所擁有的資源,在引入線程的操做系統中,一般都是把進程做爲分配資源的基本單位,而把線程做爲獨立運行和獨立調度的基本單位,因爲線程比進程更小,基本上不擁有系統資源,故對它的調度所付出的開銷就會小得多,能更高效的提升系統多個程序間併發執行的程度。
引用於這篇文章
同步是串行的順序執行,異步是並行的同時執行
併發和並行的區別就是一個處理器同時處理多個任務和多個處理器或者是多核的處理器同時處理多個不一樣的任務。
前者是邏輯上的同時發生(simultaneous),然後者是物理上的同時發生.
併發性(concurrency),又稱共行性,是指能處理多個同時性活動的能力,併發事件之間不必定要同一時刻發生。
並行(parallelism)是指同時發生的兩個併發事件,具備併發的含義,而併發則不必定並行
線程間通訊指的是:一、一個線程傳遞數據給另外一個線程,二、在一個線程中執行完特定任務後,轉到另外一個線程繼續執行任務。
在iOS中能夠用這些方法來進行線程間通訊:
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5, 2_0);
dispatch_async(otherQueue, ^{
// dosth
});
複製代碼
dispatch_group
:作完一組操做後再執行後續的代碼
它有兩種用法: 一種是dispatch_group_async
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSLog(@"group one start");
dispatch_group_async(group, queue, ^{
//do something
});
dispatch_group_async(group, queue, ^{
//do something
});
dispatch_group_notify(group, queue, ^{
//do something
});
複製代碼
第二種是dispatch_group_enter
和dispatch_group_leave
:
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_enter(group);
dispatch_async(queue, ^{
//do something
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
//do something
dispatch_group_leave(group);
});
dispatch_group_notify(group, queue, ^{
//do something
});
複製代碼
barrier
- (void)barrier {
dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
// dosth1;
});
dispatch_async(queue, ^{
// dosth2;
});
dispatch_barrier_async(queue, ^{
// doBarrier;
});
dispatch_async(queue, ^{
// dosth4;
});
dispatch_async(queue, ^{
// dosth5;
});
}
複製代碼
dispatch_semaphore
當咱們多個線程要訪問同一個資源的時候,每每會設置一個信號量,當信號量大於0的時候,新的線程能夠去操做這個資源,操做時信號量-1,操做完後信號量+1,當信號量等於0的時候,必須等待,因此經過控制信號量,咱們能夠控制可以同時進行的併發數。
信號量有如下3個函數
dispatch_semaphore_create //建立一個信號量
dispatch_semaphore_signal //信號量+1
dispatch_semaphore_wait //等待,直到信號量大於0時,便可操做,同時將信號量-1
複製代碼
-(void)dispatchSignal{
//crate的value表示,最多幾個資源可訪問
dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//任務1
dispatch_async(quene, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"run task 1");
sleep(1);
NSLog(@"complete task 1");
dispatch_semaphore_signal(semaphore);
});
//任務2
dispatch_async(quene, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"run task 2");
sleep(1);
NSLog(@"complete task 2");
dispatch_semaphore_signal(semaphore);
});
//任務3
dispatch_async(quene, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"run task 3");
sleep(1);
NSLog(@"complete task 3");
dispatch_semaphore_signal(semaphore);
});
}
複製代碼
執行結果爲
run task 1
run task 2
complete task 1
complete task 2
run task 3
complete task 3
複製代碼
因爲設定的信號值爲2,先執行兩個線程,等執行完一個,纔會繼續執行下一個,保證同一時間執行的線程數不超過2。
若是咱們把信號量設置成1dispatch_semaphore_create(1)
,那麼執行結果就會變成順序執行
run task 1
complete task 1
run task 2
complete task 2
run task 3
complete task 3
複製代碼
用鎖,或者把資源的操做放到單一線程中。
// 當應用程序啓動時(不包括已在後臺的狀況下轉到前臺),調用此回調。launchOptions是啓動參數,假如用戶經過點擊push通知啓動的應用,這個參數裏會存儲一些push通知的信息
– (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions NS_AVAILABLE_IOS(3_0);
– (void)applicationDidBecomeActive:(UIApplication *)application;
//應用即將從前臺狀態轉入後臺
- (void)applicationWillResignActive:(UIApplication *)application;
– (void)applicationDidEnterBackground:(UIApplication *)application NS_AVAILABLE_IOS(4_0);
//從後臺到前臺調用了:
– (void)applicationWillEnterForeground:(UIApplication *)application NS_AVAILABLE_IOS(4_0);
– (void)applicationDidBecomeActive:(UIApplication *)application;
複製代碼
NSLog(@"%@")
和[NSString stringWithFormat:@"%@"]
會轉換成description
返回的字符串。
使用引用計數機制管理對象內存。