快速掌握 ObjectiveC (For C/C++ developer)

本文通过触类旁通的启发方式,方便具备C/C++经验的筒子们快速掌握Objective-C。

基本语法


首先看一段简单的Objective-C的代码.

Objective-C支持和C++一样的分离编译模式。C++中是*.h和*.cpp文件; Objective-C是 *.h和 *.m文件来组成一个类。

下面是Rectangle.h文件

  1. #import <Foundation/NSObject.h> 
  2.  
  3. @interface Rectangle: NSObject { 
  4.     int width; 
  5.     int height; 
  6.  
  7. @private 
  8.     int privateVar; 
  9.     int privateVar2; 
  10.  
  11. +(int) staticMethod; 
  12. -(Rectangle*) initWithWidth: (int) w height: (int) h; 
  13. -(void) setWidth: (int) w; 
  14. -(void) setHeight: (int) h; 
  15. -(void) setWidth: (int) w height: (int) h; 
  16. -(int) width; 
  17. -(int) height; 
  18. -(void) print; 
  19. @end 
#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
  • #import 这个是引入头文件定义,和C中的#include类似,区别是它会避免重复引入。
  • 在头文件中使用@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是实际的参变量。
  • Objective-C的类一般都是直接或者间接地从NSObject派生来的。NSObject中封装了对RTTI的支持以及引用计数。
  • Objective-C是支持属性的,这个后文会说到。
  • 私有成员函数可以不出现在头文件中进行定义,毕竟它是私有的嘛,没必要出现。

下面看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

  • isEqual
  • isKindOfClass
  • isMemberOfClass
  • isSubclassOfClass

Objective-C的类有2种内存管理方式:引用计数和垃圾回收。但是在iOS上垃圾回收是不支持的,而只能使用引用计数方式。NSObject有下列方法来支持引用计数的内存管理:

  • + (id)new
  • + (id)alloc
  • + (id)allocWithZone:(NSZone *)zone
  • - (id)init
  • - (id)retain
  • - (NSUInteger)retainCount
  • - (oneway void)release
  • - (id)autorelease
  • - (void)dealloc

下面一段代码是一个典型的对象初始化与销毁。

Rectangle * rect = [[Rectangle alloc] initWithWidth: 20 height:50];

[rect release]

在C++中, new一个对象实际上分为2个步骤:1. 申请对象所需的内存; 2.调用构造函数。而在Objective-C中这2个步骤被拆开来。

  1. 首先[Rectangle alloc]调用从NSObject继承来的+ (id)alloc方法申请内存; 返回类型id等同于C中的void*指针,只是更加严格,它是一个指向对象的指针。
  2. 其次调用构造函数initWithWidth。注意在Obj-C中,构造函数的名称是没有任何限制的,但一般都是以init作为前缀。
  3. 在构造函数内部必须显式调用基类的构造函数self = [super init];, 在设置self后,(也就是this指针),构造函数的返回参数必须也是self;当然如果构造函数内部出现错误你可以马上release并返回nil. nil就是id类型的空指针。
  4. 在构造函数返回后,将返回值也就是新创建的对象指针通过Rectangle *保存下来。
  5. 在使用完后,调用[rect release]进行释放。

在释放的时候,并不需要显式地调用dealloc来释放内存。因为NSObject中有引用计数这个机制来保证对象内存的准确释放(当然前提是release没有被落下)。如果你有过Windows下COM编程的经历,一定会记得IUnknown接口中的AddRefRelease。在NSObject和它们对等的就是retainrelease方法。C++ STL中有个非常近似的类就是shared_ptr。

在NSObject内部有一个引用计数器,可以通过- (NSUInteger)retainCount获得它的值。当一个对象被NSObject初始化的时候引用计数器为1,如果需要复制这个对象的话则需要调用retain一次,也就类似于IUnknown中的AddRef方法在达到引用计数器增1; 当对象不再需要的时候调用release来达到引用计数器减1,当所有引用都release的话引用计数器变为0,触发dealloc方法的执行。所以dealloc并不需要在外部调用;所以,dealloc可以被当成析构函数使用,在基类中重载它。当触发的时候首先释放资源然后调用基类的dealloc。下图就是此过程很好的阐述。

NSObject也提供一个+ (id)new的方法,它实际上是allocinit的组合,实际中很少用到,毕竟构造函数不一定是init.

还有一个经常用到的方法是- (id)autorelease。因为很多情况下release并不能被准确调用,比如从方法中返回一个新创建的对象,这种情况下不能期望外部调用在使用完后能够准确释放。而autorelease很好地解决这类问题。当一个对象的autorelease方法被调用后,实际上该对象实例的生存期控制权就交到了NSAutoreleasePool手上,当NSAutoreleasePool被释放的时候所有关联的对象也会一起释放。这个就好比C++ STL中的smart_ptr,将一个对象的生存期控制转交给另外一个对象控制。

原文地址:https://www.cnblogs.com/caishuhua226/p/2814851.html