在 iOS 開發中,語言的選擇是最初的一步。面試
Objective-C 是蘋果爲 iOS 和 Mac 開發量身定製的語言。它隨着 iPhone 的出現而大火,直到今天國內外大多數的 App 依然是用 Objective-C 在寫。它一度在 TIOBE 排行榜上位列第三名,僅次於 Java 和 C。其市場佔有份額也遠超其餘語言。看名字咱們能夠知道,它與 C 語言有千絲萬縷的聯繫,事實上也確實如此:Objective-C 是 C 語言的超集,它在 C 語言主體上加上了面向對象的特性。這是爲了 App 開發的方便,同時也兼顧了語言的總體性能。編程
2014年來,Swift 橫空出世,功能不斷完善,逐漸成爲 Apple 全力主推的官方編程語言。自發布以來,Swift 已經歷經4個版本的迭代。在 TIOBE 編程語言排行榜上的目前位列12位,超過 Ruby 並遠遠甩開其上代語言 Objective-C。從性能上來講,它的速度是 Objective-C 的2.6倍,Python 的8.4倍。更重要的是,Swift 是一門開源的語言,它的質量和進步接受着整個業界的建議、監督、關注。不管從哪一個角度講,Swift 都將取代 Objective-C,成爲 iOS 開發的主流語言。安全
如今的面試中,傳統大廠如BAT對 Objective-C 的語言進行較多考察,平常開發也是以 Objective-C爲主。而由於 Swift 的高歌猛進,咱們往後會看到關於 Swift 的問題愈來愈多。本文收錄總結了常見的 Swift 和 Objective-C 的面試題,但願對你們有所幫助。多線程
strong表示指向並擁有該對象。其修飾的對象引用計數會增長1。該對象只要引用計數不爲0則不會被銷燬。固然強行將其設爲nil能夠銷燬它。閉包
weak表示指向但不擁有該對象。其修飾的對象引用計數不會增長。無需手動設置,該對象會自行在內存中銷燬。async
assign主要用於修飾基本數據類型,如NSInteger和CGFloat,這些數值主要存在於棧上。編程語言
weak 通常用來修飾對象,assign通常用來修飾基本數據類型。緣由是assign修飾的對象被釋放後,指針的地址依然存在,形成野指針,在堆上容易形成崩潰。而棧上的內存系統會自動處理,不會形成野指針。函數
copy與strong相似。不一樣之處是strong的複製是多個指針指向同一個地址,而copy的複製每次會在內存中拷貝一份對象,指針指向不一樣地址。copy通常用在修飾有可變對應類型的不可變對象上,如NSString, NSArray, NSDictionary。oop
Objective-C 中,基本數據類型的默認關鍵字是atomic, readwrite, assign;普通屬性的默認關鍵字是atomic, readwrite, strong。性能
__weak
,__block
__weak
與weak基本相同。前者用於修飾變量(variable),後者用於修飾屬性(property)。__weak
主要用於防止block中的循環引用。
__block
也用於修飾變量。它是引用修飾,因此其修飾的值是動態變化的,便可以被從新賦值的。__block
用於修飾某些block內部將要修改的外部變量。
__weak
和__block
的使用場景幾乎與block息息相關。而所謂block,就是Objective-C對於閉包的實現。閉包就是沒有名字的函數,或者理解爲指向函數的指針。
atomic修飾的對象會保證setter和getter的完整性,任何線程對其訪問均可以獲得一個完整的初始化後的對象。由於要保證操做完成,因此速度慢。它比nonatomic安全,但也並非絕對的線程安全,例如多個線程同時調用set和get就會致使得到的對象值不同。絕對的線程安全就要用關鍵詞synchronized。
nonatomic修飾的對象不保證setter和getter的完整性,因此多個線程對它進行訪問,它可能會返回未初始化的對象。正由於如此,它比atomic快,但也是線程不安全的。
ARC全稱是 Automatic Reference Counting,是Objective-C的內存管理機制。簡單地來講,就是代碼中自動加入了retain/release,原先須要手動添加的用來處理內存管理的引用計數的代碼能夠自動地由編譯器完成了。
ARC的使用是爲了解決對象retain和release匹配的問題。之前手動管理形成內存泄漏或者重複釋放的問題將不復存在。
之前須要手動的經過retain去爲對象獲取內存,並用release釋放內存。因此之前的操做稱爲MRC (Manual Reference Counting)。
循環引用是指2個或以上對象互相強引用,致使全部對象沒法釋放的現象。這是內存泄漏的一種狀況。舉個例子:
class Father
@interface Father: NSObject
@property (strong, nonatomic) Son *son;
class Son
@interface Son: NSObject
@property (strong, nonatomic) Father *father;
@end
上述代碼有兩個類,分別爲爸爸和兒子。爸爸對兒子強引用,兒子對爸爸強引用。這樣釋放兒子必須先釋放爸爸,要釋放爸爸必須先釋放兒子。如此一來,兩個對象都沒法釋放。
解決方法是將Father中的Son對象屬性從strong改成weak。
內存泄漏能夠用Xcode中的Debug Memory Graph去檢查,同時Xcode也會在runtime中自動彙報內存泄漏的問題。
- (void)viewDidLoad {
UILabel *alertLabel = [[UILabel alloc] initWithFrame:CGRectMake(100,100,100,100)];
alertLabel.text = @"Wait 4 seconds...";
[self.view addSubview:alertLabel];
NSOperationQueue *backgroundQueue = [[NSOperationQueue alloc] init];
[backgroundQueue addOperationWithBlock:^{
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:4]];
alertLabel.text = @"Ready to go!」
}];
}
Bug在於,在等了4秒以後,alertLabel並不會更新爲Ready to Go。
緣由是,全部UI的相關操做應該在主線程進行。當咱們能夠在一個後臺線程中等待4秒,可是必定要在主線程中更新alertLabel。
最簡單的修正以下:
- (void)viewDidLoad {
UILabel *alertLabel = [[UILabel alloc] initWithFrame:CGRectMake(100,100,100,100)];
alertLabel.text = @"Wait 4 seconds...";
[self.view addSubview:alertLabel];
NSOperationQueue *backgroundQueue = [[NSOperationQueue alloc] init];
[backgroundQueue addOperationWithBlock:^{
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:4]];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
alertLabel.text = @"Ready to go!」
}];
}];
}
緣由在於滑動時當前線程的runloop切換了mode用於列表滑動,致使timer暫停。
runloop中的mode主要用來指定事件在runloop中的優先級,有如下幾種:
Default(NSDefaultRunLoopMode):默認,通常狀況下使用;
Connection(NSConnectionReplyMode):通常系統用來處理NSConnection相關事件,開發者通常用不到;
Modal(NSModalPanelRunLoopMode):處理modal panels事件;
Event Tracking(NSEventTrackingRunLoopMode):用於處理拖拽和用戶交互的模式。
Common(NSRunloopCommonModes):模式合集。默認包括Default,Modal,Event Tracking三大模式,能夠處理幾乎全部事件。
回到題中的情境。滑動列表時,runloop的mode由原來的Default模式切換到了Event Tracking模式,timer原來好好的運行在Default模式中,被關閉後天然就中止工做了。
解決方法其一是將timer加入到NSRunloopCommonModes中。其二是將timer放到另外一個線程中,而後開啓另外一個線程的runloop,這樣能夠保證與主線程互不干擾,而如今主線程正在處理頁面滑動。示例代碼以下:
// 方法1
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
// 方法2
dispatch_async(dispatch_get_global_queue(0, 0), ^{
timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(repeat:) userInfo:nil repeats:true];
[[NSRunLoop currentRunLoop] run];
});
Swift 中,類是引用類型,結構體是值類型。值類型在傳遞和賦值時將進行復制,而引用類型則只會使用引用對象的一個"指向"。因此他們二者之間的區別就是兩個類型的區別。
舉個簡單的例子,代碼以下
class Temperature {
var value: Float = 37.0
}
class Person {
var temp: Temperature?
func sick() {
temp?.value = 41.0
}
}
let A = Person()
let B = Person()
let temp = Temperature()
A.temp = temp
B.temp = temp
A.sick()
上面這段代碼,因爲 Temperature 是 class ,爲引用類型,故 A 的 temp 和 B 的 temp指向同一個對象。A 的 temp修改了,B 的 temp 也隨之修改。這樣 A 和 B 的 temp 的值都被改爲了41.0。若是將 Temperature 改成 struct,爲值類型,則 A 的 temp 修改不影響 B 的 temp。
內存中,引用類型諸如類是在堆(heap)上,而值類型諸如結構體實在棧(stack)上進行存儲和操做。相比於棧上的操做,堆上的操做更加複雜耗時,因此蘋果官方推薦使用結構體,這樣能夠提升 App 運行的效率。
class有這幾個功能struct沒有的:
class能夠繼承,這樣子類可使用父類的特性和方法
類型轉換能夠在runtime的時候檢查和解釋一個實例的類型
能夠用deinit來釋放資源
一個類能夠被屢次引用
struct也有這樣幾個優點:
結構較小,適用於複製操做,相比於一個class的實例被屢次引用更加安全。
無須擔憂內存memory leak或者多線程衝突問題