《Objective-C》书籍阅读笔记

*   代表有地址的指针
id 可以用来存放引用的指针,如:
void drawShapes(id shapes[],int count){
id shape = shapes[0];
}
[shape draw] 在Objective_c中,方括还可以表示:用于通知某个对象改取做什么.方括号里的第一项是对象,其余部分是需要对象执行的操作. 通知名称为shape的对象执行draw操作.

在Objective_c中,通知某个对象执行某种操作成为发送消息(也有人称之为"调用方法").代码[shape draw] 表示向shape对象发送了draw消息.[shape draw]可以理解成"向shape发送draw消息.

首字母大写类名,指向对象的变量不需要首字母大写.

消息(message)是对象可以执行的操作,用于通知对象去做什么,在[shape draw]的代码中,通过想shape对象发送draw消息来通知对象绘制自身.对象接受消息后,将查询相应的类,以便找到正确的代码来运行.

方法(method)是为响应消息而运行的代码.
方法调度,是Objective-C使用的一种机制,用于推测执行什么方法以相应某个特定的消息.

编写初始化方法:
(id) init{
if(self = [super init]){
engine = [Engine new];
tires[0] = [Tire new];
tires[1] = [Tire new];
}
return (self);
}//init

点表达式的妙用
如果点表达式出现在了等号(=)的左边,该变量名称的setter方法(-setRainHandling:和-setSnowHandling:)将被调用.如果点表达式出现再了对象变量的右边,则该变量名称的getter方法(-rainHandling和 -snowHandling)将被调用
如果在访问属性时遇到了奇怪的错误信息,提示访问的对象不是struct类型,请检查当前的类是否已经包含了所需的所有必备头文件.


--------------@interface 部分

@interface Circle : NSObject
{
@private
ShapeColor fillColor;
ShapeRect bounds;
}

- (void) setFillColor:(ShapeColor) fillColor;
- (void) setBounds:(ShapeRect) bounds;

在Objective-c中只要看到@符号,就可以把它看成是对C语言的扩展.

- (void) draw; 前面的短线表明这是Objective-c方法的声明.这是区分函数原型与方法声明的一种方式,函数原型中没有先行短线.短线后是方法的返回值,位于圆括号内.

- (void) setFillColor: (ShapeColor) fillColor;
- (void) setBounds: (ShapeRect) bounds;
其中的每个方法都有一个参数,setFillColor有一个颜色参数,Circle类在绘制自身时会使用该颜色.setBounds:有一个矩形区域参数,Circle类使用该区域来确定他们的边界.

中缀符
Objective-c有一种名为中缀符(infix notation)的语法技术.方法的名称及其参数都是合在一起的.
例如,你可以这样调用带一个参数的方法:
[circle setFillColor: kRedColor]
带两个参数的方法调用如下所示:
[textThing setStringValue:@"hello three" color:kBlueColor];

注意冒号
注意,冒号是方法名称非常重要的组成部分.方法
- (void) scratchTheCat;
不同于
- (void) scratchTheCat: (CatType) critter;
如果方法使用参数,则需要冒号,否则不需要冒号.

最后一行代码告诉编译器,我们已经完成了Circle类的声明.
@end //Circle
虽然这并不是必须的,但我们还是提倡在所有的@end语句添加注释来注明类的名称.这是一个好习惯.

------------------@implementation 部分
interface部分,用于定义类的公共借口.通常,接口被称为API.而真正使对象能够运行的代码位于@implementation部分中.
以下是完整的Circle类实现:

@implementation Circle

- (void) setFillColor:(ShapeColor)c
{
fillColor = c;
}//setFillColor

- (void) setBounds: (ShapeRect) b
{
bounds = b;
}//setBounds
在@implementation Circle中,fillColor  和 bounds 是直接继承了@interface声明的变量,所以不需要再额外定义.

@implementation Circle 是一个编译器指令,表明你将为某个类提供代码.类名出现再@implementation之后.该行的结尾出没有分号,因为在Objective-c编译器指令后不必使用分号.

