C++转Objective-c的纠结惆怅 —— objective-c的怪异特性

C++转Objective-c的纠结惆怅 —— objective-c的怪异特性
令人纠结到发指的Foundation Kit
先来看看有关Foundation中几个简单class的实例:
int main(int argc, const char * argv[])
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   
    NSArray *array_static = [[NSArray alloc] initWithObjects:@"one",@"two",@"three", nil];
    for (int i = 0; i < [array_static count]; i++) {
        NSLog(@"%@",[array_static objectAtIndex:i]);
    }
   
    NSMutableArray *array_dynamic = [[NSMutableArray alloc] init];
    for (int i = 0; i < 3; i++) {
        NSNumber *number1 = [NSNumber numberWithInt:i+1];
        [array_dynamic addObject:number1];
    }
    
    for(int i = 0; i < [array_dynamic count]; i++)
    {
        NSLog(@"%i",[[array_dynamic objectAtIndex:i] intValue]);
    }
    [array_dynamic removeAllObjects];
    
    NSMutableDictionary *map = [[NSMutableDictionary alloc] initWithCapacity:3];
    
    [map setValue:@"one" forKey:@"1"];
    [map setValue:@"two" forKey:@"2"];
    [map setValue:@"three" forKey:@"3"];
    
    for(int i = 0; i < [map count]; i++)
    {
        NSLog(@"%@",[map objectForKey:[NSString stringWithFormat:@"%i",i+1]]);
    }
    [array_dynamic release];
    [array_static release];
    [map release];
    [pool drain];
    return 0;
}
自从我接触到Obj-c中的NSArray,我便一直想不通一个问题:既然NSArray不能动态改变其大小,而且 Foundation kit中还存在一个NSMutableArray可以弥补NSArray的这种缺陷,甚至能提供比NSArray更多的功能,那么NSArray还有什么 存在的必要呢? 有人说NSArray的效率比NSMutableArray的要高一些,我便更不能理解了——连C++这种直接操作内存的高效语言都没搞一个不能改变大小 的Array Class出来,OBJ-C缘何还要来画蛇添个足呢?用NSMutableArray来完全代替NSArray不好吗?这问题一直困扰着我 。。  直到刚刚才猛然间的豁然开朗了一下:C++完全没必要实现的这么复杂,有个跟NSArray功能相近的数组就行了。 如果从效率上面来理解的话,NSArray绝对是一次性分配固定内存的,而NSMutableArray则是动态分配内存的,倘若事先知道一个数组的大 小,这时NSArray就能展现出比NSMutableArray更高的效率了。。。 而对于CCArray和CCMutableArray不能存储基本内置数据类型而只能存储OBJ-C对象时,又让我纠结不已。。。 STL中的所有容器可不带这样的。。。  能想到的是CCArray内部存储对象实际是在存储对象的地址(指针)而非对象的拷贝,否则便不会有此限制。All in all, NSArray的存在绝对是有必要的。
另外对于NSDictionary,我先前以为它是个和STL中MAP功能相似的类,只不过改了个名 字罢了,现在看来却非如此。事实上,map中对于Key的要求是很随意得,只要能比较大小即可(比如数字或字符串甚至任意重载了“<"运算符的 class),而NSDictonary的Key必须是NSString类型。。如此的限制再次让我纠结到泪奔。。。
从2d开源引擎cocos2d-x看objective-c的内存管理机制
Object-C中对于内存管理不像C++那样随意创建和释放,也不再像JAVA中那样只管NEW不管释放了。 所有的内存管理都是通过Reference Count机制来实现的。 关于Reference Count机制,在我之前一篇博文
《主流RAII class的存在价值——不存在能够完全替代Dumb Pointer的RAII class 》
中 讲解shared_ptr时详细讲解过其原理,而Object-c中所有Counting机制已经不再那么透明了,苹果规定给你怎么用你就得怎么用,要想 知道其内部实现机制只能透过表象来猜测了。。 但有一点我一直感到很庆幸,那便是:目前比较主流的2D游戏引擎cocos2d-x作为cocos2d的C++版本,简直就是用C++把OBJ-C全然模 拟了一遍。 对于其Reference Count机制,尤其是AutoReleasePool的实现着实让人津津乐道。以下附上cocos2d-x中AutoReleasePool头文件部 分:
namespace cocos2d {
class CC_DLL CCAutoreleasePool : public CCObject
{
    CCMutableArray<CCObject*>*  m_pManagedObjectArray;  
public:
    CCAutoreleasePool(void);
    ~CCAutoreleasePool(void);
 
    void addObject(CCObject *pObject);
    void removeObject(CCObject *pObject);
 
    void clear();
};
 
class CC_DLL CCPoolManager
{
    CCMutableArray<CCAutoreleasePool*>* m_pReleasePoolStack;    
    CCAutoreleasePool*                  m_pCurReleasePool;
 
    CCAutoreleasePool* getCurReleasePool();
public:
    CCPoolManager();
    ~CCPoolManager();
    void finalize();
    void push();
    void pop();
 
    void removeObject(CCObject* pObject);
    void addObject(CCObject* pObject);
 
    static CCPoolManager* getInstance();
 
    friend class CCAutoreleasePool;
};
 
}
 
看的出其有一个对象池:CCMutableArray用来存储所有加入到内存池中的对象。当对象使用autorelease时候, 究其 源码在把对象放入到内存池的同时不影响对象本身的Counting值。而用new的话会在基类CCObject的构造函数中中计数值设为1。如果 counting机制的retain和release用的很混乱会如何呢?要么对象早释放,要么晚释放。。 对于晚释放的,事实上算内存泄露,而对于早释放的,在debug状态下便会出异常。
在object-c中对于一些其它创建对象的方式如:initwithXXXX之类的,在cocos2d-x中用C++模拟时候实际是在内部调用node方法,此方法通过宏定义来实现,通常是这样的:
#define LAYER_NODE_FUNC(layer) \
static layer* node() \
{ \
layer *pRet = new layer(); \
if (pRet && pRet->init()) \
{ \
pRet->autorelease(); \
return pRet; \
} \
else \
{ \
delete pRet; \
pRet = NULL; \
return NULL; \
} \
}; 
即先new后autorelease,如此明了不必再多做阐释了。。 对于cocos2d-x的开源团队能将object-c模拟的如此精致,至此,不得不感叹开源社区的强大。。。
使用category和protocol替换掉C++中的virtual function以达到多态的另类实现
对于Object-c的Category(类别),看起来是对C++中的 vitrual function的缺陷的改善,因为形式上而言,子类(这样说貌似有些不恰当,暂且这样称呼)是纯粹对父类的方法进行扩充而不需要重新继承父类很多信息, 以此便省出了virtual table的创建空间。。  也因为如此,可以只对方法进行声明而不进行实现,如此又有些像C++的特性,在object-c中又把这种在category的特性叫做“非正式协议”, 因为子类可以拓展并覆盖(同名情况)父类的方法。其另外一显著作用便是可以将庞大的类分散到很多小块中去实现,以使得条理更加清晰。。  以下是用category实现的一个proxy(代理)模式。这种模式在OBJ-C中被大量用到,现假设有个Cwnd(窗口)类,它接受点击消息但其类内 部本身不去处理,将其委托给WindowMessageCategory去处理,代码如下:
//
//  main.m
//  Language_Project
//
//  Created by wei yang on 12-6-27.
//  Copyright (c) 2012年 __MyCompanyName__. All rights reserved.
//
 
#import <Foundation/Foundation.h>
 
@interface CWnd : NSObject
{
    id delegate;
}
-(id) WindowOnClick;
 
@end
 
@implementation CWnd
 
-(id) init
{
    if(self = [super init])
    {
        delegate = self;
    }
    return self;
}
-(id) WindowOnClick
{
    NSLog(@"window on click");
    if([delegate respondsToSelector:@selector(respondsClick)])
    {
        [delegate performSelector:@selector(respondsClick)];
    }
    NSLog(@"click finished!");
    return nil;
}
@end
 
@interface CWnd(WindowMessageCategory)
-(void) respondsClick;
@end
 
@implementation CWnd(WindowMessageCategory)
 
-(void) respondsClick
{
    NSLog(@"the click event has been responded");
}
 
@end
 
int main(int argc, const char * argv[])
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    CWnd *window = [[CWnd alloc] init];
    [window WindowOnClick];
    [window release];
    [pool drain];
    return 0;
}
在C++中,运行时的多态可通过继承和虚函数来实现,而在Object-c中没有虚函数一说,取而代之的是协议。声明一个协议相当于声明了一个 pure virtual class,让子类实现其定义的接口。而多态的实现则是通过继承,协议以及委托代理三者结合来实现的。对于上述中用category来实现的proxy模 式,用protocol的方式实现如下:
//
//  main.m
//  Language_Project
//
//  Created by wei yang on 12-6-27.
//  Copyright (c) 2012年 __MyCompanyName__. All rights reserved.
//
 
