温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

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

发布时间:2020-05-01 01:17:58 来源:网络 阅读:3694 作者:yangyw_112299 栏目:移动开发

前言

应公司要求,最近开始做IOS应用了,这意味着 什么?全新的语法,全新的技术,全新得框架都要等着我去熟悉呢。。 对于我一个传统的C++程序员来说,理论上要熟悉Objective-C的语法当然用时不多了,只是接触之后才发现,这语法让人纠结到头皮发麻,全身发痒 (说的貌似有点过了)。其实几天前就想写这篇关于OBJECT-C语法方面的博文了,只是一直苦于上班不能写博客,下班又没MAC机用的恶劣境况,才推迟 到现在。好了,言归正传了。

 

令人纠结到发指的Foundation Kit

先来看看有关Foundation中几个简单class的实例:

  1. int main(int argc, const char * argv[]) 
  2.     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 
  3.     
  4.     NSArray *array_static = [[NSArray alloc] initWithObjects:@"one",@"two",@"three", nil]; 
  5.     for (int i = 0; i < [array_static count]; i++) { 
  6.         NSLog(@"%@",[array_static objectAtIndex:i]); 
  7.     } 
  8.     
  9.     NSMutableArray *array_dynamic = [[NSMutableArray alloc] init]; 
  10.     for (int i = 0; i < 3; i++) { 
  11.         NSNumber *number1 = [NSNumber numberWithInt:i+1]; 
  12.         [array_dynamic addObject:number1]; 
  13.     } 
  14.      
  15.     for(int i = 0; i < [array_dynamic count]; i++) 
  16.     { 
  17.         NSLog(@"%i",[[array_dynamic objectAtIndex:i] intValue]); 
  18.     } 
  19.     [array_dynamic removeAllObjects]; 
  20.      
  21.     NSMutableDictionary *map = [[NSMutableDictionary alloc] initWithCapacity:3]; 
  22.      
  23.     [map setValue:@"one" forKey:@"1"]; 
  24.     [map setValue:@"two" forKey:@"2"]; 
  25.     [map setValue:@"three" forKey:@"3"]; 
  26.      
  27.     for(int i = 0; i < [map count]; i++) 
  28.     { 
  29.         NSLog(@"%@",[map objectForKey:[NSString stringWithFormat:@"%i",i+1]]); 
  30.     } 
  31.     [array_dynamic release]; 
  32.     [array_static release]; 
  33.     [map release]; 
  34.     [pool drain]; 
  35.     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头文件部 分:

  1. namespace cocos2d { 
  2. class CC_DLL CCAutoreleasePool : public CCObject 
  3.     CCMutableArray<CCObject*>*  m_pManagedObjectArray;   
  4. public
  5.     CCAutoreleasePool(void); 
  6.     ~CCAutoreleasePool(void); 
  7.  
  8.     void addObject(CCObject *pObject); 
  9.     void removeObject(CCObject *pObject); 
  10.  
  11.     void clear(); 
  12. }; 
  13.  
  14. class CC_DLL CCPoolManager 
  15.     CCMutableArray<CCAutoreleasePool*>* m_pReleasePoolStack;     
  16.     CCAutoreleasePool*                  m_pCurReleasePool; 
  17.  
  18.     CCAutoreleasePool* getCurReleasePool(); 
  19. public
  20.     CCPoolManager(); 
  21.     ~CCPoolManager(); 
  22.     void finalize(); 
  23.     void push(); 
  24.     void pop(); 
  25.  
  26.     void removeObject(CCObject* pObject); 
  27.     void addObject(CCObject* pObject); 
  28.  
  29.     static CCPoolManager* getInstance(); 
  30.  
  31.     friend class CCAutoreleasePool; 
  32. }; 
  33.  

 

看的出其有一个对象池:CCMutableArray用来存储所有加入到内存池中的对象。当对象使用autorelease时候, 究其源码在把对象放入到内存池的同时不影响对象本身的Counting值。而用new的话会在基类CCObject的构造函数中中计数值设为1。如果 counting机制的retain和release用的很混乱会如何呢?要么对象早释放,要么晚释放。。 对于晚释放的,事实上算内存泄露,而对于早释放的,在debug状态下便会出异常。

在object-c中对于一些其它创建对象的方式如:initwithXXXX之类的,在cocos2d-x中用C++模拟时候实际是在内部调用node方法,此方法通过宏定义来实现,通常是这样的:

  1. #define LAYER_NODE_FUNC(layer) \ 
  2. static layer* node() \ 
  3. { \ 
  4. layer *pRet = new layer(); \ 
  5. if (pRet && pRet->init()) \ 
  6. { \ 
  7. pRet->autorelease(); \ 
  8. return pRet; \ 
  9. } \ 
  10. else \ 
  11. { \ 
  12. delete pRet; \ 
  13. pRet = NULL; \ 
  14. return NULL; \ 
  15. } \ 
  16. };  

