Object-C学习笔记(一)

时间:2014-8-9/11:51 am

1、NSLog()和@"字符串"

    与printf()一样,NSLog()接受一个字符串最为其第一个参数,该字符串可以包含格式说明符(比如%d),此函数会接受与格式说明符相匹配的其他参数。
    NSLog(),添加了一些特性,比如时间戳、日期戳和自动附加换行符('\n')等。

    (1)NS前缀
    Cocoa给其所有函数、常量和类型名称都添加了NS前缀。这个前缀告诉我们函数来自Cocoa而不是其他的工具包。
    使用前缀能避免名称冲突。
    (2)NSString: @是本体
    @符号意味着引号内的字符串应作为Cocoa的NSString元素来处理。
   

2、布尔类型

    Object-C中的布尔类型是BOOL,它具有YES和NO两个值
    Object-C中的BOOL实际上是一种对带符号的字符类型的类型定义,它使用8位的存储空间。通过#define指令吧YES定义为1,NO定义为0。Object-C并不会将BOOL最为仅能保存YES或NO值的真正布尔类型来处理。编译器仍将BOOL认作8位二进制数,YES和NO值只是在习惯上的一种理解。这样会引发一个小问题:如果不小心将一个大于1字节的整型值(比如short或int)赋给一个BOOL变量,那么只有低位字节会用作BOOL值。如果该低位字节刚好为0(比方说8960,写成十六进制为0x2300),BOOL值将会被认作是0,即NO值。
    在C语言中非0即为真,但是在Object-C中他并不等于YES(YES的值以整型表示为1),所以在Object-C中直接与NO比较一定是安全的,因为与C一样,NO的值就是0。Object-C中YES的值就是1,所以不能以return一个有可能不为1的算式来判断真假。
    在NSLog(@"are %d and %d different? %@", 23, 42, boolString(areTheyDifferent));这个程序中,%@表示返回的市一个NSString指针。NSLog()的编写者添加%@格式的说明符,是为了通知NSLog()接受适当的参数,将其作为NSString。

3、面向对象编程的基础知识

    %lu格式说明符取计算字符串长度的strlen()函数的整数值,并输出单词及其长度。
void drawShapes (id shapes[], int count)
{
    for (int i = 0; i < count; i++) {
        id shape = shapes[i];
        [shape draw];
    }
} // drawShapes
    此函数的第一个参数shapes[],此时它是一个id数组对象。
    什么是id:
id就是标示符(identifier)。
    id是一种泛型,可以用来引用任何类型的对象。
    [shape draw]
    在Object-C中,方括号还有其他意义:用于通知某个对象该去做什么。方括号里的第一项是对象,其余部分是需要对象执行的操作。    

    (1)、@interface部分     

    通常,接口被称为API(application programing interface)

@interface Circle: NSObject
{
    @private
    ShapeColor fillColor;
    ShapeRect bounds;
}
- (void) setFillColor: (ShapeColor) fillColor;
- (void) setBounds: (ShapeRect) bounds;
- (void) draw;
@end
    第一行代码:
    @interface Circle: NSObject
    这句话是告诉编译器:“这是新类Circle的接口”。
    NSObject告诉编译器,每个Circle类都是一个NSObject,并且每个Circle类都将继承NSObject类定义的的所有行为。
    声明完新类后,接下来将告诉编译器Circle对象需要的各个数据成员(即每个Circle类的实例变量)。
    {
        ShapeColor fillColor;
        ShapeRect bounds;
    }
    在类声明中指定fillColor和bounds后,每次创建Circle对象,对象中都包括这两个元素。因此,每个Circle类对象都将拥有自己的fillColor和bounds。fillColor和bounds的值称为Circle类的实例变量。
    接下来的声明:
    - (void) draw;
    - (void) setFillColor: (ShapeColor) fillColor;
    - (void) setBounds: (ShapeRect) bounds;
    在Object-C中,它们称为方法声明。
    - (void) draw
    前面的短线表明这是Object-C方法的声明。这是区分函数原型与方法声明的一种方式,函数原型中没有先行短线。短线后面是方法的返回类型,位于圆括号中。
    - (void) setFillColor: (ShapeColor) fillColor;
    - (void) setBounds: (ShapeRect) bounds;
    上面两行代码都有一个参数。setFillColor:有一个颜色参数,setBounds:有一个矩形区域参数。
    setFillColor:
    方法的名称是setFillColor:,结尾处的冒号是名称的一部分,他告诉编译器和编译人员后面会出现参数
    (ShapeColor) fillColor
    圆括号中的ShapeColor是fillColor的类型
    注意:
     冒号是方法名称非常重要的组成部分。方法
     - (void) scratchTheCat;
     不同于
     -(void) scratchTheCat: (CatType) critter;
    如果方法使用参数,则需要冒号,否则不需要冒号
    @end // Circle
    提倡在所有的@end后添加注释来注明类的名称。

    (2)、@implemention部分
    以下是完整的Circle类实现:
@implementation Circle
- (void) setFillColor: (ShapeColor) c
{
    fillColor = c;
} //setFillColor
     
- (void) setBounds: (ShapeRect) b
{
    bounds = b;
} //setBounds
    @implementation 后是方法定义。它们不必按照在@interface指令中的顺序出现。甚至可以在@implementation中定义那些在@interface中没有声明过的方法。可以把他们看做是仅能在当前类实现中使用的私有方法。
    说明:
Object-C中不存在真正的私有方法,也无法把某个方法标示为私有方法,从而禁止其他代码调用它。这是Object-C动态本质的副作用。
    我们把参数重新命名为简单的字符C了。@interface和@implementation间的参数名不同是允许的。在这里我们继续使用参数名fillColor,就会覆盖fillColor实例变量,并且比编译器会生成警告信息。
    在@interface部分的方法声明中使用名称fillColor,是为了告诉读者参数的真正作用。在实现中,我们必须区分参数名称和实例变量的名称,最简单的范式就是将参数重新命名。
    说明:
由于对象的局部变量只在对象的实例中有效,因此我们称他们为实例变量,通常简写为ivar。
int main (int argc, const char * argv[]) {
    id shapes[3];

    ShapeRect rect0 = {0, 0, 10, 30};
    shapes[0] = [Circle new];
    [shapes[0] setBounds: rect0];
    [shapes[0] setFillColor: kRedColor];

    ShapeRect rect1 = {30, 40, 50, 60};
    shapes[1] = [Rectangle new];
    [shapes[1] setBounds: rect1];
    [shapes[1] setFillColor: kGreenColor];

    ShapeRect rect2 = {15, 19, 37, 29};
    shapes[2] = [Egg new];
    [shapes[2] setBounds: rect2];
    [shapes[2] setFillColor: kBlueColor];

    drawShapes (shapes, 3);

    return (0);
} //main
    本程序含有id数组元素(它是指向任意类型对象的指针)
    下面的代码,通过向需要创建对象的类发送new消息,可以创建多个独立的对象。
    shapes[0] = [Circle new];

    shapes[1] = [Rectangle new];

    shapes[2] = [Egg new];

4、继承

(1)、直接更改由继承得到的实例变量的值是一种不好的习惯。一定要通过方法或property属性来更改。
(2)、语法:@interface Circle: NSObject
这里新类Circle就继承了NSObject。
Object-C不支持多继承,不过Object-C可以利用其其他特性来达到多继承的效果,例如类别(category)和协议(protocol)。
利用继承,Circle和Rectangle的接口代码更改如下:
@interface Circle: Shape
@end // Circle

@interface Rectangle: Shape
@end // Rectangle
    Shape类的代码如下:
@interface Shape: NSObject 
{
    // 这里的是类的实例变量
    ShapeColor fillColor;
    ShapeRect bounds;
}
- (void) setFillColor: (ShapeColor)fillColor;
- (void) setBounds: (ShapeRect)bounds;
- (void) draw;
@end // Shape
    这里可以看出,Shape类将前面不同的类中所有重复的代码都绑定到了一个包中。
    下面是Shape的实现类:
@implementation Shape
- (void) setFillColor: (ShapeColor)c
{
    fillColor = c;
} // setFillColor
- (void) setBounds: (ShapeRect)b
{
    bounds = b;
} // setBounds;
- (void) draw
{
} // draw
@end // Shape
        这里draw虽然没有任何实现功能,但是仍然需要,以便所有的子类都能通过它来实现各自的方法
        下来是各个类的实现,变得异常简单:
@implementation Circle
- (void) draw
{
    NSLog(@"drawing a circle at (%d %d %d %d) in %@", bounds.x, bounds.y, bounds.width, bounds.height, colorName(fillColor));
} // draw
@end // Circle

@implementation Rectangle
- (void) draw
{
    NSLog(@"drawing a rect at (%d %d %d %d) in %@", bounds.x, bounds.y, bounds.width, bounds.height, colorName(fillColor));
} // draw
@end // Rectangle

5、复合

    (1)、Object-C中的复合,是通过包含作为实例变量的对象指针实现的。
@interface Unicycle: NSObject
{
    Pedal *pedal;
    Tire *tire;
}
@end // Unicycle
    Pedal和Tire通过复合的方式组成了Unicycle。
    (2)、Car程序
#import <Foundation/Foundation.h>
@interface Tire: NSObject
@end //Tire
@implementation Tire
- (NSString *) description {
	return (@"I am a tire. I last a while.");
} // description
@end // Tire

@interface Engine: NSObject
@end // Engine
@implementation Engine
- (NSString *) description {
	return (@"I am an engine. Vrooom!");
} // description
@end // Engine

