Objective-C入門教程

一:Objective-C入門

 

1、Cocoa的組成

 

蘋果公司將Cocoa、Carbon、QuickTime和OpenGL等技術作爲框架集提供

 

Cocoa組成部分有:

 

Foundation框架(有很多有用的,面向數據的低級類和數據結構)

 

Application Kit(也稱AppKit)框架(包含了所有的用戶接口對象和高級類,例如NS……)

 

,還有一個支持框架的套件,包括Core Animation和Core Image。

 

 

2、NSLog相當於printf()

 

NSLog(@"hello Objective-C");

 

//注:@是Objective-C在標準C語言基礎上添加的特徵之一,雙引號的字符串前面有一個@,這表示引用的字符串應該作爲Cocoa的NSString元素處理

 

 

 

NSLog(@"are %d and %d different? %@",5,5,boolString(areTheyDifferent));

 

//注意%@:使用NSLog輸出任何對象值時,都會使用這個格式說明

 

 

3、BOOL使用8位存儲,YES定義爲1,NO定義爲0,大於1不爲YES,跟標準C不同。

 

若不小心將一個長於1字節的整型值賦給BOOL,則只截取低八位

 

Obejective-C中1不等於1,絕對不要將BOOL值和YES比較

 

 

 

二:面向對象的Objective-C

 

4、使用間接從本地讀取文件的例子

 

#import <Foundation/Foundation.h>

 

 

int main(int argc,const char * argv[])

 

{

 

if(argc == 1){

 

NSLog(@"you need to provide a file name");

 

return (1);

 

}

 

FILE *wordFile = fopen(argv[1] , "r");

 

char word[100];

 

while (fgets(word,100,wordFile)){  

 

//fget調用會保留分開每一行的換行符,我們不需要,把它替換爲0,表示字符串的結束

 

word[strlen(word)-1] ='/0';

 

NSLog(@"%s is %d characters long",word,strlen(word));

 

}

 

 

//運行用 ./Word-Length-4 /tmp/words.txt

 

若給了文件路徑,那麼argc會大於1,然後我們可以查詢argv數組得到文件路徑。argv[1]保存着用戶提供的文件名,argv[0]保存着程序名。

 

在XCode中編譯此程序需要在XCode文件列表中展開Executables,雙擊程序名,在Arguments區域中添加啓動參數

 

 

5、id

 

id是一種泛型,用於表示任何類的對象,id實際上是一個指針,指向其中的某個結構

 

 

6、[]

 

例[shape draw]

 

第一項是對象名,其餘部分是要執行的操作

 

 

7、Objective-C的OOP範例

 

1)@interface部分(一般都作爲.h單獨書寫,聲明部分)

 

@interface Circle:NSObject  //說明這是爲Circle的新類定義的接口

 

{

 

ShapeColor fillColor;

 

ShapeRect bounds;

 

}  //括號內的是Circle對象需要的各種數據成員

 

 

- (void) setFilColor:(ShapeColor) fillColor;   //先行短線表明“這是新方法的聲明”如果是“+”則表示是類方法,也稱工廠方法

 

 

- (void) setBounds:(ShapeRect) bounds;

 

 

- (void) draw;

 

 

@end  //Circle

 

 

2)@implementation部分(一般寫爲.m文件,實現部分)

 

@implementation Circle  //@implementation是一個編譯器指令,表明你將爲某個類提供代碼

 

 

- (void) setFillColor:(ShapeColor) c  //在這裏如果繼續使用參數名fillColor,就會隱藏fillColor實例變量,並且有警告

 

//我們已經定義了一個名爲fillColor的實例變量,可以在該方法中引用該變量,如果使用相同的另一個變量,那麼前一個會屏蔽

 

{

 

fillColor = c;

 

}

 

 

- (void) setBounds:(ShapeRect) b  

 

{

 

bounds = b;

 

}

 

 

- (void) draw

 

{

 

NSLog("^^^")

 

}

 

 

@end //Circle

 

可以在@implementation中定義那些在@interface中無相應聲明的方法,可以把他們看做是石油方法,僅在類的實現中使用。

 

注:Objective-C不存在真正的私有方法,從而禁止其他代碼調用它。這是Objective-C動態本質的副作用。

 

 

8、中綴符(infix natation)

 

方法的名稱和及其參數都是合在一起的

 

例如

 

一個參數:

 

[citcle setFillColor : KRedColor];

 

兩個參數:

 

[circle setStringValue : @”hello there” color : KBlueColor];

 

 

9、繼承(X是一個Y,isa)

 

1)Objective-C不支持多繼承,我們可以通過Objective-C的其他特性獲取多繼承的優點,例如分類和協議

 

 

2)繼承中方法的定義

 

可以使用空正文和一個虛(dummy)值都是可以的

 

 

3)方法調度

 

當代碼發送消息時,Objective-C的方法調度將在當前分類中搜索相應的方法,如果找不到,則在該對象的超類中進行查找

 

 

4)實例變量

 

 

 

10、複合(X有一個Y,has)

 

嚴格的講,只有對象間的組合才叫做複合,諸如int、float、enum和struct等基本類型都認爲是對象的一部分

BerwinZheng

2010-2-13 18:58:52 

11、init

 

- (id) init

 

{

 

if (self = [super init]) { //將[super init]得結果賦給self是Objective-C的標準慣例,爲了防止超類的初始化過程中返回的對象不同於原先創建的對象

 

//若要超類要完成所需的一次性初始化,需要調用[super init],init方法返回的值描述了被初始化的對象

 

engine = [Engine new];

 

tires[0] = [Tire new];

 

tires[1] = [Tire new];

 

tires[2] = [Tire new];

 

tires[3] = [Tire new];

 

}

 

return (self);

 

} // init

 

 

12、存取方法(accessor method)

 

setter和getter

 

setter方法根據他所要更改的屬性的名稱來命名,並加上set

 

getter方法根據其返回的屬性的名稱來命名,不要加get

 

 

 

 

三:源文件組織

 

13、@class * 和import *.h

 

@class創建一個類前聲明,告訴編譯器:相信我,以後你會知道這個到底是什麼,但是現在,你只需要知道這些

 

繼承一個類的時候不能用@class,因爲他們不是通過指針指向其他類,所以繼承一個類時要用import *.h 

 

 

 

四:Xcode的使用

 

14、更改自動註釋中的公司名

 

終端中:

 

defaults write com.apple.apple.Xcode PBXCustomTemplateMacroDefinitions 

 

‘{“ORGANIZATIONNAME” = “iPhone_xiaoyuan.com”;}’

 

沒有任何輸出結果

 

 

15鍵盤符號

 

1)Mac按鍵符號

 

 

2)Microsoft鍵盤和Mac鍵盤的對照

 

Alt->

 

徽標鍵->Option

 

 

16、Xcode技巧

 

1)同步顯示

 

有時候兩個窗口中顯示的內容並不是同步的,只有分別單擊了它們,才能同步更新內容

 

2)首行縮進

 

選自,右鍵->Re-indent selection

 

Alt [ 和 Alt ]可以把選中的代碼左移和右移

 

3)代碼自動完成

 

Tab 鍵可以按頻率最高的填充完成詞

 

Esc 可以彈出提示列表(E表示枚舉,f代表函數,#代表@define,m表示方法,C表示類)

 

Ctl+. 在各選項中切換

 

Shift+Ctrl+. 反向循環

 

control+/ 在佔位符之間切換

 

4)批量編輯

 

快照:File->Make Snapshot

 

查看快照:File->Snapshot

 

一次改變文件中的相同字符:選定,Edit->Edit all in Scope,更改的時候都會變

 

重構:選定,Edit->Refactor,彈出對話框,輸入要改成的字符(選中Snapshot後可以看見改變)

 

5)鍵盤代替鼠標

 

■ control-F: Move forward, to the right (same as the right arrow).

 

■ control-B: Move backwards, to the left (same as the left arrow).

 

■ control-P: Move to the previous line (same as the up arrow).

 

■ control-N: Move to the next line (same as the down arrow).

 

