程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> C++轉Objective-c的糾結惆怅 —— objective-c的怪異特性

C++轉Objective-c的糾結惆怅 —— objective-c的怪異特性

編輯: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

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved