以后題目多的話會進(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次:
- 在autorelease之后user2加上retain,實驗證明user2沒有被釋放,說明autorelease只是調(diào)用了一次release
- 在autorelease之后user2加上release,程序崩潰Thread 1: EXC_BAD_ACCESS (code=EXC_I386_GPFLT)野指針,將user2 = nil就會解決,ObjC可以向nil對象發(fā)送消息。
自動釋放池的總結(jié)
- autorelease不會改變retaincount的值,只是放入自動釋放池
- 自動釋放池,就是在代碼塊完成以后,調(diào)用autorelease的對象自動調(diào)用release方法(只是一次),如果對象的retaincount>1那么就沒法釋放
- 自動釋放池是統(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)容:
- 可以繼承、這樣子類可以使用父類的特性和方法。
- 類型轉(zhuǎn)換可以在運行時檢查和解釋一下實例的類型。
- 可以使用deinit來釋放資源。
- 一個類可以被多次引用。
struct的優(yōu)勢:
- 結(jié)構(gòu)較小,適用于復(fù)制操作,相比一下class的實例被多次引用,struct更加安全。
- 無須擔(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)的原理:
- 如果想再去調(diào)用別的方法,那么就需要返回一個對象。
- 如果想用()去執(zhí)行,那么需要返回一個block。
- 如果想讓返回的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}) 一行代碼搞定