#import <Foundation/Foundation.h>
 
@protocol ResPondsWindowMessage <NSObject>
 
@optional        
-(void) RespondsClick;
 
@end
 
@interface CWnd:NSObject<ResPondsWindowMessage>
{
    id <ResPondsWindowMessage> delegate; 
}
 
-(id) WindowOnClick;
@end
 
@implementation CWnd
-(id) init
{
    if(self = [super init])
    {
        delegate = self;
    }
    return self;
}
-(id) WindowOnClick
{
    
    NSLog(@"window on click!");
    if([delegate conformsToProtocol:@protocol(ResPondsWindowMessage)]
       && [delegate respondsToSelector:@selector(RespondsClick)] )
        {
            [delegate performSelector:@selector(RespondsClick)];
        }
    NSLog(@"click finished!");
    return nil;
}
@end
 
@interface MyWnd : CWnd
-(void) RespondsClick;
@end
 
@implementation MyWnd
 
-(id) init
{
    if(self = [super init])
    {
        delegate = self;
    }
    return self;
}
 
-(void) RespondsClick
{
    NSLog(@"click event has been responded!");
}
 
@end
 
int main(int argc, const char * argv[])
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    CWnd *window = [[MyWnd alloc] init];
    [window WindowOnClick];
    [window release];
    [pool drain];
    return 0;
}
对于category和protocol的阐述大概差不多了。 在这里我并没有很详细的说明使用其时需要注意的地方,这些基本的东西可以去看《objective-c 2.0基础教程》,其实旨在指出Objective-c与才C++不同和相似的地方,进行对比说明的同时,还以此达到减少自己对于这种“怪异”语言困惑和 纠结的地方。
作者:yangyw_112299
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值