An Objective-C program in a
.m
file is simply C with
@
keywords
and
[
square brackets]
.
An Objective-C++ program in a
.mm
file is simply C++ with
@
keywords
and
[
square brackets]
.
See
Using
C++ With Objective-C.
The variable
hello
in the
drawRect:
method of class
View
is a pointer to an Objective-C object.
The variable
world
is a C++ object.
We call its member function
c_str
,
which returns a pointer to the first character in the string.
main.m
PeaceableAppDelegate
View
Simply rename
View.m
to
View.mm
.
Right-click on
View.m
in the
Groups & Files pane of Xcode,
and select Rename.
In the comment at the start of
View.mm
,
change the filename from
View.m
to
View.mm
.
View.mm
must #include the header file for the C++ class
string
.
<string>
,
say
using namespace std;Then change the
std::string
to
string
.
std::string *world = new std::string("World"); //C++ object NSString *sum = [NSString stringWithFormat: @"%@, %s!", hello, world->c_str()]; delete world;
c_str
)
of a C++ object in the middle of Objective-C code.
Now we will call a method
(drawAtPoint:withFont:
)
of an Objective-C object by writing C++ code.
See the
Objective-C
Runtime Programming Guide
and the
Objective-C
Runtime Reference.
Include the header file
<objc/runtime.h>
in
View.mm
.
In the
drawRect:
method of class
View
,
change
[sum drawAtPoint: CGPointZero withFont: f];to the following. The pointer
p
points to a function that implements an Objective-C method
that takes two arguments (a
CGPoint
structure and a pointer to a
UIFont
object)
and returns a
CGSize
structure.
Class c = object_getClass(sum); if (c == Nil) { //upercase N for value of type Class NSLog(@"object_getClass returned Nil."); return; } NSLog(@"class_getName(c) == %s", class_getName(c)); //does the same thing as SEL s = @selector(drawAtPoint:withFont:); SEL s = sel_registerName("drawAtPoint:withFont:"); NSLog(@"sel_getName(sel) == %s", sel_getName(s)); Method m = class_getInstanceMethod(c, s); if (m == NULL) { NSLog(@"class_getInstanceMethod returned NULL."); return; } NSLog(@"sel_getName(method_getName(m)) == %s", sel_getName(method_getName(m))); CGSize (*p)(id, SEL, CGPoint, UIFont *) = reinterpret_cast<CGSize (*)(id, SEL, CGPoint, UIFont *)>(method_getImplementation(m)); if (p = NULL) { NSLog(@"method_getImplementation returned NULL."); return; } NSLog(@"p == %p", p); //Call drawAtPoint:withFont:. CGSize size = (*p)(sum, s, CGPointZero, f); NSLog(@"The method drawAtPoint:withFont: returned the size %g × %g.", size.width, size.height);
class_getName(c) == NSCFString sel_getName(sel) == drawAtPoint:withFont: sel_getName(method_getName(m)) == drawAtPoint:withFont: p == 0x2d4e3c The method drawAtPoint:withFont: returned the size 184 × 38.Two simplifications. Create a
typedef
(i.e., a one-word name)
for the data type
CGSize (*)(id, SEL, CGPoint, UIFont *)
CGSize
structure.
(Without the first pair of parentheses
it would be the type of a function that returns a pointer,
rather than a pionter to a function.)
typedef CGSize (*pointer_t)(id, SEL, CGPoint, UIFont *); pointer_t p = reinterpret_cast<pointer_t>(method_getImplementation(m));And when we finally call the function to which
p
points,
we don’t have to dereference
p
explicitly.
//Call drawAtPoint:withFont:. CGSize size = p(sum, s, CGPointZero, f);
callMethod
.
Change
[sum drawAtPoint: CGPointZero withFont: f];to
callMethod(sum, "drawAtPoint:withFont", CGPointZero, f);Of course, there’s still the problem that
callMethod
will need a variable number of arguments of variable data types.
Can we implement this with the C++ ellipsis
...
)IMP
,
C++ function name overloading,
or C++ templates?
alloc
method of class
NSObject
.
Can we write the app entirely in C++?