一些筆試面試時遇到的以及自己準備的題目

來源:互聯網
上載者:User
問:談談你對網站安全的建議 
1.後台管理程式不要放在公網上,若要放在公網上,必須設定複雜的使用者名稱和密碼。這主要是因為後台管理程式可以上傳檔案,而上傳了木馬之後對電腦有較大的危害,另外,上傳文家的程式必須要指定安全的類型,如不允許jsp、exe等帶有破壞性的檔案上傳。 
2.除開WEB服務的連接埠,包括http和https,不要開放其它的連接埠。如telnet、FTP等連接埠,讓駭客不能靠近伺服器。 

3.遠端管理的機器也必須設


問:上面的程式中哪個語句是錯誤的

 Stuct Foo{      Foo(){}      Foo(int){}      void fun(){} };int main (){     Fooa(10); (1)     a.fun(); (2)     Foo b(); (3)     b.fun(); (4)}

答:(4)出錯。(3)不是建立對象,而是一個函數的申明,(3)可以編譯通過,而當(4)時就不行了。
 
問:struct 和 class 的區別
1.      最本質的區別:預設的繼承存取權限。struct是public的,class是private的。struct作為資料結構的實現體,它的預設資料存取控制是public的,而class作為對象的實現體,他預設的城院變數存取控制是private的。
2.      struct在定義的時候可以用{}賦初值,而class不行。因為class有建構函式。受到存取控制的約束。
 
問:C++中不能被重載的運算子
重載操作符的限制:
8.1 並不是所有的操作符都能被重載。除了.  .*  ::  ? :  sizeof typeid這幾個運算子不能被重載,其他運算子都能被重載
8.2 重載不能改變該運算子用於內建類型時的函義,程式員不能改變運算子+用於兩個int型時的含義。
8.3 運算子函數的參數至少有一個必須是類的對象或者類的對象的引用。這種規定可以防止程式員運用運算子改變內建類型的函義。
8.4 重載不能改變運算子的優先順序。
8.5 重載不能改變運算子的結合律。
8.6 重載不能改變運算子運算元的個數。比如+需要兩個運算元,則重載的+也必須要有兩個運算元
 
 
問:排序方法中元素比較次數與初始化排序無關的是哪種排序方法
選擇排序。

每一趟從待排序的資料元素中選出最小(或最大)的一個元素,順序放在已排好序的數列的最後,直到全部待排序的資料元素排完。可以看到,每次都是遍曆一遍剩下要排序的部分,找出其最大值或最小值。


問:二分尋找的理論

二分尋找又稱折半尋找,優點是比較次數少,尋找速度快,平均效能好;其缺點是要求待查表為有序表,且插入刪除困難。因此,折半尋找方法適用於不經常變動而尋找頻繁的有序列表。首先,假設表中元素是按升序排列,將表中間位置記錄的關鍵字與尋找關鍵字比較,如果兩者相等,則尋找成功;否則利用中間位置記錄將表分成前、後兩個子表,如果中間位置記錄的關鍵字大於尋找關鍵字,則進一步尋找前一子表,否則進一步尋找後一子表。重複以上過程,直到找到滿足條件的記錄,使尋找成功,或直到子表不存在為止,此時尋找不成功。


問:採取 FIFO 頁面淘汰演算法,如何計算缺頁
什麼是缺頁中斷:  缺頁中斷就是要訪問的頁不在主存,需要作業系統將其調入主存後再進行訪問。
缺頁率:在進行記憶體訪問時,若所訪問的頁已在主存,則稱此次訪問成功;若所訪問的頁不在主存,則稱此次訪問失敗,併產生缺頁中斷。若程式P在運行過程中訪問頁面的總次數為S,其中產生缺頁中斷的訪問次數為F,則其缺頁率為:F/S
FIFO:根據頁面進入記憶體時間的長短作為置換標準。總是淘汰在記憶體中停留時間最長的頁面,即最先進入記憶體的頁面。這個演算法的實現很簡單:把一個進程所有在記憶體中的頁按照進入記憶體的時間順序組成隊列,選擇淘汰頁面時,總是選擇隊首的頁面,新的頁面進入記憶體,將其放置在對尾。
LRU:當需要置換一個頁面時,選擇在最近一段時間最久沒有使用過的頁面予以淘汰。
OPT:根據今後使用頁面的時間作為置換標準。

Clock:利用Clock演算法時,只需為每頁設定一個引用位,再將記憶體中所有頁面都通過連結指標連結成一個環形隊列,由一個指標指向最老的頁面


問:頁式儲存系統,如何計算分塊的大小
某頁式儲存管理中,地址寄存器長度為24位,其中頁號佔14位,則主存分塊大小為多少位元組。怎麼計算。

地址寄存器24位,頁號14位,那麼頁面大小就用10位來描述(24-14)。2的14次冥就是16K,即主存可劃分為16K個頁。2的10次冥就是1K,即每個頁面大小為1K。


問:判斷單向鏈表是否存在環的最佳方案是什麼。

答:用兩個指標,pSlow,pFast,就是一個慢一個快 慢的一次跳一步, 快的一次跳兩步, 什麼時候快的追上慢的了(就是pSlow == pFast || pSlow->next == pFast),就表示有環


問:Servet生命週期
Servlet生命週期分為三個階段:
  1,初始化階段 調用init()方法
  2,響應客戶要求階段  調用service()方法
  3,終止階段  調用destroy()方法
Servlet初始化階段:
  在下列時刻Servlet容器裝載Servlet:
    1,Servlet容器啟動時自動裝載某些Servlet,實現它只需要在web.XML檔案中的<Servlet></Servlet>之間添加如下代碼:
                              <loadon-startup>1</loadon-startup>
    2,在Servlet容器啟動後,客戶首次向Servlet發送請求
    3,Servlet類檔案被更新後,重新裝載Servlet
  Servlet被裝載後,Servlet容器建立一個Servlet執行個體並且調用Servlet的init()方法進行初始化。在Servlet的整個生命週期內,init()方法只被調用一次。
Servlet工作原理:
  首先簡單解釋一下Servlet接收和響應客戶請求的過程,首先客戶發送一個請求,Servlet是調用service()方法對請求進行響應的,通過原始碼可見,service()方法中對請求的方式進行了匹配,選擇調用doGet,doPost等這些方法,然後再進入對應的方法中調用邏輯層的方法,實現對客戶的響應。在Servlet介面和GenericServlet中是沒有doGet,doPost等等這些方法的,HttpServlet中定義了這些方法,但是都是返回error資訊,所以,我們每次定義一個Servlet的時候,都必須實現doGet或doPost等這些方法。
  每一個自訂的Servlet都必須實現Servlet的介面,Servlet介面中定義了五個方法,其中比較重要的三個方法涉及到Servlet的生命週期,分別是上文提到的init(),service(),destroy()方法。GenericServlet是一個通用的,不特定於任何協議的Servlet,它實現了Servlet介面。而HttpServlet繼承於GenericServlet,因此HttpServlet也實現了Servlet介面。所以我們定義Servlet的時候只需要繼承HttpServlet即可。
  Servlet介面和GenericServlet是不特定於任何協議的,而HttpServlet是特定於HTTP協議的類,所以HttpServlet中實現了service()方法,並將請求ServletRequest,ServletResponse強轉為HttpRequest和HttpResponse。

public void service(ServletRequest req,ServletResponse res)   throws ServletException,IOException{      HttpRequest request;      HttpResponse response;       try     {         req = (HttpRequest)request;         res = (HttpResponse)response;      }catch(ClassCastException e)      {         throw new ServletException("non-HTTP request response");       }      service(request,response);}

   代碼的最後調用了HTTPServlet自己的service(request,response)方法,然後根據請求去調用對應的doXXX方法,因為HttpServlet中的doXXX方法都是返回錯誤資訊,