■ control-A: Move to the beginning of a line (same as the as command- left arrow).

 

■ control-E: Move to the end of a line (same as the as command- right arrow).

 

■ control-T: Transpose (swap) the characters on either side of the cursor.

 

■ control-D: Delete the character to the right of the cursor.

 

■ control-K: Kill (delete) the rest of the line. This is handy if you want to redo the end of a line of code.

 

■ control-L: Center the insertion point in the window. This is great if you’ve lost your text cursor or want to quickly scroll the window so the insertion point is front and center.

 

6)任意搜索

 

在菜單欄上面搜索

 

7)快速打開

 

#import後的文件選中,File->Open Quickly,Xcode就會打開文件。若不選擇,則會打開Open Quickly對話框

 

8)打開文檔

 

Option+雙擊

 

9)調試時看數據

 

鼠標放在上面一會就可以看到

BerwinZheng

2010-2-13 18:59:15 

五:Foundation Kit

 

17、一些有用的數據結構 (結構體能減少過程中的開銷)

 

1)NSRange   //用來表示相關事物的範圍

 

typedef struct _NSRange {

 

unsigned int location;

 

unsigned int length;

 

} NSRange;

 

例如“Objective-C is a cool language”中,“cool”可以用location爲17,length爲4的範圍來表示

 

 

有3種方式可以創建新的NSRange

 

第一種:直接給字段賦值

 

NSRange range;

 

range.location = 17;

 

range.length = 4;

 

第二種:應用C語言的聚合結構賦值機制

 

NSRange range = { 17, 4 };

 

第三種:使用Cocoa提供的快捷函數NSMakeRange():

 

NSRange range = NSMakeRange(17,4);

 

//使用NSMakeRange()的好處是可以在任何使用函數的地方使用他

 

//例如 [anObject flarbulateWithRange: NSMakeRange (13, 15)];

 

 

2)幾何數據類型

 

typedef struct _NSPoint {

 

float x;

 

float y;

 

} NSPoint;

 

 

typedef struct _NSSize {

 

float width;

 

float height;

 

} NSSize;

 

 

typedef struct _NSRect {

 

NSPoint origin;

 

NSSize size;

 

} NSRect;

 

 

//Cocoa也爲我們提供了這些類型的快捷函數:NSMakePoint()、NSMakeSize()和NSMakeRect()

 

 

18、字符串(NSString和NSMutableString)

 

A:不可變的字符串(NSString)

 

1) 創建不可變的字符串

 

函數:+ (id) stringWithFormat: (NSString *) format, ...;

 

使用方法:

 

NSString *height;

 

height = [NSString stringWithFormat:@"Your height is %d feet, %dinches", 5, 11];

 

 

 

2)NSString類中的方法

 

① 大小

 

函數:- (unsigned int) length;

 

使用方法:

 

if ([height length] > 35) {

 

NSLog (@"wow, you're really tall!");

 

}

 

 

② 比較

 

函數1:- (BOOL) isEqualToString: (NSString *) aString;

 

使用方法:

 

NSString *thing1 = @"hello 5";

 

NSString *thing2;

 

thing2 = [NSString stringWithFormat: @"hello %d", 5];

 

if ([thing1 isEqualToString: thing2]) {

 

NSLog (@"They are the same!");

 

} //應用這個函數,不能用“==”,“==”只能比較字符串的指針值

 

 

函數2:- (NSComparisonResult) compare: (NSString *) string;

 

其中

 

typedef enum _NSComparisonResult {

 

NSOrderedAscending = -1,

 

NSOrderedSame,

 

NSOrderedDescending

 

} NSComparisonResult;

 

使用方法:

 

[@"aardvark" compare: @"zygote"]   return NSOrderedAscending:.

 

[@"zoinks" compare: @"jinkies"]    return NSOrderedDescending. And,

 

[@"fnord" compare: @"fnord"]       return NSOrderedSame.

 

 

不區分大小寫的比較

 

函數: - (NSComparisonResult) compare: (NSString *) string 

 

        options: (unsigned) mask;

 

options參數是一個位掩碼,可以用位或運算符(|)來添加這些選項標記

 

一些常用的標記有

 

■ NSCaseInsensitiveSearch: 不區分大小寫

 

■ NSLiteralSearch: 進行完全比較,區分大小寫

 

■ NSNumericSearch:比較字符串的字符個數,而不是字符值,若沒項,“100”會排在“99”前面(一定要加)

 

使用方法:

 

if ([thing1 compare: thing2

 

options: NSCaseInsensitiveSearch|NSNumericSearch]== NSOrderedSame) {

 

NSLog (@"They match!");

 

}

 

 

③ 包含字符串判斷

 

函數:

 

- (BOOL) hasPrefix: (NSString *) aString;  //判斷開頭

 

- (BOOL) hasSuffix: (NSString *) aString;  //判斷結尾

 

- (NSRange) rangeOfString: (NSString *) aString; //看字符串中是否包含其他字符串

 

 

使用方法:

 

NSString *filename = @"draft- chapter.pages";

 

if ([fileName hasPrefix: @"draft") {

 

// this is a draft

 

}

 

if ([fileName hasSuffix: @".mov") {

 

// this is a movie

 

}

 

NSRange range;

 

range = [fileName rangeOfString: @"chapter"];

 

//返回range.start爲6,range.length爲7,若傳遞的參數在接受字符串中沒有找到,那麼range.start則等於NSNotFound

 

 

B)可變字符串(NSMutableString)

 

1)創建可變的字符串

 

方式1:

 

函數:+ (id) stringWithCapacity: (unsigned) capacity; //這個容量只是給NSMutableString的一個建議

 

使用方法:

 

NSMutableString *string;

 

string = [NSMutableString stringWithCapacity: 42];

 

方法2:

 

繼承NSString中的方法

 

NSMutableString *string;

 

string = [NSMutableString stringWithFormat: @"jo%dy", 2];

 

 

2)NSMutableString中的方法

 

函數:

 

- (void) appendString: (NSString *) aString;

 

- (void) appendFormat: (NSString *) format, ...;

 

- (void) deleteCharactersInRange: (NSRange) range; //配合rangeOfString:一起連用

 

 

使用方法:

 

NSMutableString *string;

 

string = [NSMutableString stringWithCapacity: 50];

 

[string appendString: @"Hello there"];

 

[string appendFormat: @"human %d!",39];

 

NSMutableString *friends;

 

friends = [NSMutableString stringWithCapacity: 50];

 

[friends appendString: @"James BethLynn Jack Evan"];

 

NSRange jackRange;

 

jackRange = [friends rangeOfString: @"Jack"];

 

jackRange.length++;   // eat the space that follows

 

[friends deleteCharactersInRange: jackRange];

 

 

19、NSArray和NSMutableArray

 

A) NSArray(不可改變的數組,是一個Cocoa類,用來存儲對象的有序列表)

 

NSArray的兩個限制

 

首先:它只能存儲Objective-C的對象,而不能存儲C語言中的基本數據類型,如:int, float, enum, struct,或者是NSArray中的隨機指針

 

然後:不能存儲nil

 

 

 

1)創建方法

 

通過類的方法arrayWithObjects:創建一個新的NSArray

 

使用方法:

 

NSArray *array;

 

array = [NSArray arrayWithObjects:@"one", @"two", @"three",nil];  

 

//array是以nil結尾的,這是nil不能存儲的原因

 

2)常用方法

 

- (unsigned) count; //獲取數組包含的對象個數

 

- (id) objecAtIndex : (unsigned int) index ; //獲取特定索引處的對象

 

- componentsSeparatedByString://切分NSArray

 

- componentsJoinedByString://合併NSString

 

使用方法:

 

int i;

 

for (i = 0; i < [array count]; i++) {

 

NSLog (@"index %d has %@.",i, [array objectAtIndex: i]);

 

}

 

NSString *string = @"oop:ack:bork:greeble:ponies";

 

NSArray *chunks = [string componentsSeparatedByString: @":"];

 

string = [chunks componentsJoinedByString: @" :- ) "];

 

B)NSMutableArray(可變數組)

 

1)創建方法,通過類方法arrayWithCapacity創建

 

+ (id) arrayWithCapacity: (unsigned) numItems;

 

使用方法:

 

NSMutableArray *array;

 

array = [NSMutableArray arrayWithCapacity: 17];

 

2)常用方法

 

- (void) addObject: (id) anObject;

 

- (void) removeObjectAtIndex: (unsigned) index;

 

使用方法:

 

for (i = 0; i < 4; i++) {

 

Tire *tire = [Tire new];

 

[array addObject: tire];

 

}

 

[array removeObjectAtIndex: 1]; //刪除第二個

 

C)遍歷數組的三種方式:通過索引、使用NAEnumerator和快速枚舉

 

1)索引遍歷  //只有在真的需要索引訪問數組時才應使用-objectAtIndex,例如跳躍數組或者同時遍歷多個數組時

 

int i;

 

for (i = 0; i < [array count]; i++) {

 

NSLog (@"index %d has %@.",i, [array objectAtIndex: i]);

 

}

 

 

2)使用NSEnumerator  //Leopard中被快速枚舉替代

 

創建方法:通過函數 - (NSEnumerator *) objectEnumerator;

 

使用方法: 

 

NSEnumerator *enumerator;

 

enumerator = [array objectEnumerator];  //如果想從後往前瀏覽集合,還有一個方法reverseEnumerator可以使用

 

創建後通過while循環,條件是nextObject( 方法原型 - (id) nextObject );

 

循環遍歷的程序爲:

 

NSEnumerator *enumerator;

 

enumerator = [array objectEnumerator];

 

id thingie;

 

while(thingie = [enumerator nextObject ]) {

 

NSLog(@“i found %@” , thingie);

 

}

 

//注:對可變數組進行枚舉操作時,不能通過添加和刪除對象這類方式來改變數組容器,如果這樣做了,枚舉器會覺得困惑,爲你將會得到未定義結果

 

3)快速枚舉

 

在Leopard中才開始的,Tiger中不能用

 

for (NSString *string in array ) {

 

NSLog(@“i found %@” , string);

 

}

BerwinZheng

2010-2-13 19:35:01 

21、破除NSArray限制的方法

 

1)基本類型

 

a):Cocoa提供了NSNumber類來包裝基本類型

 

+ (NSNumber *) numberWithChar: (char) value;

 

+ (NSNumber *) numberWithInt: (int) value;

 

+ (NSNumber *) numberWithFloat: (float) value;

 

+ (NSNumber *) numberWithBool: (BOOL) value;

 

使用方法:

 

NSNumber *numner;

 

number = [NSNumber numberWithInt: 42];

 

[array addObject: number];

 

[dictionary setObject : number foyKey : @”Bork”];

 

只要將一些基本類型封裝到NSNumber中,就可以通過下面的實例方法重新獲得其值

 

- (char) charValue;

 

- (int) intValue;

 

- (float) floatValue;

 

- (BOOL) boolValue;

 

- (NSString *) stringValue; //允許自動轉換

 

Objective-C不支持自動裝箱,要自己動手

 

b):NSNumber是NSValue的子類,NSValue可以包裝任意值

 

創建新的NSValue

 

+ (NSValue *) valueWithBytes: (const void *) value

 

                  objCType: (const char *) type;

 

使用方法:

 

NSRect rect = NSMakeRect (1, 2, 30, 40);

 

NSValue *value;

 

value = [NSValue valueWithBytes: &rect

 

      objCType: @encode(NSRect)];  //encode編譯器指令可以接受數據類型的名稱併爲你生成合適的字符串

 

[array addObject: value];

 

可以使用getValue:來提取數值(注意是get方法,指針)

 

- (void) getValue: (void *) value; //調用時,要傳遞的是要存儲這個數值的變量的地址

 

使用方法

 

value = [array objectAtIndex: 0];

 

[value getValue: &rect];

 

Cocoa提供了常用的struct型數據轉換成NSValue的便捷方法

 

+ (NSValue *) valueWithPoint: (NSPoint) point;

 

+ (NSValue *) valueWithSize: (NSSize) size;

 

+ (NSValue *) valueWithRect: (NSRect) rect;

 

- (NSPoint) pointValue;

 

- (NSSize) sizeValue;

 

- (NSRect) rectValue;

 

使用方法:

 

value = [NSValue valueWithRect: rect];

 

[array addObject: value];

 

....

 

NSRect anotherRect = [value rectValue];

 

2)NSNull

 

NSNull大概是Cocoa裏最簡單的類了,只有一個方法

 

+ (NSNull *) null;

 

可以這樣添加到集合中

 

[contact setObject: [NSNull null]

 

forKey: @"home fax machine"];

 

訪問時:

 

id homefax;

 

homefax = [contact objectForKey: @"home fax machine"];

 

if (homefax == [NSNull null]) {

 

// ... no fax machine. rats.

 

}

 

//[NSNull null]總是返回一樣份數值,所以你可以使用“==”講該值與其他值進行比較……

 

 

22、NSDictionary和NSMutableDictionary

 

A) NSDictionary

 

字典是關鍵字和其定義的集合,也被成爲散列表或關聯數組,使用的是鍵查詢的優化存儲方式

 

1)創建方法: 使用dictionaryWithObjectsAndKeys:來創建字典

 

+ (id) dictionaryWithObjectsAndKeys: (id) firstObject, ...;

 

使用方法:

 

Tire *t1 = [Tire new];

 

Tire *t2 = [Tire new];

 

Tire *t3 = [Tire new];

 

Tire *t4 = [Tire new];

 

NSDictionary *tires;

 

tires = [NSDictionary dictionaryWithObjectsAndKeys:

 

t1, @"front- left", t2, @"front- right",

 

t3, @"back- left", t4, @"back- right", nil];

 

2)常用方法

 

- (id) objectForKey: (id) aKey;

 

使用方法:

 

Tire *tire = [tires objectForKey: @"back- right"];  //如果沒有則會返回nil值

 

B) NSMutableDictionary

 

1)創建方法:

 

可以向類NSMutableDictionary發送dictionary消息

 

也可以使用函數+ (id) dictionaryWithCapacity: (unsigned int) numItems;

 

2)常用方法

 

可以使用setObject:forKey:方法給字典添加元素:

 

- (void) setObject: (id) anObject forKey: (id) aKey;

 

- (void) removeObjectForKey: (id) aKey;

 

使用方法:

 

NSMutableDictionary *tires;

 

tires = [NSMutableDictionary dictionary];

 

[tires setObject: t1 forKey: @"front- left"];

 

[tires setObject: t2 forKey: @"front- right"];

 

[tires setObject: t3 forKey: @"back- left"];

 

[tires setObject: t4 forKey: @"back- right"];

 

//若已經存在,則會用新值替換原有的值

 

[tires removeObjectForKey: @"back- left"];

 

23、不要創建NSString、NSArray或NSDictionary的子類,因爲在Cocoa中,許多類實際上是以類簇的方式實現的,即他們是一羣隱藏在通用接口之下的與實現相關的類

 

24、Foundation實例 //查找文件

 

A)使用枚舉遍歷

 

int main (int argc, const char *argv[])

 

{

 

NSAutoreleasePool *pool;

 

pool = [[NSAutoreleasePool alloc] init];   //自動釋放池

 

NSFileManager *manager; //Cocoa中有很多類都是單實例構架,即只需要一個實例,你真的只需要一個文件管理器

 

manager = [NSFileManager defaultManager]; // defaultManager方法創建一個屬於我們的NSFileManager對象

 

NSString *home;

 

home = [@"~" stringByExpandingTildeInPath]; // stringByExpandingTildeInPath方法可將~替換成當前用戶的主目錄

 

NSDirectoryEnumerator *direnum; //NSEnumerator的子類

 

direnum = [manager enumeratorAtPath: home]; //創建一個枚舉條件

 

NSMutableArray *files;

 

files = [NSMutableArray arrayWithCapacity: 42]; //把搜索的結果作爲文件存儲

 

NSString *filename;

 

while (filename = [direnum nextObject]) {//調用nextObject時,都會返回該目錄中的一個文件的另一個路徑,也可搜索子目錄

 

if ([[filename pathExtension]  // pathExtension輸出文件的擴展名(去掉了前面的點.)

 

isEqualTo: @"jpg"]) {

 

[files addObject: filename];

 

}

 

}

 

NSEnumerator *fileenum;

 

fileenum = [files objectEnumerator];

 

while (filename = [fileenum nextObject]) {

 

NSLog (@"%@", filename);

 

}

 

[pool drain];

 

return (0);

 

} // main

 

B)使用快速遍歷

 

int main (int argc, const char * argv[]) {

 

NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

 

NSFileManager *manager;

 

manager = [NSFileManager defaultManager];

 

NSString *home;

 

home = [@"~" stringByExpandingTildeInPath];

 

NSMutableArray *files;

 

files = [NSMutableArray arrayWithCapacity: 42];

 

for (NSString *filename

 

in [manager enumeratorAtPath: home]) {

 

if ([[filename pathExtension]

 

isEqualTo: @"jpg"]) {

 

[files addObject: filename];

 

}

 

}

 

for (NSString *filename in files) {

 

NSLog (@"%@", filename);

 

}

BerwinZheng

2010-2-13 19:35:36 

六:內存管理

 

25、Cocoa採用引用計數(reference counting)的技術,有時稱爲保留計數。

 

每個對象有一個與之相關聯的整數,稱做爲他的引用計數器或保留計數器

 

- (id) retain;  

 

- (void) release;

 

- (unsigned) retainCount;  //當前值

 

26、對象所有權的處理

 

- (void) setEngine: (Engine *) newEngine

 

{

 

[newEngine retain];

 

[engine release];

 

engine = newEngine;

 

} // setEngine 

 

原則:先保存新對象,再釋放員對象

 

27、自動釋放

 

程序會自動建立一個自動釋放池(autorelease pool),他是一個存放實體的池(集合),這些實體可能是對象,能夠被自動釋放。

 

自動釋放池創建代碼

 

NSAutoreleasePool  *pool;

 

pool = [[NSAutoreleasePool  alloc]  init];

 

……

 

[pool  release];

 

NSObject類提供了一個antorelease方法:

 

- (id) autorelease;

 

//該方法預先定義了一條在將來某個時間發送的release消息,其返回值是接收消息的對象,retain採用了相同的技術,使嵌套調用更加容易。

 

//當給一個對象發送autorelease消息時,實際上是將對象添加到NSAutoreleasePool方法中,當自動釋放池銷燬了,會像該池中的所有對象發送release消息

 

例如

 

- (NSString *) description

 

{

 

NSString *description;

 

description = [[NSString alloc]

 

  initWithFormat: @"I am %d years old", 4];

 

return ([description autorelease]); //因爲descriptor方法首先創建了一個新的字符串對象,然後自動釋放該對象,最後將其返回給NSLog()函數

 

} // description

 

29、Cocoa內存管理原則

 

如果使用new,alloc或copy操作獲得一個對象,則該對象的保留計數器值加1,release減1

 

如果通過任何其他方法獲得一個對象,則假設該對象的保留計數器值爲1,而且已經被設置爲自動釋放

 

如果保留了某個對象,則必須保持retain方法和release方法使用的次數相同

 

30、NSColor的blueColor方法返回一個全局單例對象

 

31、一直擁有對象

 

希望在多個代碼段中一直擁有某個對象常見的方法有:在其他對象中使用這些變量,將它們加入到諸如NSArray或NSDictionary等集合中,或將其作爲全局變量使用(罕見)

 

如果你使用new,alloc或copy方法獲得一個對象,則不需要執行任何其他操作,他將一直存在,你只要在擁有該對象的dealloc方法中釋放該對象就可

 

- (void) doStuff

 

{

 

// flonkArray is an instance variable

 

flonkArray = [NSMutableArray new]; // count: 1

 

} // doStuff

 

- (void) dealloc

 

{

 

[flonkArray release]; // count: 0

 

[super dealloc];

 

} // dealloc

 

32、Cocoa程序纔開始處理事件之前創建一個自動釋放池,並在事件處理結束後銷燬該自動釋放池

 

33、保證內存佔用比較小的一種方法,分段處理

 

int i;

 

for (i = 0; i < 1000000; i++) {

 

id object = [someArray objectAtIndex: i];

 

NSString *desc = [object description];

 

// and do something with the description

 

}

 

節省內存的方法:

 

NSAutoreleasePool *pool;

 

pool = [[NSAutoreleasePool alloc] init];

 

int i;

 

for (i = 0; i < 1000000; i++) {

 

id object = [someArray objectAtIndex: i];

 

NSString *desc = [object descrption];

 

// and do something with the description

 

if (i % 1000 == 0) {

 

[pool release];

 

pool = [[NSAutoreleasePool alloc] init];

 

}

 

}

 

[pool release]

 

//自動釋放池以棧的形式存在

 

34、Objective-C 2.0的垃圾回收機制,是一個可選擇啓用的功能,項目信息屬性轉到Build選項卡,在Objective-C Garbage Collection選項選成Required [-fobjc-gc- only]即可

 

但注意:iPhone裏面不能用

 

 

 

七:對象的初始化

 

35、兩種方法

 

[類名 new]  //不熟悉Cocoa的開發人員使用的輔助方法

 

[[類名 alloc] init]   //主要使用方法

 

36、分配(allocation)

 

向某個發送

 

內存區域 :全部初始化爲0

 

BOOL :NO

 

int : 0

 

float : 0.0

 

指針 : nil

 

37、兩種格式

 

Car *car = [[Car alloc] init];  //推薦使用,這種嵌套調用非常重要,因爲初始化方法返回的對象可能與分配的對象不同,雖然很奇怪,但是它的確會發生

 

Car *car = [Car alloc];

 

[car init]; //不推薦使用

 

38、編寫init方法

 

- (id) init

 

{

 

if (self = [super init]) {

 

engine = [Engine new];

 

tires[0] = [Tire new];

 

tires[1] = [Tire new];

 

tires[2] = [Tire new];

 

tires[3] = [Tire new];

 

}

 

return (self);

 

} // init

 

//注意首行的self = [super init],從根類NSObject繼承的類調用超類的初始化方法,可以使NSObject執行所需的任何操作,以便對象能夠響應消息並處理保留計數器,而從其他類繼承的類調用超類的初始化方法,可以使子類有機會實現自己全新的初始化

 

//實例變量所在的位置到隱藏的self參數的距離是固定的,如果從init方法返回一個新對象,則需要更新self,以便其後的任何實例變量的引用可以被映射到正確的位置,這也是self = [super init]使用的原因,記住,這個賦值操作隻影響init方法中self的值,而不影響該範圍以外的任何內容

 

// if (self = [super init])使用的原因,如果[super init]返回的結果是nil,則主體不會執行,只是賦值和檢測非零值結合的方法,沿襲自C風格

 

39、便利初始化函數  //也可以自己構建

 

- (id) initWithFormat: (NSString *) format, ...;

 

- (id) initWithContentsOfFile: (NSString *) path;  //打開指定路徑上的文件,讀取文件內容,並使用文件類內容初始化一個字符串

 

使用方法:

 

string = [[NSString alloc]

 

  initWithFormat: @"%d or %d", 25, 624];

 

string = [[NSString alloc]

 

  initWithContentsOfFile: @"/tmp/words.txt"];  

 

構造便利初始化函數

 

例如

 

在@interface Tire: NSObject中添加方法聲明

 

- (id) initWithPressure : (float) pressure

 

                  treadDepth: (float) treadDepth;

 

在@implementation Tire中實現該方法

 

- (id) initWithPressure: (float) p

 

           treadDepth: (float) td

 

{

 

if (self = [super init]) {

 

pressure = p;

 

treadDepth = td;

 

}

 

return (self);

 

} // initWithPressure:treadDepth:

 

這樣就完成了初始化函數的定義,分配,初始化一體完成

 

Tire *tire;

 

tire = [[Tire alloc]

 

initWithPressure: 23 + i

 

treadDepth: 33 - i];

BerwinZheng

2010-2-13 19:36:05 

39、如果用NSMutableArray代替C數組,則就不用執行邊界檢查

 

40、指定初始化函數

 

有的時候定義了太多的初始化函數時,會出現一些細微的問題

 

例如下面的程序

 

@interface Tire : NSObject

 

{

 

float pressure;

 

float treadDepth;

 

}

 

- (id) initWithPressure: (float) pressure;

 

- (id) initWithTreadDepth: (float) treadDepth; //新增加的兩個初始化函數

 

- (id) initWithPressure: (float) pressure

 

treadDepth: (float) treadDepth;

 

- (void) setPressure: (float) pressure;

 

- (float) pressure;

 

- (void) setTreadDepth: (float) treadDepth;

 

- (float) treadDepth;

 

@end // Tire

 

//聲明瞭三個初始化函數

 

//新聲明的初始化函數的實現

 

- (id) initWithPressure: (float) p

 

{

 

if (self = [super init]) {

 

pressure = p;

 

treadDepth = 20.0;

 

}

 

return (self);

 

} // initWithPressure

 

- (id) initWithTreadDepth: (float) td

 

{

 

if (self = [super init]) {

 

pressure = 34.0;

 

treadDepth = td;

 

}

 

return (self);

 

} // initWithTreadDepth

 

問題來了

 

子類化問題:

 

@interface AllWeatherRadial : Tire

 

{

 

float rainHandling;

 

float snowHandling;

 

}

 

- (void) setRainHanding: (float) rainHanding;

 

- (float) rainHandling;

 

- (void) setSnowHandling: (float) snowHandling;

 

- (float) snowHandling;

 

@end // AllWeatherRadial

 

//枯燥的存取函數

 

- (void) setRainHandling: (float) rh

 

{

 

rainHandling = rh;

 

} // setRainHandling

 

- (float) rainHandling

 

{

 

return (rainHandling);

 

} // rainHandling

 

- (void) setSnowHandling: (float) sh

 

{

 

snowHandling = sh;

 

} // setSnowHandling

 

- (float) snowHandling

 

{

 

return (snowHandling);

 

} // snowHandling

 

- (NSString *) description

 

{

 

NSString *desc;

 

desc = [[NSString alloc] initWithFormat:

 

@"AllWeatherRadial: %.1f / %.1f / %.1f / %.1f",

 

[self pressure], [self treadDepth],

 

[self rainHandling],

 

[self snowHandling]];

 

return (desc);

 

} // description

 

//main.m函數中實現

 

int i;

 

for (i = 0; i < 4; i++) {

 

AllWeatherRadial *tire;

 

tire = [[AllWeatherRadial alloc] init];

 

[car setTire: tire

 

atIndex: i];

 

[tire release];

 

}

 

運行的結果是下面這個樣子的

 

AllWeatherRadial: 34.0 / 20.0 / 0.0 / 0.0

 

AllWeatherRadial: 34.0 / 20.0 / 0.0 / 0.0

 

AllWeatherRadial: 34.0 / 20.0 / 0.0 / 0.0

 

AllWeatherRadial: 34.0 / 20.0 / 0.0 / 0.0

 

I am a slant- 6. VROOOM!

 

//注:默認情況下初始化函數只會按最容易實現的方式去運行,這不是我要的結果,並且是錯誤的結果

 

解決辦法:指定初始化函數(designated initializer)

 

- (id) init

 

{

 

if (self = [self initWithPressure: 34

 

  treadDepth: 20]) {

 

}

 

return (self);

 

} // init

 

- (id) initWithPressure: (float) p

 

{

 

if (self = [self initWithPressure: p

 

  treadDepth: 20.0]) {

 

}

 

return (self);

 

} // initWithPressure

 

- (id) initWithTreadDepth: (float) td

 

{

 

if (self = [self initWithPressure: 34.0

 

  treadDepth: td]) {

 

}

 

return (self);

 

} // initWithTreadDepth

 

添加到AllWeatherRadial類的初始化函數

 

- (id) initWithPressure: (float) p

 

           treadDepth: (float) td

 

{

 

if (self = [super initWithPressure: p

 

treadDepth: td]) {

 

rainHandling = 23.7;

 

snowHandling = 42.5;

 

}

 

return (self);

 

} // initWithPressure:treadDepth

 

 

此時我們再運行可以得到這樣的結果

 

AllWeatherRadial: 34.0 / 20.0 / 23.7 / 42.5

 

AllWeatherRadial: 34.0 / 20.0 / 23.7 / 42.5

 

AllWeatherRadial: 34.0 / 20.0 / 23.7 / 42.5

 

AllWeatherRadial: 34.0 / 20.0 / 23.7 / 42.5

 

I am a slant- 6. VROOOM!

BerwinZheng

2010-2-13 19:36:38 

八:屬性

 

41、屬性(property)是Objective-C 2.0中引入的,爲了方便的編寫存取方法

 

42、屬性的使用方法

 

1)聲明方法的簡化

 

//舊的表示方法

 

#import <Foundation/Foundation.h>

 

#import "Tire.h"

 

@interface AllWeatherRadial : Tire {

 

float rainHandling; 

 

float snowHandling;

 

}

 

- (void) setRainHandling: (float) rainHanding;

 

- (float) rainHandling;

 

- (void) setSnowHandling: (float) snowHandling;

 

- (float) snowHandling;

 

@end // AllWeatherRadial

 

//用屬性表示後

 

#import <Foundation/Foundation.h>

 

#import "Tire.h"

 

@interface AllWeatherRadial : Tire {

 

float rainHandling;

 

float snowHandling;

 

@property float rainHandling;  //表明該類有一個名爲rainHanding的float型屬性,你可以通過-setRainHanding: 來設置屬性,通過-rainHanding來訪問屬性

 

@property float snowHandling;

 

@end // AllWeatherRadial

 

}

 

//@property預編譯命令的作用是自動聲明屬性的setter和getter方法

 

//屬性的名稱不必與實例變量名稱相同,但是一般都是相同的

 

2)實現方法的簡化

 

//百年老字號

 

#import "AllWeatherRadial.h"

 

@implementation AllWeatherRadial

 

- (id) initWithPressure: (float) p

 

             treadDepth: (float) td

 

{

 

if (self = [super initWithPressure: p

 

treadDepth: td]) {

 

rainHandling = 23.7;

 

snowHandling = 42.5;

 

}

 

return (self);

 

} // initWithPressure:treadDepth

 

- (void) setRainHandling: (float) rh

 

{

 

rainHandling = rh;

 

} // setRainHandling

 

- (float) rainHandling

 

{

 

return (rainHandling);

 

} // rainHandling

 

- (void) setSnowHandling: (float) sh

 

{

 

snowHandling = sh;

 

} // setSnowHandling

 

- (float) snowHandling

 

{

 

return (snowHandling);

 

} // snowHandling

 

- (NSString *) description

 

{

 

NSString *desc;

 

desc = [[NSString alloc] initWithFormat:

 

@"AllWeatherRadial: %.1f / %.1f / %.1f / %.1f",

 

[self pressure], [self treadDepth],

 

[self rainHandling],

 

[self snowHandling]];

 

return (desc);

 

} // description

 

@end // AllWeatherRadial

 

//改進後的方法

 

#import "AllWeatherRadial.h"

 

@implementation AllWeatherRadial

 

@synthesize rainHandling;

 

@synthesize snowHandling;

 

- (id) initWithPressure: (float) p

 

           treadDepth: (float) td

 

{

 

if (self = [super initWithPressure: p

 

treadDepth: td]) {

 

rainHandling = 23.7;

 

snowHandling = 42.5;

 

}

 

return (self);

 

} // initWithPressure:treadDepth

 

- (NSString *) description

 

{

 

NSString *desc;

 

desc = [[NSString alloc] initWithFormat:

 

@"AllWeatherRadial: %.1f / %.1f / %.1f / %.1f",

 

[self pressure], [self treadDepth],

 

[self rainHandling],

 

[self snowHandling]];

 

return (desc);

 

} // description

 

@end // AllWeatherRadial

 

//@synthesize也是一種新的編譯器功能,表示“創建該屬性的訪問器”

 

//當遇到@synthesize rainHandling;時,編譯器將輸出-setRainHanding:和- rainHanding方法的已編譯代碼

 

43、點表達式

 

點表達式(.),若出現在等號(=)左邊,該屬性名稱的setter方法將被調動,多出現在對象變量右邊,則該屬性名冊和那個的getter方法將被調用

 

//注:特性的點表達式和流行的鍵/值編碼的後臺工作沒有聯繫

 

44、特性擴展

 

特性同樣適用於int、char、BOOL和struct類型

 

(所有者對象保留被擁有的對象,而不是被擁有的對象保留所有者對象)

 

//可以使用一些聲明,用於內存處理(那個是用垃圾回收機制的路過)

 

@property (copy) NSString *name;

 

@property (retain) Engine *engine;

 

45、特性名和實例變量名字不相同的情況

 

@interface Car : NSObject {

 

NSString *appellation;

 

NSMutableArray *tires;

 

Engine *engine;

 

}

 

@property (copy) NSString *name;

 

@property (retain) Engine *engine;

 

//然後,修改@synthesize指令

 

@synthesize name = appellation;

 

編譯器扔將創建-setName:和- name方法,但是在實現中卻是用實例變量application

 

//這樣做會有錯誤,因爲我們直接訪問的實例變量name已經被修改了,我們既可以選擇搜索替換name,也可以將直接的實例變量訪問修改爲使用訪問器訪問,在init方法中,將

 

name = @”Car”;

 

修改爲:

 

self.name = @”Car” ;   //[self setName : @”Car”];

 

在dealloc中,使用一種高明的技巧:

 

self.name = nil;  //使用nil參數調用setName:方法

 

生成的訪問器將自動釋放以前的name對象,並使用nil替代name

 

最後修改-description方法需要使用第一次被修改的NSLog()函數:

 

NSLog(@”%@ has:” , self.name);

BerwinZheng

2010-2-13 19:37:00 

46、只讀特性

 

//默認的特性是支持可寫可讀的,原型如下

 

@property (readwrite, copy) NSString *name;

 

@property (readwrite, retain) Engine *engine;

 

//但爲了簡便,爲了消除重複

 

//只讀屬性的設置

 

@interface Me : NSObject {

 

float shoeSize;

 

NSString *licenseNumber;

 

}

 

@property (readonly) float shoeSize;

 

@property (readonly) NSString *licenseNumber;

 

@end

 

//這類編譯器只會生成getter方法,不會有setter方法

 

47、特性的侷限性

 

//不支持那麼需要接受額外參數的方法

 

- (void) setTire: (Tire *) tire

 

atIndex: (int) index;

 

- (Tire *) tireAtIndex: (int) index;

 

//這樣的只能使用百年老字號

 

 

 

 

 

九:類別

 

48、可以利用Objective-C的動態運行時分配機制,爲現有的類添加新方法---這就叫類別(category)

 

//特別是那些不能創建之類的類,很是cool

 

49、創建類別

 

//如果你希望想一個array或者dictionary裏面添加一個個數字,你需要一個個的封裝,如果多,你會瘋掉,可以爲string類添加一個類別來完成這項工作

 

1)聲明對象 //與類的聲明格式類似

 

@interface NSString (NumberConvenience)

 

- (NSNumber *) lengthAsNumber;

 

@end // NumberConvenience

 

//我們正在向String類裏面添加一個NumberConvenience方法,可以添加很多個,只要名稱不相同

 

2)實現部分

 

@implementation NSString (NumberConvenience)

 

- (NSNumber *) lengthAsNumber

 

{

 

unsigned int length = [self length];  //獲得字符串的長度

 

return ([NSNumber numberWithUnsignedInt: length]);

 

} // lengthAsNumber

 

@end // NumberConvenience

 

 

現在就可以用了

 

int main (int argc, const char *argv[])

 

{

 

NSAutoreleasePool *pool;

 

pool = [[NSAutoreleasePool alloc] init];

 

NSMutableDictionary *dict;

 

dict = [NSMutableDictionary dictionary];

 

[dict setObject: [@"hello" lengthAsNumber]

 

forKey: @"hello"];

 

[dict setObject: [@"iLikeFish" lengthAsNumber]

 

forKey: @"iLikeFish"];

 

[dict setObject: [@"Once upon a time" lengthAsNumber]

 

forKey: @"Once upon a time"];

 

NSLog (@"%@", dict);

 

[pool release];

 

return (0);

 

} // main

 

//任何NSString類都將響應lengthAsNumber消息,正式這種兼容性使類別稱爲一個非常偉大的概念,不需要創建NSString的之類,類別同樣可以完成同樣的工作

 

50、類別的侷限性

 

第一:無法向類別裏面添加新的實例變量,類別裏面沒有位置容納實例變量//也可以是用dictionary封裝,但是不划算

 

第二:若名稱衝突,類別的優先級更高(一般都是加一個前綴避免名稱衝突)

 

51、類別的作用

 

Cocoa中類別主要用於3個目的

 

1)將類的實現分散到多個不同的文件或不同構架中

 

用類別分離文件時注意類別的寫法,一個類的類別才能實現這個類的方法

 

2)創建對私有方法的前向引用

 

有些聲明不需要寫在.h文件中,因爲有的時候這個只是本類的一個小的實現,聲明太麻煩,而且讓code reader比較難理解,就可以在.m中用類別聲明一下

 

@interface Car (PrivateMethods)

 

 

- (void) moveTireFromPosition: (int) pos1

 

  toPosition: (int) pos2;

 

 

@end //private Methods

 

 

3)向對象添加非正式協議

 

非正式協議表示這裏有一些你可能希望實現的方法,因此你可以使用它們更好的完成工作

 

 

 

52、將類的實現分散到多個不同的文件或不同架構中

 

看文檔中的NSWindows類

 

@interface NSWindow : NSResponder

 

然後是一大堆類別:

 

@interface NSWindow(NSKeyboardUI)

 

@interface NSWindow(NSToolbarSupport)

 

@interface NSWindow(NSDrag)

 

@interface NSWindow(NSCarbonExtensions)

 

@interface NSObject(NSWindowDelegate)

 

這樣就就可以把一個大的文件分開使用,看起來方便,實用

 

 

 

53、run循環

 

[[NSRunLoop currentRunLoop] run];

 

是一種cocoa構造,它一直處於阻塞狀態(即不執行任何處理),知道某些有趣的事情發生爲止

 

//這個run方法將一直運行而不會返回,後面的代碼將一直不執行

 

 

 

54、委託和類別

 

委託強調類別的另一種應用:被髮送給委託對象的方法可以聲明爲一個NSObject的類別。NSNetService委託方法的聲明如下

 

@interface NSObject

 

(NSNetServiceBrowserDelegateMethods)

 

 

- (void) netServiceBrowser: (NSNetServiceBrowser *) browser

 

didFindService: (NSNetService *) service

 

moreComing: (BOOL) moreComing;

 

 

- (void) netServiceBrowser: (NSNetServiceBrowser *) browser

 

  didRemoveService: (NSNetService *) service

 

moreComing: (BOOL) moreComing;

 

 

@end

 

通過這些方法聲明爲NSObject的類型,NSNetServiceBrowser的實現可以將這些消息之一發送個任何對象,無論這些對象實際上屬於哪個類。這意味着,只要實現了委託方法,任何類的對象都可以成爲委託對象

 

通過這種方法可以不繼承(c++)和不實現某個特定的接口(java),就可以作爲委託對象使用

 

 

 

55、NSObject提供了一個名爲respondsToSelector:的方法,該方法訪問對象以確定其是否能夠響應某個特定的消息

 

 

56、複製的種類有Shallow Copy和deep copy

 

Shallow Copy不復制引用對象,新複製的對象只指向指向現有的引用對象

 

deep copy將複製所有的引用對象

 

 

57,[self class]妙用

 

Car *carCopy = [[[self class] allocWithZone: zone] init];

 

可以通過self的類型來判斷運行結果,子類的用這個函數就是子類的結果

 

 

58,NSDate *yesterday = [NSDate dateWithTimeIntervalSinceNow: -(24 * 60 * 60)];

 

獲取一個時間段以前的一個時間,dateWithTimeIntervalSinceNow 接受一個NSTimeInterval參數,是一個雙精度值,以秒爲單位

 

 

59.NSData 和 char*的轉化

 

const char *string = "Hi there, this is a C string!";

 

NSData *data = [NSData dataWithBytes: string

 

                           length: strlen(string) + 1];

 

NSLog (@"data is %@", data);  //輸出爲ascll碼

 

NSLog (@"%d byte string is '%s'", [data length], [data bytes]);  //格式化輸出要的內容

 

 

60,有些屬性文件(特別是首選項文件)是以二進制格式存儲的,通過使用plutil命令:plutil -convert xml1 filename.plist可以轉化成人們可讀的形式

BerwinZheng

2010-2-13 19:37:32 

61,[phrase writeToFile: @"/tmp/verbiage.txt"  atomically: YES];的atomically 是用於通知cocoa是否應該首先將文件內容保存在臨時文件中,當文件保存成功後,再將該臨時文件和原始文件交換

 

 

62,編碼對象

 

#import <Foundation/Foundation.h>

 

 

@interface Thingie : NSObject <NSCoding> {

 

    NSString *name;

 

    int magicNumber;

 

    float shoeSize;

 

    NSMutableArray *subThingies;

 

}

 

@property (copy) NSString *name;

 

@property int magicNumber;

 

@property float shoeSize;

 

@property (retain) NSMutableArray *subThingies;

 

 

- (id)initWithName: (NSString *) n

 

       magicNumber: (int) mn  

 

          shoeSize: (float) ss;

 

 

@end // Thingie

 

 

 

@implementation Thingie

 

@synthesize name;

 

@synthesize magicNumber;

 

@synthesize shoeSize;

 

@synthesize subThingies;

 

 

- (id)initWithName: (NSString *) n

 

       magicNumber: (int) mn  

 

          shoeSize: (float) ss {

 

    if (self = [super init]) {

 

        self.name = n;

 

        self.magicNumber = mn;

 

        self.shoeSize = ss;

 

        self.subThingies = [NSMutableArray array];

 

    }

 

    return (self);

 

}

 

 

- (void) dealloc {

 

    [name release];

 

    [subThingies release];

 

    [super dealloc];

 

 

 

} // dealloc

 

 

- (NSString *) description {

 

    NSString *description = 

 

    [NSString stringWithFormat: @"%@: %d/%.1f %@",

 

     name, magicNumber, shoeSize, subThingies];

 

    return (description);

 

 

 

} // description

 

 

- (void) encodeWithCoder: (NSCoder *) coder {

 

    [coder encodeObject: name

 

forKey: @"name"];

 

    [coder encodeInt: magicNumber

 

  forKey: @"magicNumber"];

 

    [coder encodeFloat: shoeSize

 

forKey: @"shoeSize"];

 

    [coder encodeObject: subThingies

 

forKey: @"subThingies"];

 

 

 

} // encodeWithCoder

 

 

 

- (id) initWithCoder: (NSCoder *) decoder {

 

    if (self = [super init]) {

 

        self.name = [decoder decodeObjectForKey: @"name"];

 

        self.magicNumber = [decoder decodeIntForKey: @"magicNumber"];

 

        self.shoeSize = [decoder decodeFloatForKey: @"shoeSize"];

 

        self.subThingies = [decoder decodeObjectForKey: @"subThingies"];

 

    }

 

    return (self);

 

} // initWithCoder

 

 

@end // Thingie

 

 

 

int main (int argc, const char * argv[]) {

 

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

 

Thingie *thing1;

 

thing1 = [[Thingie alloc]

 

  initWithName: @"thing1"

 

  magicNumber: 42

 

  shoeSize: 10.5];

 

NSLog (@"some thing: %@", thing1);

 

 

// 使用NSData的兩個子類NSKeyedArchiver和NSKeyedUnarchiver

 

    NSData *freezeDried;

 

    freezeDried = [NSKeyedArchiver archivedDataWithRootObject: thing1];

 

    [thing1 release];

 

    thing1 = [NSKeyedUnarchiver unarchiveObjectWithData: freezeDried];

 

    NSLog (@"reconstituted thing: %@", thing1);

 

 

 

    Thingie *anotherThing;

 

    anotherThing =  [[[Thingie alloc]

 

  initWithName: @"thing2"

 

  magicNumber: 23

 

  shoeSize: 13.0] autorelease];

 

    [thing1.subThingies addObject: anotherThing];

 

    anotherThing =  [[[Thingie alloc]

 

  initWithName: @"thing3"

 

  magicNumber: 17

 

  shoeSize: 9.0] autorelease];

 

    [thing1.subThingies addObject: anotherThing];

 

    NSLog (@"thing with things: %@", thing1);

 

    freezeDried = [NSKeyedArchiver archivedDataWithRootObject: thing1];

 

    thing1 = [NSKeyedUnarchiver unarchiveObjectWithData: freezeDried];

 

    NSLog (@"reconstituted multithing: %@", thing1);

 

    [thing1.subThingies addObject: thing1];

 

    // You really don't want to do this...

 

    // NSLog (@"infinite thinging: %@", thing1);

 

    freezeDried = [NSKeyedArchiver archivedDataWithRootObject: thing1];

 

[freezeDried writeToFile:@"/tmp/xiaoyuan" atomically:YES]; 

 

    thing1 = [NSKeyedUnarchiver unarchiveObjectWithData: freezeDried];

 

 

 

    [pool release];

 

    return (0);

 

} // main

 

 

63,KVC(Key Value Code)

 

Advantage one:

 

NSLog (@"horsepower is %@", [engine valueForKey: @"horsepower"]); //這個會自動打包成NSNumber或NSValue

 

[engine setValue: [NSNumber numberWithInt: 150] //這個使用的時候要自己打包

 

  forKey: @"horsepower"];

 

NSLog (@"horsepower is %@", [engine valueForKey: @"horsepower"]);

 

[car setValue: [NSNumber numberWithInt: 155] 

 

  forKeyPath: @"engine.horsepower"];//路徑的獲得

 

NSLog (@"horsepower is %@", [car valueForKeyPath: @"engine.horsepower"]);

 

NSArray *pressures = [car valueForKeyPath: @"tires.pressure"];//要是路徑是一個數組就只能獲取一個數組,不能只獲取一個

 

NSLog (@"pressures %@", pressures);

 

 

Advantage two

 

NSNumber *count;

 

count = [garage valueForKeyPath: @"cars.@count"];

 

NSLog (@"We have %@ cars", count);

 

NSNumber *sum;

 

sum = [garage valueForKeyPath: @"[email protected]"];

 

NSLog (@"We have a grand total of %@ miles", sum);

 

NSNumber *avgMileage;

 

avgMileage = [garage valueForKeyPath: @"[email protected]"];

 

NSLog (@"average is %.2f", [avgMileage floatValue]);

 

NSNumber *min, *max;

 

min = [garage valueForKeyPath: @"[email protected]"];

 

max = [garage valueForKeyPath: @"[email protected]"];

 

NSLog (@"minimax: %@ / %@", min, max);

 

NSArray *manufacturers;

 

manufacturers = [garage valueForKeyPath: @"[email protected]"];

 

NSLog (@"makers: %@", manufacturers);

 

//另外,union 運算符指一組對象的並集

 

//distinct用於刪除重複的內容

 

