iOS 面試題總結

orilme發表於2019-04-17

isa指標:

在Objective-C中,任何類的定義都是物件。類和類的例項(物件)沒有任何本質上的區別。任何物件都有 isa 指標。

isa 是一個Class 型別的指標。 每個例項物件有個 isa 的指標,他指向物件的類,而Class裡也有個 isa 的指標, 指向 meteClass (元類)。元類儲存了類方法的列表。當類方法被呼叫時,先會從本身查詢類方法的實現,如果沒有,元類會向他父類查詢該方法。同時注意的是:元類也是類,它也是物件。元類也有 isa 指標,它的 isa 指標最終指向的是一個根元類。根元類的 isa 指標指向本身,這樣形成了一個封閉的內迴圈。

例項:類物件的isa指標結構圖.png

const 含義

  • 面試題

    const int a;
    int const a;
    const int *a;
    int const *a;
    int * const a;
    int const * const a;
    複製程式碼
    1. 前兩個的作用是一樣:a 是一個常整型數
    2. 第三、四個意味著 a 是一個指向常整型數的指標(整型數是不可修改的,但指標可以)
    3. 第五個的意思:a 是一個指向整型數的常指標(指標指向的整型數是可以修改的,但指標是不可修改的)
    4. 最後一個意味著:a 是一個指向常整型數的常指標(指標指向的整型數是不可修改的,同時指標也是不可修改的)
  • 合理地使用關鍵字const可以使編譯器保護那些不希望被改變的引數,防止其被無意的程式碼修改,減少bug。

    1. 欲阻止一個變數被改變,可以使用 const 關鍵字。在定義該 const 變數時,通常需要對它進行初始化,因為以後就沒有機會再去改變它了;
    2. 對指標來說,可以指定指標本身為 const,也可以指定指標所指的資料為 const,或二者同時指定為 const;
    3. 在一個函式宣告中,const 可以修飾形參,表明它是一個輸入引數,在函式內部不能改變其值;
    4. 對於類的成員函式,若指定其為 const 型別,則表明其是一個常函式,不能修改類的成員變數;
    5. 對於類的成員函式,有時候必須指定其返回值為 const 型別,以使得其返回值不為“左值”。

static關鍵字

  1. 函式體內 static 變數的作用範圍為該函式體,不同於 auto 變數,該變數的記憶體只被分配一次,因此其值在下次呼叫時仍維持上次的值;
  2. 在模組內的 static 全域性變數可以被模組內所用函式訪問,但不能被模組外其它函式訪問;
  3. 在模組內的 static 函式只可被這一模組內的其它函式呼叫,這個函式的使用範圍被限制在宣告它的模組內;
  4. 在類中的 static 成員變數屬於整個類所擁有,只會初始化一次,並且在程式退出時才會回收記憶體;
  5. 在類中的 static 成員函式屬於整個類所擁有,這個函式不接收 this 指標,因而只能訪問類的 static 成員變數。
  • static 用途
    1. 限制變數的作用域
    2. 設定變數的儲存域

volatile關鍵字

volatile 的變數是說這變數可能會被意想不到地改變,這樣,編譯器就不會去假設這個變數的值了。精確地說就是,優化器在用到這個變數時必須每次都小心地重新讀取這個變數的值,而不是使用儲存在暫存器裡的備份。下面是

  • volatile 變數的幾個例子
    1. 並行裝置的硬體暫存器(如:狀態暫存器)
    2. 一箇中斷服務子程式中會訪問到的非自動變數(Non-automatic variables)
    3. 多執行緒應用中被幾個任務共享的變數
  • 一個引數既可以是 const 還可以是 volatile 嗎?解釋為什麼。
    是的。一個例子是隻讀的狀態暫存器。它是 volatile 因為它可能被意想不到地改變。它是 const 因為程式不應該試圖去修改它
  • 一個指標可以是 volatile 嗎?解釋為什麼。
    是的。儘管這並不很常見。一個例子是當一箇中服務子程式修該一個指向一個buffer的指標時。

self. 跟 self-> 區別?

self. 是呼叫 get 方法或者 set 方法 self 是當前本身,是一個指向當前物件的指標 self-> 是直接訪問成員變數

懶載入

懶載入——也稱為延遲載入,只在用到的時候才去初始化,比如控制器的view,在第一次用到view時才會呼叫loadView方法進行建立,所謂懶載入,寫的是其get方法。我覺得最好也最簡單的一個列子就是tableView中圖片的載入顯示了。一個延時載,避免記憶體過高,一個非同步載入,避免執行緒堵塞。
注意:如果是懶載入的話則一定要注意先判斷是否已經有了,如果沒有那麼再去進行例項化

  • 懶載入的好處:
    1. 不必將建立物件的程式碼全部寫在viewDidLoad方法中,程式碼的可讀性更強
    2. 每個控制元件的 getter 方法中分別負責各自的例項化處理,程式碼彼此之間的獨立性強,鬆耦合

Objective C中的selector 是什麼?

你可以理解 @selector()就是取類方法的編號,他的行為基本可以等同C語言的中函式指標,只不過C語言中,可以把函式名直接賦給一個函式指標,而 Objective-C的類不能直接應用函式指標,這樣只能做一個@selector語法來取.它的結果是一個SEL型別。這個型別本質是類方法的編號 (函式地址)。 方法和選擇器有何不同? 答案:selector是一個方法的名字,method是一個組合體,包含了名字和實現。通過一個selector可以找到方法地址,進而呼叫一個方法

其他

  • [[NSMutableArray alloc]init][NSMutableArray array]

    • [[NSMutableArray alloc]init] alloc 分配記憶體,init 初始化,需要手動釋放;
    • [NSMutableArray array] 不需要手動 release,遵循 autoreleasepool 機制
    • 在ARC(自動引用計數)中兩種方式並沒什麼區別
  • newalloc/init 兩種方式建立物件現在基本上一樣,區別就是使用 new 只能預設 init 進行初始化,alloc 方式可以使用其它的 init 開頭的方法進行初始化。

  • 子執行緒要主動開啟 runloop

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{  
         [self performSelector:@selector(fireBlock:) withObject:^{  
            NSLog(@"hello world");  
         } afterDelay:0.3];  
     }); 
    複製程式碼

    上面的程式碼片段原有的目的是非同步延遲0.3秒後輸出Hello world。但是執行之後發現不會輸出Hello world。
    原因是:非主執行緒的NSRunLoop預設沒有開啟,而 - (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay; 函式內部是通過NSTimer定時器實現,在NSRunLoop沒有開啟的情況下,NSTimer不會得到正常執行。

  • 類的載入和初始化

    • + (void)load;
      呼叫:程式一啟動的時候就會把所有的類載入進記憶體
      作用:載入類的時候呼叫
    • + (void)initialize;
      呼叫:當第一次使用這個類或者子類的時候呼叫
      作用:初始化類
  • 程式碼佈局還是 storyboard ?
    對於複雜的、動態生成的介面,建議使用手工編寫介面。
    對於需要統一風格的按鈕或UI控制元件,建議使用手工用程式碼來構造。方便之後的修改和複用。
    對於需要有繼承或組合關係的 UIView 類或 UIViewController 類,建議用程式碼手工編寫介面。
    對於那些簡單的、靜態的、非核心功能介面,可以考慮使用 xib 或 storyboard 來完成。

  • 有些圖片載入的比較慢怎麼處理?你是怎麼優化程式的效能的?

    1. 圖片的下載放在非同步的執行緒
    2. 圖片的下載過程使用佔點陣圖片
    3. 如果圖片較大,可以考慮多執行緒斷點下載
  • Foundation物件與Core Foundation物件有什麼區別

    • Foundation物件是OC的,Core Foundation物件是C物件
    • 資料型別之間的轉換
      ARC: __bridge_retained__bridge_transfer
      非ARC: __bridge
  • 寫 個“標準”巨集,這個巨集輸出兩個引數並返回較大的 #define MIN(X,Y) ((X)>(Y)?(Y):(X))

  • iPhone OS中有沒有垃圾回收? 沒有

  • self.name=“object” 和 name=“object”有什麼區別?
    前者實際上是呼叫了set 方法給變數賦值, 後者是直接給變數賦值

  • Objective-c中有私有方法嗎?私有變數呢?
    沒有私有法,但可以將方法直接實現在.m檔案中不在.h 件中宣告時,外部也不能訪問。
    有私有變數

  • Objective-c中有多重繼承麼?不是的話有宣告替代?
    沒有多繼承,可以通過協議模擬多繼承

  • 多型性
    答案:不同物件以自己的方式響應相同的訊息的能力叫做多型。意思就是假設生物類(life)都用有一個相同的方法-eat;那人類屬於生物,豬也屬於生物,都繼承了life後,實現各自的eat,但是呼叫是我們只需呼叫各自的eat方法。
    多型。 主要是將資料型別的確定由編譯時,推遲到了執行時。比如執行的時候修改某個方法的實現,或者列舉某個物件都有哪些屬性等等。也就是不同的物件以自己的方式響應了相同的訊息(響應了eat這個選擇器)。因此也可以說,執行時機制是多型的基礎。
    多型,子類指標可以賦值給父類。關於多型,繼承和封裝基本最好都有個自我意識的理解。
    站在程式設計的角度來說,就是以C語言的視角看待OC中的物件、類、方法等等 也就是可以理為類、物件、方法被對映成了一些結構體、函式、指標等等 比如你要列舉某個物件有哪些屬性,就可以調執行時裡的一些函式。 JSonModel、YYModel就是基於執行時實現的,其通過列舉model裡有哪些屬性、及屬性的型別,去對應的json(陣列和字典)來找相應的欄位,進而完成解析

  • obj-c的優缺點

    • 優點
      1. Cateogies
      2. Posing
      3. 執行時特性
      4. 指標計算
      5. 彈性訊息傳遞
      6. 不是一個過度複雜的 C 衍生語言
      7. Objective-C 與 C++ 可混合程式設計
    • 缺點:
      1. 不支援名稱空間,對於命名衝突,可以使用長命名法或特殊字首解決,如果是引入的第三方庫之間的命名衝突,可以使用link命令及flag解決衝突
      2. 不支援運算子過載
      3. 不支援多重繼承
      4. 使用動態執行時型別,所有的方法都是函式呼叫,所以很多編譯時優化方法都用不到。(如行內函數等),效能低劣。
  • 靜態語言:你寫的原始碼編譯之後完全變成了機器碼 效率高
    動態語言:你寫的原始碼沒有完全編譯成機器碼

  • 全域性變數和區域性變數在記憶體中是否有區別?如果有,是什麼區別?
    全域性變數儲存在靜態資料庫,區域性變數在堆疊

  • id 宣告的變數有什麼特性?

    1. 沒有 * 號
    2. 動態資料型別
    3. id宣告的變數能指向任何OC物件(設定是nil),而不關心其具體型別
    4. 在執行時檢查其具體型別
    5. 可以對其傳送任何(存在的)訊息
  • 蘋果的安全機制:

    1. 沒經過使用者同意,你不能隨便獲取使用者資訊。
    2. 所有的程式都在沙盒裡執行,B程式不能進入A程式的執行範圍。
    3. 如果跟錢有關,比如說支付寶,這些底層的實現都是保密的,只提供介面供開發者呼叫,這樣的話安全性得到保障。
    4. 如果要防止程式碼被反編譯,可以將自己的程式碼中的.m檔案封裝成靜態庫(.a檔案)或者是framework檔案,只提供給其它人.h檔案。這樣就保證了個人程式碼的安全性。
    5. 網路登入的話跟使用者名稱跟密碼相關要傳送POST請求,如果是GET請求的話密碼會直接在URL中顯示。然後同時要對帳號密碼採用加密技術,加一句:我們公司用的是MD5,但是現在MD5有一個專門的網站來破解,為了防止這個,可以採用加鹽技術。
  • 寫一個NSString類的實現

    + (id)stringWithCString: (c*****t char*)nullTerminatedCString  encoding: (NSStringEncoding)encoding { 
      NSString  *obj; 
      obj = [self allocWithZone: NSDefaultMallocZone()]; 
      obj = [obj initWithCString: nullTerminatedCString encoding: encoding]; 
      return AUTORELEASE(obj); 
    } 
    複製程式碼
  • 什麼是平衡二叉樹?
    左右子樹都是平衡二叉樹且左右子樹的深度差值的絕對值不大於1

  • 區域性變數能否和全域性變數重名?
    能,區域性會遮蔽全域性。要用全域性變數,需要使用"::"
    區域性變數可以與全域性變數同名,在函式內引用這個變數時,會用到同名的區域性變數,而不會用到全域性變數。對於有些編譯器而言,在同一個函式內可以定義多個同名的區域性變數,比如在兩個迴圈體內都定義一個同名的區域性變數,而那個區域性變數的作用域就在那個迴圈體內

  • 如何引用一個已經定義過的全域性變數?
    答:可以用引用標頭檔案的方式,也可以用extern關鍵字,如果用引用標頭檔案方式來引用某個在標頭檔案中宣告的全域性變數,假定你將那個變寫錯了,那麼在編譯期間會報錯,如果你用extern方式引用時,假定你犯了同樣的錯誤,那麼在編譯期間不會報錯,而在連線期間報錯。

  • NSString 和 NSMutableString 有什麼區別:
    NSString 相當於一個 const char* 不可以改變。
    NSMutableString 相當於 char* 可以改變內部的內容。

  • 圖片操作:
    imageNamed: 優點是當載入時會快取圖片。圖片使用頻繁的使用比較好,一般用於載入小圖片。
    iamgeWithContentsFile: 大圖片。每次呼叫,會佔快取。
    imageWithContentsOfFile:僅載入圖片,影象資料不會快取。因此對於較大的圖片以及使用情況較少時,那就可以用該方法,降低記憶體消耗。

  • 類例項(成員)變數的 @protected , @private, @public 宣告各有什麼含義?
    @protected :受保護的,該例項變數只能在該類和其子類內訪問,其他類內不能訪問。
    @private :私有的,該例項變數只能在該類內訪問,其他類內不能訪問。
    @public :共有的,該例項變數誰都可以訪問。

  • @dynamic 和 @synthesize
    @dynamic: 的意思是告訴編譯器,屬性的獲取與賦值方法由使用者自己實現, 不自動生成。對於只讀屬性需要提供 setter,對於讀寫屬性需要提供 setter 和 getter。
    @synthesize: 意思是,除非開發人員已經做了,否則由編譯器生成 getter 和 setter 屬性宣告。

  • UIscrollVew 到了什麼設計模式?還能再foundation庫中找到類似的嗎?
    組合模式(composition):所有的container view都用了這個模式
    觀察者模式(observer):所有的UIResponder都用了這個模式。
    模板模式(Template):所有datasource和delegate介面都是模板模式的典型應用

  • _btn.frame.origin.y = 10 錯誤
    原因:OC語法規定不允許直接修改某個物件的結構體屬性的成員。_btn 是個物件,frame是個結構體。
    物件和結構體是不一樣的,結構體是C語言中的,裡面可以定義許多屬性,但是不能定義方法,而物件是即可以定義屬性又可以定義方法的,是典型的物件導向語法。
    如何改變物件中結構體屬性的成員:

    // 先取出結構體
    CGRect frame = _btn.frame;
    // 修改結構體
    frame.origin.y -= 10;
    // 將修改後的結構體重新賦值回去
    _btn.frame = frame;
    複製程式碼

    或者

    // 先取出y值
    CGFloat y = _btn.frame.origin.y;
    // 修改y值
    y -= 10;
    // 重新設定_btn的y值,其他屬性和_btn保持不變
    _btn.frame = CGRectMake(_btn.frame.origin.x, y, _btn.frame.size.width, _btn.frame.size.height);
    複製程式碼
  • 如果想讓同一個控制元件同時即改變位置的移動,又放大。這樣設定是無效果的。這樣操作是建立新的transform然後賦值,給按鈕的transform,第二次賦值的會把之前賦值的給覆蓋,所以會達不到想要的效果。

    _btn.transform = CGAffineTransformMakeTranslation(0, 100);
    _btn.transform = CGAffineTransformMakeScale(1.2, 1.2);
    複製程式碼

    解決方法:

    _btn.transform = CGAffineTransformMakeTranslation(0, 100);
    // 在之前的transform情況下,繼續新增縮放的形變。
    _btn.transform = CGAffineTransformScale(_btn.transform, 1.2, 1.2);
    複製程式碼
  • 四捨五入問題

    float i = 1.7;
    // 會自動四捨五入,不保留小數
    NSLog(@"%0.f",i); // 列印結果2
    // 強轉型別不會四捨五入
    int j = (int)i;
    NSLog(@"%d",j); // 列印結果1
    複製程式碼
  • id、nil代表什麼?

    • id 和 void *並非完全一樣。在上面的程式碼中,id是指向struct objc_object的一個指標,這個意思基本上是說,id是一個指向任何一個繼承了Object(或者NSObject)類的物件。需要注意的是id是一個指標,所以你在使用id的時候不需要加星號。比如id foo=nil定義了一個nil指標,這個指標指向NSObject的一個任意子類。而id *foo=nil則定義了一個指標,這個指標指向另一個指標,被指向的這個指標指向NSObject的一個子類。
    • nil和C語言的NULL相同,在objc/objc.h中定義。nil表示一個Objctive-C物件,這個物件的指標指向空(沒有東西就是空)。
      首字母大寫的Nil和nil有一點不一樣,Nil定義一個指向空的類(是Class,而不是物件)。
  • 常見的object-c的資料型別有那些,和C的基本資料型別有什麼區別?如:NSInteger和int
    答:object-c的資料型別有NSString,NSNumber,NSArray,NSMutableArray,NSData等等,這些都是class,建立後便是物件,而C語言的基本資料型別int,只是一定位元組的記憶體空間,用於存放數值;NSInteger是基本資料型別,並不是NSNumber的子類,當然也不是NSObject的子類。NSInteger是基本資料型別Int或者Long的別名(NSInteger的定義typedef long NSInteger),它的區別在於,NSInteger會根據系統是32位還是64位來決定是本身是int還是Long。

相關文章