Mostrando entradas con la etiqueta Objective-C. Mostrar todas las entradas
Mostrando entradas con la etiqueta Objective-C. Mostrar todas las entradas

sábado, 25 de abril de 2015

Technical Take-aways from Limball

Hi!

In this post I want to summarize some of the technical issues I had to face while developing Limball. 

The first thing to highlight is that it has been my first experience, at least in a complete game, with Cocos2d-x, the framework that I have used for the implementation. In my first game, Chubby Buddy, I had used Cocos2d (now renamed to Cocos2d-SpriteBuilder), which uses Objective-C and is targeted exclusively at iOS developers. Cocos2d-x is written in C++ and targets both iOS and Android developers. I must say that the learning curve has been very smooth. The API is practically the same, so if you know how to call a certain function from Objective-C, you know almost intuitively how to call it from C++. Of course, a previous background on C++ is fundamental in order to get the most out of it as fast as posible, but you can use also other languages (e.g. Lua).



Developing for multiple platforms is made really easy thanks to Cocos2d-x, which allows keeping the same C++ codebase. Nonetheless, at some points I needed to do something different in the two platforms. For example, when integrating with Game Center, the social platform for iOS gamers, I noticed that the achievements and leaderboard sections were well integrated under a common interface. However, Google Play Game Services, the Android counterpart, does not integrate the two services under the same interface. This means that two different buttons are required in Android (one for the leaderboards, another one for the achievements), while in iOS does just fine with one button. In order to avoid lots of code duplication, I resorted to preprocessor macros. Cocos2d-x defines the macro CC_TARGET_PLATFORM, which may be assigned a value according to the platform where the code is to be executed. As a consequence, there are several parts in the code with the following pattern:

 #if CC_TARGET_PLATFORM == CC_PLATFORM_IOS  
  //do iOS-related stuff  
 #else  
  //do Android-related stuff  
 #endif  

Something else that I learnt is how to use the social platforms for gamers on iOS and Android. The integration with the former was smoother, also due to the easier integration between C++ and Objective-C. An example on how this C++/Objective-C integration can be painlessly achieved is discussed in an earlier post on localization.

As an example, consider the following code that unlocks an achievement on both platforms:

 #if CC_TARGET_PLATFORM == CC_PLATFORM_IOS  
       GKHWrapperCpp gkh;  
       gkh.reportAchievement( "Triple_Chain", 100.0, false );  
 #elif CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID  
       GooglePlayHelper::UnlockAchievement( COMBO_KIDDIE );  
 #endif  

GKHWrapperCpp is a class that belongs to a open source library that you can find here and that simplifies the management of Game Center related stuff. GooglePlayHelper is a utility class that I made in order to manage interactions to Android-specific features through the Java Native Interface (JNI).

JNI is not difficult to use, provided you have the previous background on Java and C++, but it is easy to make some small mistakes that are almost impossible to debug. In my case, I was experiencing a game crash only on Nexus 5 running Android 5.0.1. I even tested on the same device running a lower version of Android and it worked perfectly. For some time, I had no clue on what was happening and I ended up blaming the OpenGL implementation on that device for that version of Android. In the end though, it turned out that I was making a memory management mistake: I wasn't removing a local reference that I had created:

 jbyteArray bArray = t.env -> NewByteArray( key.length() );  
 jbyte bytes[50];  
 for( int i = 0; i < key.length(); ++i )  
 {  
    bytes[i] = key[i];  
 }  
 t.env -> SetByteArrayRegion( bArray, 0, key.length(), bytes );  
 res = (jstring) t.env -> CallStaticObjectMethod( t.classID, t.methodID, bArray );  
 t.env -> DeleteLocalRef(t.classID);  
 t.env -> DeleteLocalRef( bArray );  

Adding the last line of the previous snippet did the trick, just one day before the intended release date..

Another example of system-specific feature is in-app purchases. In Limball, you can remove the ads banners and interstitials (full-screen ads) by buying a non-ads product from inside the app. Both iOS and Android offer a simple way to tackle this, so that was not a problem. In the case of Android, I used the In-app Billing v3 workflow, which basically comes down to the following snippet of code:

 buyIntentBundle = _appActivity.mService.  
           getBuyIntent( 3, _appActivity.getPackageName(), productId, "inapp", "noads" );  
           //If everything is fine, then proceed with the transaction  
           if ( buyIntentBundle.getInt( "RESPONSE_CODE" ) == 0 )  
           {  
             _appActivity.pauseGame();  
             PendingIntent pendingIntent = buyIntentBundle.getParcelable( "BUY_INTENT" );  
             _appActivity.startIntentSenderForResult( pendingIntent.getIntentSender(),   
                                  REQUEST_INAPP_CODE,  
                                  new Intent(),   
                                  Integer.valueOf( 0 ),   
                                  Integer.valueOf( 0 ),  
                                  Integer.valueOf( 0 ) );  
           }  

On iOS, the strategy (as usual) is to create a delegate that will manage the purchase with the following calls:

 SKMutablePayment *payment = [SKMutablePayment paymentWithProduct: productToBuy ];  
 [[SKPaymentQueue defaultQueue] addPayment: payment];  

It first create the payment with product id, and it introduces it in the queue. Then, the payment is processed by the payment queue delegate, typically as part of the AppController. This delegate is in charge of looking at the state of the transaction and to deliver the functionality once the transaction is in the SKPaymentTransactionStatePurchased state, as depicted in the following code:

 - (void) paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions  
 {  
   for ( SKPaymentTransaction* transaction in transactions )  
   {  
     switch ( transaction.transactionState ) {  
       case SKPaymentTransactionStatePurchasing:  
         ConfigManager::GetInstance() -> PauseGame();  
         break;  
       case SKPaymentTransactionStatePurchased:  
         ConfigManager::GetInstance() -> EnableNoAds();  
         ConfigManager::GetInstance() -> ResumeGame();  
         [[SKPaymentQueue defaultQueue] finishTransaction: transaction];  
         break;  
      //...  

For the inclusion of advertisements, I have used the iAd network on iOS and Google's Admob. Actually, on iOS the strategy is to prioritize iAd, and only if it is unavailable, fall back to Admob.  Given that iAd does not provide interstitials for iPhones, I used the Admob feature for that purpose. Again, the integration was smooth, because the frameworks provide usable APIs. In this earlier post, I explained how you could integrate a Cocos2d-x project with iAD.

And these are the most important technical issues I have learnt about. Hope you found them useful.
See you!

Tweet: Technical take-aways from making #Limball. Take a look: http://ctt.ec/Nb893+ #gamedev #indiedev

martes, 3 de febrero de 2015

Integrating iAd in Cocos2d-x v3.x

Hi all!

First of all, Happy New Year! (better late than never, right?). Sorry for the delay, but I've really been busy this last month, and Christmas didn't help either... 

Today I want to share with you what I've been doing these last days. In the next game I'm working on, I wanted to include a free version with advertisements, but I had no experience on this task. The thing turned out to be quite simple (at least in iOS, which is the first target I have in mind), as the iAd Framework is intuitive. However, I had to face an additional challenge: integrating this framework with another framework I'm building my game upon, Cocos2d-x. The main contributions that helped me achieve this goal are this and this.

So, first of all, we need to understand the model that iAd Framework follows. In a nutshell, you need to create an interface that follows a protocol (which is the equivalent in Objective-C to a Java interface), that is, that should implement certain methods. The name of the protocol is AdBannerViewDelegate. So let us call our interface AdBanner and let us create a header file with it:

AdBanner.h
 #import <Foundation/Foundation.h>  
 #import <iAd/iAd.h>    
 @interface AdBanner : NSObject<ADBannerViewDelegate>  

The methods that we should implement are listed next, together with a brief description: 

- (void)bannerViewDidLoadAd:(ADBannerView *)banner

This method is called when a banner view has a new ad to display.

- (BOOL) bannerViewActionShouldBegin:(ADBannerView *)banner willLeaveApplication:(BOOL) willLeave

This method is called when the user taps the banner and should return YES if the banner action should execute. If willLeave=YES, then another app will be launched to execute the action, and no additional actions are required; NO if the action is going to be executed inside the game, so we should pause the activities that require interaction with the game.

- (void)bannerViewActionDidFinish: (ADBannerView *) banner


This method is called after the user has navigated the ad. It must resume activities if bannerViewActionShouldBegin: paused the gameplay.


- (void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error


This method is called if any error occurs. 

Note that the arguments refer to an ADBannerView object, which is the object that can contain and will show ads. The first thing therefore is to create an ADBannerView object and to include it in the hierarchy of views. Note that in a Cocos2d-x game, we typically have just one view, an EAGLView, which is a subclass of UIView that renders an OpenGL scene.  

In order to add the banner view, we need first to gain access to the RootViewController, that is, the object in charge of managing the different views. Also, we will need to access the size of the screen so as to place the banner, and therefore, we are going to obtain a UIWindow object that can provide that information. Finally, we want to add a boolean value to determine whether the banner view is currently visible or not. The resulting header and implementation files are presented next:

AdBanner.h
 #import <Foundation/Foundation.h>  
 #import <iAd/iAd.h>  
 @class RootViewController;   
 @interface AdBanner : NSObject<ADBannerViewDelegate>  
 {  
   UIWindow* window;  
   RootViewController* rootViewController;  
   ADBannerView* adBannerView;  
   bool adBannerViewIsVisible;  
 }  


AdBanner.mm (Note that the extension of Objective-C files are .m. However, as we will have to integrate with C++, we rename the file to .mm, which is an Objective-C++ file)
 -(id)init  
 {  
   if(self=[super init])  
   {  
     adBannerViewIsVisible = YES;  
     rootViewController =  
       (RootViewController*) [[[UIApplication sharedApplication] keyWindow] rootViewController];  
     window = [[UIApplication sharedApplication] keyWindow];  
     [self createAdBannerView];  
   }  
   return self;  
 }  
 - (void)createAdBannerView  
 {  
   adBannerView = [[ADBannerView alloc] initWithFrame:CGRectZero];  
   adBannerView.delegate = self;  
   [adBannerView setAutoresizingMask:UIViewAutoresizingFlexibleWidth];  
   CGRect adFrame = adBannerView.frame;  
   adFrame.origin.x = 0.0f;  
   adFrame.origin.y = window.bounds.size.height;  
   adBannerView.frame = adFrame;  
   [[rootViewController view] addSubview: adBannerView];  
 }  

In init, we state that the banner view should be visible and we retrieve the root view controller and the main window. Then, we call createAdBannerView, which creates and initializes the adBannerView object. The next step is very important: we state that the actual delegate of the adBannerView is the current object; this means that when the adBannerView object changes, it will notify our object by calling the methods of the protocol previously explained. The following method (setAutoresizingMask:) indicates that if the superview changes, the width of the banner view can be freely re-sized. This is important if you want to support changes in orientation for example. Then, we specify that the subview should be hidden, right after the bottom of the screen. Note that while the origin of the coordinates system in Cocos2d-x is in the bottom-left corner and the y component grows upwards, the origin of the UIView class is in the top-left corner, and the y component grows downwards. So, when we write:

adFrame.origin.y = window.bounds.size.height;

, we are placing the origin of the UIView on the bottom-left corner of the screen, and the banner would grow downwards. That's why it would be initially hidden. 

Finally, we add the banner view to the views hierarchy through the root view controller object. 

Now, we are going to add the protocol methods in the implementation file.

AdBanner.mm
- (BOOL)bannerViewActionShouldBegin:(ADBannerView *)banner willLeaveApplication:(BOOL)willLeave  
 {  
   NSLog(@"Banner view is beginning an ad action");  
   if ( !willLeave )  
   {  
     //insert code here to suspend any services that might conflict with the advertisement  
   }  
   return YES;  
 }  

 - (void)bannerViewActionDidFinish: (ADBannerView *) banner  
 {  
   //if necessary, insert code here to resume services suspended by the previous method  
 }  
 - (void)bannerViewDidLoadAd:(ADBannerView *)banner  
 {  
   NSLog(@"New ad available");  
   [self layoutAnimated:YES]; //shows the banner with a pop-up animation 
 }  
 
 -(void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error  
 {  
   NSLog( @"%@", error.description );  
   [self layoutAnimated: YES];  //hides the banner with an animation
 }  

Note that both bannerViewDidLoadAd: and bannerView: didFailToReceiveAdWithError: call a third instance method layoutAnimated:, which is not part of the protocol. This method is listed next:

AdBanner.mm
//... protocol methods above... 
-(void)layoutAnimated:(BOOL)animated  
 {  
   CGRect bannerFrame = adBannerView.frame;  
   //Has the banner an advestiment?  
   if ( adBannerView.bannerLoaded && adBannerViewIsVisible )  
   {  
     NSLog(@"Banner has advertisement");  
     bannerFrame.origin.y = window.bounds.size.height - bannerFrame.size.height;  
   } else  
   {  
     NSLog( @"Banner has NO advertisement" );  
     //if no advertisement loaded, move it offscreen  
     bannerFrame.origin.y = window.bounds.size.height;  
   }  
   [UIView animateWithDuration:animated ? 0.25 : 0.0 animations:^{  
     [rootViewController.view layoutIfNeeded];  
     adBannerView.frame = bannerFrame;  
   }];  
 }  

The code is more or less self-explanatory. We first check whether the banner view has actually an advertisement and whether it is visible. If it is, we place it on the bottom of the screen; otherwise, we place it offscreen (right after the bottom). We then call the static method animateWithDuration provided by UIView in order to show a simple pop-up animation.

Integrating with Cocos2d-x

Let's recap. Now, we have two files: AdBanner.h and AdBanner.mm, which encapsulates an AdBanner object capable of showing and hiding an ad banner view object. On the other hand, we have a Cocos2d-x scene onto which we would like to show the ad banner.

I'm going to show you a real example. In the new game I'm working on, I want to display the ad banner in the menu screen, which is a cocos2d:Layer, as depicted next:

MenuScreen.h
 #include "cocos2d.h"  
 class MenuScreen: public cocos2d::Layer  
 {  
 public:  
   static cocos2d::Scene* CreateScene();  
   virtual bool init();   
   CREATE_FUNC( MenuScreen );  
 private:  
   cocos2d::Size visibleSize;  
   cocos2d::Point origin;  
   cocos2d::Sprite *mBackground;  
   cocos2d::MenuItemImage *mPlayButton;  
   cocos2d::MenuItemImage *mSoundConfigButton;  
   cocos2d::MenuItemImage *mCreditsButton;  
   cocos2d::MenuItemImage *mGameCenterButton;  
   void ToNewGame();  
   void SoundConfig();  
   void ToCredits();  
   void ToGameCenter();  
 };  

It would be wonderful if I could do the following:

MenuScreen.h
#include "cocos2d.h"
#include "AdBanner.h" //THIS WON'T WORK!
 class MenuScreen: public cocos2d::Layer  
 {  
 public:  
   static cocos2d::Scene* CreateScene();  
   virtual bool init();   
   CREATE_FUNC( MenuScreen );  
 private:  
   cocos2d::Size visibleSize;  
   cocos2d::Point origin;  
   cocos2d::Sprite *mBackground;  
   cocos2d::MenuItemImage *mPlayButton;  
   cocos2d::MenuItemImage *mSoundConfigButton;  
   cocos2d::MenuItemImage *mCreditsButton;  
   cocos2d::MenuItemImage *mGameCenterButton;  
   void ToNewGame();  
   void SoundConfig();  
   void ToCredits();  
   void ToGameCenter();  
   AdBanner* adBanner; //THIS WON'T WORK!
 };  

That is, adding the header of AdBanner and defining a pointer to an AdBanner object in the class. However, as mentioned in the comments, this won't compile, because MenuScreen.h will be eventually included in its implementation, which is a .cpp file that would complain as soon as it finds Objective-C syntax (e.g. @interface).

The solution is to create an intermediate C++ object that mediates between C++ and Objective-C. For this purpose, let us create a class called AdBannerC, just like this:

AdBannerC.h
 #import "AdBanner.h";  
 class AdBannerC  
 {  
 public:  
   AdBannerC();  
   ~AdBannerC();  
 private:  
   AdBanner* impl;  
 };  

AdBannerC.mm
 #include "AdBannerC.h"  
 AdBannerC::AdBannerC()  
 {  
   impl = [[AdBanner alloc] init];  
 }    
 AdBannerC::~AdBannerC()  
 {  
   [impl removeView];  
 }   

Now, it would seem reasonable to include AdBanncerC.h in MenuScreen.h and write:

AdBannerC* adBanner = new AdBannerC();

However, THIS DOESN'T WORK! Why? Because we have said before that a .cpp file (well, to be more accurate, the C++ compiler) does not understand Objective-C syntax. If we include AdBannerC.h in MenuScreen.h, AdBanner.h is also being included, and AdBanner.h has Objective-C syntax!

The trick that we can apply here is commonly called the PIMPL idiom and it exploits the fact that a forward declaration of a type is enough to declare a pointer to that type. I'll explain myself. Consider this alternative definition of the AdBannerC class:

AdBannerC.h
 struct AdBannerImpl; //Forward declaration
 class AdBannerC  
 {  
 public:  
   AdBannerC();  
   ~AdBannerC();   
 private:  
   AdBannerImpl* impl;  
 };  

As you can see, now we don't have any imports, so any .cpp file can safely include this header file and won't find any strange Objective-C syntax. We have done a so-called forward declaration, meaning that we are telling the compiler that the type AdBannerImpl exists in some implementation file. This is enough for the compiler to let us define a pointer to this type. A struct in C++ is equivalent to a class, except that default values of its members are public.

The implementation file would be as follows:

AdBannerC.mm
 #include "AdBannerC.h"  
 #import "AdBanner.h"  
 struct AdBannerImpl  
 {  
   AdBanner* wrapped;  
 };  
 AdBannerC::AdBannerC()  
 {  
   impl = new AdBannerImpl();  
   impl -> wrapped = [[AdBanner alloc] init];  
 }  
 AdBannerC::~AdBannerC()  
 {  
   [impl -> wrapped removeView];  
   delete impl;  
 }  

Now, any scene in the game can create a banner view by just importing AdBannerC.h and doing:

adBanner = new AdBannerC();

Done!

However, as we mentioned earlier, sometimes it might be necessary to pause and resume the gameplay when the player interacts with the banner. How can we accomplish this? Easy: we just need to pass an instance of the scene to the banner object and let this banner object call the appropriate method of the scene. So, the first step is to create these methods:

MenuScreen.h
 class MenuScreen: public cocos2d::Layer  
 {  
 public:  
   // there's no 'id' in cpp, so we recommend returning the class instance pointer  
   static cocos2d::Scene* CreateScene();  
   // Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone  
   virtual bool init();  
   void PauseGame();  //It pauses the current activities
   void ResumeGame(); //It resumes the activities
   //rest of class definition
}; 

MenuScreen.cpp
 void MenuScreen::PauseGame()  
 {  
   Director::getInstance() -> stopAnimation();  
 }  
 void MenuScreen::ResumeGame()  
 {  
   Director::getInstance() -> startAnimation();  
 }  

In order to pause and resume the game, we simply call the corresponding methods provided by the cocos2d::Director class.

Next step is passing the instance of the scene to the ad banner object. For this purpose, we need to modify slightly the AdBannerC class to make it accept an instance of MenuScreen:

AdBannerC.h
 class MenuScreen; 
 struct AdBannerImpl;
 class AdBannerC  
 {  
 public:  
   AdBannerC( MenuScreen& ms );  
   ~AdBannerC();   
 private:  
   AdBannerImpl* impl;  
 };  

AdBannerC.mm
 #include "AdBannerC.h"  
 #import "AdBanner.h"  
 struct AdBannerImpl  
 {  
   AdBanner* wrapped;  
 };  
 AdBannerC::AdBannerC( MenuScreen& ms )  
 {  
   impl = new AdBannerImpl();  
   impl -> wrapped = [[AdBanner alloc] initWithMenuInstance: ms];  
 }  
 AdBannerC::~AdBannerC()  
 {  
   [impl -> wrapped removeView];  
   delete impl;  
 }  

Thus, in MenuScreen.cpp, we would do the following:

AdBannerC *adBanner = new AdBannerC( *this );

Note that we have changed the initialization of impl->wrapped by using a new method called initWithMenuInstance:. Therefore, we need to define this method in AdBanner:

AdBanner.h
 #import <Foundation/Foundation.h>  
 #import <iAd/iAd.h>  
 @class RootViewController;  
 class MenuScreen;  
 @interface AdBanner : NSObject<ADBannerViewDelegate>  
 {  
   UIWindow* window;  
   RootViewController* rootViewController;  
   ADBannerView* adBannerView;  
   bool adBannerViewIsVisible;  
   MenuScreen* ms;   
   bool needToRestore;  
 }  
 -(id)initWithMenuInstance: (MenuScreen&) ms;   
 @end  

In addition to the new method, we have also added a new variable: needToRestore. This boolean value will tell us whether we need to resume the gameplay depending on whether our app was moved to the background or not.

AdBanner.mm
 #import "RootViewController.h"  
 #import "AdBanner.h"  
 #import "MenuScreen.h"  
 @implementation AdBanner  
 -(id)initWithMenuInstance: (MenuScreen &) msVar  
 {  
   ms = &msVar;  
   needToRestore = NO;  
   return [self init];  
 }    
 - (BOOL)bannerViewActionShouldBegin:(ADBannerView *)banner willLeaveApplication:(BOOL)willLeave  
 {  
   NSLog(@"Banner view is beginning an ad action");  
   if ( !willLeave )  
   {  
     //Given that we are pausing the gameplay, we need to restore it later.
     needToRestore = YES;  
     // insert code here to suspend any services that might conflict with the advertisement  
     ms -> PauseGame();  
   }  
   return YES;  
 }  
 - (void)bannerViewActionDidFinish: (ADBannerView *) banner  
 {  
   if( needToRestore )  
   {  
     ms -> ResumeGame();  
   }  
   needToRestore = NO;  
 }  

//... rest of methods

And that's it! Wow! This post has been longer than I expected, but I hope that you found it interesting and useful for your own projects.

Some final thoughts:

  • In order to make sure that ad banner objects are actually deleted, activate Automatic Reference Counting (ARC) for the .mm files. Check here for how this is done. 
  • Always prefer smart pointers over raw pointers.
  • The solution I propose here is fine when you want to display an ad banner in just two or three scenes at most. If you will have lots of scene in your game showing ad banners, you should generalize all the scenes that may show ads into a superclass that implements the methods ResumeGame() and PlayGame(), and pass an instance of this superclass to the banner object. 

Thanks for your reading and don't hesitate to ask or correct if that's the case!
See you :)

lunes, 17 de febrero de 2014

On Delays

Wow! It's been very (too) long since the last post. I must admit that I'm a bit ashamed. Not only did it take me too long to update the blog, but Chubby Buddy has also taken me longer than expected!

Precisely, the last-hour rush in the development of Chubby Buddy has been the primary cause for the hiatus of the blog. If in this previous entry I reflected on the importance of setting deadlines, in this entry I want to put the stress on the importance and difficulties in making an accurate scheduling.

A good strategy when you've gone off schedule is to analyze the reasons that led you astray. In my case, the reasons have been the following, listed from more to less relevance in my opinion:
  1. Underestimated effort for universal app.
  2. Few experience with the framework.
  3. Underestimated effort for additional features support.
  4. Not so much commitment during Christmas.
  5. Few experience with the language.
We wanted to achieve an universal app that worked under different versions of iPhones and iPads, but this involved re-doing all the art in the game to adjust to different screen resolutions, and also make some adjustments at the code level to support these different versions. This entails a good amount of work and I definitely underestimated the required effort.

Even when Cocos2D is enjoyable and usable, it is a new framework that imposes certain rules and it is not always obvious how to accomplish some functionality. After a bunch of lines of code, I needed to check Stackoverflow or the Cocos2d forums to figure out how to achieve something new. 

In the last minute, Manuela and I thought about adding a couple of new features that supposedly wouldn't entail lots of effort; we were wrong. However, we think that the final result makes up for this extra-effort.

For several reasons, our dedication to the game during Christmas holidays was not as high as I had expected. Had we worked as initially planned, the game would have been released much earlier.

Finally, even when Objective-C is a really easy to grasp language, it presents a couple of differences with Java or C/C++, languages which I'm much more used to work with. 

It only remains to try the best in order to keep up the pace!

FM

jueves, 29 de agosto de 2013

Towards Game Development in iOS

I've been curious for a long time about how to program for iPhone and iPad (in general, for iOS) and about Objective-C, the language under which to develop for this operating system, so I finally decided yesterday that it was time to delve a little bit into the details of this programming language. 

If you can program in C/C++ and you know the fundamentals of Object-Oriented programming (knowing Java also helps), the leap to Objective-C seems pretty straightforward. I started reading the book Learning Objective-C 2.0: A Hands-on Guide to Objective-C for Mac and iOS by Robert Clair. I found this book very illustrative and well-written, so I can openly advice it. 

Objective-C is an extension to plain C and very often it presents subtle differences with C++, such as the fact that you cannot create objects in the stack (i.e. you must always hold pointers to objects allocated in the heap), there's no syntax support for abstract classes, there are some new reserved keywords like nil (a pointer to no object) and id (a pointer to an object of any class), etc. 

Possibly, the biggest change is that of the 'messaging system' used in place of function calls or method invocation. In Objective-C, you use the syntax [receiver message] where receiver is an object of a class and the message is the name of a method of that object. Technically, it seems another way of the same idea of method invocation, but the difference comes from the fact that this statement is not resolved  at compile time but at run-time, adding more flexibility. Of course you can nest expressions and the result would be used in the next level (e.g. [[receiver message] message] would first evaluate the inner [receiver message] part and then the result would be used as the receiver of the outer [receiver message]).

The book also shows you how this messaging system (and other high level stuff) works under the hood (in the end, all is translated into plain C) and how to bypass the runtime (the runtime translates from Objective-C to plain C and fills in certain structures with class information) to gain efficiency when required, given that a function call, if very simple, is more than twice faster than the receiver-message scheme.

Regarding memory management, the language traditionally required the developer to manually take care of releasing allocated memory or retaining it, using manual reference counting. However, Apple included recently Automatic Reference Counting, which means that the runtime is in charge of doing this dirty work in an efficient way. In fact, Apple encourages developers to use this feature as it can give an important performance boost to applications and prevent memory leaks. 

I've read more than half of the book and I'm enjoying it. My goal is to be able to develop games for iPhone and iPad, probably using existing and reputable frameworks such as Cocos2D. If your goal is the same, I highly advice you to learn the fundamentals of Objective-C and I think that this book is a perfect starting point.

See you!