Objective-C 是 C 语言的超集

您还能够訪问标准 C 库例程,比如在stdlib.h和stdio.h中声明的那些例程。

Objective-C 还是一种很动态的程序设计语言,并且这样的动态是其最大优势。这样的动态体如今它同意在执行应用程序时(即执行时)才去确定其行为,而不是在生成期间就已固定下来。因此,Objective-C的动态机制让程序免受约束(编译和链接程序时施加的约束);进而在用户控制下,将大多数符号解析责任转移到执行时。

当您想要在源码中包含头文件时,请在头文件或源文件的前几行之中,指定一个导入 (#import)指令,#import指令类似于 C 的#include指令,只是前者确保同一文件仅仅被包含一次。

下列框图中的语法声明名为 MyClass的类,它是从基础类(或根类)NSObject继承而来的。(根类是供其它类直接或间接继承的类。)类声明以编译器指令@interface開始,以@end指令结束。类名称后面(以冒号分隔),是父类的名称。在 Objective-C中,一个类仅仅能有一个父类。

在 @interface指令和@end指令之间,编写属性和方法的声明。这些声明组成了类的公共接口。

@interface MyClass : NSObject

{

int count;

id data;

NSString name;

}

- (id)initWithString:(NSString*)aName;

+(MyClass*)createMyClassesWithString:(NSString*)aName;

类实现的语法与类接口文件类似。它以 @implementation编译器指令開始(接着是该类的名称),以@end指令结束。中间是方法实现。(函数实现应在@implementation ...@end块之外。)一个实现应该总是将导入它的接口文件作为代码的第一行。

#import "MyClass.h"


@implementation MyClass


- (id)initWithString:(NSString *)aName

{

    // code goes here

}


+ (MyClass *)myClassWithString:(NSString *)aName

{

    // code goes here

}


@end



对于实例方法,声明前面是减号 (-);对于类方法,相应指示器是加号 (+)。类方法是一种功能,类似于 C++ 中的静态类方法。



Objective-C 还提供用于调用存取方法的点记法语法。存取方法获取并设定对象的状态,因此对于封装非常重要,是全部对象的重要功能。对象隐藏或封装其状态,并显示接口,该接口是訪问该状态的全部实例都通用的。使用点记法语法,您能够将上个演示样例又一次编写为例如以下形式:



[myAppObject.theArray insertObject:myAppObject.objectToInsert atIndex:0];



您还能够使用点记法语法进行赋值:



myAppObject.theArray = aNewArray;



此语法仅仅是编写 [myAppObject setTheArray:aNewArray];的还有一种方式。在点记法表达式中,您不能使用对动态类型化的对象(类型为id的对象)的引用。





一个对象的类定义一个接口,该接口使其对象的用户能获取并设定所封装属性的值。运行这些操作的方法,称为存取方法。



使用已声明的属性后,就不必为该类中用到的每一个属性实现 getter 和 setter 方法。



@代表“Objective-C”的标志,证明您正在使用Objective-C语言

 

Objective-C语言关键词,@property与@synthesize配对使用。

 

功能:让编译好器自己主动编写一个与数据成员同名的方法声明来省去读写方法的声明。

 

如:

1、在头文件里:




C代码  ​【IOS】IOS高速入门之OC语法_字符串


  1. @property int count;  


等效于在头文件里声明2个方法:




C代码  ​【IOS】IOS高速入门之OC语法_字符串


  1. - (int)count;  
  2. -(void)setCount:(int)newCount;  

 

2、实现文件(.m)中




C代码  ​【IOS】IOS高速入门之OC语法_字符串


  1. @synthesize count;  


等效于在实现文件(.m)中实现2个方法。




C代码  ​【IOS】IOS高速入门之OC语法_字符串


  1. - (int)count  
  2. {  
  3.     return count;  
  4. }  
  5. -(void)setCount:(int)newCount  
  6. {  
  7.     count = newCount;  
  8. }  

  

以上等效的函数部分由编译器自己主动帮开发人员填充完毕,简化了编码输入工作量。




iOS 应用程序基于 Foundation 和 UIKit 框架



開始编程时,您主要使用 Foundation 和 UIKit框架,由于它们满足大多数应用程序开发的需求。



Foundation框架为全部应用程序提供主要的系统服务

您的应用程序以及 UIKit 和其它框架,都建立在 Foundation框架的基础结构之上。Foundation框架提供很多主要的对象类和数据类型,使其成为应用程序开发的基础。它还制定了一些约定(用于取消分配等任务),使您的代码更加一致,可再用性更好。



使用 Foundation:



创建和管理集,如数组和字典

訪问储存在应用程序中的图像和其它资源

创建和管理字符串

公布和观察通知

创建日期和时间对象

自己主动发现 IP网络上的设备

操控 URL 流

异步运行代码

在“您的首个 iOS应用程序”中,您就使用了 Foundation框架。比如,您使用了 NSString类的实例,将用户的输入储存在userName中。您还使用了 Foundation实例方法initWithFormat:,创建问候语字符串。



UIKit框架提供的类,可用于创建基于触摸的用户界面

全部 iOS 应用程序都基于 UIKit。没有这个框架,就无法交付应用程序。UIKit提供基础结构,用于在屏幕上画图、处理事件,以及创建通用用户界面元素。UIKit还通过管理屏幕上显示的内容,来组织复杂的应用程序。



使用 UIKit:



构建和管理用户界面

处理基于触摸和运动的事件

显示文本和网页内容

优化应用程序以实现多任务

创建自定用户界面元素

在“您的首个 iOS应用程序”中,您使用了 UIKit。检查应用程序怎样启动时,您看到了UIApplicationMain函数,它创建了UIApplication类(处理传入的用户事件)的一个实例。您实现了UITextFieldDelegate协议,以便在用户轻按“Done”键时,让键盘消失。其实,您使用了 UIKit中的 UITextField、UILabel和UIButton类,创建了整个界面。



模型: 变量username 数据



控制器:UI helloLayer



视图: button 文本




Objective-C 培训教程

主要章节提示:

第 一 章 程序总体语法结构

第 二 章 数据类型

第 三 章 字符串

第 四 章 内存管理

第 五 章 对象的初始化

第 六 章 存取器

第 七 章 继承

第 八 章 动态绑定和 id 类型

第 九 章 分类和协议

第 十 章 属性列表

第十一章 复制对象

第十二章 归档

第一章 程序总体语法结构

程序的头文件和源文件的扩展名分别为.h 和.m。凝视语法和 C 一样。 Object_C 中的 nil 相当于 NULL。

Object_C 中的 YES 和 NO 相当于 true 和 false。

这里再解说一下 YES 和 NO:

Object-c 提供了 BOOL 类型,但这个 BOOL 类型和 C++里的并不一样:在 C++里一切非 0 值的东西都

为 true,而为 0 值的为 false。可是 Object-c 里 1 为 true 并被宏定义为 YES,0 为 false 并被宏定义为 NO。

所以,假设读者写以下的代码,则肯定是错误的:

BOOL areIntsDifferent_faulty(int thing1,int thing2)

{

return (thing1-thing2);

}

if(areIntsDifferent_faulty(23,5) == YES)

{

}

由于 areIntsDifferent_faulty 方法返回的是两个整数的差,假设这个差不为 1,那么永远不会为 YES。

先了解程序的结构:

#import <Foundation/Foundation.h>

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

{

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

NSLog(@"Programming is fun!");

[pool drain];

return 0;

}

#import <Foundation/Foundation.h>

相当于#include 导入头文件 也有两种查找方式< … > 和" … "。导入该头文件是由于在程序结尾处用

到的其它类和函数的有关信息

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

这条语句为自己主动释放池在内存中保留了空间,就是在释放内存池的时候同一时候释放调当中的全部对象,

若对象要增加该池,仅仅要发送一条 autorelease 消息。

NSLog(@"Programming is fun!");

将显示常量字符串,类似于 printf 函数,而且它会自己主动在文本后面加入'\n'。当然当中也能够使用转义

字符。比如还有

NSLog(@"The sum of 50 and 25 is %i",sum);

[pool drain]; //释放内存池

[classOrInstance method];

左方括号是类的名称或者该类实例的名称,空格后面是方法(即消息)

获得对象:(从 Car 类获得其对象)

youCar = [Car new];

定义一个新类分为 2 部分:

@interface 部分

描写叙述类、类的数据成分以及类的方法

@implementation 部分

实现这些方法的实际代码

@interface 部分的一般格式:

@interface NewClassName : ParentClassName

{

memberDeclarations;

}

methoddeclarations;

@end

命名规则:以字母或下划线开头,之后能够是不论什么字母,下划线或者 0~9 数字组合,

约定:类名以大写字母开头,实例变量、对象以及方法的名称以小写字母開始。

每次创建新对象时,将同一时候创建一组新的实例变量且唯一。注意:在对象类型的右边都有一个 *号 ,

全部的对象变量都是指针类型。Id 类型已经被提前定义为指针类型,所以不须要加一个*号。

函数开头的(-)号或者(+)号表示:

(-) 该方法是实例方法(对类的特定实例运行一些操作);

(+)是类方法(即对类本身运行某些操作的方法,比如创建类的新实例)

函数的声明演示样例:

-(void)setNumerator :( int)n

第一个表示方法类型,返回类型,接着是方法名,方法接受的參数,參数类型,參数名

注:假设不带參数则不用使用“:”号

假设没有指定不论什么返回类型,那么默认是id类型,全部的输入參数默认也是id类型(id类型可用来引用

不论什么类型的对象)。

也许到如今你会觉得将对象赋给id类型变量会有问题。

注:不管在哪里,对象总是携带它的 isa 的保护成员(能够用来确定对象所属的类),所以

即使将它存储在id类型的通用对象变量中,也总是能够确定它的类。

具有多个參数的方法:

-/+ (return type) function_name : (parameter type) parameter1 otherParameter : (parameter_type) parameter2;

假设仅仅有一个參数,在 : 后面声明參数的类型和名称;假设有多个參数的话,每一个參数前面都要有一

个 : , 然后接着是參数类型和參数名称。但是大家可能还是认为非常奇怪。比方上面这个样例中,

otherParameter 这个是什么意思,在 objective c 中,对于有多个參数的函数,能够理解为将函数的名称拆

成了几个部分,每一个部分都是对紧接着的參数的一个解释。

如在 C++中:

void initializeRectangle(int x1, int y1, int x2, int y2);

但并不知道这些參数都是什么意思;但在 objective c 中,能够这样声明:

void initializeRectange: (int)x1 LeftUpY: (int)y1 RightBottomX: (int)x2

RightBottomY:(int)y2;

@implementation 部分的一般格式:

@implementation NewClassName

methodDefinitions;

@end

//NewClassName 表示的名称与@interface 部分的类名同样。

一个简单的演示样例:

//*********************************************************************************

//Fraction.h 文件

#import <Foundation/Foundation.h>

@interface Fraction : NSObject

{

int numerator;

int denominator;

}

-(void)print;

- (void) setNumberator : (int) n;

- (void) setDenominator : (int) d;

@end

//Fraction.m 实现文件

@implementation Fraction

-(void)print

{

NSLog(@"%i/%i",numerator,denominator);

}

- (void) setNumberator : (int) n

{

numerator = n;

}

- (void) setDenominator : (int) d

{

denominator = d;

}

@end

//*********************************************************************************

Fraction * myFraction = [[Fraction alloc] init] ;

获得对象的实例而且初始化了事实上例变量(能够这样理解:将 alloc 消息发送给 Fraction 类请求创建一

个新实例,然后向新创建的实例对象发送 init 消息来初始化该对象)。

第二种方法:

Fraction * myFraction = [Fraction new];可是通常使用第一种方式

对象调用方法 [myFraction setNumerator :1];

用完释放 Fraction 对象的方法: [myFraction release];

注:创建一个新对象,都要请求分配内存,在完毕对该对象的操作时,必须释放其所用的内存空间

i Phone 平台不支持垃圾回收机制

外部要訪问实例变量须要通过类的方法来检索其值,不能直接訪问

演示样例:

//*********************************************************************************

//Rectangle.h 文件

@interface Rectangle : NSObject

{

int width ;

int heigth ;

}

@property int width ,heigth;

- (int)area ;

- (int)perimeter ;

- (void)setWidth :( int)w andHeigth :( int)h ;

@end

//Rectangle.m 文件

#import "Rectangle.h"

@implementation Rectangle

@synthesize width , heigth ;

- (void)setWidth :( int)w andHeigth :( int)h

{

width = w ;

heigth = h ;

}

- (int)area

{

return width *heigth ;

}

- (int)perimeter

{

return (width +heigth)*2 ;

}

@end

以下是 Rectangle 的子类 Square

//Square.h 文件

# import "Rectangle.h"

@interface Square : Rectangle

- (void)setSide :( int)s ;

- (int)side ;

@end ;

//Square.m 文件

# import "Square.h"

@implementation Square :Rectangle

- (void)setSide :( int)s

{

[self setWidth : s andHeight : s]

}

- (int)side

{

return width ;

}

@end ;

//*********************************************************************************

self keyword用来指明对象是当前方法的接收者。

比如以下是一个子类(正方形)的方法实现:

- (void) setSide: (int)s

{

[self setWidth : s andHeight : s]

}

利用其父类(长方形)的 setWidth: andHeight:方法来实现的。

调用消息的类能够不知道怎样响应这个消息。假设它不知道怎样处理这个消息,它会自己主动的将这个消息转

给的父类,还不行就转给父类的父类,都没有找到就会报错。

与 C 语言兼容的地方:

预处理:

#define 语句和 c 一样

#运算符: #define str(x) #x

表示在调用该宏时,预处理程序依据宏參数创建 C 风格的常量字符串。

比如:str("hello")将产生"\"hello"\"

##运算符:

表示用于把两个标记连在一起

#import 语句相当于#include 语句,可是 #import 可自己主动防止同一个文件被导入多次。

#条件编译语句(#ifdef 、#endif 、 #else 、 #ifndef)和 C 一样

#undef 语句 消除特定名称的定义

其它主要的 C 语言特性:

数组、函数、指针、结构、联合的使用方法和 C 一样。

Compound Literal 是包括在括号之内的类型名称,之后是一个初始化列表。

比如 假设 intPtr 为 int * 类型:

intPtr = (int[100]){[0] = 1, [50] = 50, [99] = 99};

假设数组大小没有说明,则有初始化列表确定。

其它如循环语句(do while、while、for)、条件语句( if 语句(if-else、复合推断条件等)、switch 语句 )、

Boolean(YES NO)、条件运算符、goto 语句、空语句、逗号表达式、sizeof 运算符、命令行參数、位操作都

和 C 一样。

第二章 数据类型

Object-c 提供基本数据类型:int 、float 、double 、char

Int:

八进制 整型第一位为 0, NSLog 的格式符为: %o 显示的八进制不带前导 0

%#o 显示的八进制带前导 0

十六进制 以 0x 开头的整型,NSLog 的格式符为: %x 显示的十六进制不带前导 0x

%#x 显示的十六进制带前导 0x

若(%X 或%#X) 显示的十六进制用大写

Float:

NSLog 的格式符:%f

NSLog 的格式符:%e 科学计数法显示值

NSLog 的格式符:%g 指数的值小于-4 大于 5,採用%e,否则採用%f

十六进制的浮点常量包括前导 0x 或 0X,后面紧跟一个或多个十进制或十六进制数字,再后是 p 或 P,最后

是能够带符号的二进制指数。例:0x0.3p10 表示的值为 3/16*

10

2

注:若无特殊说明,Object-c 将全部的浮点常量看做 double 值,要显示 double 值可使用和 float

一样的格式符。

Char:

NSLog 的格式符:%c

long double 常量写成尾部带有字母 l 或者 L 的浮点常量。1.234e+7L

注:id 类型能够通过类型转化符能够将一般的 id 类型的对象转换成特定的对象。

类型 NSLog 格式符

八进制 十六进制 十进制

long int %lo %lx %li

long long int %llo %llx %lli

long double %Lg %Le %Lf

short int %ho %hx %hi

unsigned short int %ho %hx %hu

unsigned int %o %x %u

unsigned long int %lo %lx %lu

unsigned long long int %llo %llx %llu

id %p

_Bool 处理 Boolean(即 0 或 1)

_Complex 处理复数

_Imaginary 处理抽象数字

键盘输入:

int number;

scanf("%i",&number);

实例变量的初始化值默觉得 0

实例变量作用域的指令:

@protected 实例变量可被该类及不论什么子类中定义的方法直接訪问(默认的情况)。

@private 实例变量可被定义在该类的方法直接訪问,不能被子类定义的方法直接訪问。

@public 实例变量可被该类中定义的方法直接訪问,也可被其它类或模块中定义的方法訪

问。使得其它方法或函数能够通过(->)来訪问实例变量(不推荐用)。

@package 对于 64 位图像,能够在实现该类的图像的不论什么地方訪问这个实例变量。

在类中定义静态变量和 C 一样

voaltile 说明符和 const 正好相反,明白告诉编译器,指定类型变量的值会改变。(I/O port)

比方要将输出port的地址存储在 outPort 的变量中。

volatile char *outPort;

*outPort = 'O';

*outPort = 'N';

这样就能够避免编译器将第一个赋值语句从程序中删除

枚举数据类型、typedef 语法以及数据类型的转换和 C 也是一样。

第三章 字符串

一些实用的数据类型:

表示范围作用的结构体:NSRange:

有三种方式创建新的 NSRange

1、NSRange range;

range.location = 17;

range.length = 4;

2、NSRange range = {17 ,4};

3、NSRange range = NSMakeRange(17,4); (推荐)

表示用来处理几何图形的数据类型:NSPoint(点坐标)和 NSSize(长度和宽度)另一个矩形数据类

型(由点和大小复合而成)NSRect

Cocoa 提供创建这些数据类型方法:NSMakePoint( )、 NSMakeSize()和 NAMakeRect()

表示字符串的类 NSString

NSString *heigth =[NSString stringWithFormat : @"You heigth is %d feet,%d inches", 5,11];

创建的类对象包括了指向超类的指针、类名和指向类方法的列表的指针。类对象还包括一个 long 的数据 ,

为新创建的类对象指定大小。

返回字符串中的字符的个数:

unsigned int length = [heigth length];

返回 Bool 值的字符串比較函数:

- (BOOL) isEqualToString :(NSString *)aString;//比較两个字符串的内容是否相等

还能够使用 compare :方法

-(NSComparisonResult)compare :( NSString *)string;//逐个字符比較

不区分大写和小写的比較:

-(NSComparisonResult)compare :( NSString *)string options:( unsigned)mask;

注意:NSComparisonResult 是一个枚举值

options 是一个位掩码,即:

NSCaseInsensitiveSearch:不区分大写和小写

NSLiteralSearch: 进行全然比較,区分大写和小写

NSNumericSearch: 比較字符串的字符个数,而不是字符值

检查字符串是否以还有一个字符串开头

-(BOOL)hasPrefix : (NSString *)aString;

推断字符串是否是以还有一个字符串结尾

-(BOOL)hasSuffix : (NSString *)aString;

假设你想知道字符串内的某处是否包括其它字符串,使用 rangeOfString : 方法

-(NSRange)rangeOfString :( NSString *)aString;

NSString 是不可变的,NSMutableString 是可变的。用方法 stringWithCapacity :来创建

NSMutableString *string = [NSMutableString stringWithCapacity :42];

能够使用 appendString :或 appendFormat : 来附加新字符串:

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

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

能够使用 deleteCharactersInRange : 方法删除字符串中的字符

- (void) deleteCharactersInRange : (NSRange)range;

集合家族:

NSArray:用来存储对象的有序列表(随意类型的对象)

限制:仅仅能存储 Objective—C 的对象,不能存储 C 语言的基本数据类型(int、float、enum、struct、或者 NSArray

中的随机指针)。同一时候也不能存储 nil(对象的零值或 NULL 值)

//创建一个新的 NSArray

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

//获得包括的对象个数

- (unsigned)count;

//获得特定索引处的对象

- (id)objectAtIndex :( unsigned int)index ;

切分数组:

使用 componentsSeparatedByString : 来切分 NSArray,

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

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

使用 componentsJoinedByString : 来合并 NSArray 中的元素并创建字符串

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

NSArray 是不可变数组,数组中包括的对象是能够改变的,可是数组对象本身是不会改变的。

可变数组 NSMutableArray 通过类方法 arrayWithCapacity : 来创建可变数组

+ (id)arrayWithCapacity :( unsigned)numItems ;

NSMutableArray *array = [NSMutableArray arrayWithCapacity : 17];

使用 addObject : 在数组末尾加入对象

- (void)addObject :( id)anObject

删除特定索引的对象

- (void)removeObjectAtIndex :( unsigned)index;

注:可变数组还能够在特定索引处插入对象,替换对象,为数组排序, NSArray 还提供了大量好用的功能。

枚举:

NSEnumerator 用来描写叙述这样的集合迭代器运算的方法:

要想使用 NSEnumerator,须要通过 objectEnumerator 向数组请求枚举器:

- (NSEnumerator *) objectEnumerator;

能够像这样使用这种方法:

NSEnumerator *enumerator;

enumerator = [array objectEnumerator ];

注:若想从后向前枚举集合,用法 reverseobjectEnumerator ; 也能够使用。

获得枚举器以后,開始 while 循环,每次循环都向这个枚举器请求它的 nextObject

- (id)nextObject;//返回 nil 表明循环结束

注:在枚举的过程中不能改变数组容器。

高速枚举演示样例:

for(NSString *string in array){

NSLog(@"I found %@", string);

}

数组排序:

比如:一条记录就是一条卡片的信息,包含(NSString *name 和 NSString *email)

-(void)sort

{

[book sortUsingSelector:@selector(compareNames:)]

}

当中:

@selector(compareNames:)

//创建一个 SEL 类型的 selector,sortUsingSelector:使用该方法比較数组中的两个元素,

sortUsingSelector:方法须要完毕这种比較,它先调用这个指定的 selector 方法,然后向数组(接受

者)的第一条记录发送消息,比較其參数和此记录。指定方法的返回值为 NSComparisonResult 类型,返

回值为:若小于返回 NSOrderedAscending;相等返回 NSOrderedSame;大于返回 NSOrderedDescending

-(NSComparisonResult)compareNames:(id)element

{

return [name compare:[element name]];

}

NSDictionary:(keyword和定义的组合)

NSDictionary 通常在给定一个keyword(一般是一个 NSString 字符串)下存储一个数值(能够是不论什么类

型的对象)。然后你能够使用这个keyword查找对应的数值。

使用 dictionaryWithObjectsAndKeys :来创建字典

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

比如:

Tire *t1 = [Tire new];

NSDictionary *tires = [ NSDictionary dictionaryWithObjectsAndKeys :t1,@"front-left",nil];

用法 objectForKey :来获取字典中的值

- (id)objectForKey :( id)akey;

查找轮胎能够这样:

Tire *tire = [tires objectForkey : @"front-left"];

创建新的 NSMutableDictionary 对象,向类 NSMutableDictionary 发送 dictionary 消息。也能够使用

dictionaryWithCapacity : 方法来创建新的可变字典

+ (id)dictionaryWithCapacity :(unsigned int)numItems ;

能够用法 setObject :forKey :方法给字典加入元素

setObject : forKey :( id)aKey

以下是还有一种使用发送 dictionary 消息来创建字典的方法:

NSMutableDictionary *tires;

tires = [NSMutableDictionary dictionary] ;

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

. . .

注:若对字典中已有的keyword使用 setObject :forKey :方法,则用新的替换

能够使用 removeObjectForKey : 方法来删除可变字典中的一个keyword

- (void)removeObjectForKey :(id)aKey ;

注:不要去创建 NSString、NSArray 或 NSDictionary 的子类,实在要的话能够用复合的方式来解决这个问题。

使用这样的方法枚举词典:

for(NSString *key in g)

{

. . .

}

集合对象:

Set 是一组单值对象的集合,有可变和不可变,操作包含:搜索、加入、删除集合中的成员(仅用于可变

集合)、比較两个集合,计算两个集合的交集和并集等。

#import <Foundation/NSSet.h>

经常使用的 NSSet 方法

经常使用的 NSMutableSet 方法(NSSet 的子类)

注:NSInteger 不是一个对象,基本数据类型的 typedef,被 typedef 成 64 位的 long 或 32 位 int

各种数值:

NSNumber:

能够使用对象来封装基本数值。

NSNumber 类来包装基本数据类型。

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

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

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

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

还有包含无符号版本号和各种 long 型数据及 long long 整型数据

比如:NSNumber *number = [NSNumber numberWithInt : 42] ;

将一个基本类型封装到 NSNumber 后,能够使用下列方法又一次获得:

方法 说明

+(id)setWithObjects:obj1,obj2,...,nil 使用一列对象创建新集合

-(id)initWithObjects:obj1,obj2,...,ni l 使用一列对象初始化新分配的集合

-(NSUInteger)count 返回集合的成员个数

-(BOOL)containsObject:obj 确定集合是否包括 obj

-(BOOL)member:obj 使用 isEqual:方法确定集合是否包括 obj

-(NSEnumerator *)objectEnumerator 为集合中的全部对象返回一个

NSEnumerator 对象

-(BOOL)isSubsetOfSet:nsset 确定 receiver 的每一个成员是否都出如今

nsset 中

-(BOOL)intersectsSet:nsset 确定是否 receiver 中至少一个成员出现

在对象 nsset 中

-(BOOL)isEqualToSet:nsset 确定两个集合是否相等

方法 说明

-(id)setWithCapacity:size 创建新集合,使其具有存储 size 个成员的初始空间

-(id)initWithCapacity:siz e 将新分配的集合设置为 size 个成员的存储空间

-(void)addObject:obj 将对象 obj 加入到集合中

-(void)removeObject:obj 从集合中删除对象 obj

-(void)removeAllObjects 删除接受者的全部成员

-(void)unionSet:nsset 将对象 nsset 的全部成员昂加入到接受者

-(void)minusSet:nsset 从接受者中删除 nsset 的左右成员

-(void)intersectSet:nsset 将接受者中的全部不属于 nsset 的元素删除

- (char)charValue;

- (int)intValue;

- (float)floatValue;

- (BOOL)boolValue;

- (NSString *)stringValue;

NSValue:

NSNumber 实际上是 NSValue 的子类, NSValue 能够封装随意值。能够用 NSValue 将结构放入 NSArray

和 NSDictionary 中。

创建新的 NSValue:

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

objCType : (const char *)type;

@encode 编译器指令能够接受数据类型的名称并为你生成合适的字符串。

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

NSValue * value ;

value = [NSValue valueWithBytes : &rect objCType : @encode(NSRect)];

使用 getValue :来提取数值 (传递的是要存储这个数值的变量的地址)(先找地址再取值)

value = [array objectAtIndex : 0];

[ value getValue : & rect ] ;

注:Cocoa 提供了将经常使用的 struct 型数据转化成 NSValue 的便捷方法:

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

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

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

- (NSSize)sizeValue;

- (NSRect)rectValue;

- (NSPoint)pointValue;

NSNull:

在keyword下假设属性是 NSNull 表明没有这个属性,没有数值的话表明不知道是否有这个属性。 [NSNull

null] //总返回一样的值

+ (NSNull *)null;

比如:

[contast setObject : [NSNull null] forKey: @"home"];

訪问它:

id home = [contast objectForKey : @"home" ] ;

if ( home = = [NSNull null] ) {

. . .

}

NSFileManager 同意对文件系统进行操作(创建文件夹、删除文件、移动文件或获取文件信息)

//创建一个属于自己的 NSFileManager 对象

NSFileManager *manager = [NSFileManager defaultManager] ;

//将代字符‘~’替换成主文件夹

NSString *home = [@"~" stringByExpandingTildeInPath] ;

//输出文件的扩展名

- (NSString *)pathExtension

演示样例:翻查主文件夹,查找.jpg 文件并输出找到的文件列表

//*********************************************************************************

#import <Foundation/Foundation.h>

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

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

NSFileManager *manager;

manager = [NSFileManager defaultManager];

NSString *home;

home = [@"~" stringByExpandingTildeInPath];

NSDirectoryEnumerator *direnum;

direnum = [manager enumeratorAtPath: home];

NSMutableArray *files;

files = [NSMutableArray arrayWithCapacity: 42];

NSString *filename;

while (filename = [direnum nextObject]) {

if ([[filename pathExtension] isEqualTo: @"jpg"]) {

[files addObject: filename];

}

}

NSEnumerator *fileenum;

fileenum = [files objectEnumerator];

while (filename = [fileenum nextObject]) {

NSLog (@"%@", filename);

}

[pool drain];

return 0;

}

//*********************************************************************************

第四章 内存管理

自己主动释放池:

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

[pool drain];

其实程序中能够有多个自己主动释放池。自己主动释放池其实并不包括实际的对象本身,不过对释放的对

象的引用。通过向眼下的自己主动释放池发送一条 autorelease 消息,能够将一个对象加入到当中。

引用计数:

概念:创建对象时,将它的引用次数设置为 1,每一次必须保持该对象时,就发送一条 retain 消 息 ,

使其引用次数加 1。

[myFraction retain];

不再须要对象时,能够通过发送 release 消息,使对象的引用次数减 1。

[myFraction release];

当引用计数为 0 的时候,系统就会释放它的内存,通过向对象发送 dealloc 消息。通过向对象发送

retainCount 消息能够获得这个对象的引用计数,返回的是 NSUInteger 整数。仅仅要对象的引用计数不为 0,

系统就不会释放对象使用的内存。

将对象加入到不论什么类型的集合中都会使该对象的引用计数添加。从不论什么集合中删除对象都可以使其引

用计数降低。

内存中常量字符串没有引用计数机制,由于永远不能释放这些对象。这也适用于使用常量字符串初始

化的不可变字符串对象。

当某段代码须要訪问一个对象的时候,将对象的引用计数器加 1,当其为 0 表明不再有代码訪问该对象

了,即对象将被销毁(通过调用 dealloc 方法)。

一个简单的演示样例:

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

{

NSAutoreleasePool *pool;

pool = [[NSAutoreleasePool alloc] init];

RetainTracker *tracker;

tracker = [RetainTracker new]; // count: 1

[tracker retain]; // count: 2

[tracker autorelease]; // count: still 2

[tracker release]; // count: 1

NSLog (@"releasing pool");

[pool release];

// gets nuked, sends release to tracker

return (0);

}

当使用 alloc、new 或者通过 copy 消息(生成接受对象的一个副本)创建一个对象,对象的引用计数

器被设置成 1。发送 retain 消息将添加引用计数器,release 消息减 1。

要获得引用计数器的当前值,能够发送 retainCount 消息

- (id) retain ;

- (void) release ;

- (unsinged) retainCount ;

訪问方法中的保留和释放:

最好的方法设置的原则是(保持新的释放旧的,谁拥有对象谁就复制释放对象)

- (void ) setEngine : (Engine *) newEngine

{

[newEngine retain] ;

[engine release] ;

engine = newEngine;

}

内存管理规则

内存管理规则摘要:

1、释放对象,能够释放其所占的内存,规则是:不再使用创建或者保持的对象时,就释放它们。

2、发送一条 release 消息不一定销毁对象,仅仅有引用计数变为 0 时,才销毁这个对象。系统向

对象发送一条 dealloc 消息来释放它所占的内存。

3、自己主动释放池在释放池本身的时候自己主动释放池中的对象。系统向每一个对象发送一条 release 消

息,对引用计数变为 0 的对象发送一条 dealloc 消息来释放它所占的内存。

4、若你的方法中不再须要一个对象时,但须要返回它,可发送一条 autorelease 消息来增加自

获得途径 暂时对象 拥有对象

alloc/new/copy 不再使用时释放对象 在 dealloc 方法中释放对象

不论什么其它方法 不须要运行不论什么操作 获得对象时保留,在 dealloc 方法中释放对象

动释放池。

5、若使用 alloc 或 copy 方法(或使用 allocWithZone:、 copyWithZone:或 mutableCopy 方法

来直接创建对象,则由你负责释放它。每次 retain 对象时,应该 release 或 autoreleas 它。

6、除了上以规则中提到的方法之外,不必费心地释放其它方法返回的对象,这不是你的责任。

第五章 对象的初始化

//*********************************************************************************

//Tire.h 文件

#import <Cocoa/Cocoa.h>

@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

//Tire.m 文件

#import "Tire.h"

@implementation Tire

- (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

- (id) initWithPressure: (float) p

treadDepth: (float) td

{

if (self = [super init]) {

pressure = p;

treadDepth = td;

}

return (self);

} // initWithPressure:treadDepth:

- (void) setPressure: (float) p

{

pressure = p;

} // setPressure

- (float) pressure

{

return (pressure);

} // pressure

- (void) setTreadDepth: (float) td

{

treadDepth = td;

} // setTreadDepth

- (float) treadDepth

{

return (treadDepth);

} // treadDepth

- (NSString *) description

{

NSString *desc;

desc = [NSString stringWithFormat:

@"Tire: Pressure: %.1f TreadDepth: %.1f",

pressure, treadDepth];

return (desc);

} // description

@end // Tire

//*********************************************************************************

通常的写法:- (id)init

{

if (self = [super init]){

. . .

}

return (self) ;

}

注:在自己的初始化方法中,须要调用自己的指定的初始化函数或者超类的指定的初始化函数。一定

要将超类的初始化函数的值赋给 self 对象,并返回你自己的初始化方法的值。超类可能决定返回一个全然

不同的对象。

有些类包括多个以 init 开头的方法:

比如 NSString 类中的一些初始化方法:

NSString *emptyString = [[NSString alloc] init] ;

//返回一个空的字符串

NSString *string = [[NSString alloc] initWithFormat :@"%d or %d",25,624] ;

//返回一个字符串,其值为 25or624

NSString *string = [[NSString alloc] initWithContentOfFile :@"/tmp/words.txt"] ;

//使用指定路径上的文件里的内容初始化一个字符串

初始化函数的规则:

1、若不须要为自己的类创建初始化函数方法,仅仅须要 alloc 方法将内存清 0 的默认行为,不须要操心

init 方法。

2、若构造一个初始化函数,则一定要在自己的初始化函数中调用超类的指定的初始化函数。

3、若初始化函数不止一个,则须要选定一个指定的初始化函数,被选定的方法应该调用超类的指定

的初始化函数。

第六章 存取器

//*********************************************************************************

//Car.h 文件

#import <Cocoa/Cocoa.h>

@class Tire;

@class Engine;

@interface Car : NSObject {

NSString *name ;

NSMutableArray *tires ;

Engine *engine ;

}

@property (copy)NSString *name ;

@property (retain)Engine *engine ;

- (void) setTire : (Tire *) tire atIndex : (int) index

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

- (void) print ;

@end //Car

//*********************************************************************************

//*********************************************************************************

//Car.m 文件

#import "Car.h"

@implementation Car

@synthesize name ;

@synthesize engine ;

- (id) init

{

if(self = [super init]){

name = @"Car" ;

tires = [[NSMutableArray alloc] init] ;

int i ;

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

[tires addObject : [NSNull null]] ;

}

}

return (self) ;

}

-(void)dealloc

{

[name release] ;

[tires release] ;

[engine release] ;

[super dealloc] ;

}//dealloc

- (void) setTire : (Tire *) tire atIndex : (int) index

{

[tires replaceObjectAtIndex : index withObject : tire] ;

}//setTire:atIndex

- (Tire *) tireAtIndex : (int) index

{

Tire *tire;

tire = [tires objectAtIndex : index] ;

return (tire) ;

}//tireAtIndex

- (void) print

{

NSLog (@"%@ has:",[self tireAtIndex : i]) ;

int i ;

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

NSLog(@"%@",[self tireAtIndex : i]) ;

}

NSLog(@"%@", engine) ;

}//print

@end //Car

//*********************************************************************************

最后就能够在 main()函数中使用点表示法给对象赋值

Car *car = [[Car alloc] init] ;

car . name = @"Herbie" ;

car . engine = [[Slant6 alloc] init] ; //Slant6 是 Engine 的子类

若在类中定义属性:(接口中)

@property float rainHandling //表明类的对象具有 float 类型的属性,其名称:rainHandling,并且能够调

用- setRainHandling:来设置属性,调用 - rainHandling 来訪问属性。@property 的作用是自己主动声明属性的

setter 和 getter 方法。

实现中:

@synthesize rainHandling //表示创建该属性的訪问器

有时你可能希望实例变量有还有一个名称,而公开的属性有还有一个名称:

方法:

仅仅要在.h 文件里改动实例变量,然后改动@synthesize name = appel;编译器还将创建-setName:和 -name

方法,但在事实上现中使用 appel。

加入特性:

@property (readwrite,copy)NSString *name;//对象可读写,对象将被复制

@property (readwrite,retain)NSString *name;//对象可读写,对象将被保持

@property (readonly)NSString *name;////对象仅仅读

点表达式的妙用:

点表达式(.)在等号左边,该属性名称的 setter 方法将被调用。若在右边,则能够调用属性名称的 getter

方法。

注意:在使用特性的时候常常出现,提示訪问的对象不是 struct 类型,请检查你是否包括了使用的类

所须要的全部必须的头文件

该技术相同适用于 int、char、BOOL、struct 甚至能够定义一个 NSRect 类的对象的特性。

补充:

1、C/C++中支持的内存方式 Objective-C 都支持(比如 new,delete 或 malloc,free), Objective-C

也有自 己对象分配内存的方法:alloc,allocWithZone。假设出现内存警告,须要手动清除不

必要的内存对象。假设还不够用,内存继续增长,系统会强制应用退出。

2、数据类型的字节数相应表:

类型标识符 长度(字节) 范围 备注

char 1 -128 ~ 127 -2

7

~ (2

7

-1)

int 4 -2147483648 ~ 2147483647 -2

31

~ (2

31

- 1)

short int 2 -32768 ~ 32767 -2

15

~ (2

15

- 1)

long int 4 -2147483648 ~ 2147483647 -2

31

~ (2

31

- 1)

long long int 8 -2

63

~(2

63

-1)

long double 10 19 位有效位

double 8 15 位有效位

float 4 1.18*10

-38

~ 3.40*10

38

7 位有效位

unsigned short int 2 0 ~ (2

16

- 1)

unsigned int 4 0 ~ (2

32

-1)

unsigned long int 4 0 ~ (2

32

-1)

unsigned long long int 8 0~(2

64

-1)

BOOL 1 YES 或 NO

第七章 继承

关于继承,主要讲一下三点:

1.Objective-C 不支持多继承。

2.Square 类继承于 Rectangle 类的继承演示样例

//*************************************************************************************//

//Rectangle 类 声明

#import<Foundation/Foundation.h>

@interface Rectangle: NSObject //继承于根类 NSObject

{

int width;

int height;

}

@property int width, height; // 存取器属性

-(int) area;

-(int) perimeter;

@end

// Rectangle 类 定义

#import "Rectangle.h"

@implementation Rectangle

@synthesize width, height;

-(int) area

{

return width * height;

}

-(int) perimeter

{

return (width + height) * 2;

}

@end

// Square 类 声明

#import<Foundation/Foundation.h>

#import "Rectangle.h"

@interface Square: Rectangle // 继承

//Objective-C 不支持多继承,假设将该语句改为

//@interface Square: Rectangle, Rectangle1 编译器将不能识别

//能够通过 Objective-C 的分类和协议特性获取多继承的长处。

-(void) setSide: (int) s; //由于没有利用存取器

-(int) side;

@end

// Square 类 定义

#import "Square.h"

@implementation Square

-(void) setSide: (int) s

{

[self setWidth: s andHeight: s];

// self 指令在自身类中查找 setWidth: andHeight: 方法,查找不到,则调用其父类中的该方法

}

-(int) side

{

return width;

}

@end

//*************************************************************************************//

3.@class 指令

Rectangle 类仅仅存储了矩形大小。如今要加入原点(x,y)的概念。因此,定义一个名为 XYPoint 的类。

//***********************************************************************************************//

// XYPoint 类 声明

#import<Foundation/Foundation.h>

@interface XYPoint: NSObject

{

int x;

int y;

}

@property int x, y; //存取器属性

-(void) setX: (int)xVal andY: (int) yVal;

@end

// XYPoint 类 定义

#import "XYPoint.h"

@implementation XYPoint

@synthesize x, y;

-(void) setX: (int) xVal andY: (int) yVal

{

x = xVal;

y = yVal;

}

@end

//声明

#import<Foundation/Foundation.h>

@class XYPoint; //取代#import "XYPoint.h"

//使用@class 指令提高效率,编译器不须要处理整个 XYPoint.h 文件, //仅仅须要知道 XYPoint 是一

个类名,可是假设须要引用 XYPoint 类中方 // 法, @class 指令是不够的,必须用 #import

"XYPoint.h"。

@interface Rectangle: NSObject

{

int width;

int height;

XYPoint *origin;

}

-(int) area;

-(int) perimeter;

@end

//***********************************************************************************************//

第八章 动态绑定和 id 类型

//*************************************************************************************//

#import "Fraction.h"

#import "Complex.h" //两个类中都含有 print 方法

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

{

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

Fraction *f = [[Fraction alloc] init];

Complex *c = [[Complex alloc] init];

id dataValue; //声明 dataValue 为 id 类型

//对对象的成员变量赋值

[f setTo: 2 over: 5];

[c setReal:10.0 andImaginary: 2.5];

dataValue = f; //将 Fraction f 存储到 dataValue 中

[dataValue print]; //如今 dataValue 能够调用用于 Fraction 对象的不论什么方法

dataValue = c; //将 Complex c 存储到 dataValue 中

[dataValue print]; //调用用于 Complex 对象的不论什么方法

//问题:两次遇到 [dataValue print]; 而且 Fraction 和 Complex 类中都定义有 print 方法,系统怎样知道

调用哪个?

//答案:在程序运行期间,当系统准备将 print 消息发送给 dataValue 时,它首先检查 dataValue 中存储

的对象所属的类。

[f release];

[c release];

[pool drain];

return 0;

}

//************************************************************************************//

第九章 分类和协议

9.1 分类

通过分类(category)能够以模块的方式向现有的类加入方法。它提供了一种简单的方式,用它能够将类

的定义模块化到相关方法的组或分类中。它还提供了扩展现有类定义的简便方式,而且不必訪问类的源代

码,也无需创建子类。

对于 Fraction 类,除了将两个分数相加的 add:方法外,还想要拥有将两个分数相减、相乘和相除的方

法。

//**********************************************************************************************//

// Fraction 类声明

#import<Foundation/Foundation.h>

@interface Fraction : NSObject

{

int numerator;

int denominator;

}

@property int numerator, deniminator;

-(void) setTo: (int) n over: (int) d;

-(Fraction *) add: (Fraction *) f; // 声明分数的 加法函数

-(void) print;

@end

//*********************************************************************************************//

如今,从接口部分删除 add:方法,并将其加入到新分类,同一时候加入其它三种要实现的数学运算。看一

下新 MathOps 分类的接口部分。

//*********************************************************************************************//

#import<Foundation/Foundation.h>

#import "Fraction.h"

@interface Fraction (MathOps)

-(Fraction *) add: (Fraction *) f; // 加法函数

-(Fraction *) mul: (Fraction *) f; // 乘法函数

-(Fraction *) sub: (Fraction *) f; // 减法函数

-(Fraction *) div: (Fraction *) f; // 除法函数

@end

//*********************************************************************************************//

// #inport "Fraction.h" 这里既是分类接口部分的定义,也是对现有接口部分的扩展,所以必须包含原始接口部分

// @interface Fraction (MathOps) 告诉编译器正在为 Fraction 类定义新的分类,名称为 MathOps。

能够在一个实现文件里定义 Fraction.h 接口部分中的全部方法,以及 MathOps 分类中的全部方法。

也能够在单独的实现部分定义分类的方法。在这样的情况下,这些方法的实现部分还必须找出方法所属

的分类。与接口部分一样,通过将分类名称括在类名称之后的圆括号里来确定方法所属的分类,例如以下所看到的 :

@implementation Fraction (MathOps)

// code for category methods

@end

关于分类的一些注意事项

A、 虽然分类能够訪问原始类的实例变量,可是它不能加入自身的不论什么变量。假设须要加入变量 ,

能够考虑创建子类。

B、 分类能够重载该类中的还有一个方法,可是通常觉得这样的做法不可取。由于,重载之后,再不

能訪问原来的方法。

C、 能够拥有非常多分类。

D、 和一般接口部分不同的是,不必实现分类中的全部方法。这对于程序扩展非常实用,能够在该

分类中声明全部方法,然后在一段时间之后才实现它。

E、 通过使用分类加入新方法来扩展类不仅会影响这个类,同一时候也会影响它的全部子类。

9.2 协议

协议的声明类似于类接口的声明,有一点不同的是,协议没有父类,而且不能定义成员变量。以下的

样例演示了仅仅有一个方法的协议的声明:

@protocol MyProtocol

- (void)myProtocolMethod;

@end

协议是多个类共享的一个方法列表,协议中列出的方法没有对应的实现。假设一个类採用 MyProtocol

协议,则必须实现名为 myProtocolMethod 的方法。

通过在@interface 行的一对尖括号<...>内列出协议名称,能够告知编译器你正在採用一个协议。这项

协议的名称放在类名和它的父类名称之后,例如以下所看到的:

@interface AddressBook: NSObject <myProtocol>

这说明, AddressBook 是父类为 AddressBook 的对象,而且它遵守 myProtocolMethod 协议。在

AddressBook 的实现部分,编译器期望找到定义的 myProtocolMethod 方法。

假设採用多项协议,仅仅需把它们都列在尖括号里,用逗号分开:

@interface AddressBook: NSObject < myProtocol , yourProtocol >

以上代码告知编译器 AddressBook 类採用 myProtocolMethod 和 yourProtocolMethod 协议。这次,编

译器将期望在 AddressBook 的实现部分看到为这些协议列出的全部方法的实现。

有关协议的注意事项:

A、假设一个类遵守某项协议,那么它的子类也遵守该协议。

B、协议不引用不论什么类,它是无类的(classless)。不论什么类都能够遵守某项协议。

C、通过在类型名称之后的尖括号里加入协议名称,能够借助编译器的帮助来检查变量的一致性,如

下:

id <Drawing> currentObject;

这告知编译器 currentObject 将包括遵守 Drawing 协议的对象。假设向 currentObject 指派静态类型的对

象,这个对象不遵守 Drawing 协议,编译器将给出 warning。

再次提到 id 类型,假设向 currentObject 指派一个 id 变量,不会产生这条消息,由于编译器不知道存

储在 id 变量中的对象是否遵守 Drawing 协议。

D、假设这个变量保存的对象遵守多项协议,则能够列出多项协议,例如以下:

id <Drawing, Drawing 1> currentObject;

E、定义一项协议时,能够扩展现有协议的定义。下面协议

@protocol Drawing3D <Drawing>

说明 Drawing3D 协议也採用了 Drawing 协议。因此採用 Drawing3D 协议的类都必须实现此协议列出

的方法,以及 Drawing 协议的方法。

F、分类也能够採用一项协议,如:

@interface Fraction (stuff) <NSCopying, NSCoding>

此处,Fraction 拥有一个分类 stuff,这个分类採用了 NSCopying 和 NSCoding 协议。

第十章 属性列表

说 明 : “Objective-C 编程人员能够使用与 C 绑定的全部工具,比如标准 C 库函数。能够使用 malloc()

和 free()函数处理动态内存管理问题,或者使用 open(), read(), write(), fopen()和 fread()函数处理文件。 ”

属性列表类包含 NSArray、NSDictionary、NSString、NSNumber、NSDate 和 NSData。

10.1 NSDate

NSDate 是用于处理日期和时间的基础类。能够使用[NSDate date];获取当前的日期和时间,它是一个

自己主动释放对象。下面代码:

NSDate *date = [NSDate data];

NSLog (@”today is %@”, date);

将输出:

Today is 2009-11-10 19:23:02

还能够获取与当前时间相隔一定时差的日期。比如,24 小时之间的确切日期

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

NSLog (@”yesterday is %@”, yesterday);

将输出

Yesterday is 2009-11-19 19:23:02

+dateWithTimeIntervalSinceNow: 接受一个 NSTimeInterval 參数,该參数是一个双精度值,表示以秒为

单位的时间间隔。通过该參数能够指定时间偏移的方式:对于将来的时间,使用正的时间间隔;对于过去

的时间,使用负的时间间隔。

10.2 NSData

NSData 类包装了大量字节。我们能够获得数据的长度和指向字节起始位置的指针。以下的 NSData 对

象将保存一个普通的 C 字符串(一个字节序列),然后输出数据:

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

NSData *data = [NSData dataWithBytes: string length: strlen(string) + 1];

NSLog(@”data is %@”, data);

输出结果:

data is <4869…. ……. 2100>

这是一个十六进制数据块实际上就是上面的字符串, 0x48 代表字符 H,0x69 代表字符 i,等等。

strlen(string) + 1 中的“+1”用于包括 C 字符串所需的尾部的零字节(输出结果末尾的 00)。

能够使用%s 格式的说明符输出字符串:

NSLog (@”%d bytes string is ‘%s’ ”, [data length], [data bytes]);

//-length 方法给出字节数 -bytes 方法给出指向字符串起始位置的指针

输出结果例如以下所看到的:

30 bytes string is ‘Hi there, this is a C string! ’

NSData 对象是不可改变的,它们被创建后就不能改变。 NSMutableData 支持在数据内容中加入和删除

字节。

10.3 写入和读取属性列表

集合属性列表类(NSArray、NSDictionary)具有一个-writeToFile : atomically: 方法,用于将属性列表

写入文件。NSString 和 NSData 也具有 writeToFile : atomically: 方法,可是仅仅能写出字符串和数据块。

因此,我们能够将字符串存入一个数组,然后保存该数组:

NSArray *phrase;

phrase = [NSArray arrayWithObjects: @”I”, @”seem”, @”to”, @”be”, @”a”, @”verb”, nil ];

[phrase writeToFile: @”/tmp/verbiage.txt” atomically: YES];

如今假设看一下文件/tmp/verbiage.txt,应该能够看到例如以下代码:

//***********************************************************************************************//

<?xml version=”1.0” encoding=”UTF-8”?>

<!DOCTYPE plist PUBLIC “-//Apple//DTD PLIST 1.0//EN”

“http://www.apple.com/DTDs/PropertyList-1.0.dtd”>

<plist version=”1.0”>

<array>

<string>I<string >

< string >seem<string >

< string >to< string >

< string >be< string >

< string >a< string >

< string >verb< string >

</array>

</plist>

//***********************************************************************************************//

尽管繁琐,可是正是我们要保存的内容:一个字符串数组。这些属性列表文件能够为随意复杂的形式 ,

能够包括字符串、数字和日期数组的字典数组。

如今已经将 verbiage.txt 文件保存在了磁盘上,能够使用+arrayWithContentsOfFile: 方法读取该文件。

代码例如以下所看到的:

NSArray *phrase2 = [NSArray arrayWithContentsOfFile: @” /tmp/verbiage.txt”];

NSLog (@”%@”, phrase2);

输出结果正好与前面保存的形式相匹配:

I,

seem,

to,

be,

a,

verb

)

