iOS程序員面試分類模擬17_第1頁(yè)
iOS程序員面試分類模擬17_第2頁(yè)
iOS程序員面試分類模擬17_第3頁(yè)
iOS程序員面試分類模擬17_第4頁(yè)
iOS程序員面試分類模擬17_第5頁(yè)
已閱讀5頁(yè),還剩23頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

iOS程序員面試分類模擬17簡(jiǎn)答題1.

union和unionall有什么區(qū)別?正確答案:union在進(jìn)行表求并集后會(huì)去掉重復(fù)的元素,所以會(huì)對(duì)所產(chǎn)生的結(jié)果集進(jìn)行排序運(yùn)算,刪除重復(fù)的記錄再返回結(jié)果(江南博哥)。

而unionall只是簡(jiǎn)單地將兩個(gè)結(jié)果合并后就返回。因此,如果返回的兩個(gè)結(jié)果集中有重復(fù)的數(shù)據(jù),那么返回的結(jié)果集就會(huì)包含重復(fù)的數(shù)據(jù)了。

從上面的對(duì)比可以看出,在執(zhí)行查詢操作的時(shí)候,unionall要比union快很多。所以,如果可以確認(rèn)合并的兩個(gè)結(jié)果集中不包含重復(fù)的數(shù)據(jù),那么最好使用unionall。例如,有兩個(gè)學(xué)生表table1和table2。Table1C1C2112233Table2C1C2334411select*fromTable1unionselect*fromTable2的查詢結(jié)果為:C1C211223344select*fromTablelunionallselect*fromTable2的查詢結(jié)果為:C1C2112233334411

2.

UIDevice如何獲取設(shè)備信息?正確答案:iOS開(kāi)發(fā)中獲取設(shè)備的信息指像設(shè)備號(hào)、應(yīng)用名稱以及國(guó)家語(yǔ)言等非用戶隱私信息,這些信息多數(shù)都可以在Xcode工程中看到,同時(shí)開(kāi)發(fā)者可以利用框架提供的UIDevice、NSBundle、NSLocale3個(gè)類來(lái)獲取一些開(kāi)發(fā)中常用到的信息。

1.UIDevice

UIDevice可以提供給開(kāi)發(fā)者很多移動(dòng)設(shè)備的基本信息,如設(shè)備名稱、設(shè)備模式、系統(tǒng)名稱、系統(tǒng)版本、設(shè)備唯一標(biāo)識(shí)符以及設(shè)備方向和設(shè)備電量等。

其中的設(shè)備方向是一個(gè)枚舉變量UIDeviceOrientation(TV中禁止使用),源代碼中的定義如下:

typedefNS_ENUM(NSInteger,UIDeviceOrientation){

UIDeviceOrientationUnknown,

UIDeviceOrientationPortrait,

//設(shè)備垂直,home鍵在下

UIDeviceOrientationPortraitUpsideDown,

//設(shè)備翻轉(zhuǎn),home鍵在上

UIDeviceOrientationLandscapeLeft,

//設(shè)備水平,home件在左

UIDeviceOrientationLandsoareRight,

//設(shè)備水平,home件在右

UIDeviceOrientationFaceUp,

//設(shè)備平放,面朝上

UIDeviceOrientationFaceDown

//設(shè)備平放,面朝下

}_TVOS_PROHIBITED;

在SDK源代碼中定義如下:

@property(nonatomic,readonly,strong)NSString*name;//e.g."MyiPhone"

@property(nonatomic,readonly,strong)NSString*model;//e.g.@"iPhone",@"iPodtouch"

@property(nonatomic,readonly,strong)NSString*localizedModel;//本地化的設(shè)備模式

@property(nonatomic,readonly,strong)NSString*systemName;//e.g.@"iOS"

@property(nonatomic,readonly,strong)NSString*systemVersion;//e.g@"4.0"

@property(nonatomic,readonly)UIDeviceOrientationorientation__TVOS_PROHIBITED;//返回當(dāng)前的設(shè)備方向,如果還沒(méi)有產(chǎn)生設(shè)備方向通知(設(shè)備方向未發(fā)生改變),那么返回UIDeviceOrientationUnknown

@property(nullable,nonatomic,readonly,strong)NSUUID

*identifierForVendorNS_AVAILABLE_IOS(6_0);

//可用于唯一標(biāo)識(shí)設(shè)備的一個(gè)UUID

測(cè)試示例代碼如下:

/*1.UIDevice*/

UIDevice*deviee=[UIDevicecurrentDevice];

//獲取當(dāng)前的device

/**設(shè)備基本信息**/

NSString*name=[devicename];

//@"iPhone5S"

NSString*model=[devicemodel];//@"iPhone"

NSString*localizedModel=[devicelocalizedModel];//@"iPhone"

NSString*systemName=[devicesystemName];//@"iPhoneOS"

NSString*systemVersion=[devicesystemVersion];//@"9.3.2"

/**設(shè)備方向**/

UIDeviceOrientationorientation=[deviceorientation];

//UIDeviceOrientationUnknown

/*設(shè)備類型:iPad或iPhone(iPod)等*/

if([[UIDevicecurrentDevice]userInterfaceIdiom]==UIUserInterfaceIdiomPad|UIUserInterfaceIdiomPhone){}

/**設(shè)備電量**/

device.batteryMonitoringEnabled=YES;

floatbatteryLevel=[UIDevicecurrentDevice].batteryLevel;//0~1.0對(duì)應(yīng)電量0~100%,-1.0表示電量未知

device.batteryMonitoringEnabled=YES;

/**設(shè)備ID**/

NSUUID*identifierForVendor=[deviceidentifierForVendor];

2.NSBundle

開(kāi)發(fā)者經(jīng)常用到類NSBundle的mainbundle,一個(gè)包含了nib文件、編譯代碼等資源的目錄,這個(gè)目錄下有一個(gè)infoDictionary,通過(guò)這個(gè)信息字典開(kāi)發(fā)者可以獲取有關(guān)當(dāng)前應(yīng)用的一些基本信息,像應(yīng)用名稱、應(yīng)用版本等。

測(cè)試示例代碼如下:

/*2.NSBundle*/

/*獲取當(dāng)前設(shè)備的信息字典*/

NSDictionary*info_dic=[[NSBundlemainBundle]infoDictionary];

/*本地化的信息字典*/

NSDictionary*localized_info_dic=[[NSBundlemainBundle]localizedlnfoDictionary];

NSString*app_name=[info_dicobjectForKey:@"CFBundleDisplayName"];

//應(yīng)用名稱@"Demo"

NSString*app_version=[info_dicobjectForKey:@"CFBundleShortVersionString"];//應(yīng)用版本@"1.0"

NSString*app_build_version=[info_dicobjectForKey:@"CFBundleVersion"];//應(yīng)用build版本@"1"

3.NSLocale

顧名思義,NSLocale類是用于獲取一些本地化信息的,如國(guó)家、時(shí)間日期和語(yǔ)言等。這里主要展示常用到的獲取國(guó)家和語(yǔ)言代碼的方法。

測(cè)試示例代碼如下:

/*3.NSLocale*/

/*3.1獲取偏好語(yǔ)言*/

NSArray*languageArray=[NSLocalepreferredLanguages];

NSString*language=[languageArrayobjectAtIndex0];//@"en_HK"

/*3.2獲取本地化國(guó)家或地區(qū)代號(hào)*/

NSLocale*locale=[NSLocalecurrentLocale];//當(dāng)前本地化對(duì)象

NSString*country=[localelocaleIdentifier];//@"en_HK"

3.

以下每行代碼執(zhí)行后,person對(duì)象的retaincount分別是多少?

Person*person=[[Personalloc]init];

[personretain];

[personrelease];

[personrelease];正確答案:開(kāi)始alloc創(chuàng)建對(duì)象并持有對(duì)象,初始引用計(jì)數(shù)為1,retain一次引用計(jì)數(shù)加1變?yōu)?,之后release對(duì)象兩次,引用計(jì)數(shù)減1兩次,先后變?yōu)?、0,所以分別是1-2-1-0。

4.

如何使用runtime進(jìn)行方法交換?正確答案:runtime中的MethodSwizzling被稱為黑魔法,它可以將兩個(gè)方法的實(shí)現(xiàn)交換。類的方法列表會(huì)將selector的名稱映射到相關(guān)的方法實(shí)現(xiàn)上,使得“消息傳遞系統(tǒng)”能夠據(jù)此找到應(yīng)該調(diào)用的方法。這些方法均是以函數(shù)指針的形式來(lái)表示的,這種函數(shù)指針叫作IMP。例如NSString類的selector映射表,如圖所示。

NSString類的selector映射表

runtime中提供了幾個(gè)函數(shù)用來(lái)操作selector映射表,其中就有動(dòng)態(tài)交換兩個(gè)方法實(shí)現(xiàn)的函數(shù),代碼如下:

voidmethod_exchangeImplementations(Methodm1,Methodm2)

下面在類的Category中定義description方法,當(dāng)使用NSLog()函數(shù)輸出對(duì)象時(shí),就會(huì)調(diào)用這個(gè)方法。示例代碼如下:

/*load方法會(huì)在類第一次被加載的時(shí)候調(diào)用*/

+(void)load{

Methoddescription=class_getInstanceMethod([selfclass],@selector(description));

MethodmyDescription=class_getInstanceMethod([selfclass],@selector(myDescription));

method_exchangeImplementations(description,myDescription);

}

/*替換的方法*/

-(NSString*)myDescription{

return[NSStringstringWithFormat:@"這是自定義的打印方法"];

}

/*在控制器輸出*/

aperson=[[Personalloc]init];

NSLog(@"%@",aperson);

程序的打印結(jié)果如下:

2016-11-0320:02:59.18401[2990:170401]這是自定義的打印方法

通過(guò)MethodSwizzling可以為一些完全不知道其具體實(shí)現(xiàn)的黑盒方法增加日志記錄功能,利于開(kāi)發(fā)者調(diào)試程序。并且開(kāi)發(fā)者可以將某些系統(tǒng)類的具體實(shí)現(xiàn)換成自定義的方法,以達(dá)到某些目的。

5.

進(jìn)程與線程有什么區(qū)別?正確答案:進(jìn)程是具有一定獨(dú)立功能的程序關(guān)于某個(gè)數(shù)據(jù)集合上的一次運(yùn)行活動(dòng),它是系統(tǒng)進(jìn)行資源分配和調(diào)度的一個(gè)獨(dú)立單位。例如,用戶運(yùn)行自己的程序,系統(tǒng)就創(chuàng)建一個(gè)進(jìn)程,并為它分配資源,包括各種表格、內(nèi)存空間、磁盤(pán)空間、I/O設(shè)備等,然后該進(jìn)程被放入到進(jìn)程的就緒隊(duì)列,進(jìn)程調(diào)度程序選中它,為它分配CPU及其他相關(guān)資源,該進(jìn)程就被運(yùn)行起來(lái)。

線程是進(jìn)程的一個(gè)實(shí)體,是CPU調(diào)度和分配的基本單位。線程自己基本上不擁有系統(tǒng)資源,只擁有一點(diǎn)在運(yùn)行中必不可少的資源(如程序計(jì)數(shù)器、一組寄存器和棧),但是它可以與同屬一個(gè)進(jìn)程的其他的線程共享進(jìn)程所擁有的全部資源。在沒(méi)有實(shí)現(xiàn)線程的操作系統(tǒng)中,進(jìn)程既是資源分配的基本單位,又是調(diào)度的基本單位,它是系統(tǒng)中并發(fā)執(zhí)行的單元。而在實(shí)現(xiàn)了線程的操作系統(tǒng)中,進(jìn)程是資源分配的基本單位,而線程是調(diào)度的基本單位,是系統(tǒng)中并發(fā)執(zhí)行的單元。

引入線程主要有以下4個(gè)方面的優(yōu)點(diǎn):

1)易于調(diào)度。

2)提高并發(fā)性。通過(guò)線程可以方便有效地實(shí)現(xiàn)并發(fā)。

3)開(kāi)銷小。創(chuàng)建線程比創(chuàng)建進(jìn)程要快,所需要的開(kāi)銷也更少。

4)有利于發(fā)揮多處理器的功能。通過(guò)創(chuàng)建多線程,每個(gè)線程都在一個(gè)處理器上運(yùn)行,從而實(shí)現(xiàn)應(yīng)用程序的并行,使每個(gè)處理器都得到充分運(yùn)行。

需要注意的是,盡管線程與進(jìn)程很相似,但兩者也存在著很大的不同,區(qū)別如下:

1)一個(gè)線程必定屬于也只能屬于一個(gè)進(jìn)程;而一個(gè)進(jìn)程可以擁有多個(gè)線程并且至少擁有一個(gè)線程。

2)屬于一個(gè)進(jìn)程的所有線程共享該進(jìn)程的所有資源,包括打開(kāi)的文件、創(chuàng)建的Socket等。不同的進(jìn)程互相獨(dú)立。

3)線程又被稱為輕量級(jí)進(jìn)程。進(jìn)程有進(jìn)程控制塊,線程也有線程控制塊。但線程控制塊比進(jìn)程控制塊小得多。線程間切換代價(jià)小,進(jìn)程間切換代價(jià)大。

4)進(jìn)程是程序的一次執(zhí)行,線程可以理解為程序中一個(gè)程序片段的執(zhí)行。

5)每個(gè)進(jìn)程都有獨(dú)立的內(nèi)存空間,而線程共享其所屬進(jìn)程的內(nèi)存空間。

6.

Objective-C中有二維數(shù)組嗎?如何實(shí)現(xiàn)?正確答案:Objective-C中是沒(méi)有二維數(shù)組的。二維數(shù)組是通過(guò)一維數(shù)組的嵌套實(shí)現(xiàn)的,但是別忘了還有字面量用法,實(shí)際上可以像C/C++一樣簡(jiǎn)潔地創(chuàng)建和使用二維數(shù)組。這里總結(jié)了創(chuàng)建二維數(shù)組的兩種方法以及數(shù)組的訪問(wèn)方式。

1)通過(guò)字面量創(chuàng)建和使用二維數(shù)組(推薦)方法如下:

/*字面量創(chuàng)建二維數(shù)組并訪問(wèn)(推薦)*/

NSArray*array2d=@[

@[@11,@12,@13],

@[@21,@22,@23],

@[@31,@32,@33]

];

/*字面量訪問(wèn)方式(推薦)*/

NSLog(@"array2d[2][2]:%@",array2d[2][2]);

/*數(shù)組對(duì)象訪問(wèn)*/

NSLog(@"array2d[2][2]:%@",[[array2dobjectAtIndex:2]objectAtIndex:2]);

打印結(jié)果如下:

2017-01-0521:59:49.694SingleView[10483:506166]array2d[2][2]:33

2017-01-0521:59:49.695SingleView[10483:506166]array2d[2][2]:33

2)通過(guò)嵌套原本的數(shù)組對(duì)象使用二維數(shù)組方式如下:

/*另外一種循環(huán)嵌套創(chuàng)建二維數(shù)組的方式*/

NSMutableArray*mulArrayD1=[[NSMutableArrayalloc]init];

//第一維數(shù)組

/*添加第二維*/

for(NSUIntegeri=1;i<=3;i++){

NSArray*arrayD2=@[@(i*10+1),@(i*10+2),@(i*10+3)];

[mulArrayD1addObject:arrayD2];

}

/*字面量訪問(wèn)方式(推薦)*/

NSLog(@"array2d[2][2]:%@",mulArrayD1[2][2]);

/*數(shù)組對(duì)象訪問(wèn)*/

NSLog(@"array2d[2][2]:%@",[[mulArrayD1objectAtIndex:2]objectAtIndex:2]);