你也许会认为,既然单独在@implementation指令中定义方法,就不能从该实现之外访问该方法.但事实并非如此,Objective-c中不存在真正的私有方法,也无法把某个方法标识未私有方法,从而禁止其他代码调用它.这是obective-c动态本质的副作用.

@interface和@implemention间的参数名不同是允许的.

设置engine属性的存取方法:
- (Engine *) engine;
- (void) setEngine: (Engine *) newEngine;
实现代码:
- (Engine*) engine
{
return (engine);
}

- (void) setEngine: (Engine *) newEngine
{
engine = newEngine;
}

实际使用:
Engine *engine = [Engine new]
[car setEngine: engine];
NSLog(@"the car's engine is %@",[car engine]);


设置tires属性的存取方法:
tires的存取方法稍微复杂一点:
- (void) setTire: (Tire *) tire atIndex: (int) index;
- (Tire *) tireAtIndex: (int) index;

实现代码:
- (void) setTire: (Tire *) tire atIndex: (int) inex{
if(index < 0 || index>3){
NSLog (@"bad index (%d) in setTire:atIndex:",index);
exit(1);
}
tires[index] = tire;
}

- (Tire *) tireAtIndex: (int) index
{
if(index < 0 || index >3){
NSLog(@"bad index (%d) in "tireAtIndex:",index;
exit(1);
}
return (tires[index]);
}


IOS类的代码分为两部分.
一部分是接口,用来展示类的构造.接口包含了使用该类所需要的所有信息.编译器将@interface部分编译后,你才能使用该类的对象,调用类方法,将对象复合到其他类中,以及创建子类.
源代码的另一个组成部分是实现.@implementation部分告诉Objectiveive-c编译器如何让该类工作.这部分代码实现了接口所声明的方法.


@interface是放在 .h 头文件中,  @implementation是放在.m 源文件中.
将 CarParts-Split.m文件中剪切Tire类的@interface部分,并粘贴到Tire.h中,文件应如下所示

#import <Cocoa/Cocoa.h>

@interface Tire : NSObject
@end

接下来,从CarParts-Split.m中剪切Tire类的@implementation部分,并粘贴到Tire.m中,你需要在文件首行加上语句#import "Tire.h".文件应如下所示.

#import "Tire"
@implementation Tire
- (NSString *) description
{
return (@"I am a tire .I last a while);
}
@end;
文件中的第一个import语句,并没有像之前那样导入头文件Cocoa.h或者Foundation.h,而是导入了该类的头文件.这是一个标准化的过程.在程序编译时,如果你碰到了注入"Cannot find interface declaration for Tire " (无法找到Tire类的接口定义)


减少依赖关系:
Objective-c引入了关键字@class来告诉编译器:"这是一个类,所以我只会通过指针来引用它"
在将Car类移动到属于其自身的文件时会用到@class.在Xcode中,使用和移植Tire类以及engine类相同的方法来创建Car.h和Car.m,来复制Car的@interface部分并粘贴到Car.h中,如下所示:

#import <Cocoa/Cocoa.h>

@interface Car : NSObject
- (void) setEngine: (Engine*) newEngine;
- (Engine *) engine;
- (void) setTire:(Tire *) tire atIndex: (int) index;
- (Tire *) tireAtIndex: (int) index;
- (void) print;
@end //Car
如果我们现在就使用这个头文件,就会从编译器那里得到错误消息,告诉我们不明白Trie和Engine是什么.这条错误信息很可能会像这样:error:expected a type "Tire",编译器这是在说"我无法理解这个"
有两种方法来解决这个错误问题.第一种就是用@import语句导入Tire.h和engine.h.这样编译器会获得这两个类的许多信息.
此外还有一个更好的方法.如果仔细观察Car类的借口,你会发现它只是通过指针引用Tire和Engine.这是@class可以完成的工作.下面是加入@class代码的Car.h文件内容.

#import <Cocoa/Cocoa.h>

@class Tire;
@class Engine;
@interface Car:NSObject
- (void) setEngine: (Engine *) newEngine;
- (Engine *) engine;
- (void) setTire: (Tire *) tire atIndex: (int) index;
- (Tire *) tireAtIndex: (int) index;
- (void) print;
@end //Car
这样就足以告知编译器处理Car类的@interface部分所需要的全部信息了.
说明:@class差ungjainle一个前后引用.这是在告诉编译器:"相信我.以后你自然会知道这个类到底是什么,但是现在,你知道这些足以.
如果有循环依赖关系.@class也很有用.即A类使用B类,B类也使用A类.如果视图通过#inport语句让这两个类互相引用,那么就会出现编译错误.但是如果在A.h文件中使用@class B,在B.h中使用@class A,那么这两个类就可以互相引用了.

编译器需要先知道所有关于超类的信息才能成功地为其子类编译@interface部分.


Foundation Kit介绍
一些有用的数据类型
typedef struct _NSRange{
unsigned int location;
unsigned int length;
} NSRange;
这个结构体用来表示相关事物的范围,通常是 字符串里的字符范围或者数组里的元素范围.Location字段存放在该范围的起始位置,而length字段则是该范围内所含元素的个数.在字符串"Objective-C is a cool language"中,单词cool可以用location为17,length为4的范围来表示.location 还可以用NSNotFound这个值来表示没有范围,比如变量没有初始化.
创建NSRange有三种方式,
第一种,直接给字段赋值:
NSRange range;
range.location = 17;
range.length = 4;
第二种 应用C语言的聚合结构赋值机制
NSRange range = {17,4};
第三种方式是Cocoa提供的一个快捷函数NSMakeRange();
NSRANGE range = NSMakeRange(17,4);
使用NSMakeRange();的好处是你可以在任何能够使用函数的地方使用它,列如在方法的调用中,将其作为参数进行传递.
[anObjective flarbulateWithRange:NSMakeRange(13,15)];

几何数据类型
经常看到用来处理几何图形的数据类型,它们的名称都带有CG前缀,如CGPoint 和 CGSize.这些类型是有Core Graphics框架提供,用来进行2D渲染.Core Graphics是有C语言所写的.因此可以在代码中使用C语言的数据类型.CGPoint表示的是笛卡尔平面中的一个坐标.
struct CGPoint
{
float x;
float y;
}
CGSize用来存储长度和宽度;
struct CGSize
{
float width;
float height;
}

字符串:

关于大小
NSString中另一个好用的方法(实例方法)是length,它返回的是字符串中的字符个数
- (NSString) length;
可以这样使用它;
NSUInteger length = [height length];
也可以在表达式中使用它,如下所示.
if([height length] > 35){
NSLog(@"wow,you're really tall!");
}
说明:NSString的length方法能够精确无误的处理各种语言的字符串,如含有俄文,中文或者日文字符的字符串,以及使用Unicode国际字符标准的字符串.

字符串比较
isEqualToString:可以用来比较接收方(receiver,接收消息的对象)和作为参数传递过来的字符串.isEqualToString:返回一个BOOL值(YES或NO)来表示两个字符串的内容是否相同.
要比较两个字符串,可以使用compare:方法,其声明如下.
- (NSComparisonResult) compare: (NSString *) aString;
compare:将接收对象和床底过来的字符串逐个进行比较,它返回一个NSComparisonResult(也就是一个enum型枚举)来显示比较结果.
enum{
NSOrderAscending = -1,
NSOrderSame,
NSOrderDescending
}
typedef NSInteger NSComparisonResult;

说明:正确比较字符串,
比较两个字符串是否相等时,应该使用isEqualToString;,而不能仅仅比较字符串的zhi指针值,举个例子:
if([thing1 isEqualToString: thing2]){
NSLog (@"The string are the same!");
}//比较两个字符串之间的内容

不同于
if(thing1 == thing2){
NSLog(@"They are the same Objective!");
}//判断两个字符串的指针数值,而不是他们的所指对象.

因此,如果你想检查两个对象(thing1和thing2)是否为同一事物,就应该使用运算符==.如果是想查看是否相等(即这两个字符串是否内容相同,)那么请使用isEqualToString.

compare:进行的是区分大小写的比较. @"Bork"和@"bork"的比较是不回返回NSOrdedSame的.如果compare:返回的结果是NSOrderedAscending,那么左侧的数值就小于右侧的数值,即比较的目标在字母表中的排序文职比传递进来的字符串更靠前.比如,[@"aardvark" compare: @"zygote"]将会返回NSOrderedAscending.

检查字符串是否以另一个字符串开头,判断字符串是否以另一个字符串结尾.
- (BOOL) hasPrefix: (NSString *) aString;
- (Bool) hasSuffix: (NSString *) aString;

例:
NSString *fileName = @"draft-chapter.pages";
if([fileName hasPrefix: @"draft"]){
//this is a draft
}
if([fileName hasSuffix: @".mov"]){
//this is a movie
}
如果你想知道字符串的某处是否包含其他字符串,请使用rangeOfString:.
- (NSRange) rangeOfString: (NSString *) aString;
将rangeOfString:发送给一个NSString对象时,传递的参数是要查找的字符.它将会返回一个NSRange结构体.告诉你与这个字符串相匹配的部分在哪里以及能够匹配上的字符个数.
例:
NSRange range = [fileName rangeOfString:@"chapter"];
返回的range.location为6,range.length为7.如果传递的参数在接收字符串中没有找到,那么range.location则等于NSNotFound.

NSArray类有两个限制.首先,它只能存储Objectiveive-C对象,而不能存储原始的C语言基础数据类型,如int,float,enum,struct和NSArray中的随机指针.同时,你也不能在NSArray中存储nil(对象的零值或NULL值).有很多种方法可以避开这些限制,你马上就会看到.
可以通过类方法arrayWithObjective:创建一个新的NSArray.发送一个以逗号分隔的对象列表,在列表结尾添加nil代表列表结束(顺便提一下,这就是不能在数组中存储nil的一个原因).
NSArray创建的是不可变数组.一旦创建了一个包含特定数量的对象的数组,它就固定下来了,既不能添加也不能删除任何元素.当然数组中包含的对象是可以改变的.
为了弥补NSArray类的不足,便有了NSMutableArray这个可变数组类.

初始化对象:
我们应该像下面这样嵌套调用alloc和init方法
Car *car = [[Car alloc] init];
而不是像这样
Car *car = [Car alloc]; [car init];
这种嵌套调用技术非常重要,因为初始化方法返回的对象可能与分配的对象不同,虽然这种情况很奇怪,但是它的确会发生.

点表达式的妙用,如果你在访问属性时遇到了奇怪的错误信息,提示访问的对象不是struct类型,请检查当前的类是否已经包含了所需的必备头文件.

@class Tire;
@class Engine;
@interface Car : NSObject
{
NSString *name;
NSMutableArray *tires;
Engine *engine;
}

- (void)setName: (NSString *) newName;
- (NSString *) name;
- (void) setEngine:(Engine *) newEngine;
- (void) setTire: (Tire *) tire atIndex: (int) index;
- (Tire *) tireAtIndex: (int) index;
- (void) print;
@end //Car

内存管理释放
如果是通过alloc、copy、new方法产生的对象,是需要通过release释放的.如果是通过其他的方式产生的对象,则不需要手动释放,比如numberWithUnsignedInt,因为它返回的对象可能是保留计数器的值为1并且已经被设置为自动释放了.在当前活动的自动释放池被销毁时,我们创建的这个NSNumber对象也会被清理.

//page = 第十五章 245

原文地址:https://www.cnblogs.com/lomomiao/p/4892186.html