writeToFile : 方法中的 atomically: 參数的值为 BOOL 类型,用于通知是否应该首先将文件保存在暂时

文件里,当文件成功保存后,再将该暂时文件和原始文件交换。这是一种安全机制。

第十一章 复制对象

11.1 首先回想继承部分

//***********************************************************************************************//

// XYPoint 类 声明

#import<Foundation/Foundation.h>

@interface XYPoint: NSObject

{

int x;

int y;

}

@property int x, y; //存取器属性

-(void) setX: (int)xVal andY: (int) yVal;

@end

//***********************************************************************************************//

//***********************************************************************************************//

// XYPoint 类 定义

#import "XYPoint.h"

@implementation XYPoint

@synthesize x, y;

-(void) setX: (int) xVal andY: (int) yVal

{

x = xVal;

y = yVal;

}

@end

//***********************************************************************************************//

//***********************************************************************************************//

// Rectangle 类 声明

#import<Foundation/Foundation.h>

@class XYPoint;

@interface Rectangle: NSObject

{

int width;

int height;

XYPoint *origin;

}

@property int width, height; // 存取器属性

-(XYPoint *) origin;

-(void) setOrigin: (XYPoint *) pt;

-(void) setWidth: (int) w andHeight: (int) h;

-(int) area;

-(int) perimeter;

