++ 概述 ++
目前来说,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个文件来描述一个类;
.h:类的声明文件,用于声明成员变量、方法。类的声明使用关键字@interface和@end。
[注意:.h中的方法只是做一个声明,并不对方法进行实现。也就是说,只是说明一下方法名、方法的返回值类型、方法接收的参数类型而已,并不会编写方法内部的代码。]
.m:类的实现文件,用于实现.h中声明的方法。类的实现使用关键字@implementation和@end。
.h
//用于声明Student这个类由哪些成员变量和方法(面对对象称方法,面向过程称函数)
//导包,Foundation包含了常用的类,(类似java中的java.lang类)
#import
//@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;
}
@end
stu->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(一)