一.基础语法
-(void) setNumerator: (int) n;
-符号是实例方法,+号是类方法。
(void)是返回类型,可以是(int)等。
setNumerator方法名。
:符号说明有参数
(int) n;是参数类型和参数名称。
二.文件结构
Fraction.h文件,描述类和方法。
#import
@interface Fraction: NSObject
-(void) print;
-(void) setNumerator: (int) n;
-(void) setDenominator: (int) d;
// 前面两句可以用属性代替,实现中用了@synthesize 会生成相同的方法,
// 没有用则会生成_numerator,_denominator方法。
// @property int numerator, denominator;
// 如果是x,则生成为x,setX,没有@synthesize 为_x, _setX
@end
Fraction.m文件,描述数据和实现方法:
#import “Fraction.h” // 系统的用<>引入,本地路径的用””引入.
@implementation Fraction
{
// 数据类型定义
int numerator;
int denominator;
}
-(void) print
{
NSLog(@“%I/%I”,numerator, denominator);
}
-(void) setNumerator: (int) n
{
numerator = n;
}
-(void) setDenominator: (int) d
{
denominator = d;
}
// 前面两个函数可以用@synthesize代替,因为接口定义了属性会生成相同的方法
// @synthesize numerator, denominator; 客户端调用属性一般用对象.的方式,不用也可以,而是用[myFraction setNumerator: 1]的形式。
// 其它对象方法也可以用.来调用,但是一般不这么做,目的是区分属性。
@end
// program 部分,作为客户端使用代码
Main.m
#import “Fraction.h” // 系统的用<>引入,本地路径的用””引入.
int main(int arg, char* argv[])
{
@autoreleasepool{
Fracton *myFraction;
myFraction = [Fracton alloc];
myFraction = [myFraction init];
[myFraction setNumerator: 1];
[myFraction setDenominator: 3];
NSLog(@“The value of MyFraction is:”);
[myFraction print];
}
return 0;
}
三.数据类型和变量:
基础数据类型:
Int, float, double, char
限定词有:long, long long, short, unsigned, signed.
布尔类型是:BOOL YES,NO
堆类型数据:
NSArray, NSString,NSNumber
@“strs”;
Id类型可以储存任何类型,它是一般对象类型,多态中的动态绑定就是靠该类型。
类型转换:
C语言方式的转换,float fNum =(float)nNum;
Id myNum;
Fraction *myFraction = (Fraction*) myNum;
类型的访问和生命周期作用域:
在生命周期作用域内的可见变量都可以访问,和C,C++一样。
全局变量:全局变量和静态变量一样只初始化一次(在静态,全局,常量 内存区域),在整个程序进程期间都存在。
oc允许有全局变量,.m文件前面,BOOL gAudiooPen = YES;就是一个全局变量定义。所有文件内的方法都可以访问。
extern关键字用于在.h中声明变量,声明变量不会分配内存空间,所以不能初始化。
需要使用该全局变量的其它文件,包含该头文件即可。或者不包含,而是在使用的时候添加extern声明。
静态变量:.m文件中的static变量,类似C语言中的静态变量,只属于文件,外部extern用也不访问不到。只能在.m文件中使用。
如果外部需要访问就用类方法来存取。静态变量定义时候初始化一次,后面都可以存取,也可以用extern声明,但是一般不使用。
枚举类型:枚举类型作用域在.m内则是文件作用域,如果枚举在全局文件.h内则是全局作用域的
enum CheckFlag = {true, false};
enum CheckFlag curFlag = true;
typedef enum CheckFlag ChkFlag;
也可定义枚举值:
enum Month = {
Mon,// 默认从0开始,调用常量时候不需要类似c#的Month前缀限制。
FEB,
APRIL,
}
// 声明一个没有名称的枚举,同时声明一个类型为direction
enum {east, west, south, north} direction;
四.表达式和语句
表达式和循环语句都是C类型的,continue, break,if, switch也一样, io输入也是
Int num;
scanf(“%I”,&num); 就可以输入了,printf(“输出”)。
位运算和C语言一样,可以用位来表示状态位。
五.函数和static类型
函数的多参数:
-(void) setTo:(int) n over: (int) d;
实现为:
-(void) setTo:(int) n over: (int) d
{
numerator = n;
denominator = d;
}
客户端调用为:
[myFraction setTo: 100 over: 200];
总结:多参数OC函数,命名还是蛮冗余的,这里总的方法叫做:
setTo:over:,其中over是自己取的名字。
当然后面的参数不带参数名也是可以的,例如:setTo:(int)n :(int)d形式,参数叫做:setTo::,但是很不直观还是建议使用。
这样说来方法的名称还是第一个参数的名称。
一个使用自定义类对象作为参数的例子:
-(void) add: (Fraction*) f;
实现为:
-(void) add: (Fraction*) f
{
a= a * f.b + b * f.a;
b = b * f.b;
}
也可以返回一个新的Fraction*对象,这是面向对象编程中获取N多对象实例的精髓。
方法内部 局部变量是C语言形式的,没有初始化,所以要先初始化才能使用。
值类型的参数也是局部的,但是引用类型的参数却是可以改变的。
static类型的变量只初始化一次,后面可以保留,static的变量如果在方法外部所有方法都可以访问到,但只是在定义的.m文件内,除非在.h中用了extern修饰。
在实现方法内调用自己的方法,或属性可以用self修饰:
例如:
[self reduce];
六.类实例和静态成员
1)类
类分为静态方法和非静态方法,都要继承自NSObject根类。
category分类允许以模块的方式,为类添加方法(非静态的), 当文件太大时或者没有源码时候非常方便。
初始化方法:
-(Fraction*) initWith: (int) n over: (int) d
{
self = [super init]; // 是重载了方法,所以要调用父类的
if(self)
[self setTo: n over: d];
return self;
}
-(Fraction*) init
{
return [self initWith: 0 over: 0];
}
2)封装,访问控制权限
访问控制权限只是修饰类成员变量和方法的。
.
h中的成员变量是protected类型的。
.h中属性默认是私有的 子类需要通过存取函数访问到(属性比类变量多了内存管理和复制值等优化,建议多使用属性)。
.h中-和+方法都是public类型的。
.m中有.h中没有的成员变量或方法都是private类型的。
@package这个访问权限并不是Java里的包访问权限,OC中没有包的概念,这个是框架级的访问权限,在当前的framework的类中视为@protected,在框架以外的类中访问被视为@private
需要公开成员变量用:
@interface Student : NSObject {
@public
int _age;
int _no;
}
如果想直接访问成员变量,那么它首先是public的,然后需要使用->符号访问。
3)继承父类
使用继承语法,OC不支持类的多继承:
@interface Fraction: NSObject
使用分类的语法,非匿名分类不能定义数据成员和属性,分类避免重载类方法因为会导致丢失:
@interface Fraction (MathOps)
使用协议的语法:
@interface Fraction: NSObject
在分类上使用协议为:
@interface Fraction(Stuff) , NSCoding>
Protected类型的成员变量和public类型的方法都可以继承过来,.m中私有或者.h中声明为私有的不能被子类继承。
方法重载:重载了会屏蔽父类的方法,可以通过[super fatherMethod];使用父类定义的方法。
通过访问权限,重载,就可以重用父类,和拓展父类的功能。
4)多态
多态方法:不同的类具有相同的方法名且具体实现方法不一样,称为多态。
动态类型(执行时才确定类型):id类型指向不同的实现了相同多态方法的类对象,OC运行系统可以跟踪对象所属的类,根据当前所属的类调用响应的方法。
在OC方法当中,动态方法当中使用self,谁调用这个方法self就是谁,在静态方法当中,self代表的是当前类。
id类型在编译时候不会检查指向的类存在的方法(非id类型却会), 运行时才会检查,所以使用id类型在编译时候要审查代码,多在运行时进行测试。
尽量不要用id类型,除非在多态环境下,因为id类型不能提供编译期检查,不能使用.调用公开成员,可读性也不好。
尽量不要用 id = [id1 oprmethod ] 这样的操作,这样会导致比较复杂的运行时跳转。
NSObject定义了运行时对类型的判断,类存在方法的判断,以及通过函数指针调用方法。
例如:
类对象 = [classNameOrObjName class]
判断两个类对象是否相等:
if( [obj1 class] == [obj2 class])
slector指针类型获取:
slector sltor = @selector(methodNameWthoutClass)
// isMemberOfClass一定要是本类,是父类不行
if( [mySquare isMemberOfClass: [Square class]] == YES)
// isKindOfClass 父类也行
if( [mySquare isKindOfClass: [Square class]] == YES)
// respondsToSelector 实例有无该方法
if( [mySquare respondsToSelector: @selector(setWidth:andHeight:)] == YES)
// instancesRespondTo 类有无该方法
if( [Rectangle instancesRespondToSelector: @selector(setSide:)] == YES)
// 是不是子类
if([Square isSubclassOfClass: [Rectangle class]] == YES)
动态绑定(执行时才绑定特定的对象方法):执行时动态类型,被OC运行系统根据对象所属于的类对象,将具体对象绑定到动态类型上为动态绑定。
多态没有虚函数,只有方法的重载,和动态类型的绑定(其实虚函数就是动态类型的绑定后才能进行virtualtbl跳转)。
5)组合
组合其它类,组合优于继承,如果是非常强的继承关系就继承,否则就组合,如果.h中只是使用类的指针,那么:
@class XYPoint; // 即可
如果有使用变量属性方法那么需要:
#import “XYPoint.h”
.m中需要:
#import “XYPoint.h”
关于组合对象为了避免其它指针指向它,导致一处数据修改,其它处也会出现数据修改。
那么可能要深拷贝,或者用结构体避免这样的情况。
组合对象实例:
@interface Square:NSObject
{
Rectangle *rect;
}
-(int) setSide: (int)s;
@end
构造初始化Square函数中可能需要为*rect分配内存,或者指向一个指针,ios 5后都是强指针,所以不会有crash问题,但是不用时候需要rect = nil解除强引用关系,或者用weak引用但是记得判空。不用autoreleasepool包含的,就是系统线程runloop后清理该堆资源。
6)抽象类
Abstract 类型的,定义了一些方法,属性,但是不能实例化。
子类继承,实现。
可以通过父类指针,调度子类实例。
7)category分类
分类category为现有的类添加新的方法,不需要用继承的方式,也不是组合的方式,而是拓展原有类的方式,这样不需要知道之前类的源码,分类和C#的静态拓展不同,分类可以是类的实例方法。
一般用于大型类文件的重构为多个文件,多人开发时候协作拓展方法,没有源码时候拓展原有类的方法。
实例方法和类方法都是可以的,访问方式也是之前的访问方式即可。
分类的语法:
Fraction+MathOps.h文件中(也可以在Fraction+MathOps.m中定义):
#import “Fraction.h”
@interface Fraction (MathOps)
-(Fraction *) add: (Fraction *) f;
@end
Fraction+MathOps.m文件中:
@implementation Fraction (MathOps)
-(Fraction *) add: (Fraction *) f
{
…
}
@end
客户端使用,直接用原来的方式调用就可以了。
Fraction *result = [a add:b];
未命名的分类可以定义变量和属性,但是方法必须在实现中定义也就是只能是私有的。
语法:不需要分类名字即可。
定义如下:
Fraction+MathOps.m中也可以使用@interface定义(尽管.h中已经定义过了),如下:
#import “Fraction.h”
@interface Fraction ()
-(Fraction *) add: (Fraction *) f;
@end
@implementation Fraction ()
-(Fraction *) add: (Fraction *) f
{
…
}
@end
分类的限制:
命名分类不能列出父类和成员变量。
未命名分类可以使用成员变量和属性,但是方法必须是在.m中私有的。
分类都不要重载原来的方法(分类是可以继承的),因为会导致永远访问不到原来方法了,要重载建议在子类中且子类用[super methodname]可以访问到原来的方法。
尽量少用分类,因为会破坏设计意图,提高复杂度,真的原来文件很巨大,或没有拿到源码,或重载组合不好实现才使用。
8)协议
协议类似java中的接口概念, 协议可以继承,但是子类使用父类的协议不一定是正确的,可能要自己实现协议。
定义协议:
XXXObject.h中,不需要实现:
@protocol Drawing
// 默认为必选的协议
-(Id)copyWithZone: (NSZone*) zone;
-(void) paint;
-(void) erase;
@optional // 后面为可选的协议
-(void) outline;
@required // 后面为必选的协议
-(void) area;
@end
协议之间可以拓展继承,例如:
@protocol Drawing3D
@end
使用协议的类:
多个协议逗号隔开:
XXX.h // 如果不想在头文件中公开也可以在.m中声明使用的协议,
.m中也可以再使用一遍@interface AddressBook: NSObject @end意思是拓展。
@interface AddressBook: NSObject
// 不需要在声明处重复声明协议方法
@end
XXX.m
@implement AddressBook: NSObject
// 必选的协议方法一定要实现
@end
分类也可以采用协议,例如:
@interface Fraction(Stuff)
如:
检查类对象是否实现了协议:
if( [currentObject conformsToProtocol:@protocol(Drawing)] == YES)
{
}
检查类对象是否实现了可选的协议:
if( [currentObject respondsToSelector:@selector(outline] == YES)
{
}
如果类型名称之后的尖括号添加协议名称,可以借助编译器来检查变量的一致性:
例如:
Id currentObject;
Id myDoc;
声明了检查后,如果使用协议的类没有实现协议,那么编译器会报警告,如果是id类型编译器无法判断,那么不会发出警告。
协议的使用示例:
int main(int argc, const char * argv[])
{
@autoreleasepool {
//这里是普通的类的调用方法
MyTest * myTest = [[MyTest alloc] init];
[myTest showInfo];
SEL sel = @selector(print:);//这个print转换成的方法
//确定这个print方法是否实现
if([myTest respondsToSelector:sel]){
[myTest print:20];
}
[myTest printValue:25 andValue:15];
[myTest release];
//下面的方法使用协议的方式来调用
id myProtocol = [[MyTest alloc] init];
if([myProtocol respondsToSelector:@selector(print:)]){
[myProtocol print:210];
}
[myProtocol release];
}
return 0;
}
代理类,其实就是具体实现了协议的类,客户类会id类型动态关联代理类,通过向代理类发送协议消息来实现调用。所以协议就是一个接口,面向接口编程的思维。调用类定义了调用框架。代理类实现具体的协议方法逻辑。
非正式协议,是在部分类中定义一组方法,但是不实现,子类可以选择性的实现,实现时候定义和实现部分都要写出,非正式协议没有编译检查,oc2.0后在正式协议中引入了@optional 来代替非正式协议。
七.预处理,异常处理,C语言特性
预处理:
和C语言一样,记得#define()限制符使用避免宏操作问题,#import自定义本地目录不用尖括号用""就可以了,OC中多次#import不会有问题,不需要CPP的#ifndef XXX_X形式。
异常处理:
一般不用异常处理,用C风格的返回错误码就可以了,如果因为客户环境的关系,又不影响正常程序运行,那么才需要用异常处理。
异常处理:
Int main(int argc, char *argv[])
{
@autoreleasepool{
Fraction *f = [[Fraction alloc] init];
@try{
[f noSuchMethod];
}
@catch(NSException *exception)
{
NSLog(@“Caught %@%@“, [exception name], [exception reason]); // 用name和reason方法返回错误信息
}
}
}
C语言特性主要指:
数组,指针;结构体,联合,枚举;函数指针,函数。
数组和指针和C语言一样。
结构体:结构体和C语言一样,成员都是public的,使用前需要初始化,指针和结构体可以定义灵活的数据结构类型,网络协议消息类型。
C函数可以完全在OC中调用,函数作用域定义可以在.m中,.h中声明extern,包含了该函数声明的都可以调用函数,除非函数用了static修饰那么只能在本文件内可见。
块是Apple对C函数的拓展,相比函数的优势是:能够让系统分配给其它应用的处理器执行或本应用的其它线程执行。
块的语法是:
int (^gcd) (int,int) = ^(int u, int, v){ // gcd是函数指针,等号右边是块定义
int temp;
while(v != 0)
{
temp = u %v;
u = v;
v = temp;
}
return u;
}
// 调用块时候,直接用块函数指针,类似函数一样调用即可。
Int res = gcd(2,4);
联合是共享内存的,函数指针,指针函数(返回指针的函数),数组指针,指针数组(存放指针的数组),
常量指针:int const *p = &const_value; // 常量修饰的是指针的值,可以p操作, 不能*p操作。
指针常量:int * const p = &const_value;// 常量修饰的是指针,可以*p操作, 不能p操作。
sizeof获取结构体字节大小,要注意下结构体对齐规则,合理顺序有利于提高性能。
OC和C语言联系:
1)实例变量存储在结构中不过是protected类型的,对象结构也是在结构体中。
2)对象变量实际上是指针。
3)方法是函数,消息表达式是函数调用。所有函数压栈规则,返回规则,自动栈变量和静态成员变量,堆变量表现都是一致的。
4)id类型是void*指针类型。
OC 框架:
进程线程:
NSProcessInfo和程序相关信息。
内存和数据结构:
NSNumber
NString, NSMutableString 字符串常量和可变类型。
NSArray, NSMutableArray 数组常量和可变类型。
NSMutableDictionary是OC字典数据结构。
NSSet,NSMutableSet, NSIndexSet是集合类型。
@Autorelease
{
}
磁盘io,数据库:
IO:NSFileManager类提供。
NSPathUtilities.h提供。
NSFileHandle
NSBundle 本地图片,字符串,图标
网络:
NSURL
显示系统:
声音系统:
键盘鼠标,触摸板: