oc基础知识
只在@interface中定义变量的话,你所定义的变量只能在当前的类中访问,在其他类中是访问不了的;而用@property声明的变量可以在外部访问。
用了@property去声明的变量,可以使用“self.变量名”的方式去读写变量。而用@interface的方式就不可以。
1 协议:
协议,类似于Java或C#语言中的接口,它限制了实现类必须拥有哪些方法。
它是对对象行为的定义,也是对功能的规范。
示例:
// GoodChild.h
#import <Foundation/Foundation.h>
@protocol GoodChild <NSObject>
-(void)filialPiety;
@end
// Student.h
#import <Foundation/Foundation.h>
#import "GoodChild.h" //注意实现协议的语法。 @interface Student : NSObject<GoodChild>
@end
// Student.m // protocol // // Created by sxt on 11-10-23. // Copyright 2011年 __MyCompanyName__. All rights reserved. // #import "Student.h" @implementation Student - (id)init { self = [super init]; if (self) { // Initialization code here. } return self; } -(void)filialPiety{ NSLog(@"孝敬父母。。"); } @end
此例中定义了一个协议GoodChild,类Student实现了此协议,所以必须有filialPiety方法。
每个类虽只有一个父类,但可以实现多个协议,以逗号隔开便可。语法如下:
@interface Student : NSObject<协议1,协议2> @end
2 委托:
委托是objC中使用非常频繁的一种设计模式,它的实现与协议的使用是分不开的,让我们看一个综合示例:
小公司老板日常的工作是管理公司、教导新员工、发工资与接电话。
其中管理公司、教导新员工是老板要亲为的。
而发工资与接电话老板希望招聘一个秘书来帮忙,于是对秘书的要求就是要略懂出纳发工资,要能帮助领导接电话。 而这两项要求便是协议,对类功能的限定。
// SecProtocol.h #import <Foundation/Foundation.h> @protocol SecProtocol <NSObject> //发工资 -(void)payoff; //接电话 -(void)tel; @end
然后定义一个秘书类
// Sec.h #import <Foundation/Foundation.h> #import "SecProtocol.h" @interface Sec : NSObject<SecProtocol> @end
// Sec.m #import "Sec.h" @implementation Sec - (id)init { self = [super init]; if (self) { // Initialization code here. } return self; } -(void)payoff{ NSLog(@"sec payoff"); } -(void)tel{ NSLog(@"sec tel"); } @end
紧接着是老板类:
// Boss.h #import <Foundation/Foundation.h> #import "SecProtocol.h" @interface Boss : NSObject //此属性用于指定秘书对象,此对象必须实现SecProtocol协议。 @property(nonatomic,retain) id<SecProtocol> detegate; //管理 -(void)manage; //教导新员工 -(void)teach; @end
// Boss.m #import "Boss.h" @implementation Boss @synthesize detegate=_detegate; - (id)init { self = [super init]; if (self) { // Initialization code here. } return self; } -(void)manage{ NSLog(@"boss manage"); } -(void)teach{ NSLog(@"boss teach"); } -(void)payoff{ NSAutoreleasePool *p=[[NSAutoreleasePool alloc] init]; [_detegate payoff]; [p release]; } -(void)tel{ NSAutoreleasePool *p=[[NSAutoreleasePool alloc] init]; [_detegate tel]; [p release]; } @end
那么老板就具有这4个方法,当调用前2个时是自己完成功能,而调用后2个时则转为调用秘书的方法。
此时我们跟秘书对象就叫做代理对象,代理模式的名字由来于此。
最后调用测试下:
// main.m // delegate // // Created by sxt on 11-10-23. // Copyright 2011年 Jinlong Wei. All rights reserved. // #import <Foundation/Foundation.h> #import "Boss.h" #import "Sec.h" int main (int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; //实例化老板对象 Boss *boss=[[[Boss alloc] init] autorelease]; //实例化秘书对象 Sec *sec=[[[Sec alloc] init] autorelease]; //设置老板的代理对象为秘书 boss.detegate=sec; //调用4个方法。 [boss payoff]; [boss tel]; [boss manage]; [boss teach]; [pool drain]; return 0; }
作者: Jinlong Wei 日期: 2012 年 1 月 21 日没有评论
在C语言中存在这一种叫做方法指针的东西,这是比较高级的多态特性。 在objC中为了实现类似功能引入了selector ,即选择器。
选择器的定义方式有两种:
//第一种 SEL sel1=@selector(haha1); //第二种 SEL sel2=NSSelectorFromString(@"haha2");
objC中,SEL类型实际为字符串类型,它存储着方法名称。 而C语言中为指针。
//这里假设存在Person类,且此类有方法为haha Person *p=[[[Person alloc] init] autorelease]; [p haha]; //先判断对象p是否存在sel1的方法,即是否存在名叫haha1的方法 //如果存在调用对象p的haha1方法 if ([p respondsToSelector:sel1]) { [p performSelector:sel1]; } //respondsToSelector判断sel2方法是否存在; performSelector调用sel2所代表的方法。 if ([p respondsToSelector:sel2]) { [p performSelector:sel2]; }
高级应用示例
//此方法的功能为:调用obj对象的sel方法。 +(void) doMethod:(id)obj meth:(SEL)sel{ if ([obj respondsToSelector:sel]) { [obj performSelector:sel]; }else{ NSLog(@"error"); } }
作者: Jinlong Wei 日期: 2012 年 1 月 12 日没有评论
前面学过了继承,可以对从父类中得到所有的成员,所谓站在巨人的肩膀上。
换个角度去看这个问题,继承也是对现有的类进行扩充,这种扩充是纵向的,从父类到子类,再到子类的子类。得到了许多成员,有些是需要的,而有些是多余的。
在objC中提供了一种叫category的东西,中文一般翻译为“分类”或“类别”。它可以提供类功能的横向扩充。这种扩充有些时候更好用。
示例:
// Person.h #import <Foundation/Foundation.h> @interface Person : NSObject @end // Person.m #import "Person.h" @implementation Person - (id)init { self = [super init]; if (self) { // Initialization code here. } return self; } @end
// Worker.h #import "Person.h" @interface Worker : Person -(void)doWork; @end // Worker.m #import "Worker.h" @implementation Worker - (id)init { self = [super init]; if (self) { // Initialization code here. } return self; } -(void)doWork{ NSLog(@"do work.."); } @end
// WorkerCategory.h #import "Worker.h" //分类,名称要与主类相同,小括号内为分类名称,自行决定名称。 @interface Worker (Category) -(void)strike; @end // WorkerCategory.m #import "WorkerCategory.h" @implementation Worker (Category) -(void)strike{ NSLog(@"我们在罢工!"); } @end
这个例子中,提供了父类Person,子类Worker,这就是一种典型的纵向扩充-继承。紧接着在WorkerCategory中我们写个一个分类,扩种了一个方法strke。当我们实例化一个Worker对象后可以调用strike方法,这就是一种横向扩展。
几点说明:
继承可以得到父类内容,同时可以添加属性和方法。
而分类只能添加方法,无其他功能。
横向扩展的分类,给类中添加了新方法,但却不必其他成员。
从另外一个角度看这个问题,可以将一个复杂的类拆分成多个分类,逐个完成。有利于团队开发和后期维护。并且在使用中,可以根据需要决定import进来哪个分类,而无视不需要的分类。
示例2:
// NSDate_Ext.h #import <Foundation/Foundation.h> @interface NSDate (NSDate_Ext) //判断是否是闰年 -(BOOL)isLeapYear; @end // NSDate_Ext.m #import "NSDate_Ext.h" @implementation NSDate (NSDate_Ext) -(BOOL)isLeapYear{ NSDateFormatter *f=[[NSDateFormatter alloc] init]; [f setDateFormat:@"yyyy"]; NSString *year=[f stringFromDate:self]; [f release]; int y= [year intValue]; if ((y%4==0 && y0!=0) || y@0==0) { return YES; }else{ return NO; } } @end // main.m #import <Foundation/Foundation.h> #import "NSDate_Ext.h" int main (int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; NSDate *now=[[NSDate alloc] init]; NSLog(@"%@",[now isLeapYear]?@"是闰年":@"不是闰年"); [pool drain]; return 0; }
示例3:
// NSStringExistNum.h #import <Foundation/Foundation.h> @interface NSString (NSStringExistNum) -(NSUInteger)existNum:(char)c; @end // NSStringExistNum.m #import "NSStringExistNum.h" @implementation NSString (NSStringExistNum) -(NSUInteger)existNum:(char)c{ int num=0; for(int i=0;i<[self length];i++) { char c2= [self characterAtIndex:i]; if (c2==c) { num++; } } return num; } @end // main.m #import <Foundation/Foundation.h> #import "NSStringExistNum.h" int main (int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; NSString *s=@"abcdecfg"; NSLog(@"%ld",[s existNum:'c']); [pool drain]; return 0; }
作者: Jinlong Wei 日期: 2012 年 1 月 3 日没有评论
面向对象的几点补充:
一。实例话对象有的两种办法:
1 NSObject *o1=[NSObject new];
2 NSObject *o2=[[NSObject alloc] init];
实例话出的对象是没区别的,但第二中方法是可以指定构造方法(init)的名称,有利于多态,保证了灵活性,所以后面一律使用第二种方法。
二。每个类只有一个父类,如果不指名默认为NSObject。(继承的单根性) 这点与C++不同。
三。新的“数据类型” id
作用完全等同于: NSObject *
也就是说id是对象指针。
id a; 等同于 NSObject *b;
作者: Jinlong Wei 日期: 2012 年 1 月 3 日没有评论
objC 2.0中加入了新的特性来实现快速封装。
前面我们讲过了简单的封装,如果忘记请先复习后接着往下阅读。
//Person.h #import <Foundation/Foundation.h> @interface Person : NSObject @property int age; @property(copy) NSString* name; @end //Person.m #import "Person.h" @implementation Person @synthesize age=myAge; @synthesize name; @end
两点:
第一,age=myAge;指定了熟悉名字为myAge,方法名称不变。
第二,字符串中出现的copy。property()是可以指定多个特性的,常见的有:
copy:将传递过来的参数复制一份使用。多用于字符串。
assign:生成基本的封装,此为默认值。
readwrite:默认,同时可读取可修改。
readonly:只生成读取方法,不生成set方法,只读。
retain:set时引用计数加1,涉及到内存管理,后面章节讲解,属性为对象类应该选此项。
nonatomic:不生成同步块,当不需要多线程访问时应选此项提高执行速度。
getter:获取器的方法名
setter:修改器的方法名
值类型不可以使用以上内容,只有引用类型可用。
伴随着iOS5的发布也引入了ARC特性(后面讲解)加入了两个新关键字:
weak:用于取代assign
strong:作用等同于retain,支持ARC
此部分我建立了专门的章节来讲解此内容。
示例:
@property(nonatomic,readonly) NSString* name; //生成 name方法,只读。 不使用多线程同步块。
@property(assign,readwrite,getter = age,setter = setAge:) int age; //生成简单getter和setter方法: 方法名分别叫 age 和 setAge
@property int age2; //生成 age2 和 setAge2 两个方法。
@property(nonatomic,copy) NSString *name; //生成无同步块的 name 和 setName 方法 ,其中setName中 name=[_name copy]; (注:_name为参数变量名)
@property(nonatomic,retain) NSObject *o; //生成无同步块 o 和 setO 方法,使对象引用计数加1。
示例2:
// Engine.h #import <Foundation/Foundation.h> @interface Engine : NSObject -(void)work; @end
// Engine.m #import "Engine.h" @implementation Engine - (id)init { self = [super init]; if (self) { // Initialization code here. } return self; } -(void)work{ NSLog(@"引擎发动。。"); } @end
// // Car.h #import <Foundation/Foundation.h> #import "Engine.h" @interface Car : NSObject @property(nonatomic,retain) Engine *engine; -(void)drive; @end
// // Car.m #import "Car.h" @implementation Car @synthesize engine=_engine; - (id)init { self = [super init]; if (self) { // Initialization code here. } return self; } -(void)drive{ [_engine work]; NSLog(@"小车正常运转。。。"); } -(void) dealloc{ [_engine release]; [super dealloc]; } @end
作者: Jinlong Wei 日期: 2011 年 12 月 30 日没有评论
多态
#import <Foundation/Foundation.h> @interface Calc : NSObject -(int)add:(int)a second:(int)b; -(int)add:(int)a second:(int)b third:(int)c; @end
两个叫add的方法,名称相同,参数不同,此为重载。
#import <Foundation/Foundation.h> @interface Person : NSObject -(void)faith; @end #import "Worker.h" @implementation Worker - (id)init { self = [super init]; if (self) { // Initialization code here. } return self; } -(void)faith{ NSLog(@"实现自我价值。"); } @end
父类Person中包含faith方法和完整功能(部分代码省略),子类中重新编写了faith中的代码。
此为重写。
作者: Jinlong Wei 日期: 2011 年 12 月 30 日没有评论
继承
//Car.h #import <Foundation/Foundation.h> @interface Car : NSObject @property int age; @property(copy) NSString *color; -(void)drive:(int) s; -(void)turn:(int)s second:(double) t; -(void)stop; //静态方法 +(void)helloWorld; @end //Car.m #import "Car.h" @implementation Car @synthesize age=_age; @synthesize color; +(void)helloWorld{ NSLog(@"hello objC"); } //构造,在实例化对象时自动执行。 - (id)init { self = [super init]; if (self) { // Initialization code here. NSLog(@"car闪亮登场"); } return self; } //析构,在删除对象时自动执行。 - (void)dealloc{ NSLog(@"car 结束了他的生命"); } -(void)drive:(int) s{ } -(void)turn:(int)s second:(double) t{ } -(void)stop{ } @end
以上为类Car
//BMW.h #import "Car.h" @interface BMW : Car -(void) zhuangX; @end //BMW.m #import "BMW.h" @implementation BMW - (id)init { self = [super init]; if (self) { // Initialization code here. NSLog(@"bmw 闪亮登场"); } return self; } - (void)dealloc{ NSLog(@"bmw 结束了他的声明"); } -(void) zhuangX{ NSLog(@"帅的。。。 "); } @end
objC中继承使用:关键字。无特别之处。
类的内部用self指代自身(相当于Java中的this)
用super指代父类对象。
作者: Jinlong Wei 日期: 2011 年 12 月 30 日没有评论
首先扯一个和OOP关系不大的问题,方法的参数。
//一个参数时,参数类型写在括号中。
-(void)run:(int) l;
//两个参数时,第二个参数需要有名字(at就是名字),而day是变量名,请勿搞混。
-(void)run:(int)l at:(NSString*) day;
用法:[self run:5 at:@"今天"]; //at名称必须在,而“今天” 赋值给了变量day
注解:objC中调用方法的形式比较特别,此段代码等效于Java中的: this.run(5,”今天”);
//多个参数时
-(NSInteger)add:(int)a second:(int)b third:(int)c;
用法:[self add:1 second:2 third:3];
道理同上,第二个参数开始必须有。
好,第二个问题,封装的高级写法。没人希望用大量的时间写大量的封装代码,太锻炼身体了,objC2.0中提供了新的特性来解决这个问题。
//Person.h #import <Foundation/Foundation.h> @interface Person : NSObject{ int age; } -(int)age; -(void)setAge:(int)_age; @end //Person.m #import "Person.h" @implementation Person -(int)age{ return age; } -(void)setAge:(int)_age{ age=_age; } @end
这是一个简单封装的例子,此段代码等效于:
//Person.h #import <Foundation/Foundation.h> @interface Person : NSObject @property int age; @end //Person.m #import "Person.h" @implementation Person @synthesize age; @end
代码中:
@property int age;声明了age进行封装。
@synthesize age;指定实现age的封装。
通过两个关键字简单的实现了同样的功能。
此时将生成一个用于读取的age方法,和一个用于修改的setAge方法。
属性名为:age
关于@proterty的详细内容稍后章节讲解。
作者: Jinlong Wei 日期: 2011 年 12 月 30 日没有评论
面向对象是计算机编程中非常重要的思想,毫无疑问objC是完全支持OOP(面向对象编程)。
此文假设你有OO思想,话句话说着这不是一篇OOP入门文章,而是在讲解objC中如何体现OOP思想。
定义一个类。
//Pen.h #import <Foundation/Foundation.h> //类名称为Pen , 继承自父类NSObject @interface Pen : NSObject //定义一个方法,无返回值,名称叫write //减号表示是个实例方法 ,加号表示是个静态方法 -(void)write; @end //Pen.m #import "Pen.h" @implementation Pen 此方法为objC类的构造方法 - (id)init { self = [super init]; if (self) { // Initialization code here. } return self; } //方法的实现,显示一行文字。 -(void)write{ NSLog(@"write...."); } @end
objC中每个类由2个文件组成,.h文件和.m文件。 这非常类似于C++的做法。
当然,只有一个.h文件也可以写出一个完整的类。但通常我们将类的声明部分放在.h头文件中,而将方法的实现写在.m文件中
NSObject为所有类的父类。
init为类的构造方法。在此方法中首先调用[super init]来构造出父类对象,然后判断是否成功,if语句中可以写你想在构造时所作的初始化工作。self中存放这对象的内存首地址。
实例化一个对象的两种方法:
Pen *p1=[[Pen alloc] init];
Pen *p2=[Pen new];
两种等效,但推荐第一种。 代码虽麻烦,但可以指定调用叫init的构造,便于实现多态,灵活性的体现。
alloc关键字的作用为申请内存空间
调用write方法:
[p1 write];
多数编程语言中喜欢使用 p1.write();这样的写法,objC中非常另类,开始可能写的不习惯,当配合xcode写顺手了之后非常好用。
看个复杂的:
[[[Person alloc] init] sleep];
先实例化一个Person类的对象,然后调用sleep方法,你看懂了么?
属性和封装的例子
//Person.h #import <Foundation/Foundation.h> @interface Person : NSObject{ NSString *name; @public int age; @private int height; } -(NSString*)name; -(void)setName:(NSString*)_name; -(void)sleep; @end //Person.m #import "Person.h" @implementation Person -(NSString*)name{ return name; } -(void)setName:(NSString*)_name{ name=_name; } -(void)sleep { NSLog(@"huhu..."); } @end
此例中,对象Person有3个属性:name,age,height。 name为protected (即默认为name为protected);age为public ;height为private。
紧接着我为name属性做了个封装 ,于是有了两个方法:name和setName ,objC中不喜欢把方法命名为getName,而是直接叫name,不要和属性name搞混。
在setName方法中,冒号后面的为参数,类型为NSString* ,即字符串指针,变量名叫_name。
作者: Jinlong Wei 日期: 2011 年 12 月 29 日没有评论
int a=5; //与C语言一样,非0表示真 if (a) //结果为真 { NSLog(@"hahaha"); } //C语言中的布尔型本质上是整数 bool b=true; if(b) //结果为真 { NSLog(@"hoho"); } //objC中的布尔型变量,本质上为unsigned char //iOS开发中多采用BOOL,注意大小写 BOOL b=TRUE; if (b) //结果为真 { NSLog(@"12312312"); } 同C语言中的sizeof()一样,可以显示出占用内存的大小(字节数) NSLog(@"%ld",sizeof(5)); int a=34; NSLog(@"%ld",sizeof(a)); NSLog(@"%ld",sizeof(NSInteger)); int a[]={1,2,3,4,5}; //数组占用的内存大小为每个变量的大小乘以数组长度 int n=sizeof(a); //除以5得到每个int的大小。 n=n/5; NSLog(@"%d",n);
最后一个小知识:
typedef long NSInteger;
看出来了吧,NSInteger类型就是长整数,这在iOS开发中时常出现。