本文通过触类旁通的启发方式,方便具备C/C++经验的筒子们快速掌握Objective-C。
基本语法
首先看一段简单的Objective-C的代码.
Objective-C支持和C++一样的分离编译模式。C++中是*.h和*.cpp文件; Objective-C是 *.h和 *.m文件来组成一个类。
下面是Rectangle.h文件
#import <Foundation/NSObject.h> @interface Rectangle: NSObject { int width; int height; @private int privateVar; int privateVar2; } +(int) staticMethod; -(Rectangle*) initWithWidth: (int) w height: (int) h; -(void) setWidth: (int) w; -(void) setHeight: (int) h; -(void) setWidth: (int) w height: (int) h; -(int) width; -(int) height; -(void) print; @end
@interface Classname : Baseclass @end
来定义一个类的接口。{...}
定义的是类的成员变量。默认访问级别是protected;也可以像C++中一样加入@private / @public / @protected
来指定访问级别。比较特殊的是@package
,它类似于C#中的internal
,表示在同一个程序集中是公有的。+(int) staticMethod;
;减号开头的是普通的成员函数。紧接着是方法的返回类型和方法名称,冒号后面是参数(如果有参数)。-(void) setWidth: (int) w;
。而对于后面的参数则需要加上命名参数,如-(Rectangle*) initWithWidth: (int) w height: (int) h;
,这里的height
是参数的命名,而h
是实际的参变量。下面看Rectangle.m文件,这个就是类的实现文件了。
#import "Rectangle.h" #import <stdio.h> @implementation Rectangle -(Rectangle*) initWithWidth: (int) w height: (int) h { self = [super init]; if ( self ) { [self setWidth: w height: h]; } return self; } -(void) setWidth: (int) w { width = w; } -(void) setHeight: (int) h { height = h; } -(void) setWidth: (int) w height: (int) h { width = w; height = h; } -(int) width { return width; } -(int) height { return height; } -(void) print { printf( "width = %i, height = %i", width, height ); } @end
@implementation Classname @end
中定义的是类的实现部分。 self
等同于this
指针super
表示直接基类,类似于C#中的base
,或者MSVC扩展中的__super
[super init]
表示一次调用,可以看成是super->init()
或者super.init()
NSObject
绝大部分Obj-C的类都从NSObject派生而来。当NSObject被初始化的时候,它内部的isa结构会保存关于此对象类型的描述信息。你可以看到从NSObject派生来的类都具有如下方法支持RTTI
Objective-C的类有2种内存管理方式:引用计数和垃圾回收。但是在iOS上垃圾回收是不支持的,而只能使用引用计数方式。NSObject有下列方法来支持引用计数的内存管理:
下面一段代码是一个典型的对象初始化与销毁。
Rectangle * rect = [[Rectangle alloc] initWithWidth: 20 height:50]; [rect release]
在C++中, new一个对象实际上分为2个步骤:1. 申请对象所需的内存; 2.调用构造函数。而在Objective-C中这2个步骤被拆开来。
[Rectangle alloc]
调用从NSObject继承来的+ (id)alloc
方法申请内存; 返回类型id
等同于C中的void*
指针,只是更加严格,它是一个指向对象的指针。initWithWidth
。注意在Obj-C中,构造函数的名称是没有任何限制的,但一般都是以init
作为前缀。self = [super init];
, 在设置self
后,(也就是this
指针),构造函数的返回参数必须也是self;当然如果构造函数内部出现错误你可以马上release
并返回nil
. nil
就是id
类型的空指针。Rectangle *
保存下来。[rect release]
进行释放。在释放的时候,并不需要显式地调用dealloc
来释放内存。因为NSObject中有引用计数这个机制来保证对象内存的准确释放(当然前提是release没有被落下)。如果你有过Windows下COM编程的经历,一定会记得IUnknown
接口中的AddRef
和Release
。在NSObject和它们对等的就是retain
和release
方法。C++ STL中有个非常近似的类就是shared_ptr。
在NSObject内部有一个引用计数器,可以通过- (NSUInteger)retainCount
获得它的值。当一个对象被NSObject初始化的时候引用计数器为1,如果需要复制这个对象的话则需要调用retain
一次,也就类似于IUnknown
中的AddRef
方法在达到引用计数器增1; 当对象不再需要的时候调用release
来达到引用计数器减1,当所有引用都release
的话引用计数器变为0,触发dealloc
方法的执行。所以dealloc
并不需要在外部调用;所以,dealloc
可以被当成析构函数使用,在基类中重载它。当触发的时候首先释放资源然后调用基类的dealloc
。下图就是此过程很好的阐述。
NSObject也提供一个+ (id)new
的方法,它实际上是alloc
和init
的组合,实际中很少用到,毕竟构造函数不一定是init
.
还有一个经常用到的方法是- (id)autorelease
。因为很多情况下release
并不能被准确调用,比如从方法中返回一个新创建的对象,这种情况下不能期望外部调用在使用完后能够准确释放。而autorelease
很好地解决这类问题。当一个对象的autorelease
方法被调用后,实际上该对象实例的生存期控制权就交到了NSAutoreleasePool
手上,当NSAutoreleasePool
被释放的时候所有关联的对象也会一起释放。这个就好比C++ STL中的smart_ptr
,将一个对象的生存期控制转交给另外一个对象控制。