即先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去处理,代码如下: 

  1. // 
  2. //  main.m 
  3. //  Language_Project 
  4. // 
  5. //  Created by wei yang on 12-6-27. 
  6. //  Copyright (c) 2012年 __MyCompanyName__. All rights reserved. 
  7. // 
  8.  
  9. #import <Foundation/Foundation.h> 
  10.  
  11. @interface CWnd : NSObject 
  12.     id delegate; 
  13. -(id) WindowOnClick; 
  14.  
  15. @end 
  16.  
  17. @implementation CWnd 
  18.  
  19. -(id) init 
  20.     if(self = [super init]) 
  21.     { 
  22.         delegate = self; 
  23.     } 
  24.     return self; 
  25. -(id) WindowOnClick 
  26.     NSLog(@"window on click"); 
  27.     if([delegate respondsToSelector:@selector(respondsClick)]) 
  28.     { 
  29.         [delegate performSelector:@selector(respondsClick)]; 
  30.     } 
  31.     NSLog(@"click finished!"); 
  32.     return nil; 
  33. @end 
  34.  
  35. @interface CWnd(WindowMessageCategory) 
  36. -(void) respondsClick; 
  37. @end 
  38.  
  39. @implementation CWnd(WindowMessageCategory) 
  40.  
  41. -(void) respondsClick 
  42.     NSLog(@"the click event has been responded"); 
  43.  
  44. @end 
  45.  
  46. int main(int argc, const char * argv[]) 
  47.     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 
  48.     CWnd *window = [[CWnd alloc] init]; 
  49.     [window WindowOnClick]; 
  50.     [window release]; 
  51.     [pool drain]; 
  52.     return 0; 

在C++中,运行时的多态可通过继承和虚函数来实现,而在Object-c中没有虚函数一说,取而代之的是协议。声明一个协议相当于声明了一个 pure virtual class,让子类实现其定义的接口。而多态的实现则是通过继承,协议以及委托代理三者结合来实现的。对于上述中用category来实现的proxy模 式,用protocol的方式实现如下:

  1. // 
  2. //  main.m 
  3. //  Language_Project 
  4. // 
  5. //  Created by wei yang on 12-6-27. 
  6. //  Copyright (c) 2012年 __MyCompanyName__. All rights reserved. 
  7. // 
  8.  
  9. #import <Foundation/Foundation.h> 
  10.  
  11. @protocol ResPondsWindowMessage <NSObject> 
  12.  
  13. @optional         
  14. -(void) RespondsClick; 
  15.  
  16. @end 
  17.  
  18. @interface CWnd:NSObject<ResPondsWindowMessage> 
  19.     id <ResPondsWindowMessage> delegate;  
  20.  
  21. -(id) WindowOnClick; 
  22. @end 
  23.  
  24. @implementation CWnd 
  25. -(id) init 
  26.     if(self = [super init]) 
  27.     { 
  28.         delegate = self; 
  29.     } 
  30.     return self; 
  31. -(id) WindowOnClick 
  32.      
  33.     NSLog(@"window on click!"); 
  34.     if([delegate conformsToProtocol:@protocol(ResPondsWindowMessage)] 
  35.        && [delegate respondsToSelector:@selector(RespondsClick)] ) 
  36.         { 
  37.             [delegate performSelector:@selector(RespondsClick)]; 
  38.         } 
  39.     NSLog(@"click finished!"); 
  40.     return nil; 
  41. @end 
  42.  
  43. @interface MyWnd : CWnd 
  44. -(void) RespondsClick; 
  45. @end 
  46.  
  47. @implementation MyWnd 
  48.  
  49. -(id) init 
  50.     if(self = [super init]) 
  51.     { 
  52.         delegate = self; 
  53.     } 
  54.     return self; 
  55.  
  56. -(void) RespondsClick 
  57.     NSLog(@"click event has been responded!"); 
  58.  
  59. @end 
  60.  
  61. int main(int argc, const char * argv[]) 
  62.     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 
  63.     CWnd *window = [[MyWnd alloc] init]; 
  64.     [window WindowOnClick]; 
  65.     [window release]; 
  66.     [pool drain]; 
  67.     return 0; 

对于category和protocol的阐述大概差不多了。 在这里我并没有很详细的说明使用其时需要注意的地方,这些基本的东西可以去看《objective-c 2.0基础教程》,其实旨在指出Objective-c与才C++不同和相似的地方,进行对比说明的同时,还以此达到减少自己对于这种“怪异”语言困惑和 纠结的地方。

 

结束语

这篇博文已经写了两天了,是在上班时间抽时间些的。主要是回家没MAC玩,就没办法编代码写博客了。对于objective-c的语言类学习,到此也算画上个句号了,以后的时间可能都要花在实际项目上了。对于之前写C++的我来说,纠结和困惑始终伴随着我学object-c的过程,但既然转到这个平台了,貌似只有慢慢习惯才行,毕竟,习惯成自然嘛。。。。 路漫漫其修远兮,吾将上下而求索。

 

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI