一套iOS面試題解答

一、有多繼承嗎?沒有的話用什麼代替?

答:沒有!這個是確定的。(委託只能代替繼承!)類別。在類的一個類別裏面添加方法,而所添加的方法會被原來的類所擁有,這至關於多繼承。可是類別有一個重要的限制,致使它很雞肋,不能添加實便變量。固然這個是有解決辦法的,好比存取方法,能夠在其中來存取靜態全局變量,或者使用運行時api動態添加實例變量。第二個方法是使用消息轉發。ios

關於多繼承,我還有一些要說。多繼承的做用是什麼?多繼承能夠爲一個類添加額外的方法,抽象來講的話能夠爲一個類添加額外的行爲而可能影響,又或者不影響原來的行爲。那麼多繼承的應用狀況就這兩種了,一是一個類已經定義好了,它已經有了它本身的父類,這時你須要爲經添加一些新的方法,新的行爲,這就須要多繼承的。在這種狀況下,多繼承的做用說白了,就是添加方法,那麼類別是徹底能夠知足須要的,若是不須要添加實例變量的話,固然還有一種方法,那就是動態添加方法。 可是還有一種狀況,多個類已經定義好了,而他們又有各自的父類,而這時你須要爲他們添加共同,或者相似的方法,好比你不想一份代碼寫屢次,減小冗餘,這時你能夠定義一個新的基類,實現這個須要共有的方法而由這幾個類共同繼承,這時,多繼承的意義即是共用。這時,類別就徹底不夠用了。這時,應該使用委託,注意,這裏的委拖不一樣於delegate模式,而是一種廣義上的委託,好比這幾個類要作一件共同的事,那麼就新定義一個類,用來實現這個任務,而原來的這幾個類則將任務委託給它來作。c++

二、ios沒有真正意義上的私有變量。

不管你的變量放在哪裏,只要我想,總能拿的到,kvc就是用來幹這個的!可是放在.m文件,類擴展裏的方法和變量,實際上對於別的類來講是不可見的,能夠理解爲私有的屬性。程序員

三、static的做用!

static的主要做用是隱藏。對於變量來講,不加static的全局變量是全局可見的,在其餘的文件中也能夠被訪問到。而加了static的全局變量卻只能在本文件被訪問。而局部變量若是加了static修飾,則會被放到靜態存儲區,在局部函數被再次訪問的時候他的值仍是會保持前一次的狀態,另外,這個變量只會被初始化一次,並且應該只能用直接量來進行初始化,在程序第一次遇到它的時候進行初始化,換言之,它不能被動態的值初始化。舉例來講,static a = 1;可是static a = b就不行。函數也是起到全局的隱藏效果。對於c++來講,static能夠用來定義靜態方法。
因此靜態全局變量相對於普通全局變量來講的好處就是不會被其餘文件所使用,和避免命名衝突。
局部函數中所聲明變量叫自動變量,它們會被放在棧中,隨着函數的退出,這些空間會被釋放。而由程序動態分配的變量則是放在堆中。全局變量放在靜態數據區中。面試

下面是c++中的static所起的做用。api

在成員前加static修飾能夠成爲類靜態員,類全部,爲全部該類的對象共享。靜態方法具備相同的特性。數組

四、@import的做用是隻引用一次,防止屢次引用。

即便屢次@import一個文件,該文件也只會被引用一次 #include則不一樣。@class告訴類聲明,這個類在實現的時候纔去查找他的定義,這個標識能夠用來防止交叉編譯。安全

五、進程與線程的差異:

進程能夠理解爲程序的一次執行,而線程則是進程中的一個片斷。併發

1)進程間是獨立的,通常而言進程是沒法突破程序的邊界限制而去訪問另外一個進程的存儲空間,而一個進程的線程則共享這個進程的存儲空間,並能夠進行互相通訊。

2)通常而言,進程至少須要一個線程,但能夠多於一個,但線程卻只屬於一個進程,而且只能屬於一個。

3)其實線程就是一個簡化版的進程,它的劃分尺度小進程,從而使系統的併發程度提升。

4)進程是程序的一次運行,是系統進行資源分配和調度的一個獨立單位。線程是cpu調度和分派的基本單位。

5)一個線程能夠建立和撤消另外一個線程;同一個進程之中的多個線程能夠併發執行。

六、關於內存管理:ios的內存管理使用的是引用計數模式。所謂引用計數模式,是相對於垃圾回收機制來講的。對於有垃圾回收機制的語言,程序中所建立的內存會被系統所管理,在必定的時候進行回收,程序員只管建立而不須要去釋放。相對的,使用引用計數模式的語言中,程序員須要對程序中所申請的內存負責。每個對象都維護着一個引用計數值,當一個新的引用指向它時,引用計數器就遞增,去掉一個引用時,計數器就遞減,當計數器的值爲0時,系統便認爲它是無人須要的,這時即可去它所佔有的資源進行回收。函數

具體到ios裏面來講,當一個對象被建立時,它的引用計數爲1,具體到ios語句,爲alloc。一個被建立的對象能夠被繼續引用,這時使用的是retain,它告訴這個對象,我要使用你了,在我結束使用你之前,請不要死。而這時該對象則對它做出迴應,而且自身的引用計數加1。當對象結束使用它時,對它發送release消息,告訴它我不使用你了,相應的該對象則將本身的引用計數減一。直到沒有引用直向這個對象的時候,也就是說這個對象的引用計數爲0了,系統認爲這個對象已經沒有用了,將會對它進行回收。

注意,以上所內容所說的引用爲強引用。還有一種引用叫弱引用。簡單來講,對一個對象的弱引用,代表我知道你在那裏,我會用你,可是你是死是活與我無關。即,弱引用持有一個對象的指針,但沒有屬於它的引用計數。

以上所陳述的是ios內存管理的一些基本的原理。實際操做上沒有這麼複雜,只要明白幾條基本的規則就能保證內存的合理應用。一是誰建立誰釋計,誰(強)引用誰釋放,這樣就保證了強引用與引用釋放的一一對應,避免了只引用不釋放和過分釋放問題的發生。二是對於強引用的對象能夠發送自動釋放,也就是autorelease來進行釋放;收到autorelease消息的對象會在未來的某一刻本身將引用計數減一。三是做爲返回值的強引用對象要發送autorelease消息,好比你在一個方法裏面局部建立了一個對象,而且要將這個對象做爲該方法的返回值返回,那麼在這以前你須要對它發送autorelease消息。四是對於retain的屬性,要在deleac函數裏面對它進行釋放並置空。也可使用self.property = nil這樣的語句,這個語句與arc方法進行了統一。五是使用弱引用避免引用迴環。引用迴環多出如今代理模式上,實際項目中咱們常看到a.delegate = self的語句,實際上這裏a的property屬性可能是弱引用以免引用迴環。

雖然上文頻頻提到強引用與弱引用,而且指出,所謂強引用是指持有指針加引用計數加1,可是這兩項因素並無特定的聯繫。在這種狀況下,實際內存是不安全的,由於一個弱引用也能夠對該對象發送release消息,而被引用對象並不知道對它發送release消息的對象是否持有一個有效的引用計數;同時一個強引用也能夠在不對它發送release消息的狀況下將這個引用拋掉,而該對象仍會去等待這個引用所持有的引用計數減1。因此,在非arc狀況下的強弱引用只是一個概念上的東西。舉個例子來講,若是a對b進行了retain,而且a且持有了b的指針,這時咱們能夠說a對b持有強引用。但實際上a能夠將這個指針拋掉而並不對b發送release消息,這時沒有一個有用的指針指向b,而b並不知道a已經拋棄了它,仍會一直等待,這時便出現了咱們常說的內存泄漏。另外一種狀況是,出現了另外一外對象c,它對b持有指針但並無屬於它的引用,而他仍能夠向b發送release消息,b並不知道是a仍是別的誰發送了release消息,但它仍會對這個release消息作出響應並將引用計數減一。這時b實際的引用計數值已經爲0,而且被釋放,而當a最終找到b並對b發送release消息的時候,它所持有的實際是一個已經被系統回收了的對象,這時便發生了咱們常說的過分釋放。

在非arc狀況下,強引用與弱引用的維護全在程序員的身上。

另外說一下arc。對於arc首先要強調一點,arc並非垃圾回收,雖然它簡化了內存管理,但它實際上仍是引用計數,只不過從手動變成了自動,更確切一點說,它實際上是將在非arc模式下必須遵照的一些隱式的規則顯示的表達出來並放到了編譯環境中,從編譯時便避免了內存問題的發生。arc模式下顯示的定義了強引用與弱引用,並用將相關的規則集成到其中。強引用被定義爲strong指針,而弱引用則被定義爲weak指針。strong指針的含議是,對一個對象持有引用而且有一個屬於它的引用計數。strong指針不能被release,但當一個strong指針被置爲nil的時候,相應的retainCount也會被減去。相應的weak指針也不能再對引用計數作任何的操做,由於它沒有屬於本身的引用計數。同時,當一weak指針所指向的對向被消毀的時候,這個weak指針也會被置空--這是一項重要的特性,它保證了弱引用指針不能去引用一個不可用的對象。

實際上,arc模式並非在運行的時候被執行的--它是一個編譯過程。編譯器在編譯的過程當中,自動的在合適的地方插入retain,release等代碼。

關於autorelease:oop

面試中常見的一個問題是:autorelease對象何時該用何時不應用,以及何時被釋放。首先解釋一下什麼是autorelease。手動對一個對象發送release消息會致使該對象的引用計數馬上被減一。而autorelease則會在未來的某一刻對該對象發送一個release消息。實際上被加了autorelease消息的對象會被放到autorelease pool中,而該pool在釋放時,會對放到其中的對象發送一次release消息。這個說法是官方的說法,實際上我認爲放到autorelease pool中這個說法是不許確的--對於兩次放到同一個pool的同一個對象,pool會怎麼作?實際上咱們能夠認爲,autorelease pool是一個鏈表,或者一個數組,每個被髮送autorelease pool消息的對象的指針都會被加入到autorelease pool結構中,而當autorelealse pool釋放的時候,這些指針也都會收到一次release消息。而屢次另入其中的對象實際上也會被釋放屢次。

那麼下一個問題就是,autorelease對象什麼時候被釋放?哈哈!這個問題是不專業的。實際上咱們應該問的是autorelease pool什麼時候對其中的對象發送release消息?蘋果文檔告訴咱們,在每一次run loop結束時,autorelease pool都會被drain一次,一次點擊,觸摸,均可以是一次run loop。這樣說彷佛很很差理解,讓咱們換一種說法。咱們想象整個程序都是被一個while循環驅動的,在每一次while循環中,程序不斷接收事件,而後交給相應的對象來處理。每一次循環,都是一次run loop。而autorelease pool,則能夠理解爲這個循環中的對象管理器,每一次循環中所產生的autorelease對象都被它記錄,循環完的時候,對這些對象進行一次release。
  • (void)viewDidLoad

{

[super viewDidLoad];

[self test];

NSLog(@"%@", testString);

}

  • (void)test

{

testString = [[[NSString alloc] initWithFormat:@"aaaaaa"] autorelease];

[self test1];

}

  • (void)test1

{

NSLog(@"%@", testString);

}

這兩個連續調用並無走出一個run loop的範圍,因此沒有引起內存錯誤。

還有一個問題:何時該顯示建立autorelease pool?爲何只有一個autorelease pool?這個問題太簡單,不解釋。
相關文章
相關標籤/搜索