protected void doGet(HttpServletRequest res,HttpServletResponse resp)  throws ServletException,IOException{   String protocol = req.getProtocol();   String msg = IStrings.getString("http.method_get_not_supported");   if(protocol.equals("1.1"))   {      resp.sendError(HttpServletResponse.SC.METHOD.NOT.ALLOWED,msg);    }   esle    {      resp.sendError(HttpServletResponse.SC_BAD_REQUEST,msg);    }}

所以需要我們在自訂的Servlet中override這些方法。
源碼面前,了無秘密。
---------------------------------------------------------------------------------------------------------------------------------
Servlet響應要求階段:
  對於使用者到達Servlet的請求,Servlet容器會建立特定於這個請求的ServletRequest對象和ServletResponse對象,然後調用Servlet的service方法。service方法從ServletRequest對象獲得客戶請求資訊,處理該請求,並通過ServletResponse對象向客戶返迴響應資訊。
  對於Tomcat來說,它會將傳遞過來的參數放在一個Hashtable中,該Hashtable的定義是:
private Hashtable<String String[]> paramHashStringArray = newHashtable<String String[]>();
  這是一個String-->String[]的索引值映射。
  HashMap線程不安全的,Hashtable安全執行緒。
-----------------------------------------------------------------------------------------------------------------------------------
Servlet終止階段:
  當WEB應用被終止,或Servlet容器終止運行,或Servlet容器重新裝載Servlet新執行個體時,Servlet容器會先調用Servlet的destroy()方法,在destroy()方法中可以釋放掉Servlet所佔用的資源。
-----------------------------------------------------------------------------------------------------------------------------------
Servlet何時被建立:
  1,預設情況下,當WEB客戶第一次請求訪問某個Servlet的時候,WEB容器將建立這個Servlet的執行個體。
  2,當web.xml檔案中如果<servlet>元素中指定了<load-on-startup>子項目時,Servlet容器在啟動web伺服器時,將按照順序建立並初始化Servlet對象。
  注意:在web.xml檔案中,某些Servlet只有<serlvet>元素,沒有<servlet-mapping>元素,這樣我們無法通過url的方式訪問這些Servlet,這種Servlet通常會在<servlet>元素中配置一個<load-on-startup>子項目,讓容器在啟動的時候自動載入這些Servlet並調用init()方法,完成一些全域性的初始化工作。
 
Web應用何時被啟動:
  1,當Servlet容器啟動的時候,所有的Web應用都會被啟動
  2,控制器啟動web應用
-----------------------------------------------------------------------------------------------------------------------------------------------
Servlet與JSP的比較:
  有許多相似之處,都可以產生動態網頁。
  JSP的優點是擅長於網頁製作,產生動態網頁面比較直觀,缺點是不容易跟蹤與排錯。

  Servlet是純Java語言,擅長於處理流程和商務邏輯,缺點是產生動態網頁不直觀。


問:在try的括弧裡面有return一個值,那是否還執行finally裡的代碼。是在return前執行還是return後執行

在return之後,函數返回之前


問:final,finally,finalize的區別
final—修飾符(關鍵字)如果一個類被聲明為final,意味著它不能再派生出新的子類,不能作為父類被繼承。因此一個類不能既被聲明為 abstract的,又被聲明為final的。將變數或方法聲明為final,可以保證它們在使用中不被改變。被聲明為final的變數必須在聲明時給定初值,而在以後的引用中只能讀取,不可修改。被聲明為final的方法也同樣只能使用,不能重載。
finally—再異常處理時提供 finally 塊來執行任何清除操作。如果拋出一個異常,那麼相匹配的 catch 子句就會執行,然後控制就會進入finally 塊(如果有的話)。

finalize—方法名。Java 技術允許使用finalize() 方法在垃圾收集器將對象從記憶體中清除出去之前做必要的清理工作。這個方法是由垃圾收集器在確定這個對象沒有被引用時對這個對象調用的。它是在 Object 類中定義的,因此所有的類都繼承了它。子類覆蓋 finalize() 方法以整理系統資源或者執行其他清理工作。finalize() 方法是在垃圾收集器刪除對象之前對這個對象調用的。


問:介面和抽象類別有什麼區別。你選擇使用介面和抽象類別的依據是什麼。
介面和抽象類別的概念不一樣。介面是對動作的抽象,抽象類別是對根源的抽象。
抽象類別表示的是,這個對象是什麼。介面表示的是,這個對象能做什麼。比如,男人,女人,這兩個類(如果是類的話……),他們的抽象類別是人。說明,他們都是人。
人可以吃東西,狗也可以吃東西,你可以把“吃東西”定義成一個介面,然後讓這些類去實現它.
所以,在進階語言上,一個類只能繼承一個類(抽象類別)(正如人不可能同時是生物和非生物),但是可以實現多個介面(吃飯介面、走路介面)。
第一點. 介面是抽象類別的變體,介面中所有的方法都是抽象的。而抽象類別是聲明方法的存在而不去實現它的類。
第二點. 介面可以繼承,抽象類別不行
第三點. 介面定義方法,不能實現,而抽象類別可以實現部分方法。
第四點. 介面中基礎資料型別 (Elementary Data Type)為static 而抽類象不是的。
當你關注一個事物的本質的時候,用抽象類別;當你關注一個操作的時候,用介面。
 
介面可以實現也可以繼承,抽象類別不行
抽象類別的功能要遠超過介面,但是,定義抽象類別的代價高。因為進階語言來說(從實際設計上來說也是)每個類只能繼承一個類。在這個類中,你必須繼承或編寫出其所有子類的
所有共性。雖然介面在功能上會弱化許多,但是它只是針對一個動作的描述。而且你可以在一個類中同時實現多個介面。在設計階段會降低難度的。
 
問:下列哪一個關鍵碼序列不符合堆的定義。( C )
A. a、c、d、g、h、m、p、q、r、x B. a、c、m、d、h、p、x、g、o、r
C. a、d、p、r、c、q、x、m、h、g D. a、d、c、m、p、g、h、x、r、q
 
從答案看,都是小根堆關鍵碼序列,根據小根堆的定義,
K[i]<= K[2i]
K[i]<= K[2i+1]
用完全二叉樹表示很直觀,也就是要能組成這樣一個完全二叉樹:
所有的父結點的值都應該小於左右子孩子結點的值。
 
答案c中關鍵碼序列用完全二叉樹表示後很容易看出,在d結點值d大於了左子結點值c,這不符合小根堆定義,同樣在r結點值r大於了左子結點值m和右子結點值n。
 
而其他答案都符合小根堆定義。
 
因為關鍵碼序列是符合小根堆定義的一個序列,他是完全二叉樹中關鍵字從根開始從上到下,從左至右的這樣一個順序,也即符合完全二叉樹中子樹結點是父結點k[2i]和k[2i+1]的。比如答案a組成的完全二叉樹如下:
----------------a----------------
-----------c---------d
--------g-----h-----m--p

------q--r---x


問:在 SOCKET 通訊過程中,下列哪些函數是用戶端需要調用,但是服務端不需要調用的函數?

1. socket() 2. bind() 3. connect()4. send()


socket(),
建立一個用來通訊的socket,函數建立成功返回成功的socket值,失敗返回-1.

該函數服務端和用戶端都需要調用。


bind(),
將一個socket與一個電腦IP/連接埠綁定。綁定後,該連接埠可以監聽到這個socket的資訊。
函數調用成功返回0,失敗返回-1.

該函數服務端需要調用


listen(),
監聽一個socket連接埠,看是否有用戶端串連的請求,
函數成功返回0,失敗返回-1

該函數服務端需要調用


connect(),
用戶端串連函數,串連成功服務端後,才可以與服務端通訊。
函數成功返回0,失敗返回-1.

該函數用戶端需要調用。


accept()
接受到用戶端的請求,返回一個新的socket,返回新的socket用來發送和接受資料,而原來的socket可以繼續監聽串連請求。
函數調用成功返回一個新的socket值,失敗返回-1.

該函數服務端需要調用