@interface Car: NSObject {
	Engine *engine;
	Tire *tires[4];
}
- (void) print;
@end // Car
@implementation Car
// car的init方法创建了四个新轮胎并赋值给tires数组
- (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
- (void) print {
	NSLog(@"%@", engine);
	NSLog(@"%@", tires[0]);
	NSLog(@"%@", tires[1]);
	NSLog(@"%@", tires[2]);
	NSLog(@"%@", tires[3]);
} // print
@end // car

主函数:
int main(int argc, char const *argv[])
{
	Car *car;
	car = [Car new];
	[car print];
	return 0;
} // main
    接口中没有实例变量可以省略花括号。
    Tire类的接口并没有声明description方法,我们怎么能知道可以在Tire类中调用description方法?
    这时就要靠Cocoa。

    (3)、自定义NSLog()
    记住,在NSLog()可以使用%@格式说明符来输出对象。他处理%@时会给这个对象发送一个description
    消息,然后对象的description方法生成一个NSString并将其返回。NSLog()就会在输出结果中包含
    这个字符串。在类中提供description方法就可以自定义NSLog()会如何输出对象。
    
    注:
    NSString为什么声明的为指针类型,因为NSString声明了一个字符串对象,在java中
    对象也是指针类型,但是java很好的给包装起来。
    在Object-C中,除了NSfloat NSinteger等基本类型之外,其他声明的都为指针类型。

    (4)、存取方法。
    要改变一个类中的实例变量应该通过setter去改变不应该改直接去改变实例变量的值。
    为对象中的变量赋值的成为setter方法;
    getter方法获取对象变量的值;
    mutator改变对象状态。

    set方法的命名规则和java的一样,set为前缀,加上变量名称。
    但是注意:
         get方法的命名规则和java的不一样,在编写Cocoa程序时,get方法的名称是其返回的属性名称命名。
    例如:setEngine的get方法是engine。
    原因:
         get这个词在Cocoa中有着特殊的含义。如果get出现在Cocoa的方法名称中,就意味着这个方法会将你传递的参数
         作为指针来返回数值。
         例如:
              NSData(Cocoa中的类,它可以存储任一序列的字节)中有一个getBytes:方法,它的参数就是用来存储字
             节的内存缓冲区的地址。

    为Car添加setter和getter方法:

@interface Car : NSObject
{
	Engine *engine;
	Tire *tires[4];
}
// 此为engine的get和set方法
- (Engine *) engine;
- (void) setEngine: (Engine *) newEngine;
// 此为Tire的get和set方法
- (Tire *) tireAtIndex: (int) index;
- (void) setTire: (Tire *) tire atIndex: (int) index;
- (void) print;
@end // Car
    engine的get和set方法的实现:
- (Engine *) engine
{
	return (engine);
} // engine
- (void) setEngine: (Engine *) newEngine
{
	engine = newEngine;
} // setEngine
         getter方法engine返回实例变量engine的当前值。记住,在Object-C中所有对象见的交互
    都是通过指针实现的,所以方法engine返回的是一个指针,指向Car中的发动机对象。
    同样,setter方法setEngine:将实例变量engine的值赋为参数所指向的值。实际上被赋值的
        并不是engine变量,而是指向engine的指针值。换一种方式说,就是在调用了对象Car中的setEngine:
    方法后,依然只存在一个发动机,而不是两个
        想要在代码中实际运用这些存取方法,可编写如下代码:
Engine *engine = [Engine new];
[car setEngine: engine];
NSLog(@"the car's engine is %@", [car engine]);
    tires属性的存取方法:
- (void) setTire: (Tire *) tire atIndex: (int) index;
- (Tire *) tireAtIndex: (int) index;
    方法实现:
- (void) setTire: (Tire *) tire atIndex: (int) index {
if (index < 0 || index > 3)
{
NSLog(@"bad index (%d) in setTire: atIndex:", index);
exit (1);
}
tires[index] = tire;
} // setTire:atIndex:
// 这个方法是动态方法、没有返回值,接收2个参数,所以有2个冒号:
// 这个方法的方法名就叫setTire:atIndex:
// 其实atIndex是可以省略的,它只是为了让方法名念起来通顺一点,
// 也让(int) index前面的冒号:不那么孤单
- (Tire *) tireAtIndex: (int) index {
if (index < 0 || index > 3)
{
NSLog(@"bad index (%d) in tireAtIndex:", index);
exit (1);
}
return (tires[index]);
} // tireAtIndex:
    调用上述的方法:
Tire *tire = [Tire new];
[car setTire: tire atIndex: 2];
NSLog(@"tire number two is %@", [car tireAtIndex: 2]);
    main方法:
        在main方法中的init可以去除,因为已经有了初始化engine和tire的方法。
int main(int argc, char const *argv[])
{
Car *car = [Car new];
Engine *engine = [Engine new];
[car setEngine: engine];
for (int i = 0; i < 4; i++)
{
Tire *tire = [Tire new];
[car setTire: tire atIndex: i];
}
[car print];
return 0;
} // main
    (5)、复合还是继承
            继承的类之间建立的关系为“is a”(是一个)。三角形是一个形状,Slant6是一个发动机,AllWeatherRadial是一种
    轮胎的名字。如果可以说“X是一个Y”,那就可以使用继承。
            另一方面,复合的类之间建立的关系为“has a”(有一个)。形状有一个填充颜色,汽车有一个发动机和四个轮胎。
    与继承不同,汽车不是一个发动机,也不是一个轮胎。如果可以说“X有一个Y”,那么就可以使用复合。

转载于:https://my.oschina.net/are1OfBlog/blog/304268

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值