@end

//***********************************************************************************************//

//***********************************************************************************************//

// Rectangle 类 定义

#import "Rectangle.h"

@implementation Rectangle

@synthesize width, height;

-(void) setWidth: (int) w andHeight: (int) h

{

width = w;

height = h;

}

-(void) setOrigin: (XYPoint *) pt

{

origin = pt;

}

-(int) area

{

return width * height;

}

-(int) perimeter

{

return (width + height) * 2;

}

-(XYPoint *) origin

{

return origin;

}

@end

//***********************************************************************************************//

//***********************************************************************************************//

#import "Rectangle.h"

#import "XYPoint.h"

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

{

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

Rectangle *myRect = [[Rectangle alloc] init];

[myRect setWidth: 5 andHeight: 8];

XYPoint *myPoint = [[XYPoint alloc] init];

[myPoint setX: 100 andY: 200];

myRect.origin = myPoint; //赋值

NSLog (@"Origin at (%i, %i)", myRect.origin.x, myRect.origin.y);

[myPoint setX: 50 andY: 50];

NSLog (@"Origin at (%i, %i)", myRect.origin.x, myRect.origin.y);

[myRect release];

[myPoint release];

[pool drain];

return 0;

}

//***********************************************************************************************//

myRect.origin = myPoint;

这样赋值的结果不过将对象 myPoint 的地址拷贝到 myRect.origin 中。在赋值操作结束时,两个变量

都指向内存中的同一个地址。

所以,将一个变量赋值给还有一个对象不过创建还有一个对这个对象的引用。假设 dataArray 和 dataArray2

都是 NSMutableArray 对象,那么语句

dataArray2 = dataArray;

[dataArray2 removeObjectAtIndex: 0];

将从这两个变量引用的同一个数组中删除第一个元素。

//***********************************************************************************************//

#import <Foundation/NSObject.h>

#import <Foundation/NSArray.h>

#import <Foundation/NSString.h>

#import <Foundation/NSAutoreleasePool.h>

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

{

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

NSMutableArray *dataArray = [NSMutableArray arrayWithObjects:

@"one", @"two", @"three", @"four", nil];

NSMutableArray *dataArray2;

dataArray2 = dataArray;

[dataArray2 removeObjectAtIndex: 0];

NSLog(@"dataArray: ");

for( NSString *elem in dataArray )

NSLog(@"%@", elem);

NSLog(@"dataArray2: ");

for( NSString *elem in dataArray2 )

NSLog(@"%@", elem);

//下面输出结果是:

// dataArray:

// two

// three

// four

// dataArray2:

// two

// three

// four

//以下開始 Copy

dataArray2 = [dataArray mutableCopy];

[dataArray2 removeObjectAtIndex: 0];

NSLog(@"dataArray: ");

for( NSString *elem in dataArray )

NSLog(@"%@", elem);

NSLog(@"dataArray2: ");

for( NSString *elem in dataArray2 )

NSLog(@"%@", elem);

//下面输出结果是:

// dataArray:

// two

// three

// four

//

// dataArray2:

// three

// four

[dataArray2 release];

[pool drain];

return 0;

}

