iOS應(yīng)用開發(fā)之Objective-C學(xué)習(xí)點(diǎn)滴
iOS應(yīng)用開發(fā)之Objective-C學(xué)習(xí)點(diǎn)滴是本文要介紹的內(nèi)容,Objective-C 是編寫 Mac 軟件的主要語言,如果你適應(yīng)基本的面向?qū)ο蠛?a >C語言,會給向你展示許多這些內(nèi)容。如果你不知到C,你應(yīng)當(dāng)先閱讀 C 指南[英文]。
這個指南由Scott Stevenson撰寫并排版。
1. Calling Methods
為了盡快開始,讓我們先來看一些簡單的例子。調(diào)用某個對象的方法的一些基本語法如下:
- [object method];
- [object methodWithInput:input];
方法可以返回一個值:
- output = [object methodWithOutput];
- output = [object methodWithInputAndOutput:input];
同樣可以調(diào)用類的方法,用于創(chuàng)建對象。在下面的例子中,調(diào)用了類NSString的string方法,返回了一個新的NSString對象:
- id myObject = [NSString string];
類型id表示myObject變量可以是任何類型的對象的引用,所以在編譯的時候,它并不知道實(shí)際實(shí)現(xiàn)的類和方法。
在這個例子中,顯然對象類型是NSString,所以可以改變類型為:
- NSString* myString = [NSString string];
現(xiàn)在這是一個NSString變量,這樣編譯器會在常識調(diào)用NSString不支持的方法的時候進(jìn)行警告。
留意在對象類型的右邊有一個星號。所有的Objective-C對象變量都是指針類型。id類型被預(yù)定義為指針類型,所以不需要添加星號。
嵌套消息
在許多語言中,嵌套的方法或函數(shù)調(diào)用像下面的形式:
- function1 ( function2() );
函數(shù)2的結(jié)果作為函數(shù)1的輸入。在Objective-C中,嵌套消息是如下的形式:
- [NSString stringWithFormat:[prefs format]];
避免在移行中嵌套超過兩次的調(diào)用,這樣代碼可以更加易懂。
多輸入方法
一些方法接受多輸入值。在Objective-C中,一個方法名可以被分為許多段。首先,多輸入方法是如下形式:
- -(BOOL)writeToFile:(NSString *)path atomically:(BOOL)useAuxiliaryFile;
你可以這樣調(diào)用這個方法:
- BOOL result = [myData writeToFile:@"/tmp/log.txt" atomically:NO];
這不僅僅是命名參數(shù)。在運(yùn)行時的系統(tǒng)中,方法名實(shí)際上是writeToFile:atomically:。
2. 存取器
在Objective-C中,默認(rèn)所有的實(shí)例變量都是私有的,所以在多數(shù)情況下,應(yīng)當(dāng)使用存取器來獲取或者設(shè)置值。有兩種語法。這是傳統(tǒng)的1.x語法:
- [photo setCaption:@"Day at the Beach"];
- output = [photo caption];
代碼的第二段不是直接訪問實(shí)例變量。它實(shí)際上調(diào)用了叫做caption的方法。在多數(shù)情況下,在Objective-C中,你不用向getter添加“get”前綴。
當(dāng)你看到在方括號中的代碼,就表示向?qū)ο蠡蝾惏l(fā)送消息。
點(diǎn)語法
在Mac OS X 10.5帶的Objective-C 2.0中新增了點(diǎn)語法支持getter和setter:
- photo.caption = @"Day at the Beach";
- output = photo.caption;
可以在兩種語法中任選一種使用,但是在每個工程中只能使用一種。點(diǎn)語法只能用在setter和getter,不能用在其他目的的方法上。
3. 創(chuàng)建對象
主要有兩種方式創(chuàng)建對象。下面是***種:
- NSString* myString = [NSString string];
這是更加方便的自動創(chuàng)建的形式。在這個例子中,創(chuàng)建了一個自動釋放的對象,一會我們會了解更多的細(xì)節(jié)。在許多情況下,你需要用手工模式創(chuàng)建對象:
- NSString* myString = [[NSString alloc] init];
這是嵌套方法調(diào)用。首先NSString本身調(diào)用alloc方法。這是相對低等級的調(diào)用,用于分配內(nèi)存并實(shí)例化對象。
第二個部分是調(diào)用新對象的init。init通常實(shí)現(xiàn)一些基本的設(shè)置,例如創(chuàng)建實(shí)例變量。對于使用類的用戶來說實(shí)現(xiàn)細(xì)節(jié)是不知道的。
在一些情況下,可以使用帶有輸入的不同的init版本:
- NSNumber* value = [[NSNumber alloc] initWithFloat:1.0];
4. 基本內(nèi)存管理
當(dāng)編寫Mac OS X程序時,可以選擇開啟垃圾收集。通常,這意味著在遇到相當(dāng)復(fù)雜的情況之前,無需關(guān)注內(nèi)存管理。
然而,不可能總是工作在支持垃圾收集的環(huán)境。在這種情況下,需要了解一些關(guān)鍵點(diǎn)。
如果使用手工的alloc形式創(chuàng)建對象,需要在使用后釋放對象。不應(yīng)當(dāng)手動釋放任何自動釋放對象,如果這么做的話應(yīng)用會異常退出。
這有兩個例子:
- // string1 將會被自動釋放
- NSString* string1 = [NSString string];
- // 使用完后必須釋放must release this when done
- NSString* string2 = [[NSString alloc] init];
- [string2 release];
在這個指南中,可以認(rèn)為自動對象在當(dāng)前函數(shù)結(jié)束后總是會被釋放。
關(guān)于內(nèi)存管理還有許多需要學(xué)習(xí),不過需要等到我們了解更多的一些其他要點(diǎn)之后。
#p#
5. 設(shè)計(jì)類接口
Objective-C創(chuàng)建類的語法非常簡單。通常有兩部分。
類接口通常保存在文件ClassName.h,用于定義實(shí)例變量和公共方法。
實(shí)現(xiàn)在文件ClassName.m中,包含其方法的實(shí)際代碼。通常也定義類的用戶不可用的私有方法。
下面展示了接口文件的樣子。類被稱作Photo,因此文件被命名為Photo.h:
- #import <Cocoa/Cocoa.h>
- @interface Photo : NSObject
- {
- NSString* caption;
- NSString* photographer;
- }
- @end
首先,引入了Cocoa.h,用于引入Cocoa應(yīng)用所需的所有基本類。#import指令自動處理在一個文件中多次引用的問題。
@interface表示這是類Photo的定義。冒號后定義了父類,這里是NSObject。
在花括號中,有兩個實(shí)例變量:caption和photographer。這里都是NSString,但其實(shí)變量可以是任何類型,包括id。
***的@end標(biāo)記結(jié)束了類的定義。
添加方法
為實(shí)例變量添加getter和setter:
- #import <Cocoa/Cocoa.h>
- @interface Photo : NSObject
- {
- NSString* caption;
- NSString* photographer;
- }
- - caption;
- - photographer;
- @end
記住,Objective-C方法通常不要“get”前綴。在方法名前的橫線表示這是一個實(shí)例方法。在方法名前的加號表示這是一個類方法。
默認(rèn),編譯器假設(shè)方法返回id對象,并且所有輸入值都為id。上面的代碼技術(shù)上是正確的,但是通常不用。給返回值指定類型:
- #import <Cocoa/Cocoa.h>
- @interface Photo : NSObject
- {
- NSString* caption;
- NSString* photographer;
- }
- - (NSString*) caption;
- - (NSString*) photographer;
- @end
現(xiàn)在添加setter:
- #import <Cocoa/Cocoa.h>
- @interface Photo : NSObject
- {
- NSString* caption;
- NSString* photographer;
- }
- - (NSString*) caption;
- - (NSString*) photographer;
- - (void) setCaption: (NSString*)input;
- - (void) setPhotographer: (NSString*)input;
- @end
Setter不需要返回值,所以設(shè)置為void。
6、類實(shí)現(xiàn)
從getter開始創(chuàng)建實(shí)現(xiàn):
- #import "Photo.h"
- @implementation Photo
- - (NSString*) caption
- {
- return caption;
- }
- - (NSString*) photographer
- {
- return photographer;
- }
- @end
這部分的代碼由@implementation和類名開始,并且像接口一樣有@end。所有的方法必須放在這兩個聲明之間。
如果之前做過編碼,getter看起來應(yīng)該非常熟悉,所以將精力放在setter上,這需要一些解釋:
- - (void) setCaption: (NSString*)input
- {
- ;
- caption = [input retain];
- }
- - (void) setPhotographer: (NSString*)input
- {
- [photographer autorelease];
- photographer = [input retain];
- }
每個setter有兩個變量。***個是已有對象的引用,第二個是新輸入的對象。在垃圾回收環(huán)境中,可以直接設(shè)置新值:
- - (void) setCaption: (NSString*)input
- {
- caption = input;
- }
但是如果不能使用垃圾回收,就需要釋放(release)舊對象,并保持(retain)新對象。
實(shí)際上有兩種方式釋放對象的引用:釋放(release)和自動釋放(autorelease)。標(biāo)準(zhǔn)的釋放將會立即移除引用。自動釋放方法將會在一小會后釋放,但可以明確的是它會保留到當(dāng)前函數(shù)結(jié)束(除非添加自定義的代碼明示改變這個規(guī)則)。
自動釋放方法在setter中更加安全一些,因?yàn)樽兞康男屡f值會指向相同的對象。肯定不想立刻釋放需要保持的對象。
現(xiàn)在似乎有一些混亂,但是將會按照進(jìn)度有一個整體的介紹。現(xiàn)在無需弄清楚所有內(nèi)容。
Init
可以創(chuàng)建一個init方法用于初始化實(shí)例變量:
- - (id) init
- {
- if ( self = [super init] )
- {
- [self setCaption:@"Default Caption"];
- [self setPhotographer:@"Default Photographer"];
- }
- return self;
- }
這段代碼自己已經(jīng)解釋了很多問題,除了第二行看起來不同尋常以外。這里有一個等號,將[super init]的結(jié)果賦值給self。
這實(shí)質(zhì)上是告訴父類進(jìn)行其自己的初始化。那個if語句用于保證在嘗試設(shè)置變量默認(rèn)值之前驗(yàn)證初始化已經(jīng)成功。
Dealloc
dealloc方法在對象被從內(nèi)存中移除時調(diào)用。這通常是***的釋放所有子實(shí)例變量引用的時機(jī):
- - (void) dealloc
- {
- [photographer release];
- [super dealloc];
- }
在前兩行,發(fā)送了release到每個實(shí)例變量。不需要在這里使用autorelease,標(biāo)準(zhǔn)的release會更快一些。
***一行非常重要。必須發(fā)送[super dealloc]消息告訴父類進(jìn)行它自己的清理。如果不這樣做的話,對象不會被移除,從而導(dǎo)致內(nèi)存泄露。
在執(zhí)行垃圾收集激活的情況下,dealloc方法不會被調(diào)用。 需要實(shí)現(xiàn)finalize方法。
#p#
7、內(nèi)存管理進(jìn)階
Objective-C的內(nèi)存管理系統(tǒng)叫做引用計(jì)數(shù)。需要做的全部事情就是跟蹤引用,在運(yùn)行時進(jìn)行真正的內(nèi)存釋放。
在通常的情況下,alloc一個對象,可能在某個位置retain它,需要對每個alloc/retain消息發(fā)送對應(yīng)的release。 所以,如果使用alloc一次,然后retain一次,需要release兩次。
這個算法叫做引用計(jì)數(shù)。但是在實(shí)踐中,只有兩種情況需要創(chuàng)建一個對象:
1. 將其保存為實(shí)例變量
2. 在函數(shù)中臨時使用
在多數(shù)情況下,實(shí)例變量的setter應(yīng)當(dāng)自動釋放(autorelease)舊的對象,并且保持(retain)新的。同時只要確保dealloc中正確釋放即可。
所以真正需要做的,只是管理函數(shù)中的本地引用。并且只有一個規(guī)則:如果通過alloc或copy創(chuàng)建了一個對象,在函數(shù)的***向其發(fā)送release或autorelease消息。如果通過其他方式創(chuàng)建的對象,什么也不要做。
這里有***種情況的例子,管理實(shí)例變量:
- - (void) setTotalAmount: (NSNumber*)input
- {
- [totalAmount autorelease];
- totalAmount = [input retain];
- }
- - (void) dealloc
- {
- [totalAmount release];
- [super dealloc];
- }
下面是另一種情況,本地引用。只需要釋放通過alloc創(chuàng)建的對象:
- NSNumber* value1 = [[NSNumber alloc] initWithFloat:8.75];
- NSNumber* value2 = [NSNumber numberWithFloat:14.78];
- // 只釋放value1,不操作value2
- [value1 release];
這里有一個集成:使用本地引用設(shè)置對象的實(shí)例變量:
- NSNumber* value1 = [[NSNumber alloc] initWithFloat:8.75];
- [self setTotal:value1];
- NSNumber* value2 = [NSNumber numberWithFloat:14.78];
- [self setTotal:value2];
- [value1 release];
注意這和管理本地引用的規(guī)則完全一致,不論是否將其設(shè)置為實(shí)例變量。也不用考慮setter是如何實(shí)現(xiàn)的。
如果明白了這個,就明白了90%需要知道的關(guān)于Objective-C內(nèi)存管理的內(nèi)容。
日志
在Objective-C中向控制臺記錄消息非常簡單。實(shí)際上,NSLog()函數(shù)相當(dāng)接近C的printf()函數(shù),除了額外增加的用于對象的%@標(biāo)記。
NSLog ( @”The current date and time is: %@”, [NSDate date] );
可以將一個對象作為日志記錄到控制臺。NSLog函數(shù)調(diào)用對象的description方法,然后打印其返回的NNString。可以在類中重寫description方法返回自定義字符串。
9、屬性
之前編寫caption和author訪問器方法的時候,可能已經(jīng)留意到那些代碼很直接,并且很普通。
屬性是Objective-C的特性之一,用于自動創(chuàng)建通用的訪問器,同時還有一些其他的功能。現(xiàn)在來修改Photo類使用屬性。
這是修改之前的樣子:
- #import <Cocoa/Cocoa.h>
- @interface Photo : NSObject
- {
- NSString* caption;
- NSString* photographer;
- }
- - (NSString*) caption;
- - (NSString*) photographer;
- - (void) setCaption: (NSString*)input;
- - (void) setPhotographer: (NSString*)input;
- @end
這里是轉(zhuǎn)換為屬性寫法的樣子:
- #import <Cocoa/Cocoa.h>
- @interface Photo : NSObject
- {
- NSString* caption;
- NSString* photographer;
- }
- @property (retain) NSString* caption;
- @property (retain) NSString* photographer;
- @end
@property是Objective-C用于定義屬性的指令。圓括號中的“retain”表示setter應(yīng)當(dāng)保持(retain)輸入的值,剩下的部分只是指定屬性的類型和名稱。
現(xiàn)在看看類的實(shí)現(xiàn):
- #import "Photo.h"
- @implementation Photo
- @synthesize caption;
- @synthesize photographer;
- - (void) dealloc
- {
- ;
- [photographer release];
- [super dealloc];
- }
- @end
@synthesize指令自動生成了setter和getter,所以這個類僅剩需要實(shí)現(xiàn)的是dealloc方法。
訪問器僅在其不存在時自動創(chuàng)建,所以對你的屬性隨意使用@synthesize,如果需要可以另外實(shí)現(xiàn)自定義的getter和setter。編譯器將自動填充不完整的方法。
屬性定義還有許多其他選項(xiàng),不過那些已經(jīng)超出了本指南的范圍。
10、在Nil上調(diào)用方法
在Objective-C中,nil對象等同于其他許多語言的NULL指針。不同之處在于你可以在nil上調(diào)用方法而不會引起崩潰或異常。
這個技術(shù)通過許多不同的方式使用在框架中,但是現(xiàn)在這主要意味著不需要在調(diào)用對象方法前檢查對象是否為nil。如果調(diào)用了一個nil對象的方法返回一個對象,返回值將會是nil。
可以用這個來改進(jìn)dealloc方法:
- - (void) dealloc
- {
- self.caption = nil;
- self.photographer = nil;
- [super dealloc];
- }
由于將實(shí)例變量設(shè)置為nil,setter僅僅保存了nil(這個什么也不做)并且釋放了原有的值,所以這個能正常工作。這個寫法的dealloc通常更好,因?yàn)檫@避免了變量指向一個隨機(jī)的數(shù)據(jù),而這個數(shù)據(jù)是變量之前存儲的位置。
注意這里使用了self.語法,這意味著使用了setter和內(nèi)存管理。如果像這樣直接設(shè)置值,就會有內(nèi)存泄露產(chǎn)生:
- // 錯誤,引起內(nèi)存泄露。
- // 通過self.caption使用setter
- caption = nil;
分類
分類是Objective-C的又一常用功能。本質(zhì)上說,分類允許在不繼承或不了解類的任何實(shí)現(xiàn)細(xì)節(jié)的情況下對已有的類添加方法。
因?yàn)榭梢韵騼?nèi)建對象添加方法,所以通常這是很有用的。如果希望在應(yīng)用中對所有的NSString實(shí)例添加一個方法,僅僅需要添加一個分類。不需要將所有工作都放到子類來完成。
例如,為NSString添加一個方法來判斷是否是合法的URL,可能類似這樣:
- #import <Cocoa/Cocoa.h>
- @interface NSString (Utilities)
- - (BOOL) isURL;
- @end
這非常接近類定義。不同之處在于沒有列出父類,同時在括號中有分類的名稱。名稱可以是期望的任何內(nèi)容,雖然它應(yīng)當(dāng)同內(nèi)部的方法進(jìn)行通信。
這里是實(shí)現(xiàn)。需要留意這不是一個很好的URL檢測的實(shí)現(xiàn)。這僅僅是為了簡介一下分類:
- #import "NSString-Utilities.h"
- @implementation NSString (Utilities)
- - (BOOL) isURL
- {
- if ( [self hasPrefix:@"http://"] )
- return YES;
- else
- return NO;
- }
- @end
現(xiàn)在可以在任何NSString上使用這個方法。接下來的代碼將會在控制臺打印”string1 is a URL”:
- NSString* string1 = @"http://pixar.com/";
- NSString* string2 = @"Pixar";
- if ( [string1 isURL] )
- NSLog (@"string1 is a URL");
- if ( [string2 isURL] )
- NSLog (@"string2 is a URL");
不象子類那樣,分類不能添加實(shí)例變量。然而,可以使用分類重寫類中已有的方法,但是這么做需要相當(dāng)小心。
記住,使用分類修改一個類時,應(yīng)用中的所有這個類的實(shí)例都會受到影響。
小結(jié):iOS應(yīng)用開發(fā)之Objective-C學(xué)習(xí)點(diǎn)滴的內(nèi)容介紹完了,這是Objective-C一個基本的概覽。如同已經(jīng)看到的,這個語言相當(dāng)簡單。沒有許多語法需要學(xué)習(xí),相似的約定一遍又一遍的在Cocoa中被使用。***希望本文對你有所幫助!