Use objective-C, C ++, and objective-C ++ in combination.

Source: Internet
Author: User
Some time ago, I participated in an objective-C project using the C ++ library. I wrote an article about mixed editing, but the result was unexpectedly one of the top results about objective-C ++ in Google search.
Later, Apple uses clang Based on llvm as the primary compiler. One of its functions is to ensure the evolution of objective-C, while the evolution of GCC is too slow. I have received some feedback in this process, which has prompted me to write this article.
Review
To put it simply, if you have some C ++ code or libraries and you want to use them in the objective-C Project, This is the question we will study. Generally, the C ++ Code defines some classes you want to use. change M file extension. mm can be changed to objective-C ++ for compilation, and then the code of C ++ and objective-C can be easily mixed. This is a simple practice, but the two worlds are indeed quite different. Such a deep mix can sometimes be tricky.
You may want to use equivalent objective-C Types and functions to encapsulate C ++ code (WRAP. For example, you have a C ++ class (cppobject. h) named cppobject ):

#include <string>class CppObject{public:  void ExampleMethod(const std::string& str);  // constructor, destructor, other members, etc.};

In the Objectiv-C class, the member variables of the C ++ class can be defined. Therefore, you can first define an objcobject encapsulation class (objcobject. h ):

#import <Foundation/Foundation.h>#import "CppObject.h"@interface ObjcObject : NSObject {  CppObject wrapped;}- (void)exampleMethodWithString:(NSString*)str;// other wrapped methods and properties@end

Then implement these methods in objcobject. Mm. However, a preprocessing and compilation error will be obtained in the two header files (objcobject. H & cppobject. h. The problem lies in # include and # import. For a Preprocessor, it only performs text replacement operations. So # include and # import are essentially recursively copying and pasting the content of the referenced file. In this example, using # import "objcobject. H" is equivalent to inserting the following code:

// [First, there are a large number of foundations/foundations. code in H] // [cannot contain <string>], because it only exists in class cppobject {public: void examplemethod (const STD :: string & Str); // constructor, destructor, other members, etc .}; @ interface objcobject: nsobject {cppobject wrapped;}-(void) examplemethodwithstring :( nsstring *) STR; // other wrapped methods and properties @ end

Because class cppobject is not a valid objective-C syntax, the compiler is confused. The error is usually as follows:Unknown type name 'class'; did you mean 'class '?It is precisely because objective-C does not have the class keyword. therefore, to be compatible with objective-C, the header file of the objective-C ++ class must only contain the objective-C code, there is absolutely no C ++ code-this mainly affects the type definition (like the cppobject class in the example ).
Keep simple header filesSome solutions have been mentioned in previous articles. The best one is pimpl, which also applies to the current situation. There is also a new method for clang, which can separate C ++ code from objective-C. This is the class
In extensions.
Class extensions (not mixed with categories) has existed for a while: they allow you to define extensions outside the class interface before the @ implementation segment, rather than in the public header file. This example can be declared in objcobject. mm:

#import "ObjcObject.h"@interface ObjcObject () // note the empty parentheses- (void)methodWeDontWantInTheHeaderFile;@end@implementation ObjcObject// etc.

GCC also supports this operation. However, clang also supports adding Ivar blocks, that is, you can declare C ++ instance variables, either in class extension or at the beginning of @ implementation. In this example, objcobject. h can be reduced:

#import <Foundation/Foundation.h>@interface ObjcObject : NSObject- (void)exampleMethodWithString:(NSString*)str;// other wrapped methods and properties@end

 

The removed parts are moved to the class extension (objcobject. mm) of the implementation file ):

# Import "objcobject. H "# import" cppobject. H "@ interface objcobject () {cppobject wrapped;} @ end @ implementation objcobject-(void) examplemethodwithstring :( nsstring *) STR {// note: if 'str' is nil, an empty string is created instead of a null pointer to 'utf8string. STD: String cpp_str ([STR utf8string], [STR lengthofbytesusingencoding: nsutf8stringencoding]); wrapped. examplemethod (cpp_str );}

If we do not need interface extension to declare additional attributes and methods, the Ivar block can still be placed at the beginning of @ implementation:

# Import "objcobject. H "# import" cppobject. H "@ implementation objcobject {cppobject wrapped;}-(void) examplemethodwithstring :( nsstring *) STR {// Note: If STR is nil, an empty string is created, instead of referencing a null pointer to utf8string. STD: String cpp_str ([STR utf8string], [STR lengthofbytesusingencoding: nsutf8stringencoding]); wrapped. examplemethod (cpp_str );}
When the defined cppobject instance wrapped is created in objcobject, the default constructor of cppobject will be called. When the objcobject is called dealloc, The destructor of objcobject will also be called. If objcobject does not provide the default constructor, compilation fails.
Manage the lifecycle of encapsulated C ++ objectsThe solution is to grasp the construction process through the New Keyword, for example:

@ Interface objcobject () {cppobject * Wrapped; // pointer! Will be initially null in alloc .} @ end @ implementation objcobject-(ID) initwithsize :( INT) size {self = [Super init]; If (Self) {wrapped = new cppobject (size); If (! Wrapped) Self = nil;} return self ;}//...

If the C ++ exception is used, you can also use try {...} catch {...} to encapsulate the creation process. Correspondingly, you must explicitly release the closed object:

-(Void) dealloc {Delete wrapped; [Super dealloc]; // skip this sentence if arc is used}

The author then mentioned another method, showing that a piece of memory is allocated and then calling New to create an object based on it. First declare char wrapped_mem [sizeof (cppobject)]; then use wrapped = new (wrapped_mem) cppobject (); create the instance wrapped. If (wrapped) wrapped-> ~ Cppobject (); this is feasible, but is not recommended.


SummaryMake sure that the encapsulated method only returns and uses the return values and parameters of the C or objective-C type. At the same time, do not forget that nil does not exist in C ++, and NUL cannot be used for unreferencing.
Reverse: Use the objective-C class in C ++ codeThis problem also exists in the header file. You cannot pollute the C ++ header file because of the introduction of the objective-C type, or be referenced by pure C ++ code. For example, the objective-C Class abcwidget we want to encapsulate is declared:

#import <Foundation/Foundation.h>@interface ABCWidget- (void)init;- (void)reticulate;// etc.@end

This class definition is fine in objective-C ++, but it is not allowed in pure C ++ code:

#import "ABCWidget.h"namespace abc{  class Widget  {    ABCWidget* wrapped;  public:    Widget();    ~Widget();    void Reticulate();  };} 

An error occurred when the code and abcwidget declaration position of a pure C ++ compiler in foundation. h.


Eternal pimplIs there anything like this as a class of extension C ++ that will not work properly. On the other hand, pimpl works well and is actually a commonly used pure C ++. In our example, we minimize the number of C ++ classes.
C ++ does not have the class extension mentioned earlier, but there is another common method: pimpl (Private implementation, private implementation ). Here, the definition of C ++ class is simplified:

namespace abc{  struct WidgetImpl;  class Widget  {    WidgetImpl* impl;  public:    Widget();    ~Widget();    void Reticulate();  };}

Then in widget. mm:

#include "Widget.hpp"#import "ABCWidget.h"namespace abc{  struct WidgetImpl  {    ABCWidget* wrapped;  };  Widget::Widget() :    impl(new WidgetImpl)  {    impl->wrapped = [[ABCWidget alloc] init];  }  Widget::~Widget()  {    if (impl)      [impl->wrapped release];    delete impl;  }  void Widget::Reticulate()  {    [impl->wrapped reticulate];  }} 

Its working principle is the pre-declaration. It is enough to declare the member variables, structures, or classes of such structures or class objects.
Note that the encapsulated object will be released in the destructor. Even for projects that use arc, we recommend that you block this type of file that references C ++/objective-C. Do not make C ++ code dependent on arc. In xcode, arc can be blocked for individual files. Target properties-> build phase tab, expand 'compile sources ', and add the compilation option-fno-objc-arc to a specific file.
Shortcuts for encapsulating objective-C classes in C ++You may have noticed that the pimpl Solution uses two levels of indirect references. If the encapsulated target class is as simple as in this example, the complexity may increase. Although the objective-C type cannot be used in pure C ++, some of them are actually defined in C. The ID type is one of them, and its declaration is in the header file <objc/objc-runtime.h>. Although it will lose some objective-C Security, you can still directly upload your object to the C ++ class:

#include <objc/objc-runtime.h>namespace abc{  class Widget  {    id /* ABCWidget* */ wrapped;  public:    Widget();    ~Widget();    void Reticulate();  };}

It is not recommended to send messages directly to the ID object. In this way, you will lose a lot of compiler checking mechanisms, especially for different methods with the same selector name in different classes. Therefore:

#include "Widget.hpp"#import "ABCWidget.h"namespace abc{  Widget::Widget() :    wrapped([[ABCWidget alloc] init])  {  }  Widget::~Widget()  {    [(ABCWidget*)impl release];  }  void Widget::Reticulate()  {    [(ABCWidget*)impl reticulate];  }} 

Conversions like this can easily hide errors in code and try a better way. In the header file:

#ifdef __OBJC__@class ABCWidget;#elsetypedef struct objc_object ABCWidget;#endifnamespace abc{  class Widget  {    ABCWidget* wrapped;  public:    Widget();    ~Widget();    void Reticulate();  };} 

If this header file is referenced by a mm file, the compiler can fully identify the correct class. If it is referenced in pure C ++ mode, abcwidget * is an equivalent ID type: defined as typedef struct objc_object * ID ;. # Ifdef blocks can be further put into a reusable macro:

#ifdef __OBJC__#define OBJC_CLASS(name) @class name#else#define OBJC_CLASS(name) typedef struct objc_object name#endif 

Now we can pre-declare a line in the header file to apply to all four languages: objc_class (abcwidget); reprinted please note the Source: http://blog.csdn.net/horkychen

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.