打印結(jié)果如下:

2017-01-0521:59:49.695SingleView[10483:506166]array2d[2][2]:33

1:59:49.695SingleView[10483:506166]array2d[2][2]:33

7.

UIViewController的生命周期方法有哪些?正確答案:在開(kāi)發(fā)過(guò)程中,大量的邏輯代碼主要會(huì)寫(xiě)在UIViewController的生命周期方法中,了解這些方法的含義和調(diào)用時(shí)機(jī)十分重要。UIViewController的主要生命周期方法及其含義見(jiàn)表。UIViewController的主要生命周期方法及其含義方法含義[UIViewControlleralloc]創(chuàng)建對(duì)象并分配內(nèi)存空間[UIViewControllerinitWithNibName:bundle:]視圖控制器nib初始化,初始化對(duì)象以及數(shù)據(jù)[ViewControllerinit]視圖控制器初始化,初始化對(duì)象以及數(shù)據(jù)-(void)loadView首次創(chuàng)建view調(diào)用該方法(例如首次調(diào)用view的getter方法:self.view,controller.view),如果使用了xib創(chuàng)建視圖,那么從nib加載視圖-(void)loadViewIfNeeded如果視圖控制器的視圖還沒(méi)有加載,那么加載-(void)viewDidLoad視圖加載完成,可以動(dòng)態(tài)添加其他UI控件-(void)viewWillAppear:(BOOL)animated視圖即將出現(xiàn)到屏幕上之前(每一次要顯示該視圖都會(huì)重復(fù)調(diào)用,不僅僅是第一次創(chuàng)建初始化之后,下面另外3個(gè)時(shí)機(jī)亦然)-(void)viewDidAppear:(BOOL)animated視圖已經(jīng)顯示到屏幕上之后-(void)viewWillDisappear:(BOOL)animated視圖即將從屏幕上消失(消失包括被銷毀、被隱藏、被覆蓋等情況)-(void)viewDidDisappear:(BOOL)animated視圖已經(jīng)從屏幕上消失-(void)viewWillLayoutSubviews在視圖的layoutSubviews方法激活(View自身的frame發(fā)生改變)前調(diào)用-(void)viewDidLayoutSubviews在視圖的layoutSubviews方法激活后立刻調(diào)用-(void)didReceiveMemoryWarning應(yīng)用收到內(nèi)存警告導(dǎo)致視圖無(wú)法加載時(shí)調(diào)用,需要手動(dòng)清理因?yàn)閕OS6以后不會(huì)默認(rèn)自動(dòng)清理,通常在應(yīng)用占用太大內(nèi)存時(shí)被系統(tǒng)進(jìn)行內(nèi)存警告,需要在下面的viewWillUnload方法中將UI手動(dòng)置nil-(void)viewWillUnload視圖將不會(huì)繼續(xù)加載,對(duì)應(yīng)于下面的viewDidUnload,iOS6后不會(huì)再對(duì)其進(jìn)行調(diào)用-(void)viewDidUnload在視圖控制器被釋放或者被設(shè)置為nil時(shí)調(diào)用,例如收到內(nèi)存警告無(wú)法加載時(shí),即非正常加載,而不是dealloc時(shí)的正常銷毀,這里要手動(dòng)清理界面元素資源;iOS6以后該回調(diào)方法被棄用了,任何情況下都不會(huì)再被調(diào)用,因?yàn)閮?nèi)存低時(shí)系統(tǒng)不再在這里清理視圖元素了,官方建議之前該回調(diào)內(nèi)進(jìn)行的清理工作應(yīng)轉(zhuǎn)移到didReceiveMemoryWarning回調(diào)方法中,但實(shí)際上也并不需要再進(jìn)行之前在viewDidUnload中進(jìn)行的視圖清理工作,系統(tǒng)會(huì)獨(dú)立于視圖單獨(dú)進(jìn)行處理-[UIViewControllerdealloc]視圖銷毀,可以釋放初始化時(shí)的對(duì)象等資源

8.

靜態(tài)鏈接與動(dòng)態(tài)鏈接有什么區(qū)別?正確答案:靜態(tài)鏈接指把要調(diào)用的函數(shù)或者過(guò)程直接鏈接到可執(zhí)行文件中,成為可執(zhí)行文件的一部分。換句話說(shuō),函數(shù)和過(guò)程的代碼就在程序的.exe文件中,該文件包含了運(yùn)行時(shí)所需的全部代碼。靜態(tài)鏈接的缺點(diǎn)是當(dāng)多個(gè)程序都調(diào)用相同函數(shù)時(shí),內(nèi)存中就會(huì)存在這個(gè)函數(shù)的多個(gè)副本,這樣就浪費(fèi)了內(nèi)存資源。

動(dòng)態(tài)鏈接是相對(duì)于靜態(tài)鏈接而言的,動(dòng)態(tài)鏈接所調(diào)用的函數(shù)代碼并沒(méi)有被復(fù)制到應(yīng)用程序的可執(zhí)行文件中去,而是僅僅在其中加入了所調(diào)用函數(shù)的描述信息(往往是一些重定位信息)。僅當(dāng)應(yīng)用程序被裝入內(nèi)存開(kāi)始運(yùn)行時(shí),在操作系統(tǒng)的管理下,才在應(yīng)用程序與相應(yīng)的動(dòng)態(tài)鏈接庫(kù)(DynamicLinkLibrary,DLL)之間建立鏈接關(guān)系。當(dāng)要執(zhí)行所調(diào)用.dll文件中的函數(shù)時(shí),根據(jù)鏈接產(chǎn)生的重定位信息,操作系統(tǒng)轉(zhuǎn)去執(zhí)行.dll文件中相應(yīng)的函數(shù)代碼。

靜態(tài)鏈接的執(zhí)行程序能夠在其他同類操作系統(tǒng)的機(jī)器上直接運(yùn)行。例如,一個(gè).exe文件是在Windows2000系統(tǒng)上靜態(tài)鏈接的,那么將該文件直接復(fù)制到另一臺(tái)Windows2000的機(jī)器上,是可以運(yùn)行的。而動(dòng)態(tài)鏈接的執(zhí)行程序不可以,除非把該.exe文件所需的.dll文件都復(fù)制過(guò)去,或者對(duì)方機(jī)器上也有所需的相同版本的.dll文件,否則是不能保證正常運(yùn)行的。

9.

庫(kù)函數(shù)調(diào)用與系統(tǒng)調(diào)用有什么不同?正確答案:庫(kù)函數(shù)是語(yǔ)言或應(yīng)用程序的一部分,它是高層的、完全運(yùn)行在用戶空間、為程序員提供調(diào)用真正在幕后完成實(shí)際事務(wù)的系統(tǒng)調(diào)用接口。系統(tǒng)函數(shù)是內(nèi)核提供給應(yīng)用程序的接口,屬于系統(tǒng)的一部分。簡(jiǎn)單地說(shuō),函數(shù)庫(kù)調(diào)用是語(yǔ)言或應(yīng)用程序的一部分,而系統(tǒng)調(diào)用是操作系統(tǒng)的一部分。

庫(kù)函數(shù)調(diào)用與系統(tǒng)調(diào)用的區(qū)別見(jiàn)表。庫(kù)函數(shù)調(diào)用與系統(tǒng)調(diào)用的區(qū)別庫(kù)函數(shù)調(diào)用系統(tǒng)調(diào)用在所有的ANSIC編譯器版本中,C語(yǔ)言庫(kù)函數(shù)是棚同的各個(gè)操作系統(tǒng)的系統(tǒng)調(diào)用是不同的它調(diào)用函數(shù)庫(kù)中的一段程序(或函數(shù))它調(diào)用系統(tǒng)內(nèi)核的服務(wù)與用戶程序相聯(lián)系足操作系統(tǒng)的一個(gè)入口點(diǎn)在用戶地址空間執(zhí)行在內(nèi)核地址空間執(zhí)行它的運(yùn)行時(shí)間屬于“用戶時(shí)間”它的運(yùn)行屬于“系統(tǒng)時(shí)間”屬于過(guò)程調(diào)用,調(diào)用開(kāi)銷較小需要在用戶空間和內(nèi)核上下文環(huán)境間切換,開(kāi)銷較大在C函數(shù)庫(kù)libc中有大約300個(gè)函數(shù)在UNIX中有大約90個(gè)系統(tǒng)調(diào)用典型的C函數(shù)庫(kù)調(diào)用:system、fprintf和malloc等典型的系統(tǒng)調(diào)用:chdir、fork、write和brk等

庫(kù)函數(shù)調(diào)用通常比行內(nèi)展開(kāi)的代碼慢,因?yàn)樗枰冻龊瘮?shù)調(diào)用的開(kāi)銷。但系統(tǒng)調(diào)用比庫(kù)函數(shù)調(diào)用還要慢很多,因?yàn)樗枰焉舷挛沫h(huán)境切換到內(nèi)核模式。

10.

Objective-C和Swift中有重載嗎?正確答案:Swift中有重載,但Objective-C中基本不支持重載,事實(shí)上Objective-C支持參數(shù)個(gè)數(shù)不同的函數(shù)重載。

Swift是基于C語(yǔ)言和Objective-C語(yǔ)言優(yōu)化后更加完善的新型語(yǔ)言,擺脫了C的兼容性限制,采用安全的編程模式并且增加了一些新的特性使編程更加有趣、友好,適應(yīng)語(yǔ)言發(fā)展的趨勢(shì)和期望。函數(shù)重載作為多態(tài)性的一個(gè)部分在Swift中是支持的,可能也是考慮到要彌補(bǔ)Objective-C中不完全支持函數(shù)重載的這一缺陷。Objective-C不完全支持重載,因?yàn)镺bjective-C學(xué)習(xí)者應(yīng)該會(huì)發(fā)現(xiàn)同一個(gè)類中不允許定義函數(shù)名相同且參數(shù)個(gè)數(shù)相同的兩個(gè)函數(shù),無(wú)論參數(shù)類型和返回值類型相同與否。但是說(shuō)完全不支持也太絕對(duì),因?yàn)镺bjective-C中允許定義函數(shù)名相同但參數(shù)個(gè)數(shù)不同的兩個(gè)函數(shù),也就是說(shuō)Objective-C支持參數(shù)個(gè)數(shù)不同的函數(shù)重載。例如,可以在一個(gè)類中定義兩個(gè)參數(shù)個(gè)數(shù)不同的函數(shù),調(diào)用時(shí)通過(guò)參數(shù)個(gè)數(shù)進(jìn)行區(qū)分。

重載函數(shù)定義如下:

-(void)test:(int)one;

-(void)test:(int)oneandTwo:(int)two;

重載函數(shù)實(shí)現(xiàn)如下:

-(void)test:(int)one{

NSLog(@"oneparameter!");

}

-(void)test:(int)oneandTwo:(int)two{

NSLog(@"twoparameters!");

}

多態(tài)調(diào)用:

[selftest;1];

//output:oneparameter!

[selftest:1andTwo:2];

//output:twoparameter!

可以看出Objective-C可以通過(guò)參數(shù)個(gè)數(shù)實(shí)現(xiàn)函數(shù)重載,但是如果參數(shù)相同,那么無(wú)論參數(shù)和返回值類型相同與否,都無(wú)法編譯通過(guò)。下面的定義是無(wú)法通過(guò)Xcode編譯的。

-(void)test:(int)one;

-(int)test:(float)one;

//Duplicatedeclarationofmethod'test'

11.

如何使用RAC防止button短時(shí)間內(nèi)重復(fù)單擊?正確答案:很多時(shí)候,我們要防止按鈕或cell的重復(fù)單擊,特別是當(dāng)單擊按鈕會(huì)觸發(fā)網(wǎng)絡(luò)請(qǐng)求的時(shí)候。如果在短時(shí)間內(nèi)多次單擊按鈕,那么會(huì)使得網(wǎng)絡(luò)在短時(shí)間內(nèi)重復(fù)請(qǐng)求,上一個(gè)請(qǐng)求未結(jié)束,下一個(gè)請(qǐng)求又發(fā)出了,這樣有可能會(huì)帶來(lái)意想不到的后果,嚴(yán)重的話會(huì)造成程序崩潰。因此,防止按鈕在短時(shí)間內(nèi)重復(fù)單擊是非常有必要的。如果是經(jīng)驗(yàn)豐富的程序員,那么他們會(huì)想到AOP,利用iOS中的runtime機(jī)制,使用MethodSwizzling實(shí)現(xiàn)單擊事件的hook,然后延時(shí)執(zhí)行單擊事件,從而避免短時(shí)間內(nèi)重復(fù)單擊。如果開(kāi)發(fā)者會(huì)RAC,那么這個(gè)問(wèn)題就很簡(jiǎn)單了,解決方法如下:

[[[self.buttonrac_signalForControlEvents:UIControlEventTouchUpInside]throttle:2]subscribeNext:^(__kindofUIControl*_Nullablex){

NSLog(@"buttonciick");

}];

以上代碼的功能是使用throttle(節(jié)流)方法,實(shí)現(xiàn)2s內(nèi),多次單擊按鈕只響應(yīng)最后一次單擊的事件。

12.

自動(dòng)釋放池跟GC(垃圾回收)有什么區(qū)別?iPhone上有GC嗎?[poolrelease]和[pooldrain]有什么區(qū)別?正確答案:[poolrelease]和[pooldrain]在作用上是一樣的,它們都是清理自動(dòng)釋放池,區(qū)別是drain在支持GC的系統(tǒng)中(Mac系統(tǒng))可以引起GC回收操作,而release不可以。對(duì)于自動(dòng)釋放池一般建議使用[pooldrain],原因是它的功能對(duì)系統(tǒng)兼容性更強(qiáng),二者也是為了跟普通對(duì)象的release釋放區(qū)別開(kāi)。自動(dòng)釋放池的釋放操作指向池內(nèi)所有的對(duì)象發(fā)送release消息,以讓系統(tǒng)及時(shí)釋放池內(nèi)的所有對(duì)象。

13.

iOS中如何觸發(fā)定時(shí)任務(wù)或延時(shí)任務(wù)?正確答案:定時(shí)任務(wù)指周期性地調(diào)用某個(gè)方法,實(shí)現(xiàn)任務(wù)的反復(fù)執(zhí)行,如倒計(jì)時(shí)等;延時(shí)任務(wù)指等待一定的時(shí)間后再執(zhí)行某個(gè)任務(wù),如頁(yè)面的延時(shí)跳轉(zhuǎn)等。iOS中控制任務(wù)的延時(shí)或定時(shí)執(zhí)行的方法有很多,使用中要注意是同步還是異步,是否會(huì)阻塞主線程等問(wèn)題。延時(shí)和定時(shí)的實(shí)現(xiàn)方法依次如下。

1.performSelector實(shí)現(xiàn)延時(shí)任務(wù)

延時(shí)任務(wù)可以通過(guò)當(dāng)前UIViewController的perfonnSelector隱式創(chuàng)建子線程實(shí)現(xiàn),不會(huì)阻塞主線程。

/*延遲10s執(zhí)行任務(wù)*/

[selfperformSelector:@selector(task)withObject:nilafterDelay:10];

-(void)task

{

//delaytask

}

2.利用sleep實(shí)現(xiàn)后面任務(wù)的等待

慎用,會(huì)阻塞主線程N(yùn)SThreadsleepForTimeInterval:10.0];

3.GCD實(shí)現(xiàn)延時(shí)或定時(shí)任務(wù)

通過(guò)GCD實(shí)現(xiàn)block代碼塊的延時(shí)執(zhí)行。

dispatch_time_tdelay=dispatch_time(DiSPATCH_TIME_NOW,10*NSEC_PER_SEC);

dispatch_after(delay,dispatch_get_main_queue0,^{

//delaytask

});

GCD還可以用來(lái)實(shí)現(xiàn)定時(shí)器功能,還能設(shè)置延時(shí)開(kāi)啟計(jì)時(shí)器,使用中注意一定要定義強(qiáng)引用指針來(lái)指向計(jì)時(shí)器對(duì)象才可讓計(jì)時(shí)器生效。

/*必須要用強(qiáng)引用指針,計(jì)時(shí)器才會(huì)生效*/

@property(nonatomic,strong)dispatch_source_ttimer;

/*在指定線程上定義計(jì)時(shí)器*/

dispatch_queue_tqueue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

dispatc_souree_t_timer=dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,0,0,queue);

/*開(kāi)始的時(shí)間*/

dispatch_time_twhen=dispatch_time(DISPATCH_TIME_NOW,(int64_t)(1.0*NSEC_PER_SEC));

/*設(shè)置計(jì)時(shí)器*/

dispatch_source_set_timer(_timer,when,1.0*NSEC_PER_SEC,0);

/*計(jì)時(shí)器回調(diào)block*/

dispatch_source_set_event_handler(_timer,^{

NSLog(@"dispatch_source_set_timerisworking!");

});

/*開(kāi)啟計(jì)時(shí)器*/

dispatch_resume(_timer);

/*強(qiáng)引用計(jì)時(shí)器對(duì)象*/

self.timer=_timer;

4.NSTimer實(shí)現(xiàn)定時(shí)任務(wù)

NSTimer主要用于開(kāi)啟定時(shí)任務(wù),但要正確使用才能保證它能夠正常有效地運(yùn)行。尤其要注意以下兩點(diǎn):

1)確保NSTimer已經(jīng)添加到當(dāng)前RunLoop。

2)確保當(dāng)前RunLoop已經(jīng)啟動(dòng)。

創(chuàng)建NSTimer有兩種方法,代碼如下:

+(NSTimer*)timerWithTimeInterval:(NSTimeInterval)titarget:(id)aTargetselector:(SEL)aSelectoruserInfo:(nullableid)userInforepeats:(BOOL)yesOrNo;

+(NSTimer*)scheduledTimerWithTimeInterval:(NSTimeInterval)titarget:(id)aTargetselector:(SEL)aSelectoruserInfo:(nullableid)userInforepeats:(BOOL)yesOrNo;

這兩種方法的主要區(qū)別為,使用timerWithTimeInterval創(chuàng)建的timer不會(huì)自動(dòng)添加到當(dāng)前RunLoop中,需要手動(dòng)添加并指定RunLoop的模式:[[NSRunLoopcurrentRunLoop]addTimer:mainThreadTimerforMode:NSDefaultRtmLoopMode];而使用scheduledTimerWithTimeInterval創(chuàng)建的RunLoop會(huì)默認(rèn)添加到當(dāng)前RunLoop中。

NSTimer可能在主線程中創(chuàng)建,也可能在子線程中創(chuàng)建。主線程中的RunLoop默認(rèn)是啟動(dòng)的,所以timer只要添加到主線程RunLoop中就會(huì)被執(zhí)行;而子線程中的RunLoop默認(rèn)是不啟動(dòng)的,所以timer添加到子線程RunLoop中后,還要手動(dòng)啟動(dòng)RunLoop才能使timer被執(zhí)行。

NSTimer只有添加到啟動(dòng)起來(lái)的RunLoop中才會(huì)正常運(yùn)行。NSTimer通常不建議添加到主線程中執(zhí)行,因?yàn)榻缑娴母略谥骶€程中進(jìn)行,這會(huì)影響NSTimer的準(zhǔn)確性。

以下代碼為4種情形下NSTimer的正確使用方法。

-(void)viewDidLoad{

[superviewDidLoad];

/*第一種,主線程中創(chuàng)建timer,需要手動(dòng)添加到RunLoop中*/

NSTimer*mainThreadTimer=[NSTimertimerWithTimeInterval:10.0target:selfselector:@selector(mainThreadTimer_SEL)userInfo:nilrepeats:YES];

/*第二種,主線程中創(chuàng)建timer,不需要手動(dòng)添加到RunLoop中*/

NSTimer*mainThreadSchduledTimer=[NSTimer

scheduledTimerWithTimeInterval:10.0target:selfselector:@selector(mainThreadSchduledTimer_SEL)userInfo:nilrepeats:YES];

/*將mainThreadTimer添加到主線程runloop*/

[[NSRunLoopcurrentRunLoop]addTimer:mainThreadTimer

forMode:NSDefaultRunLoopMode];

dispatch_asyne(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{

/*第三種,子線程中創(chuàng)建timer,需要手動(dòng)添加到RunLoop中*/

NSTimer*subThreadTimer=[NSTimertimerWithTimeInterval:10.0target:selfselector:@selector(subThreadTimer_SEL)userInfo:nilrepeats:YES];

/*第三種,子線程中創(chuàng)建timer,不需要手動(dòng)添加到RunLoop中*/

NSTimer*subThreadSchduledTimer=[NSTimerscheduledTimerWithTimeInterval:10.0target:selfselector:@selector(subThreadSchduledTimer_SEL)userInfo:nilrepeats:YES];

/*將subThreadTimer添加到子線程runloop*/

[[NSRunLoopcurrentRunLoop]addTimer:subThreadTimerforMode:NSDefaultRunLoopMode];

/*啟動(dòng)子線程mnloop*/

[[NSRunLoopcurrentRunLoop]runMode:NSDefaultRunLoopModebeforeDate:[NSDatedistantFuture]];

});

}

-(void)mainThreadTimer_SEL{

NSLog(@"mainThreadTimerisworking!");

}

-(void)mainThreadSchduledTimer_SEL{

NSLog(@"mainThreadSchduledTimerisworking!");

}

-(void)subThreadTimer_SEL{

NSLog(@"subThreadTimerisworking!");

}

-(void)subThreadSchduledTimer_SEL{

NSLog(@"subThreadSchduledTimerisworking!");

}

上述代碼的打印結(jié)果為:

2018-01-3120:24:10.729259+0800IOSDemo[41891:4382661]mainThreadSchduledTimerisworking!

2018-01-3120:24:20.728351+0800IOSDemo[41891:4382661]mainThreadTimerisworking!

2018-01-3120:24:20;728409+0800IOSDemo[41891:4382720]subThreadTimerisworking!

2018-01-3120:24:20.728693+0800IOSDemo[41891:4382720]subThreadSchduledTimerisworking!

NSTimer的釋放方法:[timerinvalidate];

5.CADisplayLink實(shí)現(xiàn)定時(shí)任務(wù)

CADisplayLink實(shí)現(xiàn)的定時(shí)器與屏幕刷新頻率綁定在一起,是一種幀率刷新,適用于界面的不斷重繪(例如流暢動(dòng)畫(huà)和視頻播放等)。CADisplayLink以特定模式注冊(cè)到RunLoop后,每當(dāng)屏幕顯示內(nèi)容刷新結(jié)束后就會(huì)向CADisplayLink指定的target發(fā)送一次消息,實(shí)現(xiàn)target的每幀調(diào)用。根據(jù)需求也可以設(shè)置每幾幀調(diào)用一次,默認(rèn)每幀都調(diào)用。另外,通過(guò)CADisplayLink還可以獲取幀率和時(shí)間等信息。

CADisplayLink實(shí)現(xiàn)的定時(shí)器精度非常高,但如果調(diào)用的方法十分耗時(shí),超過(guò)一幀的時(shí)間間隔,那么會(huì)導(dǎo)致跳幀,跳幀次數(shù)取決于CPU的忙碌程度。

下面是CADisplayLink定時(shí)器的實(shí)現(xiàn):

-(void)viewDidLoad{

[superviewDidLoad];

CADisplayLink*displayLink=[CADisplayLinkdisplayLinkWithTarget:selfselector:@selector(displayLink_SEL)];

/*添加到當(dāng)前運(yùn)行的RunLooP中啟動(dòng)*/

[displayLinkaddToRunLoop:[NSRunLoopcurrentRunLoop]

forMode:NSDefaultRunLoopMode];

/*暫停、繼續(xù)對(duì)selector的調(diào)用*/

//[displayLinksetPaused:YES];

//[displayLinksetPaused:NO];

/*設(shè)置每幾幀調(diào)用一次selector,默認(rèn)為1*/

//[displayLinksetPreferredFramesPerSecond:2];

/*移除,不再使用*/

//[displayLinkinvalidate];

//displayLink=nil;

}

-(void)displayLink_SEL{

NSLog(@"displayLinkisworking!");

}

打印結(jié)果如下,每一幀都調(diào)用selector。

2018-02-0111:48:02.707283+0800IOSDemo[42328:4522489]displayLinkisworking!

2018-02-0111:48:02.724375+0800IOSDemo[42328:4522489]displayLinkisworking!

2018-02-0111:48:02.742995+0800IOSDemo[42328:4522489]displayLinkisworking!

14.

如何理解并使用CAKeyframeAnimation?正確答案:CAKeyframeAnimation即關(guān)鍵幀動(dòng)畫(huà),它是CAPropertyAnimation的一個(gè)子類,它作用于單一的屬性,但和CABasicAnimation不一樣的是,它不需要設(shè)置一個(gè)起始和結(jié)束的值,而是可以根據(jù)開(kāi)發(fā)者提供的一連串隨意的值來(lái)做動(dòng)畫(huà)。這些值就是關(guān)鍵幀,每幀之間的繪制將由系統(tǒng)自動(dòng)完成。示例代碼如下:

-(void)startKeyframeAnimation{

CAKeyframeAnimation*animation=[CAKeyframeAnimationanimation];

/*指定需要做動(dòng)畫(huà)的屬性*/

animation.keyPath=@"backgroundColor";

/*指定關(guān)鍵幀*/

animation.values=@[(id)[UIColorwhiteColor].CGColor,(id)[taColorreaColor].CGColor,(id)(id)[UIColorblueColor].CGColor,(id)[UIColorblackColor].CGColor,(id)[UIColorwhiteColor].CGColor];

/*指定關(guān)鍵幀的過(guò)渡時(shí)間*/

animation.keyTimes=@[@0,@0.2,@0.5,@0.7,@1];

animation.duration=5;

/*指定關(guān)鍵幀的過(guò)渡緩沖方式*/

animation.timingFunctions=@[[CAMediaTimingFunctionfunctionWithName:kCAMediaTimingFunctionEaseInEaseOut],[CAMediaTimingFunctionfunctionWithName:kCAMediaTimingFunetionEaseInEaseOut],[CAMediaTimingFunctionfunctionWithName:kCAMediaTimingFunctionEaseInEaseOut],[CAMediaTimingFunctionfunctionWithName:kCAMediaTimingFunctionEaseInEaseOut]];

[self.view.layeraddAnimation:animationforKey:nil];

}

使用關(guān)鍵幀動(dòng)畫(huà)需要注意下面3點(diǎn):

1)注意到values數(shù)組中開(kāi)始和結(jié)束的顏色都是白色,這是因?yàn)殛P(guān)鍵幀動(dòng)畫(huà)并不能將當(dāng)前值作為第一幀。動(dòng)畫(huà)在執(zhí)行后會(huì)立即跳轉(zhuǎn)到第一幀的值,然后在動(dòng)畫(huà)結(jié)束的時(shí)候突然恢復(fù)到原始值。所以為了使動(dòng)畫(huà)更加平滑自然,需要使用開(kāi)始和結(jié)束的關(guān)鍵幀來(lái)匹配當(dāng)前屬性的值。

2)除了使用values來(lái)指定關(guān)鍵幀以外,還可以使用其path屬性。它是CGPath類型,它可以用一種非常直觀的方式來(lái)定義動(dòng)畫(huà)的運(yùn)動(dòng)序列。在做一些基于位置改變的動(dòng)畫(huà)時(shí),它非常有用。

@property(nullable)CGPathRefpath;

3)指定要做動(dòng)畫(huà)的屬性并非只能是可動(dòng)畫(huà)屬性(animatable),可以對(duì)子屬性甚至是虛擬屬性(不能直接使用)做動(dòng)畫(huà)。例如,指定keyPath為transform.rotaion來(lái)做旋轉(zhuǎn)動(dòng)畫(huà)。

15.

MethodSwizzling的應(yīng)用場(chǎng)景有哪些?正確答案:MethodSwizzling主要用于在運(yùn)行時(shí)對(duì)編譯器編譯好的方法再次進(jìn)行編輯,應(yīng)用場(chǎng)景主要有以下4種。

1.替換類中某兩個(gè)類方法或?qū)嵗椒ǖ膶?shí)現(xiàn)

替換函數(shù):method_exchangeImplementations(method1,method2)。

這里隨便定義一個(gè)Test類,類中定義兩個(gè)實(shí)例方法和類方法并在.m文件中實(shí)現(xiàn),在運(yùn)行時(shí)將兩個(gè)實(shí)例方法的實(shí)現(xiàn)對(duì)調(diào),以及將兩個(gè)類方法的實(shí)現(xiàn)對(duì)調(diào)。注意運(yùn)行時(shí)代碼寫(xiě)在類的load方法內(nèi),該方法只會(huì)在該類第一次加載時(shí)調(diào)用一次,且編寫(xiě)運(yùn)行時(shí)代碼的地方需要引入運(yùn)行時(shí)頭文件:

#import<o(jì)bjc/runtime.h>

Test類定義:

/*Test.h*/

#import<Foundation/Foundation.h>

@interfaceTest:NSObject

/*定義兩個(gè)公有實(shí)例方法*/

-(void)instanceMethod1;

-(void)instanceMethod2;

/*定義兩個(gè)公有類方法*/

+(void)ClassMethod1;

+(void)classMethod2;

@end

/*Test.m*/

#import"Test.h"

#import<o(jì)bjc/runtime.11>

@implementationTest

/*實(shí)例方法的原實(shí)現(xiàn)*/

-(void)instanceMethod1{

NSLog(@"instanceMethod1...");

}

-(void)instanceMethod2{

NSLog(@"instanceMethod2...");

}

/*類方法的原實(shí)現(xiàn)*/

+(void)classMethod1{

NSLog(@"classMethod1...");

}

+(void)classMethod2{

NSLog(@"classMethod2...");

}

@end

/*runtime代碼寫(xiě)在類第一次被調(diào)用加載的時(shí)候(load方法有且只有一次會(huì)被調(diào)用)*/

+(void)load{

/*1.獲取當(dāng)前類名*/

Classclass=[selfclass];

/*2.獲取方法名(選擇器)*/

SELselInsMethodl=@selector(instanceMethod1);

SELselInsMethod2=@selector(instanceMethod2);

SELselClassMethod1=@selector(classMethod1);

SELselClassMethod2=@selector(classMethod2);

/*3.根據(jù)方法名獲取方法對(duì)象*/

MethodInsMethod1=class_getInstanceMethod(class,selInsMethod1);

MethodInsMethod2=class_getInstanceMethod(class,selInsMethod2);

MethodClassMethod1=class_getClassMethod(class,selClassMethod1);

MethodClassMethod2=class_getClassMethod(class,selClassMethod2);

/*4.交換實(shí)例方法的實(shí)現(xiàn)和類方法的實(shí)現(xiàn)*/

if(!InsMethod1||!InsMethod2){

NSLog(@"實(shí)例方法實(shí)現(xiàn)運(yùn)行時(shí)交換失敗!");

return;

}

/*交換實(shí)例方法的實(shí)現(xiàn)*/

method_exchangeImplementations(InsMethod1,InsMethod2);

if(!ClassMethod1||!ClassMethod2){

NSLog(@"類方法實(shí)珧運(yùn)行時(shí)交換失敗!");

return;

}

/*交換類方法的實(shí)現(xiàn)*/

method_exchangeImplementations(ClassMethod1,ClassMethod2);

}

測(cè)試代碼:

#import"ViewController.h"

#import"Test.h"

@implementationViewController

-(void)viewDidLoad{

[superviewDidLoad];

/*測(cè)試類方法調(diào)用*/

[TestclassMethod1];

[TestclassMethod2];

Test*test=[[Testalloc]init];

/*測(cè)試實(shí)例方法調(diào)用*/

[testinstanceMethod1];

[testinstanceMethod2];

}

@end

通過(guò)下而的輸出結(jié)果可知,兩個(gè)實(shí)例方法和類方法的實(shí)現(xiàn)都被互換了。

2017-03-0617:47:13.684SingleView[41495:1196960]classMethod2...

2017-03-0617:47:13.684SingleView[41495:1196960]classMethod1...

2017-03-0617:47:13.685SingleView[41495:1196960]instanceMethod2...

2017-03-0617:47:13.685SingleView[41495:1196960]instanceMethod1...

2.重新設(shè)置類中某個(gè)方法的實(shí)現(xiàn)

設(shè)置函數(shù):method_setImplementation(method,IMP)

理解了上面的例子,現(xiàn)在略微修改其中運(yùn)行時(shí)代碼,通過(guò)重新設(shè)置方法的實(shí)現(xiàn)來(lái)實(shí)現(xiàn)上面同樣的效果。修改部分的運(yùn)行時(shí)代碼為:

/*獲取方法的實(shí)現(xiàn)*/

IMPimpInsMethod1=method_getImplementation(InsMethod1);

IMPimpInsMethod2=method_getImplementation(InsMethod2);

IMPimpClassMethod1=method_getImplementation(ClassMethod1);

IMPimpClassMethod2=method_getImplementation(ClassMethod2);

/*重新設(shè)置方法的實(shí)現(xiàn)*/

/*重新設(shè)置instanceMethod1的實(shí)現(xiàn)為instanceMethod2的實(shí)現(xiàn)*/

method_setImplementation(InsMethod1,impInsMethod2);

/*重新設(shè)置instanceMethod2的實(shí)現(xiàn)為instanceMethod1的實(shí)現(xiàn)*/

method_setImplementation(InsMethod2,impInsMethod1):

/*重新設(shè)置classMethod1的實(shí)現(xiàn)為classMethod2的實(shí)現(xiàn)*/

method_setImplementation(ClassMethod1,impClassMethod2);

/*重新設(shè)置classMethod2的實(shí)現(xiàn)為classMethod1的實(shí)現(xiàn)*/

method_setImplementation(ClassMethod2,impClassMethod1);

運(yùn)行后打印結(jié)果和上面方法實(shí)現(xiàn)交換的例子結(jié)果相同。

2017-03-0618:27:53.032SingleView[41879:1212691]classMethod2...

2017-03-0618:27:53.032SingleView[41879:1212691]classMethod1...

2017-03-0618:27:53.033SingleView[41879:1212691]instanceMethod2...

2017-03-0618:27:53.033SingleView[41879:1212691]instanceMethod1...

3.替換類中某個(gè)方法的實(shí)現(xiàn)

替換方法實(shí)現(xiàn)函數(shù):class_replaceMethod(Classcls,SELname,IMPimp,constchar*types)。

這種方法只能替換實(shí)例方法的實(shí)現(xiàn),而不能替換類方法的實(shí)現(xiàn),修改的代碼如下:

/*獲取方法編碼類型*/

constchar*typeInsMethod1=method_getTypeEncoding(InsMethod1);

constchar*typeInsMethod2=method_getTypeEncoding(InsMethod2);

constchar*typeClassMethod1=method_getTypeEncoding(ClassMethod1);

constchar*typeClassMethod2=method_getTypeEncoding(ClassMethod2);

/*替換InsMethod1的實(shí)現(xiàn)為InsMethod2的實(shí)現(xiàn)*/

class_replaceMethod(class,selInsMethod1,impInsMethod2,typeInsMethod2);

/*替換InsMethod2的實(shí)現(xiàn)為InsMethod1的實(shí)現(xiàn)*/

class_replaceMethod(class,selInsMethod2,impInsMethod1,typeInsMethod1);

/*嘗試替換類方法的實(shí)現(xiàn)*/

class_replaceMethod(class,selClassMethod1,impClassMethod2,typeClassMethod2);

class_replaceMethod(class,selClassMethod2,impClassMethod1,typeClassMethod1);

通過(guò)結(jié)果可見(jiàn)實(shí)例方法的實(shí)現(xiàn)成功被替換,而類方法的實(shí)現(xiàn)沒(méi)有被替換。

2017-03-0618:47:03.598SingleView[42106:1221468]classMethod1...

2017-03-0618:47:03.599SingleView[42106:1221468]classMethod2...

2017-03-0618:47:03.600SingleView[42106:1221468]instanceMethod2...

2017-03-0618:47:03.600SingleView[42106:1221468]instanceMethod1...

以上介紹的是同一個(gè)類中方法實(shí)現(xiàn)的再改動(dòng),實(shí)際上也可以修改或交換不同類之間方法的實(shí)現(xiàn)。

4.在運(yùn)行時(shí)為類添加新的方法

添加函數(shù):class_addMethod()。

除了在編譯期顯式的定義方法,還可以在運(yùn)行時(shí)補(bǔ)加新的實(shí)例方法,但不可以添加新的類方法。這里接上面的例子為T(mén)est類在運(yùn)行時(shí)添加一個(gè)新的名為newInsMethod的方法,方法的實(shí)現(xiàn)設(shè)置為InsMethod1的實(shí)現(xiàn),修改后的運(yùn)行時(shí)代碼如下:

/*6.為類添加新的實(shí)例方法和類方法*/

SELselNewInsMethod=@selector(newInsMethod);

BOOL

isInsAdded=class_addMethod(class,selNewInsMethod,impInsMethod1,typeInsMethod1);

if(!isInsAdded){

NSLog(@"新實(shí)例方法添加失敗!");

}

測(cè)試新函數(shù)代碼如下:

/*測(cè)試運(yùn)行時(shí)新添加實(shí)例方法調(diào)用*/

Test*test=[[Testalloc]init];

[testnewInsMethod];

運(yùn)行結(jié)果打印出“instanceMethod1...”證明新實(shí)例方法動(dòng)態(tài)添加成功。

2017-03-0619:07:15.447SingleView[42354:1230571]instanceMethod1...

此外,除了在運(yùn)行時(shí)為類添加新的方法,還可以通過(guò)其他運(yùn)行時(shí)函數(shù)classaddIvar、classaddProperty、class_addProtocol等動(dòng)態(tài)地為類添加新的變量、屬性和協(xié)議等。

16.

在Objective-C的數(shù)組或字典中,添加nil對(duì)象會(huì)有什么問(wèn)題?正確答案:數(shù)組或字典如果通過(guò)addObject方法添加nil,那么程序會(huì)崩潰,但如果使用initWithObjects方法來(lái)初始化數(shù)組,其中的nil會(huì)被編譯器過(guò)濾去掉,不會(huì)出現(xiàn)崩潰問(wèn)題。另外,如果使用糖衣語(yǔ)法初始化數(shù)組或字典,那么也不可以有nil,此時(shí)nil不會(huì)被過(guò)濾掉也會(huì)導(dǎo)致程序崩潰。

/*1.糖衣語(yǔ)法*/

NSArray*array=@[@1,@2,@3,nil];//錯(cuò)誤,不可有ni1,會(huì)編譯不通過(guò):void*不是Objective-C對(duì)象

NSDictionary*dic=@{

@"KEY":@"VALUE",

@"KEY1":@"VALUE1",

@"KEY2":nil

};//語(yǔ)法就是錯(cuò)誤的,編譯不通過(guò)

/*2.原用法*/

NSMutableArray*mulArray=[[NSMutableArrayalloc]initWithObjects:@1,@2,@3,nil];//正確

NSMutableDictionary*mulDic=[[NSMutableDictionaryalloc]initWithObjectsAndKeys:

@"VALUE",@"KEY",

@"VALUE1",@"KEY1",nil];//正確

/*下面添加nil都會(huì)編譯警告,運(yùn)行起來(lái)會(huì)崩潰*/

[mulArrayaddObject:nil];

[mulDicsetObject:nilforKey:@"KEY2"];

17.

在runtime中類與對(duì)象如何表示?正確答案:在runtime庫(kù)中,對(duì)象是用C語(yǔ)言中的結(jié)構(gòu)體表示,而方法用C語(yǔ)言中的函數(shù)來(lái)實(shí)現(xiàn),另外也加入了一些額外的特性。這些結(jié)構(gòu)體和函數(shù)被Runtime函數(shù)封裝后,開(kāi)發(fā)者就可以利用運(yùn)行時(shí)特性在程序運(yùn)行階段動(dòng)態(tài)地創(chuàng)建、查看、修改類、對(duì)象和它們的方法。在iOS的SDK中,文件夾usr/include/objc下的文件基本都是和運(yùn)行時(shí)相關(guān)的文件,其中使用的函數(shù)主要定義在message.h和runtime.h兩個(gè)文件中。在這兩個(gè)文件中,包含了運(yùn)行時(shí)對(duì)象結(jié)構(gòu)體的描述和對(duì)運(yùn)行時(shí)進(jìn)行操作的函數(shù)。

1.類(Class)

在runtime中,Objective-C中的類是由Class類型來(lái)表示的,通過(guò)objc/objc.h可以查看到Class類型實(shí)際上是指向objc_class結(jié)構(gòu)的指針。它的定義如下:

///AnopaquetypethatrepresentsallObjective-Cclass.

typedefstructobjc_class*Class;

通過(guò)查看objc/runtime.h文件,可以看到objc_class結(jié)構(gòu)體的定義如下:

structobjc_class{

Classisa

OBJC_ISA_AVAILABILITY;

#if!__OBJC2__

Classsuper_classOBJC2_UNAVAILABLE;

constchar*name

OBJC2_UNAVAILABLE;

longversion

OBJC2_UNAVAILABLE;

longinfo

OBJC2_UNAVAILABLE;

longinstance_size

OBJC2_UNAVAILABLE;

structobjc_iVar_list*ivarsOBJC2_UNAVAILABLE;

structobjc_method_list**methodListsOBJC2_UNAVAILABLE;

structobjc_cache*cache

OBJC2_UNAVAILABLE;

structobjc_protocol_list*protocols

OBJC2_UNAVAILABLE;

#endif

}OBJC2_UNAVAILABLE;

/*Use'Class'insteadof'structobjc_elass*'*/

在這個(gè)結(jié)構(gòu)體中,有幾個(gè)重要的字段,它們的含義如下。

1)isa:在Objective-C中,所有的類自身也是一個(gè)對(duì)象,這個(gè)對(duì)象的Class里面也存在一個(gè)isa指針。對(duì)象需要通過(guò)isa指針找到它的類,類需要通過(guò)isa找到它的元類(MetaClass)。元類可以理解為類對(duì)象的類,每個(gè)類都會(huì)有一個(gè)單獨(dú)的元類。事實(shí)上,元類也是一個(gè)類,它也存在isa指針,它的isa指針指向父類的元類。也就是說(shuō),任何NSObject繼承體系下的元類都使用NSObject的元類作為自己的所屬類,而基類的元類的isa指針是指向它自己。這樣就形成了一個(gè)完美的閉環(huán)。isa指針在調(diào)用實(shí)例方法和類方法的時(shí)候會(huì)起到重要的作用。

2)super_class:指向該類的父類,如果該類已經(jīng)是最頂層的類(RootClass),那么super_class就是nil。

3)cache:它主要用于緩存類中最近使用的方法。當(dāng)開(kāi)發(fā)者調(diào)用過(guò)一個(gè)方法后,runtime會(huì)將這個(gè)方法緩存到cache列表中,如果再次調(diào)用這個(gè)方法,那么runtime就會(huì)優(yōu)先去cache中查找,如果cache沒(méi)有,那么就會(huì)去methodLists中查找該方法。這樣就大大提高了調(diào)用方法的效率。

4)ivars:指向該類的成員變量鏈表。

5)methodLists:指向方法定義的鏈表。

6)protocols:指向協(xié)議鏈表。

這些類型的定義將一個(gè)類進(jìn)行了完全的分解,將類的定義或者對(duì)象的每一個(gè)部分都抽象為一個(gè)類型Type,這樣對(duì)操作一個(gè)類屬性和方法非常方便。開(kāi)發(fā)者可以通過(guò)runtime提供的函數(shù)獲取類結(jié)構(gòu)體中的各個(gè)類型的值。例如,如果想要獲取Class的name,那么可以通過(guò)如下的方法獲取。

/*導(dǎo)入runtime頭文件*/

#import<o(jì)bjc/runtime.h>

#import<o(jì)bjc/message.h>

/*獲取ClassName*/

ClasspersonClass=Person.class;

constchar*className=class_getName(personClass);

NSLog(@"%s",className);

2.對(duì)象(objc_object)

在objc/objc.h中,同樣可以找到關(guān)于類的實(shí)例定義。

///Representsaninstanceofaclass.

structobjc_object{

Classisa

OBJC_ISA_AVAILABILITY;

};

從以上代碼可以看到,在objc_object結(jié)構(gòu)體中只有一個(gè)字段,即指向其類的isa指針。通過(guò)這個(gè)指針,runtime會(huì)找到這個(gè)實(shí)例對(duì)象所屬的類。當(dāng)創(chuàng)建一個(gè)特定類的實(shí)例對(duì)象時(shí),分配的內(nèi)存包含一個(gè)objc_object數(shù)據(jù)結(jié)構(gòu),然后是類的實(shí)例變量的數(shù)據(jù)。NSObject類的alloc和allocWithZone:方法使用方法class_createInstance來(lái)創(chuàng)建objc_object數(shù)據(jù)結(jié)構(gòu)。

在objc.h中還可以看到id類型的定義。

///Apointertoaninstanceofaclass.

typedefstructobjc_object*id:

顯然,它也是一個(gè)objc_object結(jié)構(gòu)類型的指針。它的存在可以讓開(kāi)發(fā)者實(shí)現(xiàn)類似于C++中泛型的一些操作。該類型的對(duì)象可以轉(zhuǎn)換為任何一個(gè)種對(duì)象,有點(diǎn)類似C語(yǔ)言中void*指針類型的作用。

18.

如何使用CAShapeLayer繪制圖層?正確答案:CAShapeLayer是一個(gè)通過(guò)矢量圖形來(lái)進(jìn)行繪制的圖層子類。開(kāi)發(fā)者通過(guò)指定諸如顏色和線寬等屬性,用CGPath來(lái)定義指定形狀的圖形,最后CAShapeLayer對(duì)象就會(huì)自動(dòng)渲染出來(lái)了。與直接使用CoreGraphics在原始的CALayer對(duì)象中繪制的方式相比,使用CAShapeLayer有以下優(yōu)點(diǎn):

1)渲染速度更快。CAShapeLayer使用了硬件加速的方式繪制圖形,繪制速度比用CoreGraphics快很多。

2)內(nèi)存使用更加高效。普通的CALayer對(duì)象需要?jiǎng)?chuàng)建一個(gè)寄宿圖,而CAShapeLayer不需要,這樣就節(jié)約了內(nèi)存。

3)不會(huì)被邊界圖層裁剪掉。一個(gè)CAShapeLayer可以在圖層邊界繪制。CAShapeLayer的圖層路徑不會(huì)

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論