//遺憾的一點就是不能添加自的運算符

 

 

Advantage three

 

car = [[garage valueForKeyPath: @"cars"] lastObject];

 

NSArray *keys = [NSArray arrayWithObjects: @"make", @"model", @"modelYear", nil];

 

NSDictionary *carValues = [car dictionaryWithValuesForKeys: keys];

 

NSLog (@"Car values : %@", carValues);

 

NSDictionary *newValues =

 

[NSDictionary dictionaryWithObjectsAndKeys:

 

@"Chevy", @"make",

 

@"Nova", @"model",

 

[NSNumber numberWithInt:1964], @"modelYear",

 

[NSNull null], @"mileage",

 

nil];

 

[car setValuesForKeysWithDictionary: newValues];

 

NSLog (@"car with new values is %@", car);

 

//新裝配過的car

 

 

KVC的一些特殊情況處理

 

case one: nil的處理

 

[car setValue:nil forKey: @"mileage"];

 

NSLog (@"Nil miles are %@", car.mileage);

 

這裏的標量值mileage中的nil表示的是什麼0?-1?pi?cocoa無法知道,可以再car類裏面重寫

 

- (void) setNilValueForKey: (NSString *) key {

 

if ([key isEqualToString: @"mileage"]) {

 

mileage = 0;

 

} else {

 

[super setNilValueForKey: key];

 

}

 

} // setNilValueForKey

 

 

case two:未定義的健的處理

 

在控制類裏面寫

 

- (void) setValue: (id) value  forUndefinedKey: (NSString *) key {

 

if (stuff == nil) {

 

stuff = [[NSMutableDictionary alloc] init];

 

}

 

[stuff setValue: value forKey: key];

 

} // setValueForUndefinedKey

 

 

 

- (id) valueForUndefinedKey:(NSString *)key {

 

id value = [stuff valueForKey: key];

 

return (value);

 

} // valueForUndefinedKey

BerwinZheng

2010-2-13 19:37:51 

64、Cocoa中提供NSPredicate的類,它用於指定過濾的條件

 

Cocoa用NSPredicate描述查詢的方式,原理類似於在數據庫中進行查詢

 

計算謂詞:

 

//基本的查詢

 

NSPredicate *predicate;

 

predicate = [NSPredicate predicateWithFormat: @"name == 'Herbie'"];

 

    BOOL match = [predicate evaluateWithObject: car];

 

    NSLog (@"%s", (match) ? "YES" : "NO");

 

 

//在整個cars裏面循環比較

 

    predicate = [NSPredicate predicateWithFormat: @"engine.horsepower > 150"];

 

    NSArray *cars = [garage cars];

 

    for (Car *car in [garage cars]) {

 

        if ([predicate evaluateWithObject: car]) {

 

            NSLog (@"%@", car.name);

 

        }

 

    }

 

 

//輸出完整的信息

 

    predicate = [NSPredicate predicateWithFormat: @"engine.horsepower > 150"];

 

    NSArray *results;

 

    results = [cars filteredArrayUsingPredicate: predicate];

 

    NSLog (@"%@", results);

 

 

//含有變量的謂詞

 

    NSPredicate *predicateTemplate = [NSPredicate predicateWithFormat:@"name == $NAME"];

 

    NSDictionary *varDict;

 

    varDict = [NSDictionary dictionaryWithObjectsAndKeys:

 

               @"Herbie", @"NAME", nil];

 

    predicate = [predicateTemplate predicateWithSubstitutionVariables: varDict];

 

    NSLog(@"SNORGLE: %@", predicate);

 

    match = [predicate evaluateWithObject: car];

 

  NSLog (@"%s", (match) ? "YES" : "NO");

 

//注意不能使用$VARIABLE作爲路徑名,因爲它值代表值

 

 

//謂詞字符竄還支持c語言中一些常用的運算符

 

    /*

 

*>: 大於

 

*>=和=>: 大於或等於

 

*<: 小於

 

*<=和=<: 小於或等於

 

*!=和<>: 不等於

 

*括號運算符,AND,OR,NOT,&&,||,! (可以不區分大小寫,但建議一致)

 

*/

 

    predicate = [NSPredicate predicateWithFormat:

 

                 @"(engine.horsepower > 50) AND (engine.horsepower < 200)"];

 

    results = [cars filteredArrayUsingPredicate: predicate];

 

    NSLog (@"oop %@", results);

 

 

 

    predicate = [NSPredicate predicateWithFormat: @"name < 'Newton'"];

 

    results = [cars filteredArrayUsingPredicate: predicate];

 

    NSLog (@"%@", [results valueForKey: @"name"]);

 

 

//強大的數組運算符

 

    predicate = [NSPredicate predicateWithFormat:

 

                 @"engine.horsepower BETWEEN { 50, 200 }"];

 

    results = [cars filteredArrayUsingPredicate: predicate];

 

    NSLog (@"%@", results);

 

 

 

    NSArray *betweens = [NSArray arrayWithObjects:

 

                         [NSNumber numberWithInt: 50], [NSNumber numberWithInt: 200], nil];

 

    predicate = [NSPredicate predicateWithFormat: @"engine.horsepower BETWEEN %@", betweens];

 

    results = [cars filteredArrayUsingPredicate: predicate];

 

    NSLog (@"%@", results);

 

 

    predicateTemplate = [NSPredicate predicateWithFormat: @"engine.horsepower BETWEEN $POWERS"];

 

    varDict = [NSDictionary dictionaryWithObjectsAndKeys: betweens, @"POWERS", nil];

 

    predicate = [predicateTemplate predicateWithSubstitutionVariables: varDict];

 

    results = [cars filteredArrayUsingPredicate: predicate];

 

    NSLog (@"%@", results);

 

 

//IN運算符

 

    predicate = [NSPredicate predicateWithFormat: @"name IN { 'Herbie', 'Snugs', 'Badger', 'Flap' }"];

 

    results = [cars filteredArrayUsingPredicate: predicate];

 

    NSLog (@"%@", [results valueForKey: @"name"]);

 

 

    predicate = [NSPredicate predicateWithFormat: @"SELF.name IN { 'Herbie', 'Snugs', 'Badger', 'Flap' }"];

 

    results = [cars filteredArrayUsingPredicate: predicate];

 

    NSLog (@"%@", [results valueForKey: @"name"]);

 

 

 

    names = [cars valueForKey: @"name"];

 

    predicate = [NSPredicate predicateWithFormat: @"SELF IN { 'Herbie', 'Snugs', 'Badger', 'Flap' }"];

 

    results = [names filteredArrayUsingPredicate: predicate];//這裏限制了SELF的範圍

 

    NSLog (@"%@", results);

 

 

//BEGINSWITH,ENDSWITH,CONTAINS

 

//附加符號,[c],[d],[cd],c表示不區分大小寫,d表示不區分發音字符,cd表示什麼都不區分

 

    predicate = [NSPredicate predicateWithFormat: @"name BEGINSWITH 'Bad'"];

 

    results = [cars filteredArrayUsingPredicate: predicate];

 

    NSLog (@"%@", results);

 

 

 

    predicate = [NSPredicate predicateWithFormat: @"name BEGINSWITH 'HERB'"];

 

    results = [cars filteredArrayUsingPredicate: predicate];

 

    NSLog (@"%@", results);

 

 

 

    predicate = [NSPredicate predicateWithFormat: @"name BEGINSWITH[cd] 'HERB'"];

 

    results = [cars filteredArrayUsingPredicate: predicate];

 

    NSLog (@"%@", results);

 

 

//LIKE運算符(通配符)

 

    predicate = [NSPredicate predicateWithFormat: @"name LIKE[cd] '*er*'"];

 

    results = [cars filteredArrayUsingPredicate: predicate];

 

    NSLog (@"%@", results);

 

 

 

    predicate = [NSPredicate predicateWithFormat: @"name LIKE[cd] '???er*'"];

 

    results = [cars filteredArrayUsingPredicate: predicate];

 

    NSLog (@"%@", results); 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章