//***********************************************************************************************//

11.2 copy 和 mutableCopy

利用名为 copy 和 mutableCopy 的方法,能够创建对象的副本。结合 NSMutableArray 对象 dataArray

和 dataArray2,语句

dataArray2 = [dataArray mutableCopy];

在内存中创建了一个新的 dataArray 副本,并复制了它的全部元素。随后,运行语句 [dataArray2

removeObjectAtIndex: 0];

删除了 dataArray2 中的第一个元素,可是不会删除 dataArray 中的。

注意:

A、产生一个对象的可变副本并不要求被复制的对象本身是可变的。也能够创建可变对象的不可

变副本。

B、在产生数组的副本时,数组中每一个元素的保持计数将通过复制操作自己主动增 1。所以,须要

[dataArray2 release]; 释放它的内存。

11.3 浅复制和深复制

浅复制代码分析:

//***********************************************************************************************//

#import <Foundation/NSObject.h>

#import <Foundation/NSArray.h>

#import <Foundation/NSString.h>

#import <Foundation/NSAutoreleasePool.h>

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

{

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

NSMutableArray *dataArray = [NSMutableArray arrayWithObjects:

[NSMutableString stringWithstring: @"one"],

[NSMutableString stringWithstring: @"two"],

[NSMutableString stringWithstring: @"three"],

nil

];

NSMutableArray *dataArray2;

NSMutableString *mStr;

NSLog(@"dataArray: ");

for( NSString *elem in dataArray )

NSLog(@"%@", elem);

//输出结果:

// dataArray:

// one

// two

// three

dataArray2 = [dataArray mutableCopy];

mStr = [dataArray objectAtIndex: 0]; //检索 dataArray 的第一个元素

[mStr appendString: @"ONE"]; //将字符串附加到这个元素

NSLog(@"dataArray: ");

for( NSString *elem in dataArray )

NSLog(@"%@", elem);

NSLog(@"dataArray2: ");

for( NSString *elem in dataArray2 )

NSLog(@"%@", elem);

//下面输出结果是:

// dataArray:

// oneONE

// two

// three

// dataArray2:

// oneONE

// two

// three

[dataArray2 release];

[pool drain];

return 0;

}

//***********************************************************************************************//

dataArray 的第一元素发生改变:从集合中获取元素时,得到了这个元素的一个新引用,但并非一个

新副本。所以,对 dataArray 调用 objectAtIndex: 方法时,返回的对象与 dataArray 中的第一个元素都指向

内存中的同一个对象。随后,改动 string 对象的 mStr 的副作用就是同一时候改变了 dataArray 的第一个元素。

dataArray2 的第一元素发生改变:这与默认的浅复制方式有关。使用 mutableCopy 方法复制数组时,

在内存中为新的数组对象分配了空间,而且将单个元素拷贝到新数组中。可是,这不过将一个数组的元

素拷贝到还有一个数组。那么,这两个数组中的每一个元素指向内存中的同一个字符串。这与将一个对象复制

给还有一个对象没有什么差别。

要为数组中每一个元素创建全然不同的副本,须要运行所谓的深复制,也就是要创建数组中的每一个对象

内容的副本。比如,如果想要更改上面 dataArray2 的第一个元素,可是不更改 dataArray 的第一个元素,

能够创建一个新字符串,并将它存储到 dataArray2 的第一个位置,例如以下所看到的:

mStr = [NSMutableString stringWithstring: [dataArray2 objectAtIndex: 0] ];

然后,能够更改 mStr,并使用 replaceObjectAtIndex: withObject: 方法将它加入到数组中,例如以下所看到的:

[mStr appendString @”ONE”];

[dataArray2 replaceObjectAtIndex: 0 withObject: mStr];

这样,替换数组中的对象后, mStr 和 dataArray2 的第一个元素仍指向内存中的同一个对象。这意味着

随后在程序中对 mStr 做的改动也将更改数组的第一个元素。

有关深复制的内容,将在下一章继续学习。

第十二章 归档

归档是指用某种格式来保存一个或多个对象,以便以后还原这些对象的过程。即包含将多个对象写入

文件,以便以后读回该对象。

两种归档的方法:属性列表和带键值的编码

若对象是 NSString、NSDictionary、NSArray、NSData、NSNumber 对象时,能够使用 writeToFile:

atomically:方法将数据写到文件里,是以属性列表的形式写到文件里的。

參数 atomically 为 YES,表示先将数据写到暂时备份文件里,一旦成功,再转移到文件里。

演示样例:

//*****************************************************************************

# import<Foundation/NSObject.h>

# import<Foundation/NSString.h>

# import<Foundation/NSDictionary.h>

# import<Foundation/NSAutoreleasePool.h>

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

{

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

NSDictionary *glossary1 =

[NSDictionary. dictionaryWithObjectsAndKeys :

@"A class defined so other classes can inhert",@"abstract class",

@"To implement all the method defined in a protocol",@"adopt",

@"Storing an object for later use",@"archiving",

nil

];

if ([glossary writeToFile : @"glossary "atomically : YES] == NO)

NSLog(@"Save to file failed !");

NSDictionary *glossary2 = [NSDictionary dictionaryWithContentOfFile:"glossary"] ;

for (NSString *key in glossary2)

NSLog(@"%@:%@",key,[glossary objectForKey:key]);

[pool drain] ;

return 0 ;

}

