Terminal编译OC代码的指令:
mkdir test01
cd test01
vi main.m
clang -fobjc-arc -framework Foundation main.m -o test01
./test01
@autoreleasepool{
NSLog(@"Program is running!");
}
@autoreleasepool:应用创建新对象时,系统能够有效的管理应用使用的内存
NSLog:
- 后面参数中的@"...."成为常量NSString对象,如果不加@,就是常量C类型的字符串
- NSLog函数会输出函数的执行日期与实践、程序名、以及其他一些数值
创建类和对象的方式:(分数程序-分子/分母)
//
// main.m
// OC-Test01
//
// Created by 修宇亮 on 14-7-22.
// Copyright (c) 2014年 修宇亮. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface Fraction : NSObject
-(void) setNumeration : (int) n;
-(void) setDenomination : (int) d;
-(int) numeration;
-(int) denomination;
-(void) print ;
@end
@implementation Fraction
{ int Numeration;
int Denomination;
}
//声明变量最好在@implementaion,这样可以保证数据的封装性
//如果只声明一行数据,可以不用大括号
-(void) setNumeration:(int)n
{
Numeration = n;
}
-(void) setDenomination:(int)d
{
Denomination = d;
}
-(int) numeration{
return Numeration;
}
-(int) denomination{
return Denomination;
}
-(void)print
{
NSLog(@"The fraction is %i / %i",Numeration,Denomination);
}
@end
int main(int argc, const char * argv[])
{
@autoreleasepool {
Fraction *frac1 = [[Fraction alloc]init];
Fraction *frac2 = [[Fraction alloc]init];
//另一种写法:Fraction *frac1 = [Fraction new];
//但建议用第一种方法书写,因为这样可以更加容易的理解“先分配内存空间,再初始化对象”这个过程
[frac1 setNumeration : 2];
[frac1 setDenomination:3];
[frac2 setNumeration : 2];
[frac2 setDenomination:7];
[frac1 print];
[frac2 print];
//另一种写法:
//NSLog(@"The fraction is %i / %i",[frac1 numeration],[frac1 denomination]);
//体现了每个对象中数据的封装性,外界只有通过创建对象或者访问已有对象,才能获得该对象的数据
}
return 0;
}
数据类型和表达式
NSLog转换字符:
int | char | float | double | id |
---|---|---|---|---|
%i | %c | %f,%e,%g,%a | %f,%e,%g,%a | %p |
%f是指用浮点数的形式输出,也就是用小数了
%e是用指数的形式输出
%g是选用%f%e中较短的一种形式输出
浮点数科学计数法:
1.7e4 = 1.7*10^4
限定词:
long:修饰int,double,float,以扩大值域,可以连续运用,如long long int
short:与long的效果相反,可以起到节约内存的作用
unsigned:只有正值,没有负值,从而扩大正值的值域
id:通用类型,id object,在多态和动态绑定中有重要作用
模运算符
NSLog(@"a%%b = %i",a%b);
//当需要在输出的字符串中加入%时,需要用%%,%相当于java中的\转义符
//%是取模运算,得到的结果是余数
//如果数据类型是int,那么/运算的结果是整数,小数部分被砍掉了
//a/b+a%b = a
类型强制转换
15/10 = 1
(float)15/10 = 1.5000
(float)15/(float)10 = 1.5000
id object;
Fraction *myfraction;
myfraction = (Fraction *) object;//将id强制类型转换为Fraction *
键盘输入
scan("%i",&number);//这里不需要@,这里是C语法,不是NSString类型参量
scan(" %c",char);//注意,这里%c之前有一个空格,功能是自动跳过空白字符(空格,回车,换 //行符,制表符),需要从键盘输入单个字符的时候要尤其注意这一点
break & continue
break:跳出循环,从循环后面的语句执行
continue:跳出正在执行的本次循环,从下一次循环开始执行,但仍旧在循环之内运行
关于类:
工程中文件的分布:
一般在写项目的时候
interface和implementation是分开在两个文件里面写的
class.h和class.m
其中class.m需要导入class.h
而main.m也需要导入class.h,但不需要导入class.m
这种文件分配,很有利于大量代码的管理
自动生成取值、设值方法
@interface
@property int a,b;
@end
-----------------
@implementation
@synthesize a,b;
@end
使用点运算符访问属性
object.property == [object property]
//点运算符多用于取值设值的时候,如果是单纯调用方法,还是推荐用[]
具有多个参数的方法
@interface
-(void) setTo : (int) n over : (int) m
@end
---------------------
@implementation
-(void) setTo : (int) n over : (int) m
{
numeration = n;
denomination = m;
}
@end
----------------------
main.m
[object setTo :1 over :3];
----------------------
求最大公约数
-(void) reduce : (Fraction *) f
{
int u = f.numeration;
int v = f.denomination;
int temp;
while(temp!=0)
{
temp = u%v;
u = v;
v= temp
}
f.numeration \= u;
f.denomination \= u;
}
关键字
static:
累加器,在方法中修饰局部变量可以使得局部变量可以实现累加功能,不会因为每次重新初始化而使得局部变量“归零”
self:
-(void) add : (Fraction *) f
{
//a/b+c/d = (a*d+c*b)/b*d
numeration = f.numeration*denomination+f.denomination*numeration;
denomination = denomination*f.denomination;
[self reduce];//直接对得到的结果进行约简,相当于java的this
}
关于继承
详情见代码包——Rectangle
main.m(import "XYPoint.h"&&"Rectangle.h")
XYPoint.h
XYPoint.m(import "XYPoint.h")
Rectangle.h
Rectangle.m(import "Rectangle.h")
@class
#import <Foundation/Foundation.h>
@class XYPoint;
//声明,如果下面出现XYPoint字样,说明要使用XYPoint类,相当于一个类名称标记
//也可以换成#import "XYPoint.h",但这个样子需要导入和处理整个XYPoint文件,
//但如果需要调用XYPoint类的方法和参数,则需要导入完整的XXPoint.h文件
//需要放在@interface之前声明,不能放在之后
@interface Rectangle : NSObject
@property int width,height;
-(int) area;
-(int) perimeter;
-(XYPoint *) origin;
-(void) setWidth : (int) w andHeight : (int) h;
-(void) setOrigin : (XYPoint *) pt;
@end
有关类的问题
if ([com1 isMemberOfClass :[Complex class]] == YES) ;//对象是不是该类的成员
if ([com1 isKindOfClass :[NSObject class]] == YES) ;//对象是不是该类或者该类子类的成员
if ([com1 respendsToSelector :@selector (setReal:andIma:)] == YES) ;//对象能不能响应这个方法
if ([Complex instancesRespendToSelector :@selector (setReal:)] == YES) ;//该类能不能响应这个方法
if ([Complex isSubclassOfClass :[NSObject class]] == YES) ;//该类是不是后面类的子类
有关异常处理
@try
{
int b = 0;
switch (b)
{
case 0:
@throw(ex);//b=0,则抛出异常;
break;
default:
break;
}
}
@catch (NSException *exception)//捕获抛出的异常
{
NSLog(@"b==0 Exception!");
}
@finally
{
NSLog(@"finally!");
}
[ex release];
}
//运行的结果:
b==0 Exception!
finally!
重写对象的初始函数
-(Fraction *) initWith :(int) n over: (int) d
{
self = [super init];
if(self)
{
[self setTo :n over :d];
}
return self;
}
-(void) setTo :(int) n1 over :(int) d1
{
self.Numeration = n1;
self.Denomination = d1;
}
//这里之所以使用id的返回值,是为了给后续对init的继承做准备,因为后续需要对init继承的时候,子类的类名和父类是不相同的,因此用id可以规避掉因为类名改变而不断变化返回值类型的麻烦
-(id) init
{
return [self initWith :0 over:0];
}
执行语句
Fraction *frac1 = [[Fraction alloc]initWith :4 over :6];
实例变量作用域
@protected : 可被本类及子类访问,interface中定义的变量默认为这种类型
@private : 仅能被本类的方法访问,implementation中定义的变量默认为是这种类型
@public : 哪都可以用
@package : 本类的映像的任何敌方都可以访问这个变量
@synthesize windown = _windown;
//windown是类的属性,而_windown是具体的类属性的实例变量
[windown makeKey];//没法运行
[_windown makeKey];//可以运行
[self.windown makeKey];//可以运行
extern
- 如果class A的变量 x 需要被好多个class使用:
那么在classA中把x定义成:
extern int x;
x = 100;
使用该变量的类,可以直接:
int x;
- 如果class A中的变量虽然会被其他class用,但是用的不多:
那么class A中把x定义成:
int x = 100;
使用该变量的类,可以引用为:
extern int x;
x = 200;
类函数和成员函数
//类函数:可以直接用类名调用,不能有成员变量
Fraction *fraction = [[Fraction allocF]init];
+(Fraction *) init;
+(Fraction *) allocF;
//成员函数:必须通过对象调用
Fraction *fraciton = [[Fraction allocF]init];
fraction.count = 1;
[fraction add :3 over :4];
-(int) count;
-(void) add :(int) n over(int) m;
-----------------------------------------
static int counter;
+(Fraction *) allocF
{
extern int counter;
counter++;
return [Fraction alloc];
}
+(int) count
{
extern int counter;
return counter;
}
枚举语法和typedef重命名
//声明enum
enum Direction{north=1,south,east,west};//1,2,3,4
enum Direction{north,south,east,west};//0,1,2,3
//定义enum对象
enum Direction direction1;
direction1 = (enum Direction) 3;
//免enum名,定义enum对象
enum{north,south,east,west} direction2;
//typedef重命名enum
typedef enum {north,south,east,west} Direction;
Direction direction3,direction4;
//typedef的作用主要是,为一些数据类型起一个“小名”,以方便程序的阅读,比如你写一个int定义x,大家不知道什么意思,但是你写Counter定义一个x,大家就知道,这个x其实是一个计数器
typedef int Counter;
//这两条定义语句功能相同
int x = 1;
Counter x =1;
分类与类的扩展
//Fraction.m
#import "Fraction.h"
//分类MathOps,在Fraction.m里面一起实现
//不必列出父类和实例变量,因为这些已经包括在Fraction.h
@interface Fraction(MathOps)
-(Fraction *) reduce :(Fraction *) f;
-(Fraction *) multiple :(Fraction *) f;
-(Fraction *) divide :(Fraction *) f;
@end
@implementation Fraction(MathOps)
-(Fraction *) reduce :(Fraction *) f
{
......
}
@end
类的扩展/未命名分类
@interface Fraction()
//括号里面没有命名
//可以定义实例变量来扩展类,这个是相比于命名分类的一个优势,命名分类不能定义实例变量
//定义的扩展方法必须在主实现区域内实现,否则会编译报错
@property int UniqueID;
-(Fraction *) square :(Fraction *) f;
@end
@implementation Fraction(MathOps)
@syntheasize UniqueID;
-(Fraction *) square :(Fraction *) f
{
}
@end
协议与代理(协议实现的过程代理给继承他的子类)
声明一组协议
NSObject.h
@protocol NSCopying
-(id)copyWithZone :(NSZone *)zone;
@end
实现一组协议
@interface AddressBook :NSObject<NSCopying,NSCoding>
//虽然不需要定义协议包含的接口,但必须在下面进行实现
可选择协议与死规定协议
//协议都是父类定义,子类实现
@protocol Drawing//死规定协议,继承的子类必须实现
-(void) paint;
-(void) erase;
@optional//可选择协议,继承的子类可实现可不实现
-(void) outline;
@end
检察一个对象是否遵循Drawing协议
if([currentObject conformsToProtocol : @protocol (Drawing)] == YES)
//同类概念——检查一个类是否实现了一个方法
if ([Complex instancesRespendToSelector :@selector (setReal:)] == YES) ;
检察一个对象是否实现了Drawing 协议中的outline方法
if([currentObject respondsToSelector : @selector (outline) == YES])
检察变量的协议一致性
id<Drawing> currentObject;
扩展现有的协议
@protocol Drawing3D(Drawing)
分类也可以实现一组协议
@interface Fraction (stuff)<NSCopying,NSCoding>
合成对象
square是Rectangle类的子类,是父子继承关系,但是Rectangle里面的很多方法并不适用于square,如果只单纯继承,会继承很多无用的方法,因此用下面的代码可以直接新定义一个类,这个类有很多新定义的属于自己的方法,这个方法和分类、子类都是扩展类定义的一种方法
//@interface square:Rectangle 这是原来的方法
@interface square :NSObject
{
Rectangle *rect;
}
-(int) size;
-(int) setsize: (int) s;
-(int) area;
-(int) perimeter;
@end
@implementation square
-(void) area
{
return [rect area];//使用父类方法的方式
}
@end
------------
//但是这种方法在初始化的时候需要为square和rectangle都预留一部分地方
//因此要重写init或者添加initWithSide之类的新方法来分配空间
预处理
预处理语句可以放在任何位置,但是大多数程序员把定义放在头文件,以便在多个源文件中引用
定义的变量和方法一般都用大写
#define:名称置换(相当于word中的替换,所以不能加;这样分号也就替换进去了)+宏方法定义
#define TRUE 1
#define PI 3.1415926
#define TWO_PI 2.0*PI
#define AND &&
if(time AND place) ==if(time && place)
#define OR ||
if(time OR place) ==if(time || place)
#define IS_LEAP_YEAR year%4 ==0&&year%100 !=0\
||year%400 ==0 \是为了提示编译器还有一个后续
if(IS_LEAP_YEAR).....
//这样就不局限与当前year变量,而是普遍适用于任意年
#define IS_LEAP_YEAR(y) y%4 ==0&&y%100 !=0||y%400 ==0
if(IS_LEAP_YEAR(y)).....
#define SQUARE(X) ((X)*(X))
//如果不加括号,会导致v+1*v+1,加了括号就是(V+1)*(V+1)
#define MakeFraction(x,y) ([[Fraction alloc]initWith: x over :y])
myFract = MakeFraction(1,3);
#define MAX(a,b) ((a)>(b)?(a):(b))
//用括号括起来,是为了避免运算错误
//1.MAX(x+1,y)*100 2.MAX(x&y,z)
#define IS_LOWER_CASE(x) (((x)>('a'))&&(x)<('z'))//返回真值
#define TO_UPPER(x) (IS_LOWER_CASE(x)? ((x)-'a'+'A'):(x))//返回字母
#define IPAD 1
#ifdef IPAD
# define IMAGE @"ipad.jpg"//别忘了空格
#elif IPHONE//#else
# define IMAGE @"iphone.jpg"//别忘了空格
#endif
编译过程中,可以通过 -D IPAD 来声明IPAD,从而是所有出现#ifdef IPAD的地方都通过
Xcode在Buildsetting中设定 IPAD DEBUG
gcc -framework-Foundation -D IPAD main.m -
#if defined(DEBUG)
NSLog(@"usename = %s,userid = %i",Username,UserId);
#elif......
#endif
//功能等价于
#ifdef DEBUG
NSLog(@"usename = %s,userid = %i",Username,UserId);
#elif......
#endif
//要保证DEBUG不仅仅被定义而且值非零才能继续进行下一步
#if defined(DEBUG)&&DEBUG
#define.....
//消除定义
#undef IPAD
之后的#ifdef IPAD和#if defined(IPAD)两句话的值都是false