Objective-C 淺析
1、簡介
從今天開始接觸IOS開發,而後則接觸PHP開發,接觸是為了更好的理解開發。IOS的開發前提是Objective-C,本篇簡單總結其知識點。
什麼是Objective-C,它是C的面向對象的擴展。
2、知識點
1、Objective-C類定義有兩個部分:接口部分和實現部分。
接口部分:
@interface className:superclassName
{
Instance variable declarations
}
Method declarations
@end
實現部分:類的方法的實現。
2、NSObject
唯一根類,幾乎所有的Objective-C類都是直接或間接的是NSObject的子類,它定義了類工廠方法alloc,負責為那些需要與Objective-C的內存管理系統交互的對象實例和實例方法分配內存。
3、消息
Objective-C使用一種不同的方法,叫做消息。
[receiver message]
receiver執行一個方法的對象
message 方法的名稱
在任何情況下,在方括號中代碼都意味著你是在給一個對象或者一個類型發送一個消息(即一個方法調用)。
4、編譯器指令
以‘@’字符開頭的單詞是編譯器指令,而不是執行代碼。
5、關鍵字
id 保存指向對象的指針的類型( id類型意味著變量myObject可以是任意類型的對象。所以,當你編譯這段代碼時,它的實際類型以及它所實現的方法編譯器是不知道的);在Objective-C中,所有的對象變量都是指針類型。id類型已經被預定義為指針類型,所以不需要加一個星號。
nil是一個定義的常量,指向沒有對象的指針。
nil消息表示沒有操作:它們不做任何事情,並且繼續執行下一行代碼。
在Objective-C中,nil對象的作用等同於很多其他語言的NULL指針;不同的地方在於,在nil上調用方法不會導致程序崩潰或拋出異常。我們不用在調用一個對象的方法之前檢查該對象是否為空。如果你調用了一個nil對象的方法並且該方法有返回值的話,你會得到一個nil返回值。
BOOL,YES,NO 布爾型
SEL selector的縮寫,選擇器,保存一個Objective-C方法名表示的一種類型。
IMP 一個typedef,用於‘一個指針,它指向接收參數id、sel以及可能的其它參數並且返回id的函數’
Class 保存一個類的引用
6、Cocoa數字類型
NSInteger
NSUInteger
CGFloat
NSLog
7、開發文件
.l UNIX man頁面的文件
.m 程序源文件
.pch 預編譯的頭文件
.h 程序頭文件
8、消息轉發
NSInvocation是封裝為對象的一個Objective-C消息表達式,它定義了設置和獲取接受者、選擇器以及封裝的消息表達式的參數方法以及獲取返回值的方法。
9、內省
運行時支持在運行時發現對象的各種屬性,這個過程叫內省。
10、@class執行
一個類類型
11、copy
NSObject的copy實現對象的拷貝。
淺復制、深賦值、可變復制(NSArray、NSDictionary、NSSet)、不可變賦值(NSNumber、NSColor)。
12、內存管理
在堆上分配內存時,不再需要就釋放。
malloc -》free
13、消息細節
1、嵌套
任何消息參數都可以用返回相應類型的一個消息表達式來替換。
消息傳遞的關鍵是,編譯器構建每個類和對象時所采用的數據結構。
每個類都包含以下兩個必要元素:
- 一個指向父類的指針。
- 一個調度表(dispatch table)。
該調度表將類的selector與方法的實際內存地址關聯起來。
每個對象都有一個指向所屬類的指針isa。通過該指針,對象可以找到它所屬的類,也就找到了其全部父類。
2、向nil發送消息
向一個nil接收者發送一條消息沒有任何效果
3、向self發送消息
一個對象的方法調用同一對象的另一個方法,必須使用變量self作為消息接受者。
4、覆蓋並向super發送消息
類可以通過在其@implementation部分提供一個不同的實現,來覆蓋在其超類中定義的方法。
14、選擇器
編譯器指令 @selector() 將一個方法名稱轉換為一個SEL
一條消息的方法名部分有時候叫做選擇器或方法選擇器,因為,運行時使用它來選擇要執行哪一個接收者方法。
15、效率
編譯器將你的方法轉換為C函數的時候,它在參數列表的前面添加了self和_cmd參數,通過其IMP調用函數時,要填充這些參數。直接函數調用快數倍。
16、類類型
類型為class的變量,用作指向類對象的指針。通過使用類名作作為接收者來調用類方法從而獲得指向類對象的一個指針。
17、單體
一個只有單個共享的實例的類。單體類通常用來包裝操作系統服務,或者用於檢測面板這樣的UI項。
18、框架
框架是包的一種類型,動態的載入共享的資源。總稱存在frameworkName.framework的形式。
1、Objective-C或C框架
2、Cocoa框架
apple用來表示用Objective-C技術編寫的mac程序的名稱。它是一個傘式框架,包含3個主要框架(Foundation、Appkit、Core Data);
3、AppKit框架
構建GUI應用程序所需要的類
4、Core Foundation
一個低級層的C語言框架,對象有一個引用計數內存管理系統,對象擁有相同的內存布局則可自由轉換。
5、Core Graphics
用於Quartz 2D圖形的低層級API。
6、Core Animation
動畫框架
7、WebKit
8、ImageIO
9、Core Image
10、Core Audio
11、OpenGL
12、OpenAL
19、可變類和不可變類
基本的類是不可變的,但用於字符或用於字節的有可變與不可變之分。
20、類簇
將復雜性隱藏到一個簡單的接口的後面的一種方式。公有可見的類是一個抽象類。
類:NSString、NSArray、NSDictionary、NSSet、NSNumber和NSData都是類簇。
21、NSString、NSMutableString
字符的字節的一個數組,後面跟著一個NULL字節。
如果我們在NSString對象上調用NSString類型對象不支持的方法,編譯器就會發出警告。
C字符串可以和NSString互換
NSMutableString是可變的子類
22、集合類
NSArray、NSDictionary、NSSet以及其可變的子類,都是集合類。
23、NSArray
C數組,無邊界檢查
24、NSDictionary
提供一種方式來處理鍵值對集合
25、NSSet
對象集合實現一個數學集合
26、NSNumber
存儲並返回你喜歡的所有數字類型
27、NSNull
沒有對象
28、NSData
字節數據、處理文件或圖片等。
29、NSURL
url用來指定文件資源和網絡資源的方向前進。
30、隱式循環
使用NSArray的makeObjectsPerformSelectior創建隱式循環
31、NSEnumerator
接收集合中的對象,並將其傳遞出來,每次一個,並且使用nextObject方法,發送完畢返回nil。
32、異常
阻止程序繼續執行的不正常條件。使用編譯器指令@try、@catch、@finally處理
33、分類
分類允許我們向一個已有的類添加方法,而不用子類化它,並且不必訪問類的源代碼。
34、擴展
擴展允許你通過在類的聲明文件中添加一個接口部分,從而在公有視野之外聲明方法。提供一個內部的setter方法來訪問你希望公有且只讀的實例變量。
35、@property語句
@property(attributes)type name;
attributes描述了如何編寫訪問器
#import
@interface Photo : NSObject {
NSString* caption;
NSString* photographer;
}
@property (retain) NSString* caption;
@property (retain) NSString* photographer;
@end
Category類別
#import
@interface NSString (Utilities)
- (BOOL) isURL;
@end
36、assign、retain、copy
這些屬性影響到合成的setter如何構建。
37、readwrite和readonly
讀寫、只讀
38、nonatomic
沒有聲明為nonatomic的特性默認都是atomic的。如果指定nonatomic,編譯器則合成訪問器而不考慮線程安全性。
? readwrite(默認)或者 readonly:設置屬性是可讀寫的(擁有 getter/setter)或是只讀的(只有 getter);
? assign(默認),retain 或 copy:設置屬性的存儲方式;
? nonatomic:不生成線程安全的代碼,默認是生成的(沒有 atomic 關鍵字);
? getter=…,setter=…:改變訪問器默認的名字
39、協議
協議是一組預定義的方法,一個類可以選擇來實現它們。
protocol-協議,就是使用了這個協議後就要按照這個協議來辦事,協議要求實現的方法就一定要實現。
delegate-委托,顧名思義就是委托別人辦事,就是當一件事情發生後,自己不處理,讓別人來處理。
當一個A view 裡面包含了B view b view需要修改a view界面,那麼這個時候就需要用到委托了。
需要幾個步驟
1。首先定一個協議
2。a view實現協議中的方法
3。b view設置一個委托變量
4。把b view的委托變量設置成a view,意思就是 b view委托a view辦事情。
5。事件發生後,用委托變量調用a view中的協議方法
有很多時候是需要在B類口中來完成一些對A類的操作, 這時就需要A設個代理讓B來完成了,這個在應用中很常見也很實用。
關於正式協議:
. @protocol protocolName
. @optional -(void)delegateMethodA
. @required -(void)delegateMethodB
. // other methods . ...
. @end
正式協議類似於java的借口或抽象類。
@optional 的方法,可實現也可不實現,但@required 的方法必需實現。默認為@required。
委托代理就是在在一個對象a內部設置一個 id類型的實例變量,然後將另外一個對象b賦值給a的這個實例變量,這樣就可用通過操作實例變量a的這個id變量來調用b對象的方法。
采用一個協議的類,必須實現協議的必需方法,可以自由的實現或不實現協議的任何可選的方法。
其中@required指令聲明的方法是必需的。
40、TablePrinter
為其它對象提供一個表格打印任務服務
TablePrinterDataSource 協議的任何對性愛那個打印出一個表格
41、引用計數
內存管理提供了兩種替代系統:引用計數和自動垃圾收集。
原理:每個對象存儲了一個計數,它是使用該對象的其它對象的數目。
1,alloc, allocWithZone,new(帶初始化)
為對象分配內存,retainCount為“1”,並返回此實例
2,release
retainCount 減“1”,減到“0”時調用此對象的dealloc方法
3,retain
retainCount 加“1”
4,copy,mutableCopy
復制一個實例,retainCount數為“1”,返回此實例。所得到的對象是與其它上下文無關的,獨立的對象(干淨對象)。
5,autorelease
在當前上下文的AutoreleasePool棧頂的autoreleasePool實例添加此對象,由於它的引入使Objective-C(非GC管理環境)由全手動內存管
理上升到半自動化。AutoreleasePool使Objective-C成為內存管理半自動化語言。
在設置器裡面使用autorelease方法會更加安全一些,因為要改變的變量的新舊兩個值可能指向的是同一個對象。而你可能不希望立刻釋放實際上你要保留的對象。
42、所有權
你創建了一個對象,你擁有它。
通過向其他人所創建的對象發送一條retain消息,你擁有它。
當你使用完時,必須發送release消息來釋放所有權。
當不再使用時,必須銷毀它。
43、垃圾收集
通過一個運行程序來找到不再使用的對象並銷毀他們,並將他們的字節返給堆。)。
. 釋放一個對象的引用實際上有兩種方法:release 和 autorelease。標准的release會立刻釋放對象的引用。autorelease會等一會兒才釋放,但是引用實際上會一直存在,直到當前方法結束(除非你添加自定義的代碼來明確的改變它)。
dealloc方法在一個對象從內存中刪除時被調用。通常在這個方法裡面釋放所有對象裡的實例變量。
分配的(alloc)對象,或者是保留(retain)在一些地方的對象,都需要給他們發送一個release消息。
44、強引用和弱引用
在引用計數之下,一個對象通過保留另一個對象,形成對其的強引用,這確保了被保留的對象保持活的狀態,直到保留對象
使用完它並釋放它。相反,一個對象通過保留另一個對象的指針而不保留對象本身,形成對另一個對象的弱引用。
45、XCode、Objective-C、Cocoa
XCode:你可以把它看成是一個開發環境,就好像Visual Studio或者Netbeans或者SharpDevelop一樣的玩意。你可以將Interface Builder認為是Visual Studio中用來畫界面的那部分功能單獨提出來的程序。
Objective-C:這是一種語言,就好像c++是一種語言,Java是一種語言,c#是一種語言,莺歌歷史也是一種語言一樣。
Cocoa:是一大堆函數庫,就好像MFC、.NET、Swing這類玩意,人家已經寫好了一堆現成的東西,你只要知道怎麼用就可以了。
46、#import、@interface
#import
你可以把它認為是#include,一樣的。但是最好用#import,記住這個就行了。
#import指令會自動防止將同一個文件導入多次。
@interface 接口指令
類的接口(interface)通常存放在類似ClassName.h的文件中,定義實例變量(protected)和公用(public)方法。
類的實現存放在ClassName.m這樣的文件中,它包含了這些方法的實際實現代碼。它通常還定義了客戶類不能訪問的私有(private)實體變量、方法。
47、方法調用
調用對象的方法:
output = [object methodWithOutput];
output = [object methodWithInputAndOutput:input];
調用類的方法:(創建對象)
id myObject = [NSString string];
或
NSString* myString = [NSString string];
嵌套調用:
[NSString stringWithFormat:[prefs format]];
多輸入參數:
聲明:
-(BOOL)writeToFile:(NSString *)path atomically:(BOOL)useAuxiliaryFile;
調用:
BOOL result = [myData writeToFile:@"/tmp/log.txt" atomically:NO];
48、訪問器
點語法只能使用在設置器(setter)和獲取器(getter)上,而不能用於普通方法。
setter
[photo setCation:@”Day at the Beach”];
output = [photo caption];
點操作符
photo.caption = @”Day at the Beach”;
output = photo.caption;
只有當訪問器不存在的時候,@synthesize才會自動生成訪問器,所以,即使是使用@synthesize聲明了一個屬性,你仍然可以實現自定義的getter和setter。編譯器只會自動生成你沒有自定義的方法。
49、創建對象
創建自動釋放的對象
NSString* myString = [NSString string];
手工alloc創建
NSString* myString = [[NSString alloc] init];
注:[NSString alloc] 是NSString類本身的alloc方法調用。這是一個相對低層的調用,它的作用是分配內存及實例化一個對象。
[[NSString alloc] init] 調用新創建對象的init方法。init方法通常做對象的初始化設置工作,比如創建實例變量。
50、內存管理
//string1 將被自動釋放
NSString* string1 = [NSString string];
//必須在用完後手工釋放
NSString* string2 = [[NSString alloc] init];
[string2 release];
51、alloc
alloc方法調用,作用是分配內存及實例化一個對象。調用新創建對象的init方法,通常做對象的初始化設置工作,比如創建實例變量。
如果你通過手工alloc的方式創建一個對象,之後你需要release這個對象。同樣,你也不能手工釋放(release)一個能自動釋放(autoreleased)的對象,因為這將會使你的應用程序崩潰。
52、IBOutlet、IBAction
如果你希望在Interface Builder中能看到這個控件對象,那麼在定義的時候前面加上IBOutlet,在IB裡就能看到這個對象的outlet,如果你希望在Interface Builder裡控制某個對象執行某些動作,就在方法前面加上(IBAction)。
相關面試:
1、obj-c有多重繼承麼?不是的話有什麼替代方法?
cocoa中所有的類都是NSObject的子類 多繼承在這裡是用protocol委托代理來實現的 你不用去考慮繁瑣的多繼承,虛基類的概念. ood的多態特性 在obj-c中通過委托來實現.
2、obj-c有私有方法麼?私有變量呢
objective-c -類裡面的方法只有兩種, 靜態方法和實例方法. 這似乎就不是完整的面向對象了,按照OO的原則就是一個對象只暴露有用的東西.
如果沒有了私有方法的話, 對於一些小范圍的代碼重用就不那麼順手了. 在類裡面聲名一個私有方法
@interface Controller :
NSObject { NSString *something; }
(void)thisIsAStaticMethod;
(void)thisIsAnInstanceMethod;
@end
@interface Controller
(private) - (void)thisIsAPrivateMethod;
@end
@private可以用來修飾私有變量 在Objective‐C中,所有實例變量默認都是私有的,所有實例方法默認都是公有的 。
3、c和obj-c如何混用
1、obj-c的編譯器處理後綴為m的文件時,可以識別obj-c和c的代碼,處理mm文件可以識別obj-c,c,c++代碼,但cpp文件必須只能用c/c++代碼,而且cpp文件include的頭文件中,也不能出現obj-c的代碼,因為cpp只是cpp 。
2、在mm文件中混用cpp直接使用即可,所以obj-c混cpp不是問題 。
3、在cpp中混用obj-c其實就是使用obj-c編寫的模塊是我們想要的。
4、目標-動作機制
目標是動作消息的接收者。一個控件,或者更為常見的是它的單元,以插座變量(參見"插座變量"部分) 的形式保有其動作消息的目標。 動作是控件發送給目標的消息,或者從目標的角度看,它是目標為了響應動作而實現的方法。 程序需要某些機制來進行事件和指令的翻譯。這個機制就是目標-動作機制。
5、#import和#include的區別,@class代表什麼?
@class一般用於頭文件中需要聲明該類的某個實例變量的時候用到,在m文件中還是需要使用#import而#import比起#include的好處就是不會引起重復包含。
6、談談Object-C的內存管理方式及過程?
1、當你使用new,alloc和copy方法創建一個對象時,該對象的保留計數器值為1.當你不再使用該對象時,你要負責向該對象發送一條release或autorelease消息.這樣,該對象將在使用壽命結束時被銷毀.
2、當你通過任何其他方法獲得一個對象時,則假設該對象的保留計數器值為1,而且已經被設置為自動釋放,你不需要執行任何操作來確保該對象被清理.如果你打算在一段時間內擁有該對象,則需要保留它並確保在操作完成時釋放它.
3、如果你保留了某個對象,你需要(最終)釋放或自動釋放該對象.必須保持retain方法和release方法的使用次數相等.
7、內存管理 Autorelease、retain、copy、assign的set方法和含義?
1、你初始化(alloc/init)的對象,你需要釋放(release)它。例如:
NSMutableArray aArray = [[NSArray alloc] init]; 後,需要 [aArray release];
2、你retain或copy的,你需要釋放它。例如:
[aArray retain] 後,需要 [aArray release];
3、被傳遞(assign)的對象,你需要斟酌的retain和release。例如:
obj2 = [[obj1 someMethod] autorelease];
對象2接收對象1的一個自動釋放的值,或傳遞一個基本數據類型(NSInteger,NSString)時:你或希望將對象2進行retain,以防止它在被使用之前就被自動釋放掉。但是在retain後,一定要在適當的時候進行釋放。
關於索引計數(Reference Counting)的問題
retain值 = 索引計數(Reference Counting)
NSArray對象會retain(retain值加一)任何數組中的對象。當NSArray被卸載(dealloc)的時候,所有數組中的對象會 被 執行一次釋放(retain值減一)。不僅僅是NSArray,任何收集類(Collection Classes)都執行類似操作。例如 NSDictionary,甚至UINavigationController。Alloc/init建立的對象,索引計數為1。無需將其再次retain。
[NSArray array]和[NSDate date]等“方法”建立一個索引計數為1的對象,但是也是一個自動釋放對象。所以是本地臨時對象,那麼無所謂了。如果是打算在全Class中使用的變量(iVar),則必須retain它。缺省的類方法返回值都被執行了“自動釋放”方法。(*如上中的NSArray),在類中的卸載方法“dealloc”中,release所有未被平衡的NS對象。(*所有未被autorelease,而retain值為1的)
8、淺拷貝和深拷貝區別是什麼
簡單的來說就是,在有指針的情況下,淺拷貝只是增加了一個指針指向已經存在的內存,而深拷貝就是增加一個指針並且申請一個新的內存,使這個增加的指針指向這個新的內存,采用深拷貝的情況下,釋放內存的時候就不會出現在淺拷貝時重復釋放同一內存的錯誤。
9、C和obj-c 如何混用
1、obj-c的編譯器處理後綴為m的文件時,可以識別obj-c和c的代碼,處理mm文件可以識別obj-c,c,c++代碼,但cpp文件必須只能用c/c++代碼,而且cpp文件include的頭文件中,也不能出現obj-c的代碼,因為cpp只是cpp。
2、在mm文件中混用cpp直接使用即可,所以obj-c混cpp不是問題。
3、在cpp中混用obj-c其實就是使用obj-c編寫的模塊是我們想要的。
如果模塊以類實現,那麼要按照cpp class的標准寫類的定義,頭文件中不能出現obj-c的東西,包括#import cocoa的。實現文件中,即類的實現代碼中可以使用obj-c的東西,可以import,只是後綴是mm。
如果模塊以函數實現,那麼頭文件要按c的格式聲明函數,實現文件中,c++函數內部可以用obj-c,但後綴還是mm或m。
總結:只要cpp文件和cpp include的文件中不包含obj-c的東西就可以用了,cpp混用obj-c的關鍵是使用接口,而不能直接使用 實現代 碼,實際上cpp混用的是obj-c編譯後的o文件,這個東西其實是無差別的,所以可以用。obj-c的編譯器支持cpp
10、Objective-C中類別和類擴展的區別
category和extensions的不同在於後者可以添加屬性。另外後者添加的方法是必須要實現的。
extensions可以認為是一個私有的Category。
11、我們說的Objective-C是動態運行時語言是什麼意思?
多態。 主要是將數據類型的確定由編譯時,推遲到了運行時。
這個問題其實淺涉及到兩個概念,運行時和多態。
簡單來說,運行時機制使我們直到運行時才去決定一個對象的類別,以及調用該類別對象指定方法。
多態:不同對象以自己的方式響應相同的消息的能力叫做多態。意思就是假設生物類(life)都用有一個相同的方法-eat;
那人類屬於生物,豬也屬於生物,都繼承了life後,實現各自的eat,但是調用是我們只需調用各自的eat方法。
也就是不同的對象以自己的方式響應了相同的消息(響應了eat這個選擇器)。
因此也可以說,運行時機制是多態的基礎?
12、Objective-C堆和棧的區別?
管理方式:對於棧來講,是由編譯器自動管理,無需我們手工控制;對於堆來說,釋放工作由程序員控制,容易產生memory leak。
申請大小:
棧:在Windows下,棧是向低地址擴展的數據結構,是一塊連續的內存的區域。這句話的意思是棧頂的地址和棧的最大容量是系統預先規定好的,在 WINDOWS下,棧的大小是2M(也有的說是1M,總之是一個編譯時就確定的常數),如果申請的空間超過棧的剩余空間時,將提示overflow。因 此,能從棧獲得的空間較小。
堆:堆是向高地址擴展的數據結構,是不連續的內存區域。這是由於系統是用鏈表來存儲的空閒內存地址的,自然是不連續的,而鏈表的遍歷方向是由低地址向高地址。堆的大小受限於計算機系統中有效的虛擬內存。由此可見,堆獲得的空間比較靈活,也比較大。
碎片問題:對於堆來講,頻繁的new/delete勢必會造成內存空間的不連續,從而造成大量的碎片,使程序效率降低。對於棧來講,則不會存在這個問題,因為棧是先進後出的隊列,他們是如此的一一對應,以至於永遠都不可能有一個內存塊從棧中間彈出
分配方式:堆都是動態分配的,沒有靜態分配的堆。棧有2種分配方式:靜態分配和動態分配。靜態分配是編譯器完成的,比如局部變量的分配。動態分配由alloca函數進行分配,但是棧的動態分配和堆是不同的,他的動態分配是由編譯器進行釋放,無需我們手工實現。
分配效率:棧是機器系統提供的數據結構,計算機會在底層對棧提供支持:分配專門的寄存器存放棧的地址,壓棧出棧都有專門的指令執行,這就決定了棧的效率比較高。堆則是C/C++函數庫提供的,它的機制是很復雜的。