++ 概述 ++ 目前来说,Objective-C(简称OC)是iOS开发的核心语言,在开发过程中也会配合着使用C语言、C++,OC主要负责UI界面,C语言、C++可用于图形处理。 * 基于C语言:C语言是一门面向过程的语言,OC是在C语言的基础上,增加了一层最小的面向对象语法,为什么说是最小的面向对象语法呢?因为OC把一些比较复杂的面向对象语法都去掉了,剩下的都是面向对象的精华,因此OC是一门面向对象的语言,而且会比C++简单很多。因为OC是基于C语言的,所以完全兼容C语言,也就是说我们在开发iOS程序过程中,可以在OC代码中混入C语言代码,甚至是C++代码。
语法概述: 1.没有包名(命名空间)的概念
在java中,为了防止两个类名相同的类冲突,你可以将这2个类放在不同的包里面。OC中并没有”包”的概念,也就是没有命名空间机制,取而代之的是开发人员给类名加上前缀,使用前缀可以有效的防止类名冲突。比如NSString(OC中的字符串类)、NSArray(OC的数组类),它们的前缀都是NS 2.关键字都以@开头 OC代码中是可以混入C语言、C++代码的,而C语言和C++作为一门编程语言,都有自己的关键字。为了防止跟C语言、C++关键字冲突,OC的关键字都以*@*开头。 甚至字符串都是以@开头的,比如@“Hello”是OC中的字符串,而”Hello”则是C语言中的字符串。 语法要点:
Oc没有垃圾回收; 源文件后缀为.m;入口程序同c,也是main() 导包使用 #import 也不用使用条件编译加入头文件;#import会自动判断是否已经添加过该头文件。
++ 面向对象语法+ 类 * 一般用2个文件来描述一个类; 1> .h:类的声明文件,用于声明成员变量、方法。类的声明使用关键字@interface和@end。 [注意:.h中的方法只是做一个声明,并不对方法进行实现。也就是说,只是说明一下方法名、方法的返回值类型、方法接收的参数类型而已,并不会编写方法内部的代码。] .m:类的实现文件,用于实现.h中声明的方法。类的实现使用关键字@implementation和@end。 .h
//用于声明Student这个类由哪些成员变量和方法(面对对象称方法,面向过程称函数)//导包,Foundation包含了常用的类,(类似java中的java.lang类)#import <Foundation/Foundation.h>//@interface表示声明一个类,后是类名;必须要指定继承的父类(java中是自动继承object)// : 即是继承//@end表示一个类的结束@interface Student : NSObject{ //成员变量要定义在大括号中; int age; int no;}//OC在.h中声明的方法都是公用方法//Oc中凡是方法类型都用括号括起来(int)//成员变量age的get方法// - 代表动态方法,就是对象内部方法;+ 代表静态方法- (int)getAge;//OC中不建议使用get*写法,建议直接与变量名一样-(int)age;//age的set方法;一个冒号对应一个参数- (void)setAge:(int)newAge;//同时设置两个参数的set方法-(void)setAge:(int)newAge andNo:(int)newNo;-(int)no;@end使用:
int main(int argc, const char * argv[]) { @autoreleasepool { //创建对象 //1.调用一个静态方法alloc来分配内存 //2.创建一个该对象的指针指向刚创建的内存空间 Student *stu = [Student alloc]; //3.调用一个动态方法进行初始化; //相当于调用了Student对象的init方法; stu = [stu init]; [stu setAge:100]; [stu getAge]; [stu age]; //上面三步可合并为 Student *stu1 = [[Student alloc] init]; [stu1 setAge:10 andNo:101]; NSLog(@"age :%d,no %d:",[stu1 age],[stu1 no]); //释放对象,但若选择了ARC编译器会自动添加 [stu release]; } return 0;}++ 点语法 ++ 为了方便其他程序员熟悉而做的处理;其本质还是方法调用
Person *person = [[Person alloc] init]; //点语法,为了方便其他程序员熟悉而做的处理 // [person setAge:10]; // 注意:不是访问成员变量;为了防止混淆,OC规范成员变量以_开头 person.age = 10;//等效于[person setAge:10];( // int age = [person age]; int age = person.age;//等效于 int age = [person age];方法名:
//方法也是方法名的一部分;故该方法的方法名是setAge:-(void)setAge:(int)newAge;//故该方法的方法名是setAge:andNo://-(void)setAge:(int)newAge andNo:(int)newNo;//方法名是age;-(int)age;注意:
@implementation Person-(void)setAge:(int)newAge{ _age = newAge; //方法相当于[self setAge:newAge],即又调用了自身,则会无限循环调用自己// self.age = newAge;}-(int)age{ //相当于[self age],也会调用自身,会无限调用自己// return self.age; return _age;}@endstu->age;是直接访问成员变量,但只能访问@public修饰的变量;
stu.age=18;是调用setAge方法。 ++ 构造方法 ++ .h中
//自定义一个构造方法,是一个动态方法,内部方法//由于系统的构造方法返回都是id,为保持一致,也返回id;(id可代表所有OC对象指针)-(id)initWithSize:(int)size andPRice:(double)price;.m中
//实现构造方法-(id)initWithSize:(int)size andPrice:(double)price{ //首先要调用父类的实现方法 // self= [super init]; //再进行赋值 //父类返回的对象可能会为空 //相当于if (self!=nil) // if (self){ // _size = size; //或 self._size = size; // _price = price; // } //或者上面的可简化为 if (self = [super init]) { _size = size; _price = price; } return self;}//重写,类似于java中对象的toString//...同java,代表多个参数//NSString *str = @"str";是OC中的字符串,为区别C,都加 @;- (NSString *)description{ return [NSString stringWithFormat:@"size:%i,price:%f", _size,_price];}//%@表示打印一个OC对象,需重写(NSString *)description方法(同java的toString); NSLog(@” to string %@“,car);
…同java,代表多个参数 [所有返回*的地方都可以用id代替,且id是关键字,不能直接做变量名]
++ 变量的使用域: 类的成员变量默认是protected的,一般使用默认的即可,不需要添加修饰符 @public :全局都可访问,成员变量可用->直接访问; @protected:只能在类内部和子类访问 @private :只能在类内部访问 (OC中没有包的的概念,故没有default) - 代表动态方法,就是对象内部方法; + 代表静态方法 [静态方法同java,也可使用self,但不同的是在静态方法中的self意思是指向的类名,而不是self对象] (即 谁调用方法,self就指向谁) 静态方法不能访问成员变量; 在头文件中声明的方法都是public的; 若直接写在.m文件中的方法,没有在.h文件中进行声明,那么这个方法是私有方法
++ 内存释放 系统自带的静态方法创建的对象都会自动释放; 1.在定义时自动释放: Car *car = [[[Car alloc] initWithSize:10 andPrice:23.0f] autorelease]; 2.手动释放 [person release]; ** autorelease 是在适当的时机释放,而不是马上释放. ++new ++ Student *stu= [Student new];//相当于Student *stu = [[Student alloc] init] 但很少用new,这是新版本才添加的关键字;
++ @property++ 生成set/get的声明 为了解决set/get方法的冗余的麻烦; 只用在.h文件里,用于声明方法(set/get) @property int age;//编译器遇到@property时,会生动生成set/get方法的声明
++ @synthesize++ 生成set/get的实现[同@property是编译器的特性] 为了解决set/get的实现 @synthesize age,_no,height;//相当于三个成员变量的实现 [若用synthesize了,即可在.h文件中不写成员变量age,_no,height,会默认去访问与age同名的变量,若找不到同名的变量,会自动生成一个同名变量,并且若自己定义的成员变量的名字与@synthesize不一样时,会默认创建自动生成的] [但为了统一风格,成员变量以_开头,需要指定@synthesize默认生成的变量名,如:@synthesize age=_age,no=_no,height=_height;]
**故在定义类的成员变量时,一般需声明变量名;直接在头文件使用@property,.m文件中使用@synthesize; [Xcode在4.5后添加了新特性:.m文件中的@synthesize都可省略(会自动生成@synthesize age=_age,no=_no,height=_height;这句代码,默认自带下划线),也就是直接在.h文件中用@property声明即可] [但自己写的set/get会优先使用;若自己手动实现了get/set方法,Xcode就不会自成生成@synthesize,也不会生成set/get方法]
++ 内存管理 ++ 管理范围:任何继承了NSObject的对象,对基本的数据类型不需要; 原理:每个对象内部都保存了一个与之有关的整数,称为引用计数器;每当使用alloc,new,copy创建一个对象时,对象的引用计数器被设置为1;给对象发送一条retain消息,可以引用计数器值+1;给对象发送一条release消息,可使引用计数器值-1;当一个对象的引用计数器值为0时,那么它将被销毁,其占用的内存被系统回收,OC也会自动向对象发送一条dealloc消息.一般会重写dealloc方法,在这里释放相关资源.[但一定不要直接调用dealloc方法,是系统自动调用该方法] — 可用retainCount消息获得当前对象的引用计数器值; [java中new了一个对象后若没有变量引用,则会自己销毁;但在OC中,若新建了一个对象,没有变量引用,计数器值会为1,若没有手动回收或关闭app,则永不会被回收,]
- (void)dealloc{ NSLog(@"系统调用回收方法"); //一定要调用super的dealloc方法,而且最好放在最后面调用 [super dealloc];}[stu release];方法只能调用一次,若调用多次,会发生野指针错误(访问了不属于你的内存)
— “谁创建,谁释放”;若用alloc,new,copy的创建的对象,就必须调用release或autorelease来释放;不是你创建的,就不用你去释放; —“谁retain,谁release”只要调用retain,无论这个对象是谁创建的,都需要release释放;
ARC特性(auto reference count):4.5后的新特性,由编译器自动进行内存释放;
++ 对象之间的内存管理 ++ //直接调用set的一个对象,对象没有被回收,会造成内存泄露 [stu setBook:[[Book alloc] initWithID:100]]; 对象的set方法也会创建一个对象,对象的计数+1;需要对原对象释放;
-(void)setBook:(Book *)book{if(_book != book){//传进来的变量可能已经释放,为了正确,需判断是否为同一个,不是同一个才进行释放和引用;也能保证在多次调用时引用的计数器值不会增加,不会出现内存泄露的情况; [_book relase];//先释放旧的成员变量,且不用担心空指针; _book = [book retain];//再retain传进来的对象}}[//可在头文件中用 @property (retain )Book *book;代替上面的set方法 ][**OC中若对象为nil,调用其方法不能出现空指针异常] [stu release];/若stu已经释放过了再释放,是野指针会出错 [nil release];//是空指针,再释放,不会报错; //野指针;在内存中指针仍保留原地址,但现在该块内存的内容已不在原来的内容,也不于属于原内容,故会报错;为避免报错,需要清空原指针(相当于java中的置null) stu = nil;//清空指针中的地址; ++ 若类中有成员变量是自定义的对象,要先释放:
- (void)dealloc{ NSLog(@"系统调用回收方法"); //一定要调用super的dealloc方法,而且最好放在最后面调用 //成员变量要释放 [_book release];// NSLog(@"内部对象book计数值:%i",[_book retasinCount]); [super dealloc];}++ 通常引用一个类的两种方法:#import;@class; ++ @class ++用于声明一个类,不知道实现,在使用时才用#import **若在.h文件中只是要用到某个类,如定义一个成员变量,没必要用#import “Book.h”;这样是把.h文件的内容拷贝到此处,影响性能(若有上百个类用#import一个类A,A中使用一点改动,则所有子类都会重新编译;而@class则不会); 若只是声明一个成员变量,只用使用@class Book;即可,声明Book这个类即可,提高效率; [对自定义的对象相互持有的情况下必须用 @class;如A中有B,B中有A;就不能使用#import,会造成递归导入;编译错误] [对于继承的父类,不能使用@class,须使用#import,用于将父类的头文件拷贝到此处,让子类知道哪些方法已经定义过。] 但在.m要用到这个类下的方法,还是需要#import “Book.h”
++ @property 内存管理 ++ @(参数1,参数2)类型 名字; 参数分类: 读写属性:readwrite/readonly;//默认是readwrite,并生成set/get;readonly代表只生成get方法 setter属性:assign/retain/copy;//默认是assign,直接赋值;retain,引用+1; 原子性:atomic/nonatomic;//默认atomic,加锁,提供了多线程安全;nonatomic,禁止多线程,变量保护,可提高性能;[对于在iPone这个小型设备上,内存很小,默认情况下不需要考虑太多的多线程,以提高性能] (在做IOS开发时,尽量多使用nonatomic) 如:
@property (nonatomic,assign) int ID;@property (nonatomic,retain) Book *book;@property (nonatomic,getter=isEnable) BOOL enable;//指定get的方法名;@property (retain)Card *card;//就相当于上面手动判断,释放再retain的set方法;(若是基础类型不需要添加,否则要添加retain)++autorelease pool ++自动释放池 OC中的一种内存自己回收机制,一般可将一些临时变量添加到自动释放池中,统一回收释放; 当自动释放池销毁时,池里面的所有对象都会调用一次release方法; 使用:OC对象只需要发送一条autorelease消息,就会把这个对象添加到最近的自动释放池中(栈顶的释放池);autorelese实际上是把对release的调用延迟了,对于每一次autorelease,系统只是把该对象放入了autorelease pool中,当pool被释放时,该pool会调用一次所有的对象relase方法。 [若使用autorelease 后就不需手动释放] [autorelease pool会在程序执行完成后销毁]
//代表创建一个自动释放池; @autoreleasepool { // testMemoeryReales(); Student *stu = [[Student alloc] init]; //只是把对象放到autorelease pool中;当autoreleasepool被销毁时,会自动调用一次池里所有的对象的release方法, [stu autorelease]; //则上面可简化成 Student *stu1= [[[Student alloc] init] autorelease]; }*[IOS5.0之前: NSAutoreleasePool *pool = [[NSAutorelease] alloc]init]; [pool release];(在ARC下,不能使用这种方法,只能使用@autoreleasepool;ARC本质上就是一个释放池的操作)]
[NOTE:*不要在一个autoreleasepool中放入大量循环操作(for,while等,应使用手动release);尽量以免对大内存使用方法,对于这种延迟释放机制,尽少使用;SDK中一般利用静态方法创建并返回的对象都是已经autorelease的,不需要再release操作;] 自定义一个快速创建的构造方法: +(id)student{ return [[[Student alloc] init] autorelease]; } [规范:静态方法返回的对象都是自己负责释放的]
感谢李明杰老师@M了个J的讲解及时详细的课件(http://www.cnblogs.com/mjios)
博客地址:IOS学习笔记之Object-C(一)
新闻热点
疑难解答