所以:
服務端調用API的順序是:
socket()A建立一個socketA
bind () A將socketA與server的IP,連接埠綁定
listen() A,監聽socketA上是否有用戶端串連的請求
accept(A)返回socket B,如果A上接受到一個用戶端的請求,會得到一個用來與這個用戶端接受發送資料的socketB
用戶端調用API的順序是:
socket A,建立一個通訊用的socketA
connect A,用A串連一個服務端

如果用戶端與服務端串連成功,雙方可以用recv()來接受資料,用sendto()發送資料。


問:將網路地址映射為鏈路層相應地址的協議是( C )

A.DNS   B.TCP  C.ARP   D.RARP


問:交換器不具有下面哪項功能。【 C 】

 A、轉寄過濾     B、迴路避免      C、路由轉寄      D、地址學習


問:求運行結果()

public class Test {    public static final String MESSAGE="taobao";    public static void main(String[] args) {       String a = "tao"+"bao";       String b = "tao";       String c = "bao";       System.out.println(a==MESSAGE);           System.out.println( (b+c)==MESSAGE);      } } 
對於這道題,考察的是對String類型的認識以及編譯器最佳化。Java中String不是基本類型,但是有些時候和基本類型差不多,如String b = "tao"; 可以對變數直接賦值,而不用 new 一個對象(當然也可以用 new)。所以String這個類型值得好好研究下。
Java中的變數和基本類型的值存放於棧記憶體,而new出來的對象本身存放於堆記憶體,指向對象的引用還是存放在棧記憶體。例如如下的代碼:
int i=1; 
String s = new String("HelloWorld");  
變數i和s以及1存放在棧記憶體,而s指向的對象”Hello World”存放於堆記憶體。
棧記憶體的一個特點是資料共用,這樣設計是為了減小記憶體消耗,前面定義了i=1,i和1都在棧記憶體內,如果再定義一個j=1,此時將j放入棧記憶體,然後尋找棧記憶體中是否有1,如果有則j指向1。如果再給j賦值2,則在棧記憶體中尋找是否有2,如果沒有就在棧記憶體中放一個2,然後j指向2。也就是如果常量在棧記憶體中,就將變數指向該常量,如果沒有就在該棧記憶體增加一個該常量,並將變數指向該常量。
如果j++,這時指向的變數並不會改變,而是在棧內尋找新的常量(比原來的常量大1),如果棧記憶體有則指向它,如果沒有就在棧記憶體中加入此常量並將j指向它。這種基本類型之間比較大小和我們邏輯上判斷大小是一致的。如定義i和j是都賦值1,則i==j結果為true。==用於判斷兩個變數指向的地址是否一樣。i==j就是判斷i指向的1和j指向的1是同一個嗎。當然是了。對於直接賦值的字串常量(如String s=“Hello World”;中的Hello World)也是存放在棧記憶體中,而new出來的字串對象(即String對象)是存放在堆記憶體中。如果定義String s=“Hello World”和String w=“Hello World”,s==w嗎。肯定是true,因為他們指向的是同一個Hello World。
堆記憶體沒有資料共用的特點,前面定義的String s = new String("Hello World");後,變數s在棧記憶體內,Hello World 這個String對象在堆記憶體內。如果定義Stringw =new String("Hello World");,則會在堆記憶體建立一個新的String對象,變數w存放在棧記憶體,w指向這個新的String對象。堆記憶體中不同對象(指同一類型的不同對象)的比較如果用==則結果肯定都是false,比如s==w。當然不等,s和w指向堆記憶體中不同的String對象。如果判斷兩個String對象相等呢。用equals方法。
說了這麼多隻是說了這道題的鋪墊知識,還沒進入主題,下面分析這道題。MESSAGE成員變數及其指向的字串常量肯定都是在棧記憶體裡的,變數a運算完也是指向一個字串“taobao”啊。是不是同一個呢。這涉及到編譯器最佳化問題。對於字串常量的相加,在編譯時間直接將字串合并,而不是等到運行時再合并。也就是說String a = "tao"+"bao";和String a = "taobao";編譯出的位元組碼是一樣的。所以等到運行時,根據上面說的棧記憶體是資料共用原則,a和MESSAGE指向的是同一個字串。而對於後面的(b+c)又是什麼情況呢。b+c只能等到運行時才能判定是什麼字串,編譯器不會最佳化,想想這也是有道理的,編譯器怕你對b的值改變,所以編譯器不會最佳化。運行時b+c計算出來的"taobao"和棧記憶體裡已經有的"taobao"是一個嗎。不是。b+c計算出來的"taobao"應該是放在堆記憶體中的String對象。這可以通過System.out.println((b+c)==MESSAGE);的結果為false來證明這一點。如果計算出來的b+c也是在棧記憶體,那結果應該是true。Java對String的相加是通過StringBuffer實現的,先構造一個StringBuffer裡面存放”tao”,然後調用append()方法追加”bao”,然後將值為”taobao”的StringBuffer轉化成String對象。StringBuffer對象在堆記憶體中,那轉換成的String對象理所應當的也是在堆記憶體中。下面改造一下這個語句System.out.println( (b+c).intern()==MESSAGE);結果是true,intern()方法會先檢查String池(或者說成棧記憶體)中是否存在相同的字串常量,如果有就返回。所以intern()返回的就是MESSAGE指向的"taobao"。再把變數b和c的定義改一下
final String b = "tao"; final String c = "bao"; System.out.println( (b+c)==MESSAGE); 
現在b和c不可能再次賦值了,所以編譯器將b+c編譯成了”taobao”。因此,這時的結果是true。
在字串相加中,只要有一個是非final類型的變數,編譯器就不會最佳化,因為這樣的變數可能發生改變,所以編譯器不可能將這樣的變數替換成常量。例如將變數b的final去掉,結果又變成了false。這也就意味著會用到StringBuffer對象,計算的結果在堆記憶體中。
    如果對指向堆記憶體中的對象的String變數調用intern()會怎麼樣呢。實際上這個問題已經說過了,(b+c).intern(),b+c的結果就是在堆記憶體中。對於指向棧記憶體中字串常量的變數調用intern()返回的還是它自己,沒有多大意義。它會根據堆記憶體中對象的值,去尋找String池中是否有相同的字串,如果有就將變數指向這個string池中的變數。