//*****************************************************************************

glossary 文件中保存的数据格式是<key>...</key> <string>...</string>

要读回数据使用 dataWithContentOfFile:方法

要读回字符串对象使用 stringWithContentOfFile:方法

要读回依据字典创建的属性列表使用 dictionaryWithContentOfFile:方法或者

arrayWithContentOfFile:方法

注:属性列表能够来自不论什么的源,能够来自文本编辑器或者 Property List Editor 程序来

创建的属性列表。

NSKeyedArchiver 类

使用 NSKeyedArchiver 类创建带键的档案,在带键的档案中,每一个归档的字段都有一个名称。归档某

个对象的时候,会为他提供一个名称,即键。从归档中检索该对象的时候,是依据这个键来检索它的。这

样,能够依照随意的顺序将对象写入归档并进行检索。另外,假设向类加入了新的实例变量或删除了实例

变量,程序也能够进行处理。

NSKeyedArchiver 类中的 archiveRootObject:toFile:方法将数据对象存储到磁盘上。

比如:

NSDictionary *glossary = [...];

[NSKeyedArchiver archiveRootObject:glossary toFile:@"file"];

通过 NSKeyedUnarchiver 类中的 unArchiveObjectWithFile:方法将创建的归档文件读入运行的程序中。

比如:

NSDictionary *glossary = [NSKeyedUnarchiver unArchiveObjectWithFile:@“file”]

//将指定的文件打开并读取文件的内容。该文件必须是前面归档操作的结果。能够为文件指定完整路径名

或相对路径名。

编码方法和解码方法:

依照<NSCoding>协议,在类定义中加入编码方法 encodeWithCoder:方法和解码方法 initWithCoder:

方法实现的。

对于基本 objective-C 类(NSString、NSArray、NSDictionary、NSSet、NSDate、NSNumber、NSData)

使用 encodeObject:forKey:编码方法和 decodeObjectt:forKey:解码方法

若要确保继承的实例变量也被编码:

[super encodeWithCoder:encoder];

若要确保继承的实例变量也被解码:

[super initWithCoder:encoder];

encodeObject:forKey:方法能够用于不论什么在其类中实现对 encodeWithCoder:方法的对象。相同

decodeObject:forKey:方法传递在编码时用的同样的键,就能够解码每一个实例。

注:

另一些基本数据类型,如(char、short、long、long long)在上表中没有列出,此时你必须确定数据

对象的大小并使用对应的例程。比如:要归档 short int 的数据,首先将其保存在 int 中,然后使用 encodeInt:

在带键的档案中编码和解码基本数据类型

编码方法 解码方法

encodeBool:forKey: decodeBool:forKey:

encodeInt:forKey: decodeInt:forKey:

encodeInt32:forKey: decodeInt32:forKey

encodeInt64:forKey: decodeInt64:forKey:

encodeFloat:forKey: decodeFloat:forKey:

encodeDouble:forKey: decodeDouble:forKey:

forKey:归档它,反向使用 decodeInt:forKey:能够恢复它,最后将其赋值给 short int 的数据。

演示样例:

//*****************************************************************************

# import<Foundation/NSObject.h>

# import<Foundation/NSString.h>

# import<Foundation/NSKeyedArchiver.h>

@interface AddressCard:NSObject<NSCoding,NSCopying>

{

NSString *name;

NSString *email;

}

@property (copy,nonatomic) NSString *name,*email ;

-(void)setName:(NSString *)theName andEmail:(NSString *)theEmail;

...

@end

//以下是加入到当中的编码和解码的方法

-(void)encodeWithCoder:( NSCoder*)encoder

{

[encoder encodeObject:name forKey:@"AddressCardName"];

[encoder encodeObject:email forKey:@"AddressCardEmail"] ;

}

-(id)initWithCoder:( NSCoder*)decoder

{

name = [[decoder decodeObjectforKey:@"AddressCardName"] retain];

email = [[decoder decodeObjectforKey:@"AddressCardEmail"] retain];

}

//*****************************************************************************

使用 NSData 创建自己定义档案:

NSMutableData *dataArea = [NSMutableData data];

NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc]

initForWritingWithMutableData:dataArea];

//以指定要写入归档数据的存储空间,即 dataArea。此时就能够向 archiver 发送编码消息,以归档程序中的

对象。全部编码消息在收到 finishEncoding 消息之前都被归档并存储在指定的数据空间中。

最后向 dataArea 发送 writeToFile:atomically:消息。请求它把它的数据写到指定的文件里(若文件名称

为 file)

[dataArea writeToFile:@"file" atomically:YES];

相反若要从档案文件里恢复数据和归档工作相反:首先,分配一个数据空间。然后,把档案中的数据

读入该空间,接着创建一个 NSKeyedUnarchiver 对象,告知它从空间解码数据。必须调用解码方法来提取

和解码归档的对象,做完以后,向 NSKeyedUnarchiver 对象发送一条 finishEncoding 消息。

使用归档文件复制对象(深复制):

使用 Foundation 的归档能力来创建对象的深复制。在归档和解归档的过程中产生的字符串的新副本。

须要生成一个对象(或不支持 NSCopying 协议的对象)的深复制时,记住使用这项技术。

比如:

实现复制

NSMutableArray *dataArray = [...];

NSMutableArray *dataArray2;

NSData *data = [NSKeyedArchiver archivedDataWithRootObject:dataArray];

dataArray2 = [NSKeyedUnarchiver unarchivedObjectWithData:data];

甚至能够避免中间赋值即,

NSMutableArray *dataArray2 = [NSKeyedUnarchiver unarchivedObjectWithData:

[NSKeyedArchiver archivedDataWithRootObject:dataArray]];

演示样例:

//*****************************************************************************

# import<Foundation/NSObject.h>

# import<Foundation/NSString.h>

# import<Foundation/NSArray.h>

# import<Foundation/NSAutoreleasePool.h>

# import<Foundation/NSKeyedArchiver.h>

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

{

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

NSData *data ;

NSMutableArray *dataArray = [ NSMutableArray arrayWithObjects :

[ NSMutableArray stringWithString : @"one"] ,

[ NSMutableArray stringWithString : @"two"] ,

[ NSMutableArray stringWithString : @"three"] ,

nil

] ;

NSMutableArray *dataArray2 ;

NSMutableString *mStr ;

data = [NSKeyedArchiver archivedDataWithRootObject:dataArray];

dataArray2 = [NSKeyedUnarchiver unarchivedObjectWithData:data];

mStr = [dataArray2 objectAnIndex : 0] ;

[mStr appendString :@"ONE"] ;

NSLog(@"dataArray:");

for(NSString *elem in dataArray)

NSLog(@"%@",elem);

NSLog(@"\ndataArray2:");

for(NSString *elem in dataArray2)

NSLog(@"%@",elem);

[pool drain] ;

return 0;

}

//*****************************************************************************

结果例如以下:

//-------------------------------------------------------------------------------------------------------------------dataArray:

one

two

three

dataArray2:

oneONE

two

Three

//-------------------------------------------------------------------------------------------------------------------