objective-C 笔记
一、
- #import <Foundation/Foundation.h>
- int main (int argc, const char * argv[]) {
- NSLog(@"Hello, <span style="">objective</span>-c! %d %f",100,3.3);
- //printf("test");
- return (0);
- }
1.import:作用如同include ,用于声明头文件
- #import <Foundation/Foundation.h>
2.NSLog():系统函数,作用类似printf,同样可以以%d的形式接收参数。
3.@ :在objective-c中 如果置于字符串前,表示该串为一个NSString元素,和NSLog函数一样同属于cocoa
4.函数返回值 return(0):同c一样返回0表示程序执行成功。
- #import <Foundation/Foundation.h>
- BOOL areIntsDifferent(int thing1,int thing2){
- if(thing1==thing2)
- return(NO);
- else
- return(YES);
- }
- NSString *boolString(BOOL yesNo){
- if(yesNo==YES)
- return (@"yes!");
- else
- return(@"no!");
- }
- BOOL areIntsDifferent_faulty(int thing1,int thing2){
- return (thing1-thing2);
- }
- int main (int argc, const char * argv[]) {
- BOOL aredifferent;
- //NSLog(@"test bool default value :%@",aredifferent);
- aredifferent=areIntsDifferent(4, 5);
- NSLog(@"are %d and %d different ? %@",4,5,boolString(aredifferent));
- //if(areIntsDifferent_faulty(3, 4)==YES)//"NO"
- if(areIntsDifferent_faulty(3, 4))
- NSLog(@"test other cond : yes");
- else
- NSLog(@"test other cond : no");
- //NSLog(@"test error: %@",areIntsDifferent_faulty(4,5));
- return 0;
- }
5.BOOL类型:在objective-c可以用YES,NO来表示,YES定义为1,NO定义为0。不能将BOOL值与YES,NO直接比较。
6.%@ :表示接收一个NSString类型指针的参数
二、
- void drawShapes(Shape shapes[],int count){
- int i;
- for(i=0;i< count;i++){
- id shape=shapes[i];
- [shape draw];
- }
- }
1.shapes为一个类型是Shape的结构体数组。
- id shape=shapes[i];
id 为一个指向其中某个结构的指针。
- [shape draw];
表示对对象shape做draw操作(向shape发送消息draw)。
2.
- @interface Circle:NSObject{
- ShapeColor fillColor;
- ShapeRect bounds;
- }
- -(void) setFillColor:(ShapeColor) fillColor;
- //[circle setFillColor: fillColor]
- -(void) setBounds: (ShapeRect) bounds;
- //[circle setBounes:bounds]
- -(void) draw;
- //[circle draw]
- @end
这里定义了一个名为Circle并继承自NSObject的接口,其中包含了两个属性,分别死color和bounds;三个方法声明。方法声明和函数原形的区别在于是否有先行短线"-" 。
- -(void) setFillColor:(ShapeColor) fillColor;
方法声明的结构为:
- (返回类型)方法名[:(参数类型) 参数1值名 参数2名:(参数类型) 参数2值名 ...]
方法调用(中缀符的使用):
- [circle setFillColor: fillColor]
意思同:
- circle.setFillColor(fillColor);
如果是多个参数:
- [textThing setStringValue: @"hello there"
- color: kBlueColor];
@"hello there"和 kBlueColor都为传入的参数 (?setStringValue和color是参数名还是方法名的一部分?)
3.Circle的实现
- @implementation Circle
- -(void) setFillColor:(ShapeColor) c{
- //fillColor=c;
- self->fillColor=c;
- }//set color
- -(void) setBounds:(ShapeRect) b{
- bounds=b;
- }//set bounds
- -(void) draw{
- NSLog(@"draw a circle at (%d,%d,%d,%d) in %@ ",bounds.x,bounds.y,bounds.width,bounds.height,colorName(fillColor));
- }
- @end
和声明差别不大,对原先的方法做了具体的实现。
- self->fillColor=c;
self是一个隐藏传送的参数,代表消息接收对象本身。
4.Circle的实例化
- [Circle new]
objective-c中可以把类当成对象一样来发送消息。
三、
1.objective-C中没有多继承,但是可以通过其他特性来实现
2.fraglie base class problem(脆弱的基类问题):变量在内存中的位置是通过基地址+偏移量来确定的,偏移位置通过硬编码实现,如果向NSObject中添加其他实例变量,会改变所有实例变量的偏移位置。(已经通过间接寻址方式确定偏移量的位置解决)。
3.超类的访问
- [super setFillColor:KBColor];
四、
- @interface Tire: NSObject
- @end //Tire
- @implementation Tire
- -(NSString *) description{
- return (@"i am a tire . i last a while");
- }
1.description方法的作用类似于java中的 toString(),可以通过定义description方法来达到自定义NSLog输出的类的内容的目的。如:
- Tire* tire=[Tire new]
- NSLog(@"%@",tire);
输出的结果就是:
- i am a tire . i last a while
2.类的初始化
以下是一个car类的初始化,该类包含了一个Engine(发动机)和一个Tire(轮胎)数组:
声明部分
- @interface Car: NSObject
- {
- Tire* tires[4];
- Engine* engine;
- }
- -(void) print;
- @end//Car
实现部分
- @implementation Car
- -(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);
- }
- -(void) print{
- NSLog(@"%@",engine);
- NSLog(@"%@",tires[0]);
- NSLog(@"%@",tires[1]);
- NSLog(@"%@",tires[2]);
- NSLog(@"%@",tires[3]);
- @end //car
init方法会在对象内存分配后自动执行,使对象处于可用状态。
3.
- if(self=[super init]){
?该句的作用是防止父类在初始化过程中返回的对象不同于原先创建的对象?
[super init]的返回值为一个id型数据,描述了被初始化的对象。
4.添加存取方法。setter/getter方法的写法:
以
- Engine* engine;
为例
- -(Engine*) engine{
- return (engine);
- }//engine
- -(void) setEngine:(Engine*) newEngine{
- engine=newEngine;
- }//setEngine
objective-c中的getter方法为了避免和Cocoa中的用法混淆,不使用getxxx做为getter方法的前缀。get在Cocoa中有比较特殊的含义,一般使用get前缀的方法名,其他程序员认为这个方法需要将指针作为参数传入。
setter方法是以set作为前缀,这点和很多其他语言一样。
5.objective-C中所有对象间交互都通过指针来完成
五、
1 .@class
创建一个向前引用,使类之间可以相互引用。比如,类a和类b需要相互引用,但是使用#import会出现编译错误,这时可以在A.h中添加@class B ,然后在B.h中添加@class A来解决。
注意:
a.类中对其他类的引用是以指针的形式实现,这样可以使用@class。
b.如果是继承关系就不能使用@class ,因为编译器需要知道父类的详细信息,并编译成功才能保证子类的编译成功,使用@class只能达到 "相信我,你最终能够了解这个名称的类" ,所以只能用#import的方式引入。
六、
1.两种常用的结构体:
范围:
- typedef struct _NSRange {
- NSUInteger location;
- NSUInteger length;
- } NSRange;
用来表示字符串中字符的位置和数组中的元素范围。
location表示存放该范围的起始位置。
length表示该范围内的所含元素的个数。
例:在字符串"objective-C is a cool language"中,要表示cool的范围,可以用三种形式表示
- NSRange *range1;
- range1.location=17;
- range1.length=4;
- NSRange *range2={17,4};
- NSRange *range3=NSMakeRange(17, 4);
几何数据类型:
- typedef struct _NSPoint {
- CGFloat x;
- CGFloat y;
- } NSPoint;
- typedef struct _NSSize {
- CGFloat width; /* should never be negative */
- CGFloat height; /* should never be negative */
- } NSSize;
NSPoint 表示笛卡儿平面中的一个点(x,y);NSSize 用来存储长度和宽度。
- typedef struct _NSRect {
- NSPoint origin;
- NSSize size;
- } NSRect;
NSRect表示一个矩形数据类型,由点和大小复合而成;
创建这些结构同样可以使用函数 :NSMakePoint(),NSMakeSize(),NSMakeRect;
*考虑到性能的原因,objective-c中的对象都是动态分配,创建对象会造成比较大的系统开销,所以采用struct的形式而不用对象。
2.字符串
(1)
- NSString *height=[NSString stringWithFormat:@"Your height is %d feet,%d inches",5,11];
可以使用NSString 的类方法stringWithFormat来创建字符串。
类方法的表示:
- + (id)stringWithFormat:(NSString *)format, ...;
最前面的"+"表示了该方法为类方法,以此来和实例方法的"-" 区分;
(2)
- unsigned int length=[height length];
length 方法用来获取NSString的长度
*length方法可以正确的获取国际字符串的字符数
(3)
- NSString* str1=@"wo kao 110";
- NSString* str2=[NSString stringWithFormat:@"wo kao %d",1101];
- if([str1 isEqualToString:str2]){
- NSLog(@"there is equals");
- }
isEqualToString函数可以比较两个NSString字符串是否相同。结果返回一个BOOL值;
要逐个比较字符,可以使用 compare函数
- - (NSComparisonResult)compare:(NSString *)string;
它的返回值NSComparisonResult是一个enum类型
- enum _NSComparisonResult {NSOrderedAscending = -1, NSOrderedSame, NSOrderedDescending};
NSOrderedAscending表示左侧数值小于右侧;
NSOrderedDescending表示右侧数值小于左侧;
NSOrderedSame 则相等;
该方法的比较是区分大小写的,要不区分大小写比较需要使用方法:
- - (NSComparisonResult)compare:(NSString *)string options:(NSStringCompareOptions)mask;
options是个掩码,可以进行位运算,options的选项:
NSCaseInsensitiveSearch:不区分大小写;
NSLiteralSearch:完全比较,区分大小写;
NSNumericSearch: 比较字符串的个数;
....
(4)
字符串包含:
- - (BOOL)hasPrefix:(NSString *)aString;
判断字符串是否以某个字符串开头;
- - (BOOL)hasSuffix:(NSString *)aString;
判断字符串是否以某个字符串结尾;
- - (NSRange)rangeOfString:(NSString *)aString;
查找某字符串中包含的一串字符串的位置,如果不存在nsrange的start值为NSNotFound
(5)
可变字符串NSMutableString
创建一个可变字符串前,需要分配一个容量,该容量并不能影响字符串的大小,这个容量仅仅是个最优值,按照这个容量,可以预分配一块内存来存储,以提高后续操作的速度。
- NSMutableString *string=[NSMutableString stringWithCapacity:42];
要达到动态改变字符串的目的,可以采用以下的方法:
- - (void)appendString:(NSString *)aString;
- - (void)appendFormat:(NSString *)format, ...;
参数形式和普通字符串操作函数的参数类似;
要删除某个字符串,可以先取得要删除字符串的range,然后调用对应的delete方法即可
- - (void)deleteCharactersInRange:(NSRange)range;
下面是可变字符串的一个例子:
- //定义一个可变长的字符串
- NSMutableString *string=[NSMutableString stringWithCapacity:42];
- //再末尾添加一个字符串
- [string appendString:@"James BethLynn Jack Evan"];
- //查找‘jack’在字符串中的位置
- NSRange range=[string rangeOfString:@"jack"];
- //删除该串
- [string deleteCharactersInRange:range];
结果:
- James BethLynn Evan
3.数组
(1)NSArray:以下定义了一个NSArray数组
- NSArray *array=[NSArray arrayWithObject:@"a",@"b",nil];
如同java中的集合类一样,NSArray中可以存储任何对象,但是不能存储c中的基本数据类型。数组的定义以nil(对象的零值或null值)作为结尾,nil代表了列表结束,所以换句话就是array中不能存储nil。
通过方法
- [array count];
来获取对象的个数,通过方法:
- [array objectAtIndex:index];
来确定某一索引处的对象。
- NSString *string=@"one:two:three:four";
- NSArray *result=[string componentsSeparatedByString:@":"];
- string=[result componentsJoinedByString:@"_"];
通过特定的方法可以实现字符串与数组之间的转换,有些类似java中集合对象可以通过toArray方法转换.
(2)可变数组NSMutableArray
和可变字符串类似,需要分配一个容量
- + (id)arrayWithCapacity:(NSUInteger)numItems;
增加对象的方法通过
- - (void)addObject:(id)anObject;
来实现。
删除对象时:
- - (void)removeObjectAtIndex:(NSUInteger)index;
需要提供要删除对象的索引。
(3)枚举
要循环一个NSArray对象,可以通过NSEnumerator来完成迭代
如:
- NSEnumerator *enumerator=[array objectEnumerator];
- id thingie;
- while(thingie=[enumerator nextObject]){
- NSLog(@"i found %@",thingie);
- }
通过获得一个array对象的enumerator来遍历获得数组中的单个对象。while循环中,只要不等于nil,就会一直循环直至thingie的值为nil;
另一种实现方式类似与java 1.5中的for each:
- for(NSString *string in array){
- NSLog(@"i found %@",string);
- }
(4)NSDictionary:结构类似于java中map,以键值对的形式定义集合。如下,创建了一个NSDictionary类型的轮子的集合
- Tire *t1=[Tire new];
- Tire *t2=[Tire new];
- Tire *t3=[Tire new];
- Tire *t4=[Tire new];
- NSDictionary *tires=[NSDictionary dictionaryWithObjectsAndKeys:t1,@"front-left",t2,@"front-right",t3,@"back-left",t4,@"back-right"];
- Tire *tire=[tires objectForKey:@"front-left"];
通过objectForKey方法传入一个key,就可以获得对应的对象。
NSDictionary 是不可变的,如果要能动态增删集合元素,可以使用 NSMutableDictionary,以前面定义的4个轮子为例,同样的可以创建一个可变轮子的NSMutableDictionary集合:
- NSMutableDictionary *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-left"];
- [tires removeObjectForKey:@"back-left"];
通过setObject方法可以动态的添加元素到集合中,也可以通过对应的删除方法 removeObjectForKey删除元素。
如果添加相同的key,如:
- [tires setObject:t1 forKey:@"big-front-left"];
就会覆盖原先的值。
4.
(1)NSNumber类可以包装基本类型,这样,就可以在NSArray中存储。
char对应的包装方法是 numberWithChar;
int 对应的包装方法是 numberWithInt;
float 对应的包装方法是 numberWithFloat;
bool对应的包装方法是 numberWithBool;
这个功能如同java中的装箱,但是在objective-c中并不支持自动装箱;
要从已经被NSNumber封装中取出基本类型,用以下对应的方法即可:
charValue,intValue,floatValue,boolValue 同样的,还有stringValue可以取到NSString对象。
(2)NSValue
可以包装任何任何对象,以下为包装一个结构类型的示例:
- NSRect rect=NSMakeRect(1, 2, 30, 40);
- NSValue *value=[NSValue valueWithBytes:&rect objCType:@encode(NSRect)];
- [array addObject:value];
- //使用getValue:来提取数值,需要要存储这个数值变量的地址做为参数
- value=[array objectAtIndex:0];
- [value getValue:&rect];
(3)NSNull:
通过null方法包装后,可以在NSArray等中插入nil
七、
1.objective-c中,对象的生命周期通过 引用计数来标示,当使用 alloc,new方法或通过copy消息时,引用计数就会加一,通过retain方法可以为对象再增加引用计数,要减少对象的引用计数,可以向该对象发送一条release消息。要获得对象引用对象的引用数量时,可以向对象发送retainCount消息来获取数量。
当引用计数0时,会调用dealloc方法销毁对象。
2.对象的自动释放
使用NSAutoreleasePool对象,可以创建一个自动释放池,给对象发送一个autorelease消息时,就可以将该对象放入到池中,当池被销毁时(即池对象调用了release方法),池中所有的对象都会被销毁。
*创建一个NSAutoreleasePool对象时,同样会有个引用计数指向该对象,并且值为1,所以当这个NSAutoreleasePool对象调用了release方法时,引用计数减为0,objective-c 就会去调用dealloc方法,销毁该池对象。
以下为自动释放池的示例:
- #import <Foundation/Foundation.h>
- @interface RetainTracker : NSObject
- @end
- @implementation RetainTracker
- -(id) init{
- if(self=[super init]){
- NSLog(@"init: Retain count of %d",[self retainCount]);
- }
- return (self);
- }//init
- -(void) dealloc{
- NSLog(@"dealloc called. Bye Bye.");
- [super dealloc];
- }//dealloc
- @end //RetainTracker
- int main (int argc, const char * argv[]) {
- NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
- RetainTracker *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
- return 0;
- }
a.使用init方法,创建了一个自动释放池对象,并且该对象的引用计数为1;
b.当执行了 new 方法时,创建了一个对象,对象引用计数增加为1
c.retain方法使计数再加1
d.向对象发送autorelease消息,将该对象置入自动释放池中,此时不影响对象的引用计数
e.release方法使对象的引用计数减为1(在这个过程中,自动释放池对象的引用计数一直为1)
f. pool对象执行realse方法,引用计数减为0,池对象销毁,dealloc方法被调用。
执行的结果为
- init: Retain count of 1
- releasing pool
- dealloc called.Bye Bye
3.Cocoa内存管理规则:
总结起来其实就一句话: 如果我们使用了new,alloc或copy方法获得一个对象,则我们必须释放或自动释放该对象。
4.objective-c2.0中引入了垃圾回收机制,可以通过设置工程属性里,build选项卡中的Required[-fobjc-gc-only]来启用
八、
1.要使用一个对象必须经过两个过程:分配(allocation)和初始化(initialization)。
通过向类发送alloc消息从而分配一块内存来存储,并且将分配的内存初始化为0;通常init方法来完成初始化,如下一段为一个car对象的分配初始化的过程:
- Car *car=[[Car alloc] init];
2.一般在init方法中,会有一句
- if(self=[super init]){
- ...
使父类先完成自己的初始化操作,并且init方法可能返回完全不同的对象,此时就需要更新self对象。
*该赋值操作只影响init方法中的self。不影响方法以外的任何内容。
website:http://qzww5324.iteye.com/blog/484470