String a ="tao"+"bao"; String b = newString("taobao"); System.out.println(a==MESSAGE); //true  System.out.println(b==MESSAGE);  //false b = b.intern(); System.out.println(b==MESSAGE); //true System.out.println(a==a.intern());//true  

問:堆和棧的區別。
管理方式:對於棧來講,是由編譯器自動管理,無需我們手工控制;對於堆來說,釋放工作由程式員控制,容易產生memory leak。
申請大小:
棧:在Windows下,棧是向低地址擴充的資料結構,是一塊連續的記憶體的地區。這句話的意思是棧頂的地址和棧的最大容量是系統預先規定好的,在WINDOWS下,棧的大小是2M(也有的說是1M,總之是一個編譯時間就確定的常數),如果申請的空間超過棧的剩餘空間時,將提示overflow。因此,能從棧獲得的空間較小。
堆:堆是向高地址擴充的資料結構,是不連續的記憶體地區。這是由於系統是用鏈表來儲存的空閑記憶體位址的,自然是不連續的,而鏈表的遍曆方向是由低地址向高地址。堆的大小受限於電腦系統中有效虛擬記憶體。由此可見,堆獲得的空間比較靈活,也比較大。
片段問題:對於堆來講,頻繁的new/delete勢必會造成記憶體空間的不連續,從而造成大量的片段,使程式效率降低。對於棧來講,則不會存在這個問題,因為棧是先進後出的隊列,他們是如此的一一對應,以至於永遠都不可能有一個記憶體塊從棧中間彈出
分配方式:堆都是動態分配的,沒有靜態分配的堆。棧有2種分配方式:靜態分配和動態分配。靜態分配是編譯器完成的,比如局部變數的分配。動態分配由alloca函數進行分配,但是棧的動態分配和堆是不同的,他的動態分配是由編譯器進行釋放,無需我們手工實現。

