读《Objective-C培训资料》的摘要
#definenilNULL
bool型与c/c++是一样的,非0为TRUE/YES,0为FALSE/NO,用if(flag==YES)就要注意了,此时YES就是1,不过你直接用if(flag).....来判断就只要不是0就成立。
#import和c/C++的include一样也有<>和""两种查找方式。但是它不怕重复引入。
NSLog()会自动在输出后加\n,相当于System.out.println().
Objective-C的对象需要直接或间接的继承自NSObject。Objective-C的@interface相当java的class,而@protocol才是Java的interface。类声明的基本方式如下:
@interface<#class#>:<#superclass#>
{
<#ivars#>
}
<#methods#>
@end
@implementation<#class#>
<#methods#>
@end
(-)表示实例方法
(+)是类方法,即静态方法
函数的形式是:-(void)setNum:(int)n;
多个参数用-(void)setNum:(int)n:(int)m;用[carsetNum(1:3)];调用
或者给第一个之后的参数指定参数名,如
-(void)setNum:(int)nandHeight:(int)handWidth:(int)w;用[carsetNum:1andHeight:5andWidth:10];
Objective-C的方法参数有点怪异,第一个参数是没有参数名的,如果硬要说有,那就是方法名,统一说来,见到冒号,冒号前面那个就是参数名
没有指定返回类型就是id类型,输入参数默认也是id类型。
实例化:
Car*car=[[Caralloc]init];或Car*car=[Carnew];通常用前一种。
释放对象[carrelease];iPhone平台不支持垃圾回收机制。
#define和C的一样
数据声明及初始化intPtr=(int[100){[0]=1,[50]=50,[51]=200};
intnumber;
scanf("%i",&number);
NSLog(@"Numberis:%i",number);
输入
@protected@private$public@package修饰变量,voaltile和const
所有方法都是public的,私有方法该如何声明呢?
NSString和NSMutableString一个是不可变,一个是可变的,相应的有NSArray和NSMutableArray.
NSLog(@"StringHERE%@",@"Helloworld.");
[objectretain][objectrelease]分别使引用计数加1和减1。
NSAutoreleasePool*pool=[[NSAutoreleasePoolalloc]init];
RetainTracker*tracker;
tracker=[RetainTrackernew];//count:1
[trackerretain];//count:2
[trackerautorelease];//count:still2
[trackerrelease];//count:1
NSLog(@"releasingpool");
[poolrelease];
//getsnuked,sendsreleasetotracker
当使用alloc、new或者通过copy消息(生成接受对象的一个副本)创建一个对象,对象的引用计数器被设置成1。发送retain消息将增加引用计数器,release消息减1。
不必费心地释放其他方法返回的对象,这不是你的责任
通常的写法:
-(id)init
{
if(self=[superinit]){
...
}
return(self);
}
注:在自己的初始化方法中,需要调用自己的指定的初始化函数或者超类的指定的初始化函数。一定要将超类的初始化函数的值赋给self对象,并返回你自己的初始化方法的值。超类可能决定返回一个完全不同的对象。
存取器(属性)的例子:
//Car.h文件
#import<Cocoa/Cocoa.h>
@interfaceCar:NSObject{
NSString*name;
}
@property(copy)NSString*name;//表明类的对象具有NSString*类型的name属性,表明可以调用-setName和-name方法,即相应的setter/getter方法
@end//Car
//Car.m文件
#import"Car.h"
@implementationCar
@synthesizename;//表示实际创建该属性的访问器,即-setName和-name方法
@end//Car
//*********************************************************************************
最后就可以在main()函数中使用点表示法给对象赋值
Car*car=[[Caralloc]init];
car.name=@"Herbie";//调用了setName方法
NSLog("%@",car.name);//调用了name方法
@property和@synthesize可同时指定多个。
@property(copy)intwidth,height;
@synthesizewidth,heigth;
该技术同样适用于int、char、BOOL、struct甚至可以定义一个NSRect类的对象的特性
@property(readwrite,copy)NSString*name;//对象可读写,对象将被复制
@property(readwrite,retain)NSString*name;//对象可读写,对象将被保持
@property(readonly)NSString*name;////对象只读
C/C++中支持的内存方式Objective-C都支持(例如new,delete或malloc,free),Objective-C也有自己对象分配内存的方法:alloc,allocWithZone。
Objective-C不支持多继承,可以通过Objective-C的分类和协议特性获取多继承的优点
@classXYPoint;//代替#import"XYPoint.h"
//使用@class指令提高效率,编译器不需要处理整个XYPoint.h文件,只需要知道XYPoint是一个类名,但是如果需要引用XYPoint类中方//法,@class指令是不够的,必须用#import"XYPoint.h"。
分类的应用:
#import<Foundation/Foundation.h>
@interfaceCar:NSObject
{
intc;
}
-(id)init;
-(void)toString;
@end
@implementationCar
-(id)init
{
if(self=[superinit])
{
c=100;
}
returnself;
}
-(void)toString
{
NSLog(@"YESid:%i",c);//分类后最原始的方法访问不到了
}
@end
//进行分类,类名与已有类名相同
@interfaceCar(Unmi)//括号中任意指定一个Category名
-(void)toString;
@end
@implementationCar(Unmi)
-(void)toString//分类方法的实现
{
NSLog(@"Categoryid:%i",c);//分类中的方法可以引用变量
}
@end
intmain(intargc,constchar*argv[]){
NSAutoreleasePool*pool=[[NSAutoreleasePoolalloc]init];
Car*car=[[Caralloc]init];
[cartoString];//这行的输出是Categoryid:100
[pooldrain];
return0;
}
关于分类的一些注意事项
A、尽管分类可以访问原始类的实例变量,但是它不能添加自身的任何变量。如果需要添加变量,可以考虑创建子类。
B、分类可以重载该类中的另一个方法,但是通常认为这种做法不可取。因为,重载之后,再不能访问原来的方法。
C、可以拥有很多分类。
D、和一般接口部分不同的是,不必实现分类中的所有方法。这对于程序扩展很有用,可以在该分类中声明所有方法,然后在一段时间之后才实现它。
E、通过使用分类添加新方法来扩展类不仅会影响这个类,同时也会影响它的所有子类。
@synthesizename=_name;的用法:
@interfaceCar:NSObject
@property(copy)NSString*name;
@end
@implementationCar
@synthesizename=_name;//这个的效果会同时声明一个_name实例变量,方便于内部操作
@end
上面相当于,声明了一个实例变量
@interfaceCar:NSObject
{
NSString*_name;
}
@property(copy)NSString*name;
@end
@implementationCar
@synthesizename;
@end
这样在实例方法中就可以直接引用_name,如
-(void)foo
{
_name=@"abc";
}
协议间也是可以相互继承的,并且不能定义成员变量,只能定义方法。protocol的声明方式:
@protocolMyProtocol
-(void)myProtocolMethod;
@end
应用协议的代码:
@interfaceCat:NSObject<myProtocol>;
可以应用多个协议
@interfaceCat:NSObject<myProtocol,myProtocol2>
应用了协议必须实现协议中定义的方法,虽然未实现协议中的方法编译能通过,但运行时会报错的:
2011-05-2415:05:35.065test[5781:a0f]-[CatmyProtocolMethod]:unrecognizedselectorsenttoinstance0x10010c6b0
2011-05-2415:05:35.083test[5781:a0f]***Terminatingappduetouncaughtexception'NSInvalidArgumentException',reason:'-[CatmyProtocolMethod]:unrecognizedselectorsenttoinstance0x10010c6b0'
有关协议的注意事项:
A、如果一个类遵守某项协议,那么它的子类也遵守该协议。
B、通过在类型名称之后的尖括号中添加协议名称,可以借助编译器的帮助来检查变量的一致性,如下:
id<Drawing>currentObject;//协议是不有点像泛型那么回事啊
这告知编译器currentObject将包含遵守Drawing协议的对象。如果向currentObject指派静态类型的对象,这个对象不遵守Drawing协议,编译器将给出warning。
再次提到id类型,如果向currentObject指派一个id变量,不会产生这条消息,因为编译器不知道存储在id变量中的对象是否遵守Drawing协议。
C、如果这个变量保存的对象遵守多项协议,则可以列出多项协议,如下:
id<Drawing,Drawing1>currentObject;
D、定义一项协议时,可以扩展现有协议的定义。以下协议
@protocolDrawing3D<Drawing>
说明Drawing3D协议也采用了Drawing协议。因此采用Drawing3D协议的类都必须实现此协议列出的方法,以及Drawing协议的方法。
E、分类也可以采用一项协议,如:
@interfaceFraction(stuff)<NSCopying,NSCoding>
此处,Fraction拥有一个分类stuff,这个分类采用了NSCopying和NSCoding协议。
在构建数组NSArray,用arrayWithObjects时最后一个元素需要是nil,即:
NSArray*array=[NSArrayarrayWithObjects:@"ab",@"cd",nil];
用对象的copy、mutableCopy可复制出对象。
Archive(归档),序列化与反序列化:对象是NSString、NSDictionary、NSArray、NSData、NSNumber对象时,可以使用writeToFile:atomically:方法将数据写到文件中,是以属性列表的形式写到文件中的。参数atomically为YES,表示先将数据写到临时备份文件中,一旦成功,再转移到文件中。
相应的,像dictionaryWithContentOfFile函当我可以从文件中读取序列化出对象来,
写出的文件内容格式,NSArray的样子是:
<?xmlversion="1.0"encoding="UTF-8"?>
<!DOCTYPEplistPUBLIC"-//Apple//DTDPLIST1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plistversion=”1.0”>
<array>
<string>I<string>
<string>seem<string>
<string>to<string>
<string>be<string>
<string>a<string>
<string>verb<string>
</array>
</plist>
NSDictionarywriteToFile的文件是:
<key>...</key>
<string>...</string>
读回数据还可以用的方法dataWithContentOfFile/stringWithContentOfFile/dictionaryWithContentOfFile/arrayWithContentOfFile
还有NSKeyedArchivers的archiveRootObject:toFile和unArchiveObjectWithFile