iOS 面試題及答案20道1~20(一)

以后題目多的話會進(jìn)行分類,請持續(xù)關(guān)注
面試題20道:

1.#import和#include的區(qū)別,@class代表什么?
2.談?wù)凮bjective-C的內(nèi)存管理方式和過程?
3.Objective-C有私有方法嗎?私有變量呢?
4.Objective-C的類可以多重繼承么?可以實現(xiàn)多個接口么?Category是什么?重寫一個類的方式用繼承好還是分類好?為什么?
5.類(class)和結(jié)構(gòu)體(struct)有什么區(qū)別?
6.Swift是面向?qū)ο缶幊踢€是函數(shù)式的編程語言?
7.Objective-C中的函數(shù)式編程?
8.Swift中可選型(optioonal)?
9.OBjective-C當(dāng)中有沒有可選類型?
10.Swift中,什么是泛型(Generics)?
11.Swift說明比較關(guān)鍵字Open、Public、internal、File-private和private關(guān)鍵字
12.Swift中說明并比較關(guān)鍵字:Strong,Weak和Unownet
13.在Swift中,如何理解copy-on-write
14.在Swift中什么是屬性觀察(property observer)?
15.在Swift中mutating的作用。
16.在Swift中protocol怎么定義成員變量?
17.Swift在結(jié)構(gòu)體中如果修改成員變量?
18.Swift如果實現(xiàn)(||)操作?
19.輸入任意一個整數(shù),輸出的是輸入整數(shù)的+2、+3、+4。。。 請定義一個函數(shù)來實現(xiàn)。
20.寫一個函數(shù),求0~100(包括0和100)中為偶數(shù)并且恰好是其他數(shù)字平方的數(shù)字?

1.#import和#include的區(qū)別,@class代表什么?

#import會代入頭文件的所有信息,包括實例變量和方法等,
#include也是帶入文件的所有信息,跟#import差異是在#import引用的文件只會被引用一次,不會遞歸包含的問題
@class代表的意思是,不需要導(dǎo)入文件內(nèi)容,也不需要知道如何定義的,我只告訴編譯器這個就是個類的名稱,

2.談?wù)凮bjective-C的內(nèi)存管理方式和過程?

在App運行時創(chuàng)造了大量的對象,Objective-C中的對象時存儲在堆中的,系統(tǒng)并不會自動釋放堆中的內(nèi)存(基本類型也就是值類型是由系統(tǒng)自己管理的,放在棧上),so,OBC的內(nèi)存管理是需要開發(fā)去手動維護(hù)的,之前xcode4.2版本之前是MRC,之后則是ARC(主要由runtime和LLVM協(xié)作完成)。

一、引用計數(shù)

在Xcode4.2以后的版本中都是引入了ARC機制,程序編譯時xcode可以自動給你的代碼添加內(nèi)存釋放代碼,但是如果編寫釋放內(nèi)存的代碼就會報錯,所以需要在xcode中主動關(guān)閉ARC,這樣才能有助于ObjC的理解。

在ObjC中,每個對象內(nèi)部都會有一個retainCount整數(shù),叫"引用計數(shù)",當(dāng)一個對象創(chuàng)建之后它的引用計數(shù)為1,當(dāng)調(diào)用這個對象的alloc、retain、new、copy方法之后引用計數(shù)會在動在原有的基礎(chǔ)上加1,當(dāng)調(diào)用這個對象的release方法之后它的引用計數(shù)會減1,如果一個對象的引用計數(shù)為0,則系統(tǒng)會自動調(diào)用這個對象的dealloc方法來銷毀這個對象,在手動管理內(nèi)存的時候需要遵循一個原則誰創(chuàng)建、誰釋放。

當(dāng)用戶調(diào)用方法的時候?qū)嶋H上是在向這個對象發(fā)送一條消息,且ObjC中允許向一個nil的對象發(fā)送消息,在釋放完成以后最好直接給這個對象設(shè)置為nil,為了防止野指針的出現(xiàn),當(dāng)這個對象的引用計數(shù)為0的時候,系統(tǒng)會自動調(diào)用dealloc方法來銷毀對象,可以在這里將所有的野指針設(shè)置為nil。

二、屬性參數(shù)

@property自動實現(xiàn)你的屬性的getter、setter方法,并且提供一些參數(shù)供選擇

參數(shù) 詳解
atomic 對屬性加鎖,多線程下線程安全,默認(rèn)值
nonatomic 對屬性不加鎖,多線程不安全,但是速度快
readwrite 生成getter、setter方法,可讀可寫,默認(rèn)值
readonly 只生成getter方法,只讀
assign 直接賦值,默認(rèn)值
weak 相當(dāng)于assign,多了一點就是對象被干掉時,weak引用自動設(shè)置為nil
retain 先release原來的值,再retain新值
Strong 跟retain一樣,在ARC中,不需要手動釋放內(nèi)存
copy 先release原來的值,在copy新值

assign,setter方法

-(void)setNamenum:(int)num{
    _Namenum=num;
}

retain,setter方法

-(void)setNamenum:(calss*)num{
    if(_Namenum != num)
    [_Namenum release];
    _Namenum = [num retain];
}

copy,setter方法

-(void)setNamenum:(calss*)num{
    if(_Namenum != num)
    [_Namenum release];
    _Namenum = [num copy];
}

自動釋放池

在ObjC中還有一種自動釋放機制, 使用的時候首先用@autoreleasepool關(guān)鍵字聲明一個代碼塊,在代碼塊中初始化你的對象,如果對象在初始化的時候加入了autorelease方法,那么在這個代碼塊執(zhí)行完成以后,在塊中只要調(diào)用過autorelease方法的對象都會自動調(diào)用release方法。這就是自動釋放池,這樣release方法是一起唄調(diào)用的,可能有點用處。

    @autoreleasepool {
        UserModel *user1 = [[[UserModel alloc]init]autorelease];
        user1.UserName = @"用戶1";
        
        UserModel *user2 = [[[UserModel alloc]init]autorelease];
        user2.UserName = @"用戶2";
        
    }

打印結(jié)果

2019-01-25 23:26:53.031860+0800 MRC_demo[3264:662385] UserModel = (用戶2)dealloc method.
2019-01-25 23:26:53.031967+0800 MRC_demo[3264:662385] UserModel = (用戶1)dealloc method.

從結(jié)果來看自動釋放池是一個隊列,先進(jìn)后出,壓棧的形式。

根據(jù)理解又實驗了2次:

  1. 在autorelease之后user2加上retain,實驗證明user2沒有被釋放,說明autorelease只是調(diào)用了一次release
  2. 在autorelease之后user2加上release,程序崩潰Thread 1: EXC_BAD_ACCESS (code=EXC_I386_GPFLT)野指針,將user2 = nil就會解決,ObjC可以向nil對象發(fā)送消息。
自動釋放池的總結(jié)
  1. autorelease不會改變retaincount的值,只是放入自動釋放池
  2. 自動釋放池,就是在代碼塊完成以后,調(diào)用autorelease的對象自動調(diào)用release方法(只是一次),如果對象的retaincount>1那么就沒法釋放
  3. 自動釋放池是統(tǒng)一銷毀,那么在這個代碼塊中占用的內(nèi)存會相對來說多一點,可以使用多個自動釋放池

3. Objective-C有私有方法嗎?私有變量呢?

  • 在ObjC中按照正常的規(guī)則來將是有私有方法,跟私有變量的,只要我不在.h文件聲明就可以,只是在表面上是這樣的,
  • 因為ObjC是一個動態(tài)語言,我們可以通過runtime來動態(tài)的獲取這些property、方法、ivar,得到名稱或者直接修改值都是可以的。

代碼如下:

/********************************************************/
                       對象類代碼
/********************************************************/
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface UserModel : NSObject
- (instancetype)initWithUserName:(NSString *)name;
@end

NS_ASSUME_NONNULL_END


@interface UserModel ()
{
    NSString * userName;
}
@end

@implementation UserModel

- (instancetype)initWithUserName:(NSString *)name{
    
    self = [super init];
    if(self){
        userName = name;
    }
    return self;
}

- (void)doing{
    NSLog(@"這是我的私有方法");
}
@end

/********************************************************/
                       調(diào)用私有方法代碼
        當(dāng)然這段代碼是將所有的對象都得到然后都調(diào)用一遍
/********************************************************/

     int Count;
     Method *methodList = class_copyMethodList([UserModel class], &Count);
     for (unsigned int i = 0; i < Count; i++) {
         Method method = methodList[i];
         NSLog(@"method --> %@", NSStringFromSelector(method_getName(method)));
        objc_msgSend(user, NSSelectorFromString(method_getName(method)));
     }
     
/********************************************************/
                       調(diào)用私有變量代碼
            這里是將所有的變量都取出來然后都修改成"我去"
/********************************************************/

    int varcount;
    Ivar *varlist = class_copyIvarList([user class], &varcount);
    
    for (unsigned int i = 0; i < varcount; i++) {
        Ivar ivar = varlist[i];
        object_setIvarWithStrongDefault(user, ivar, @"我去");
        NSLog(@"%@",object_getIvar(user, ivar));
    }

當(dāng)然runtime還是可以做到很多事情并不僅僅是這些,不過從題目來看已經(jīng)夠充分的說明了ObjC是否存在私有變量跟私有方法了。答案是嚴(yán)格意義上的私有方法、私有變量是不存在。

4.Objective-C的類可以多重繼承么?可以實現(xiàn)多個接口么?Category是什么?重寫一個類的方式用繼承好還是分類好?為什么?

Objc的類不可以多重繼承,延伸問題有沒有可能去實現(xiàn)多繼承?

有的,兩種方式:
1)偽繼承消息轉(zhuǎn)發(fā),這樣可以本類中沒有這個方式有第二次機會可以去調(diào)用別的類中的方法
2)委托,也就是protocol的方式實現(xiàn),當(dāng)我需要某個方法的時候,我直接調(diào)用協(xié)議的方法,發(fā)送給實現(xiàn)方法的對象(也就是遵守協(xié)議的對象,這個對象我完全不必知道具體內(nèi)容),這樣就,通過委托的形式,也就沒有必要去考慮多繼承的問題,專心搞自己的類就可以。

Category是什么?重寫一個類的方式用繼承好還是用分類好?為什么?

Catogory是分類, 重寫類的話,應(yīng)該首先看一下具體的需求:
1)如果只是為了擴展一些方法,不修改類的實現(xiàn)、內(nèi)容等,那么使用分類比較好,因為分類擴展方法僅限這個分類當(dāng)中有效,并不會影響原有類的具體實現(xiàn)。
2)如果有目的性的需要修改類中的某一項內(nèi)容、方法,那么最好的形式還是繼承比較好。

5. Swift中類(class)和結(jié)構(gòu)體(struct)有什么區(qū)別?

class是引用類型,struct是值類型。值類型在傳遞和賦值時將進(jìn)行復(fù)制,而引用類型只會使用引用對象的一個"指向"。所以兩者之間的區(qū)別就是兩個類型的區(qū)別。

class中有struct沒有的內(nèi)容:

  1. 可以繼承、這樣子類可以使用父類的特性和方法。
  2. 類型轉(zhuǎn)換可以在運行時檢查和解釋一下實例的類型。
  3. 可以使用deinit來釋放資源。
  4. 一個類可以被多次引用。

struct的優(yōu)勢:

  1. 結(jié)構(gòu)較小,適用于復(fù)制操作,相比一下class的實例被多次引用,struct更加安全。
  2. 無須擔(dān)心內(nèi)存泄漏或者多線程沖突問題。

6.Swift是面向?qū)ο缶幊踢€是函數(shù)式的編程語言?

Swift既是面向?qū)ο蟮木幊陶Z言,也是函數(shù)式的編程語言。

面向?qū)ο?因為支持類的封裝、繼承、多態(tài),從這一點看,Swift是一個面向?qū)ο蟮恼Z言

函數(shù)式:為什么說是函數(shù)式編程?因為Swift支持map、flatmap更加強調(diào)運算結(jié)果而不是中間過程。

7.Objective-C中的函數(shù)式編程?

在OC中典型的函數(shù)式編程式masonry這個框架。在OC中的函數(shù)式編程實現(xiàn)的原理:

  1. 如果想再去調(diào)用別的方法,那么就需要返回一個對象。
  2. 如果想用()去執(zhí)行,那么需要返回一個block。
  3. 如果想讓返回的block再調(diào)用對象的方法,那么這個block就需要返回一個對象(即返回值為一個對象的block)。

8.Swift中可選型(optioonal)?

在Swift中,可選型式為了表達(dá)當(dāng)一個變量值為空的情況,當(dāng)一個變量值為空時,他就是nil。在Swift中,無論變量時引用類型還是值類型,都可以時可選型變量。
! ?這兩個的區(qū)別。?可選類型, !隱式解析可選類型

9.OBjective-C當(dāng)中有沒有可選類型?

OC當(dāng)中沒有明確提出可選型的概念,所有的引用類型都可以為nil,以此來表示變量值為空的情況,當(dāng)時在Swift中就明確提出了可選型的概念,并擴大到了值類型。

10.Swift中,什么是泛型(Generics)?

泛型主要是為了增加代碼的靈活性,它可以使對應(yīng)的代碼滿足任意類型的變量,比如
func sum<T>(_ a: inout T,_ b: inout T){
(a,b) = (b,a)
}
// inout 關(guān)鍵字的含義是告訴編輯器,將值類型的對象跟引用類型對象一樣都是按照引用傳遞,也就是地址傳遞。
這個方法T就是泛型,它既可以是任意對象,也可以是任意的值類型,但是必須a與b的類型保持一致,主要Swift是一個類型安全的語言,兩個變量之間進(jìn)行運算,賦值操作必須要類型一致。

11.Swift說明比較關(guān)鍵字Open、Public、internal、File-private和private關(guān)鍵字

上述幾個關(guān)鍵字統(tǒng)稱是:訪問權(quán)限控制
Swift有5個級別的訪問控制權(quán)限,從高到低依次為Open、Public、Internal、File-private、Private。

  • Open是最高權(quán)限,修飾的類和方法可以在任意的Module中被訪問和重寫
  • Public的權(quán)限僅次于Open。與Open唯一的區(qū)別在于,它修飾的對象可以在任意Module中被訪問,但不能重寫。
  • Internal是默認(rèn)的權(quán)限。它表示只能在當(dāng)前定義的Module中訪問和重寫,它可以被一個Module中的多個文件訪問,但不可以被其他的Module訪問。
  • File-private修飾的對象只能在當(dāng)前文件中使用。
  • private最低級別的訪問權(quán)限,它修飾的對象只能在作用域中使用。即使在同一個文件中的其他作用域,也沒法訪問。

12.Swift中說明并比較關(guān)鍵字:Strong,Weak和Unownet

  • Strong代表強引用,是默認(rèn)屬。當(dāng)一個對象被聲明為Strong時,表示父層級對該對象有一個強引用的指向,該對象的引用計數(shù)+1

  • Weak代表弱引用,如果一個對象在被聲明為Weak的時候,那么父層級的對象對該對象并沒有指向,該對象的引用計數(shù)也不會+1,當(dāng)該對象被釋放掉的時候,這個對象會自動設(shè)置成nil,繼續(xù)訪問不會崩潰。

  • unownet與弱引用的本質(zhì)一樣。不同的是對象在被釋放掉的時候,依然會有一個無效的引用指向該對象,它不是optional也不是nil,訪問就會崩潰。

加入weak和unownet是為了防止strong帶來的循環(huán)引用,簡單的說當(dāng)兩個對象互相有一個強引用指向?qū)Ψ降臅r候。就會導(dǎo)致兩個對象都無法在內(nèi)存中得到釋放從而導(dǎo)致的循環(huán)引用。

訪問對象可能被釋放的時候請用weak,比如是delegate,訪問你對象一定不會被釋放的時候請用unownet。

13.在Swift中,如何理解copy-on-write

在值類型在復(fù)制的時候,復(fù)制的對象和原來的對象實際上在內(nèi)存上面都指向同一個對象,當(dāng)且僅當(dāng)修改復(fù)制后的對象時,才會在內(nèi)存中重新創(chuàng)建一個新的對象
let array = [1,2,3]
var array1 = array
array1.append(4)
然后這兩個對象的地址就會不一樣了。以上

14.在Swift中什么是屬性觀察(property observer)?

當(dāng)前類型內(nèi)對特定的屬性進(jìn)行監(jiān)聽,并作出相應(yīng)的行為,就是屬性觀察,簡單的來說就是類型中的兩個方法。
比方:
var name:String{
willSet{
//將要修改
}

didSet{
    //修改完成
}

}

注意當(dāng)初始化和在類型內(nèi)部進(jìn)行修改的時候是不會調(diào)用這兩個方法的

15.在Swift中mutating的作用。

mutating的作用是可以在protocol、enum、struct中定義的方法修改其中的成員變量

16.在Swift中protocol怎么定義成員變量?

在protocol中可以聲明成員變量、和靜態(tài)成員變量,但是不能對其進(jìn)行初始化的操作。并且只能是兩種狀態(tài) get,Set、Get,
比如:
protocol MyProtocol{

var Str:String{  get  } //只讀
var changeStr:String {get set} //可讀寫

}

17.Swift在結(jié)構(gòu)體中如果修改成員變量?

protocol MyProtocol{
    var changeStr:String {get set} //可讀寫
}

struct UserModel:MyProtocol{

    var changeStr:String
    
    mutating func MyChangeStr(Name:String){
        self.changeStr = Name
    }

}

18.Swift如果實現(xiàn)(||)操作

或操作的本質(zhì)是,當(dāng)表達(dá)式左邊的值為真的時候,無須計算表達(dá)式右邊的值。
實現(xiàn)||的操作有三種方式:

一、普通的實現(xiàn)方式:

    //實現(xiàn)
    func huo(_ value1: Bool, _ value2: Bool) -> Bool {
      if value1 {
        return true
      }
      if value2 {
        return true
      }
       
      return false
    }
    //調(diào)用
    let res = huo(vLeft, getRightRes())
這種方式雖然可以實現(xiàn)或,但是并不高效,即便是左邊的值等于true,右邊的函數(shù)也會調(diào)用的。
如果右邊的函數(shù)比較耗費性能或者費時操作,在已經(jīng)確定左邊的值為true的情況下,再去執(zhí)行右邊的函數(shù)是真沒必要去執(zhí)行的。

二、閉包概念的或:
    實現(xiàn)
   func huo(_ left:Bool, _ right:()->Bool)->Bool{
        if left{
            return true
        }
        return right()
    }
    //調(diào)用
    let res = huo(true) { () -> Bool in
        return getRightRes()
    }
第二種方式解決了第一種方式中耗費性能耗時的操作。

三、@autoclosure 關(guān)鍵字優(yōu)化。
    //實現(xiàn)
    func huo(_ value1: Bool, _ value2: @autoclosure() -> Bool) -> Bool {
      if value1 {
        return true
      }
      return value2()
    }
    //調(diào)用
    let res = huo(true, getRightRes())
    這種方式就是第二中方式的優(yōu)化,加入@autoclosure后,調(diào)用時跟第一種方式時一樣的形式,它將右邊的計算函數(shù)隱性的放入了右邊的閉包

19.輸入任意一個整數(shù),輸出的是輸入整數(shù)的+2、+3、+4。。。 請定義一個函數(shù)來實現(xiàn)。

func Add(_ num:Int)->(Int)->Int{
    return { value in
        return num + value
    }
}
這體現(xiàn)了Swift的函數(shù)式編程的思想

20.寫一個函數(shù),求0~100(包括0和100)中為偶數(shù)并且恰好是其他數(shù)字平方的數(shù)字

在swift中我們需要寫的方法是:

第一種方法:
func sqNums(_ from:Int,_ to:Int)->[Int] {
    var array:[Int] = [Int]()
    for num in from...to where num%2 == 0 { //查到偶數(shù)
        if(from...to).contains(num*num){
            array.append(num*num)
        }
    }
    return array
}
這種方法如果在OC當(dāng)中還是可以的,因為Swift是函數(shù)式編程思想的內(nèi)容,我們換一種方式來思考,0 ~ 100  中找到N*N那么100是最大的,那么100 = 10*10,那么 0~10也就滿足了N*N這個條件,然后在判斷偶數(shù),在使用Swift的map、filter方法

print({0...10}.map{$0*$0}.filter{$0%2==0}) 一行代碼搞定

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容