分配效率:棧是機器系統提供的資料結構,電腦會在底層對棧提供支援:分配專門的寄存器存放棧的地址,壓棧出棧都有專門的指令執行,這就決定了棧的效率比較高。堆則是C/C++函數庫提供的,它的機制是很複雜的。


問:定義屬性時,什麼情況使用copy、assign、retain。
assign用於單一資料型別,如NSInteger,double,bool,
retain和copy用於對象,
copy用於當a指向一個對象,b也想指向同樣的對象的時候,如果用assign,a如果釋放,再調用b會crash,如果用copy 的方式,a和b各自有自己的記憶體,就可以解決這個問題。
retain 會使計數器加一,也可以解決assign的問題。
另外:atomic和nonatomic用來決定編譯器產生的getter和setter是否為原子操作。在多線程環境下,原子操作是必要的,否則有可能引起錯誤的結果。
加了atomic,setter函數會變成下面這樣:
if (property != newValue) {
[property release];
property = [newValue retain];

}

iOS有沒有記憶體回收。
Objective-C 2.0也是有記憶體回收機制的,但是只能在Mac OS X Leopard 10.5 以上的版本使用。


tableView的重用機制。
 查看UITableView標頭檔,會找到NSMutableArray*  visiableCells,和NSMutableDictnery* reusableTableCells兩個結構。visiableCells內儲存當前顯示的cells,reusableTableCells儲存可重用的cells。
  TableView顯示之初,reusableTableCells為空白,那麼tableView dequeueReusableCellWithIdentifier:CellIdentifier返回nil。開始的cell都是通過[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]來建立,而且cellForRowAtIndexPath只是調用最大顯示cell數的次數。
  比如:有100條資料,iPhone一屏最多顯示10個cell。程式最開始顯示TableView的情況是:
  1. 用[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]建立10次cell,並給cell指定同樣的重用標識(當然,可以為不同顯示類型的cell指定不同的標識)。並且10個cell全部都加入到visiableCells數組,reusableTableCells為空白。
  2. 向下拖動tableView,當cell1完全移出螢幕,並且cell11(它也是alloc出來的,原因同上)完全顯示出來的時候。cell11加入到visiableCells,cell1移出visiableCells,cell1加入到reusableTableCells。
3. 接著向下拖動tableView,因為reusableTableCells中已經有值,所以,當需要顯示新的cell,cellForRowAtIndexPath再次被調用的時候,tableView dequeueReusableCellWithIdentifier:CellIdentifier,返回cell1。cell1加入到visiableCells,cell1移出reusableTableCells;cell2移出visiableCells,cell2加入到reusableTableCells。之後再需要顯示的Cell就可以正常重用了。


問:ViewController 的loadView、viewDidLoad、viewDidUnload分別是什麼時候調用的,在自訂ViewCointroller時在這幾個函數中應該做什麼工作。
由init、loadView、viewDidLoad、viewDidUnload、dealloc的關係說起


init方法
在init方法中執行個體化必要的對象(遵從LazyLoad思想)
init方法中初始化ViewController本身


loadView方法
當view需要被展示而它卻是nil時,viewController會調用該方法。不要直接調用該方法。
如果手工維護views,必須重載重寫該方法
如果使用IB維護views,必須不能重載重寫該方法
loadView和IB構建view
你在控制器中實現了loadView方法,那麼你可能會在應用啟動並執行某個時候被記憶體管理控制調用。 如果裝置記憶體不足的時候, view 控制器會收到didReceiveMemoryWarning的訊息。 預設的實現是檢查當前控制器的view是否在使用。 如果它的view不在當前正在使用的view hierarchy裡面,且你的控制器實現了loadView方法,那麼這個view將被release, loadView方法將被再次調用來建立一個新的view。


viewDidLoad方法
viewDidLoad 此方法只有當view從nib檔案初始化的時候才被調用。
重載重寫該方法以進一步定製view
在iPhone OS 3.0及之後的版本中,還應該重載重寫viewDidUnload來釋放對view的任何索引
viewDidLoad後調用資料Model


