http://www.cnblogs.com/ldnh/tag/iOS面試/
1、#import 跟#include、@class有什么區別?#import<> 跟 #import”"又什么區別?
include和#import都能完整的包含某個文件的內容,#import可以防止一個文件被導入多次。@class只是聲明一個類名,并不會包含包含類的完整聲明,@class可以解決循環包含的問題。
include通常是用來包含系統自帶的文件,而import則是用來包含自己自定義的文件。
import會包含這個類的所有信息,包括實體變量和方法,而@class只是告訴編譯器,其后面聲明的名稱是類的名稱,至于這些類是如何定義的,暫時不用考慮,后面會再告訴你。
在頭文件中, 一般只需要知道被引用的類的名稱就可以了。 不需要知道其內部的實體變量和方法,所以在頭文件中一般使用@class來聲明這個名稱是類的名稱。 而在實現類里面,因為會用到這個引用類的內部的實體變量和方法,所以需要使用#import來包含這個被引用類的頭文件。
在編譯效率方面考慮,如果你有100個頭文件都#import了同一個頭文件,或者這些文件是依次引用的,如A–>B, B–>C, C–>D這樣的引用關系。當最開始的那個頭文件有變化的話,后面所有引用它的類都需要重新編譯,如果你的類有很多的話,這將耗費大量的時間。而是用@class則不會。
如果有循環依賴關系,如:A–>B, B–>A這樣的相互依賴關系,如果使用#import來相互包含,那么就會出現編譯錯誤,如果使用@class在兩個類的頭文件中相互聲明,則不會有編譯錯誤出現。
所以,一般來說,@class是放在interface中的,只是為了在interface中引用這個類,把這個類作為一個類型來用的。 在實現這個接口的實現類中,如果需要引用這個類的實體變量或者方法之類的,還是需要import在@class中聲明的類進來.
2、屬性readwrite,readonly,assign,retain,copy,nonatomic 各是什么作用,在那種情況下用?
readwrite--同時生成get方法及set方法的聲明和實現。
readonly--只生成get方法的聲明和實現。
assign--set方法的實現是直接賦值,用于基本數據類型。
retain--set方法的實現是release舊值,retain新值,用于OC對象。
copy--set方法的實現是release舊值,copy新值,用于NSString,Block類型
nonatomic--非原子性,set方法的實現不加鎖(atomic主要加的是自旋鎖)
unsafe_unretained 用unsafe_unretained聲明的指針,指針指向的對象一旦被釋放,這些指針將成為野指針
3、寫一個setter方法用于完成@property (nonatomic,retain)NSString name,寫一個setter方法用于完成@property(nonatomic,copy)NSString name.
- (void)setName:(NSString *)name
{
if (_name != name) {
//release舊值
[_name release];
_name = [name copy];
}
}
- (void)setName:(NSString *)name
{
if (_name != name) {
[_name release];
_name = [name retain];
}
}
4、對于語句NSString*obj = [[NSData alloc] init]; ,編譯時和運行時obj分別是什么類型?(待補充)
編譯時是NSString類型,執行時是NSData類型。
runtime是一個C語言框架,蘋果底層就是這個,
5、常見的object-c的數據類型有那些, 和C的基本數據類型有什么區別?
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。
6、id聲明的變量有什么特性
id聲明的對象能指向任何OC對象
用于修飾代理,
id相當于NSObject *
7、Objective-C如何對內存管理的,說說你的看法和解決方法?
Objective-C內存管理主要有三種方式,MRC(Manual Reference Counting )和ARC(Automatic Reference Counting)、自動釋放池
每個對象都有一個引用計數,當他被alloc init出來時,引用計數為1,然后retain一次加1,release減1,當引用計數為0時,系統就會自動調用delloc方法,在delloc方法中[super delloc]必須寫,這個對象就會被回收。
Autorelease-自動釋放池,在iOS運行過程中,會創建無數個池子,這些池子都是以棧型結構存儲的,當一個對象調用autorelease 系統會把該對象放到棧頂的自動釋放池中,當自動釋放池銷毀的時候,系統會對池子中的所有對象進行一次release操作,系統自帶的方法中,如果不包含alloc copy new,那么這些方法的返回對象都是autorelease的,如[NSData data].
自動釋放池的創建方式
iOS 5之前
NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
iOS 5之后
@autoreleasepool
{//開始代表創建自動釋放池
·······
}//結束代表銷毀自動釋放池
ARC--只要沒有強指針指向的對象,對象就會被釋放,強指針__strong ,屬性中的strong相當于MRC中的retain,弱指針__weak,屬性中的weak相當于assign,成員變量是弱指針,ARC不允許調用retain release retainCount方法,可以重寫delloc方法,但是不允許調用[super delloc]方法。
8、看下面的程序,三次NSLog會輸出什么?為什么?
NSMutableArray* ary = [[NSMutableArray array] retain];
NSString *str = [NSString stringWithFormat:@"test"];? // 1
[str retain];? // 2
[ary addObject:str]; // 3
NSLog(@"%d", [str retainCount]);
[str retain];? // 4
[str release];? // 3
[str release];? // 2
NSLog(@"%d", [str retainCount]);
[ary removeAllObjects]; // 1
NSLog(@"%d", [str retainCount]);
str的retainCount創建+1,retain+1,加入數組自動+1
3
retain+1,release-1,release-1
2
數組刪除所有對象,所有數組內的對象自動-1
1
9、內存管理的幾條原則時什么?按照默認法則.那些關鍵字生成的對象
需要手動釋放?在和property結合的時候怎樣有效的避免內存泄露?
誰申請,誰釋放
遵循Cocoa Touch的使用原則;
內存管理主要要避免“過早釋放”和“內存泄漏”,對于“過早釋放”需要注意@property設置特性時,一定要用對特性關鍵字,對于“內存泄漏”,一定要申請了要負責釋放,要細心。
關鍵字alloc 或new 生成的對象需要手動釋放;
設置正確的property屬性,對于retain需要在合適的地方釋放,
10、Object C中創建線程的方法是什么?如果在主線程中執行代碼,方法是什么?如果想延時執行代碼、方法又是什么?
線程創建有三種方法:使用NSThread創建、使用GCD的dispatch、使用子類化的NSOperation,然后將其加入NSOperationQueue;
在主線程執行代碼,方法是performSelectorOnMainThread,如果想延時執行代碼可以用performSelector:onThread:withObject:waitUntilDone:
11、ViewController的didReceiveMemoryWarning怎么被調用?
[supper didReceiveMemoryWarning];
12、什么時候用delegate,什么時候用Notification?
Delegate是一種點對點的消息傳送機制。傳遞給自己或者其他對象。有時候他還會返回一個影響事件如何被處理的值。
在內存管理環境中,delegate是弱引用。在垃圾回收環境中,delegate是強引用。
Notification是一種一對多的消息傳遞方式。他的實質是廣播信息給所有observer。消息發送者不需要知道誰是消息的接收者。
他減少了對象之間的依賴。
13、深拷貝和淺拷貝
淺拷貝
//創建一個可變的數組
NSMutableArray *array = [NSMutableArray array];
//創建兩個person對象,然后把他們加入到數組中
Person *p1 = [[Person alloc]init];
p1.name = @"小玉";
Person *p2 = [[Person alloc]init];
p2.name = @"小小玉";
[array addObject:p1];
[array addObject:p2];
//淺拷貝
NSArray *newArray = [array copy];
Person *p = newArray[0];
p.name = @"小王八";
//輸出array[0]和newArray[0],結果發現他兩輸出都為小王八,
NSLog(@"array = %@ newArray = %@",((Person *)array[0]).name,((Person *)newArray[0]).name);
深拷貝
//創建一個可變的數組
NSMutableArray *array = [NSMutableArray array];
//創建兩個person對象,然后把他們加入到數組中
Person *p1 = [[Person alloc]init];
p1.name = @"小玉";
Person *p2 = [[Person alloc]init];
p2.name = @"小小玉";
[array addObject:p1];
[array addObject:p2];
NSMutableArray *newArray = [NSMutableArray array];
for (Person *p in array) {
Person *p2 = [[Person alloc]init];
p2.name = p.name;
[newArray addObject:p2];
}
Person *person = newArray[0];
person.name = @"小王八";
//輸出為小王八
NSLog(@"%@",((Person *)newArray[0]).name);
//輸出為小玉
NSLog(@"%@",((Person *)array[0]).name);
14 OC的理解和特性
OC作為一個面向對象的語言,他也就具有面向對象的特點-封裝,繼承,多態。
OC是一門動態性的語言,他具有動態綁定,動態加載,動態類型。動態即就是在運行時才會做的一些事情。
動態類型-即運行時才決定對象的類型,簡單來說,就是id類型,他可以存儲任意類型的變量,他自己本身就相當于指針,類似于void*。
靜態類型和動態類型--
編譯期間檢查和運行時檢查
靜態類型在編譯期間就能檢查出錯誤,靜態類型聲明代碼可讀性良好,動態類型只有在運行時才能發現錯誤。
動態綁定-程序直到執行時才知道執行哪個方法,動態綁定需要做的,即就是在實例所屬類確定后,將某些屬性和方法綁定到實例上。
SEL是類方法方法的指針,他就相當于C語言中的中函數指針。SEL class_func = @selector(),OC類里面的方法都是被轉換成SEL變量進行存儲的,當類聲明一個對象,對象調用方法時,系統會將這個方法轉換成SEL,然后拿這個SEL在類方法中進行查找,我們可以手動將方法轉換為SEL,然后用SEL去查找方法(performSelector)。
動態加載-根據需求加載相應的資源,在iOS開發中,主要是做屏幕的適配。
15 如何理解MVC設計模式
M指的是model層,它主要是進行一些數據的收集和存儲。
V指的是view層,它主要是來展示一些UI方面的東西,比如說布局界面。
C指的是ViewController,他主要有一些業務邏輯的算法,還有一些功能的設計,Controller負責把model層的數據展示在view層,但是view層不能直接和Controller交互,這樣,就需要用到代理或者Block了,model層和view層不能直接進行交互。
16 如何理解MVVM模式。
V層指的是viewController層,從viewModel層或得數據,然后展示。
VM指的是viewMOdel,他是model和V層的粘合劑,說實在的,就是把MVC中ViewController中的一些業務邏輯和功能模塊給抽出來。
17 協議的基本概念和默認的類型
協議就是一些方法,但是僅僅只是聲明這些方法,但是沒有實現,他可以被任意類使用,但是他并不是類,協議分為非正式協議和正式協議,非正式協議是NSObject的類別。
協議的用法,當一個類遵循一個協議時,如果協議中的方法使用@optional修飾時,表示這個方法可以實現也可以不實現,但是要是使用@required修飾的話,說明這個類必須要實現這個方法。
默認類型是@required,必須要實現的。
18 什么是單例,單例的寫法
單例模式在他的核心結構中只包含一個叫單例的特殊類,他只有一個實例對象并且易于外部訪問,從而實現對實例個數的控制,并且減少系統資源,如果想要實現實例對象只有一個,那么單例模式是你最好的選擇。
單例創建步驟
創建一個類方法,名字以shared,default current開頭。
創建一個全局變量來保存對象的引用。
判斷該對象是否存在,如果沒有則創建。
單例寫法。
非線程安全的單例
/**
*? 非線程安全的單例
*
*? @return
*/
+ (instancetype)defaultPerson
{
//創建一個全局變量,以保存對象的引用
static Person *person = nil;
if (person == nil) {
person = [[self alloc]init];
}
return person;
}
線程安全單例寫法1 - 加了一個互斥鎖
+ (instancetype)defaultPerson
{
static Person *person = nil;
@synchronized(self) {
if (person == nil) {
person = [[self alloc]init];
}
}
return person;
}
線程安全單例寫法2
+ (void)initialize
{
static Person *person = nil;
if([self class] == [Person class])
{
person = [[Person alloc]init];
}
}
線程安全單例寫法3 -- GCD,蘋果推薦的
+ (instancetype)defaultPerson
{
static Person *person = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
person = [[self alloc]init];
});
return person;
}
19 load方法和initialize方法的區別
load方法:
當類被引用進程序的時候會執行這個函數
一個類的load方法不用寫明[super load],父類就會收到調用,并且在子類之前。
Category的load也會收到調用,但順序上在主類的load調用之后。
initialize方法:
initialize的自然調用是在第一次主動使用當前類的時候
和load不同,即使子類不實現initialize方法,會把父類的實現繼承過來調用一遍。注意的是在此之前,父類的方法已經被執行過一次了,同樣不需要super調用。
load和initialize有很多共同特點,下面簡單列一下:
在不考慮開發者主動使用的情況下,系統最多會調用一次
如果父類和子類都被調用,父類的調用一定在子類之前
都是為了應用運行提前創建合適的運行環境
在使用時都不要過重地依賴于這兩個方法,除非真正必要
20 簡述類目Category優點和缺點
優點
不要要增加子類就可以給現有類增加方法。
通過類目可以對一個類的方法進行劃分,有益于代碼的維護,管理,提高代碼的可閱讀性。
如果類目中的方法名稱和原有類的方法名字一樣,類目中的方法優先級更高,系統會優先調用類目中的方法。
缺點
無法向類目添加實例變量,如果需要添加實例變量,只能通過定義子類的方式;
類目中的方法與原始類以及父類方法相比具有更高優先級,如果覆蓋父類的方法,可能導致super消息的斷裂。因此, 最好不要覆蓋原始類中的方法。
21 循環引用的產生原因,以及解決方法
比如兩個對象A和B,他們相互都持有對方作為自己的成員變量,只有自己銷毀的時候,對象的引用計數才能減1,對象A依賴于對象B的銷毀,對象B依賴于對象A的銷毀,這樣就造成了循環引用,
解決方法 - 把其中一個對象使用__weak修飾,使其中一個對象編程弱對象。
22 鍵值編碼KVC
KVC全稱key valued coding 鍵值編碼
提到KVC,就不能不提反射機制,反射機制就是在運行狀態中,對于任意一個類,都能夠調用他的所有屬性和方法,對于任意一個對象,都能夠調用他的任意一個方法和屬性,java和C#都有,ObjC也有,所以你根本不必進行任何操作就可以進行屬性的動態讀寫,這就是KVC.KVC的操作方法基本上都由NSKeyValueCoding提供,而他是NSObject的類別,也就是說所以的對象都支持KVC操作。
使用KVC間接修改對象屬性時,系統會自動判斷對象屬性的類型,并完成轉換。
KVC可以訪問成員變量,無論是否提供get,set方法,無論可見性咋樣,是否有readonly修飾。
KVC的主要作用dict和model之間的互轉
字典轉模型
[self setValuesForKeysWithDictionary:dict];
模型轉字典
[p dictionaryWithValuesForKeys:array];
使用KVC計算屬性
舉例:[p valueForKeyPath:@"books.@sum.price"];
@avg:平均值
@count:總數
@max:最大
@min:最小
@sum:總數
23 鍵值觀察KVO
鍵值觀察機制是一種能夠使對象獲取到其它對象屬性變化的通知,極大的簡化了代碼。
實現KVO鍵值觀察模式,被觀察的對象必須使用KVC鍵值編碼來修改他的實例變量,這樣才可以被觀察者觀察到,所以說KVC是KVO的基礎。
KVC,KVO原理剖析
24 協議
概念:定義了一個接口,其他類負責實現這個接口,如果你的類實現了一個協議的方法時,則說明該類遵循此協議
非正式協議:NSObject的類別
協議的修飾符:@required 表示這個方法是必須實現的,@optional,這個表示這個方法是可以實現也可以不實現的,
conformsToProtocol方法--監測一個方法是否遵循某個協議
協議是無類型的,一個協議可以被多個類使用,一個類可以遵循多個協議
25 委托
代理又叫委托,是一種設計模式,代理是對象與對象之間的通訊,代理解除了對象之間的耦合性。
代理可以理解為JAVA重的回調監聽機制。
委托步驟
定義協議
創建委托對象,委托對象必須實現委托協議
實現協議方法
26 NSNotification、Block、代理、和KVO的區別。
代理是一種回調機制,且是一對一的關系,通知是一對多。
Delegate比NSNotification效率高
代理和Block一般是一對一的通信
代理需要定義協議方法,代理對象需要實現協議方法,并且需要建立代理關系
Block比較簡潔,不需要定義繁瑣的協議方法,如果通信事件比較多的話,建議使用代理
27 當我們調用一個靜態方法時,需要對對象進行release嗎??
不需要,靜態方法(類方法)創建一個對象時,對象已經被放入自動釋放池中
28 static self super關鍵字的作用
函數體內static變量的作用范圍是函數體,該變量只分配一次內存,只初始化一次。
在類中satic成員變量屬于整個類所擁有,對類的所有對象只有一份拷貝
self 當前消息的接受者
super 向父類發送消息
29 iOS數據持久化有幾種??
屬性列表:plist文件
歸檔:歸檔通過序列化的形式,鍵值關系存儲到本地,轉為二進制流,通過runtime實現自動化歸檔和解檔
遵循NSCodeing協議,需要實現encodeWithCoder和initWithCoder方法,如果是子類繼承父類的話,需要實現父類的歸檔和解檔方法
如果需要歸檔的屬性過多,可以使用runtime來進行歸檔
SQlite數據庫-一種關系型數據庫
CoreData:通過管理對象進行、增、刪、改、查的,他的底層其實還是SQL語句,但是他卻是一個對象型數據庫
30 CoreData
CoreData介紹
CoreData是面向對象的API,CoreData是iOS中非常重要的一項技術,幾乎在所有編寫的程序中,CoraData也作為數據存儲的基礎。
他是蘋果官方提供的一套框架,用來解決與對象聲明周期管理,對象關系管理和持久化等問題
他提供的是對象-關系映射功能,可以將OC對象轉換成數據,保存到SQL中,然后將保存后的數據還原成OC對象。
CoreData的特征
通過CoreData管理應用程序的數據模型,可以極大程度減少需要編寫的代碼數量。
將對象數據存儲在SQLite數據庫已獲得性能優化。
提供NSFetchResultsController類用于管理表視圖的數據,即將Core Data的持久化存儲在表視圖中,并對這些數據進行管理:增刪查改。
管理undo/redo操縱;
檢查托管對象的屬性值是否正確。
CoreData的6成員對象
NSManageObject:被管理的數據記錄Managed Object Model是描述應用程序的數據模型,這個模型包含實體(Entity)、特性(Property)、讀取請求(Fetch Request)等。
NSManageObjectContext:管理對象上下文,持久性存儲模型對象,參與數據對象進行各種操作的全過程,并監測數據對象的變化,以提供對undo/redo的支持及更新綁定到數據的UI。
NSPersistentStoreCoordinator:連接數據庫的Persistent Store Coordinator相當于數據文件管理器,處理底層的對數據文件的讀取和寫入,一般我們與這個沒有交集。
NSManagedObjectModel:被管理的數據模型、數據結構。
NSFetchRequest:數據請求;
NSEntityDescription:表格實體結構,還需知道.xcdatamodel文件編譯后為.momd或者.mom文件。
CoreData的功能
對于KVC和KVO完整且自動化的支持,除了為屬性整合KVO和KVC訪問方法外,還整合了適當的集合訪問方法來處理多值關系;
自動驗證屬性(property)值;
支持跟蹤修改和撤銷操作;
關系維護,Core Data管理數據的關系傳播,包括維護對象間的一致性;
在內存上和界面上分組、過濾、組織數據;
自動支持對象存儲在外部數據倉庫的功能;
創建復雜請求:無需動手寫SQL語句,在獲取請求(fetch request)中關聯NSPredicate。NSPreadicate支持基本功能、相關子查詢和其他高級的SQL特性。它支持正確的Unicode編碼、區域感知查詢、排序和正則表達式;
延遲操作:Core Data使用懶加載(lazy loading)方式減少內存負載,還支持部分實體化延遲加載和復制對象的數據共享機制;
合并策略:Core Data內置版本跟蹤和樂觀鎖(optimistic locking)來支持多用戶寫入沖突的解決,其中,樂觀鎖就是對數據沖突進行檢測,若沖突就返回沖突的信息;
數據遷移:Core Data的Schema Migration工具可以簡化應對數據庫結構變化的任務,在某些情況允許你執行高效率的數據庫原地遷移工作;
可選擇針對程序Controller層的集成,來支持UI的顯示同步Core Data在IPhone OS之上,提供NSFetchedResultsController對象來做相關工作,在Mac OS X上我們用Cocoa提供的綁定(Binding)機制來完成的。
31 Runloop
想深入了解Runloop,點這里
32 解釋self = [super init]方法
容錯處理,當父類初始化失敗,會返回一個nil,表示初始化失敗,由于繼承的關系,子類是要得到父類的實例和行為,因此我們必須先初始化父類,然后初始化子類
33 棧和堆的區別
棧區(stack)由編譯器自動分配釋放 ,存放方法(函數)的參數值, 局部變量的值等,棧是向低地址擴展的數據結構,是一塊連續的內存的區域。即棧頂的地址和棧的最大容量是系統預先規定好的。
堆區(heap)一般由程序員分配釋放, 若程序員不釋放,程序結束時由OS回收,向高地址擴展的數據結構,是不連續的內存區域,從而堆獲得的空間比較靈活。
碎片問題:對于堆來講,頻繁的new/delete勢必會造成內存空間的不連續,從而造成大量的碎片,使程序效率降低。對于棧來講,則不會存在這個問題,因為棧是先進后出的隊列,他們是如此的一一對應,以至于永遠都不可能有一個內存塊從棧中間彈出.
分配方式:堆都是動態分配的,沒有靜態分配的堆。棧有2種分配方式:靜態分配和動態分配。靜態分配是編譯器完成的,比如局部變量的分配。動態分配由alloca函數進行分配,但是棧的動態分配和堆是不同的,他的動態分配是由編譯器進行釋放,無需我們手工實現。
分配效率:棧是機器系統提供的數據結構,計算機會在底層對棧提供支持:分配專門的寄存器存放棧的地址,壓棧出棧都有專門的指令執行,這就決定了棧的效率比較高。堆則是C/C++函數庫提供的,它的機制是很復雜的。
全局區(靜態區)(static),全局變量和靜態變量的存儲是放在一塊 的,初始化的全局變量和靜態變量在一塊區域, 未初始化的全局變量和未初始化的靜態變量在相鄰的另一塊區域。程序結束后有系統釋放。
文字常量區—常量字符串就是放在這里的。程序結束后由系統釋放。
程序代碼區—存放函數體的二進制代碼
34 cell重用問題
UITableView通過重用單元格來達到節省內存的目的,通過為每個單元格指定一個重用標示(reuseidentifier),即指定了單元格的種類,以及當單元格滾出屏幕時,允許恢復單元格以便復用。對于不同種類的單元格使用不同的ID,對于簡單的表格,一個標示符就夠了。
如一個TableView中有10個單元格,但屏幕最多顯示4個,實際上iPhone只為其分配4個單元格的內存,沒有分配10個,當滾動單元格時,屏幕內顯示的單元格重復使用這4個內存。實際上分配的cell的個數為屏幕最大顯示數,當有新的cell進入屏幕時,會隨機調用已經滾出屏幕的Cell所占的內存,這就是Cell的重用。
35 使用Block有什么好處?使用NSTimer寫一個使用Block顯示
Block傳值和回調相比代理方便,而且操作也簡單,省了很多代碼
NSTimer封裝的Block,具體實現