viewDidUnload方法
當系統記憶體吃緊的時候會調用該方法(註:viewController沒有被dealloc)
記憶體吃緊時,在iPhone OS 3.0之前didReceiveMemoryWarning是釋放無用記憶體的唯一方式,但是OS 3.0及以後viewDidUnload方法是更好的方式
在該方法中將所有IBOutlet(無論是property還是執行個體變數)置為nil(系統release view時已經將其release掉了)
在該方法中釋放其他與view有關的對象、其他在運行時建立(但非系統必須)的對象、在viewDidLoad中被建立的對象、快取資料等 release對象後,將對象置為nil(IBOutlet只需要將其置為nil,系統release view時已經將其release掉了)
一般認為viewDidUnload是viewDidLoad的鏡像,因為當view被重新請求時,viewDidLoad還會重新被執行
viewDidUnload中被release的對象必須是很容易被重新建立的對象(比如在viewDidLoad或其他方法中建立的對象),不要release使用者資料或其他很難被重新建立的對象


dealloc方法
viewDidUnload和dealloc方法沒有關聯,dealloc還是繼續做它該做的事情


問:ViewController的didReceiveMemoryWarning是在什麼時候調用的。預設的操作是什麼。

當程式接到記憶體警告時View Controller將會收到這個訊息:didReceiveMemoryWarning

從iOS3.0開始,不需要重載這個函數,把釋放記憶體的代碼放到viewDidUnload中去。

這個函數的預設實現是:檢查controller是否可以安全地釋放它的view(這裡加粗的view指的是controller的view屬性),比如view本身沒有superview並且可以被很容易地重建(從nib或者loadView函數)。

如果view可以被釋放,那麼這個函數釋放view並調用viewDidUnload。

你可以重載這個函數來釋放controller中使用的其他記憶體。但要記得調用這個函數的super實現來允許父類(一般是UIVIewController)釋放view。

如果你的ViewController儲存著view的子view的引用,那麼,在早期的iOS版本中,你應該在這個函數中來釋放這些引用。而在iOS3.0或更高版本中,你應該在viewDidUnload中釋放這些引用。

問:delegate和notification區別,分別在什麼情況下使用。
KVC(Key-Value-Coding)
KVO(Key-Value-Observing)
理解KVC與KVO(鍵-值-編碼與鍵-值-監看)
當通過KVC調用對象時,比如:[self valueForKey:@”someKey”]時,程式會自動試圖通過幾種不同的方式解析這個調用。首先尋找對象是否帶有 someKey 這個方法,如果沒找到,會繼續尋找對象是否帶有someKey這個執行個體變數(iVar),如果還沒有找到,程式會繼續試圖調用 -(id) valueForUndefinedKey:這個方法。如果這個方法還是沒有被實現的話,程式會拋出一個NSUndefinedKeyException異常錯誤。
(Key-Value Coding尋找方法的時候,不僅僅會尋找someKey這個方法,還會尋找getsomeKey這個方法,前面加一個get,或者_someKey以及_getsomeKey這幾種形式。同時,尋找執行個體變數的時候也會不僅僅尋找someKey這個變數,也會尋找_someKey這個變數是否存在。)
設計valueForUndefinedKey:方法的主要目的是當你使用-(id)valueForKey方法從對象中請求值時,對象能夠在錯誤發生前,有最後的機會響應這個請求。

問:id、nil代表什麼。
id
id和void *並非完全一樣。在上面的代碼中,id是指向struct objc_object的一個指標,這個意思基本上是說,id是一個指向任何一個繼承了Object(或者NSObject)類的對象。需要注意的是id是一個指標,所以你在使用id的時候不需要加星號。比如id foo=nil定義了一個nil指標,這個指標指向NSObject的一個任意子類。而id *foo=nil則定義了一個指標,這個指標指向另一個指標,被指向的這個指標指向NSObject的一個子類。
 
nil
nil和C語言的NULL相同,在objc/objc.h中定義。nil表示一個Objctive-C對象,這個對象的指標指向空(沒有東西就是空)。


聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.