Table Of Contents

Class (Objective-C)

Class Inheritance

Inherit a GC or non-GC class

A GC class (also known as GC-enabled class), can inherit from another GC class or a non-GC class.

For example, in the following GC code (MRRGC, ARCGC or ARCGC_C), the GC class MyViewController inherits a non-GC class UITableViewController defined in Apple’s UIKit framework.

@interface MyViewController : UITableViewController {
    IBOutlet UITextField *textField;  // instance variables
    ...
}
...     // properties and methods
@end

ivar and subclassing

When adding an instance variable to a class, we recommend declaring it as @private to only allow direct access from within the class. Others can access it via @property accessors.

Declaring a instance variable as @public is not recommended by Apple either (see Coding Guidelines for Cocoa).

If you declare instance variables as @protected for subclassing, you are not recommended to subclass your GC-enabled class under a non-GC environment. In other word, avoid instance variables of a GC class being accessed by non-GC code. That may cause retain cycle leaks that iGC cannot reclaim.

C++ smart pointer as ivar

GC under any compilers without patches

In Objective-C++, you can use C++ smart pointer to define instance variables. iGC runtime can detect these smart pointer ivars, and trace them in garbage collection. Therefore, you don’t need a iGC-patched compiler to develop Objective-C apps, any standard Objective-C++ compiler should work.

The following garbage collected ARC code works fine using a unpatched Clang compiler.

#import <Foundation/Foundation.h>
#include "hnxgc.h"

@interface Foo : NSObject {
    gcptr<>::i      pObject;    // same as `id'
    gcptr<Foo>::i   pFoo;       // same as `Foo *'
}
@end

@implementation Foo
int main() {
    HXSystem_setTraceType(true, kHXLogObjLifeTime);
    @autoreleasepool {
        Foo * foo = [[Foo alloc] init];
        foo->pObject = foo;     // cycle 1
        foo->pFoo = foo;        // cycle 2
    }
    printf("-- explicitly invoke garbage collection --\n");
    HXSystem_gc();
}
@end
... 10:17:37.046 [11303] Create object 0x78f8910(Foo)
-- explicitly invoke garbage collection --
... 10:17:37.074 [12d03] Destructing 0x78f8910(Foo)
... 10:17:37.074 [12d03] Free object 0x78f8910(Foo)

Custom traverse routine

iGC can recognize instance variables and properties automatically. For some special cases, you can define a traverse routine to help iGC traverse live objects. A Traverse routine tells the system which objects are currently being used by the object.

Here is a pseudo code example:

@interface MyClass : ...
    ...
@end

@implementation MyClass
- (void)HNXGCTraverse: (void *)caller {
    for (;;) {
        void * pObjs[100];
        int nObjs;

        < prepare the contents of pObjs[] ...>

        _hnxgc_traverse_report_objc(caller, nObjs, pObjs);
    }
}
@end

Class (C++)

Create a GC enabled class

Although you can use HnxGC gcnew keyword to create managed objects of standard C/C++ class or struct, a GC-enabled class/struct allows you to use gcptr<> smart pointers as member variables to hold objects and have cycles collected.

Enable GC for your class

To declare a GC class, add a HNXGC_ENABLE() statement at the beginning of class declaration.

class Foo {
public:
    HNXGC_ENABLE(Foo)
    ...
}

Auto-detected GC member pointers

Then, add HnxGC smart pointers as member variables. For example:

class Foo {
public:
    HNXGC_ENABLE(Foo)

    gcptr<Foo>::m           m_pFoo;
    gcptr<sockaddr_in>::m   m_pAddr[5];
    gcptr<>                 m_pObject;
};

HnxGC automatically detects these smart pointers, and trace them in garbage collection.

There may be a little delay when creating the first instance of a specific class. Because system will generate an internal per-class data structure for the class, describing characteristics of the class, such as layout of member pointers. Once done, the speed is resumed, and the class information will not change.

Note

You can call hnxgc.RegisterClass<T>() to create the internal per-class data structure, so that the creation of the first instance will be a little faster.

See also

gcptr<T>, gcptr<T>::m

Manual member pointers

For performance sensitive classes, you can also use “Manual Member Pointer” of gcptr<> as member variables.

gcptr<Foo>::mm          m_pFoo;         // please notice the ``mm`` specifier
gcptr<>::mm             m_pObject;

If you use manual member pointers, you must provide a CLASSINFO routine to tell the system where the locations of all manual member pointers of your class, including all base classes and embedded objects.

class D : public B1, public B2 {
public:
    HNXGC_ENABLE(Foo)

    gcptr<D>::mm        m_pD;
    gcptr<B1>::mm       m_pB1;
    gcptr<int>::mm      m_pInt;
    B1                  m_vB1;

    HNXGC_CLASSINFO() {
        HNXGC_CLASSINFO_MP(m_pD);
        HNXGC_CLASSINFO_MP_BEGIN
        { m_pInt, m_pB1 };
        HNXGC_CLASSINFO_MP_END

        HNXGC_CLASSINFO_CHAIN((B1*)this);
        HNXGC_CLASSINFO_CHAIN((B2*)this);
        HNXGC_CLASSINFO_CHAIN(&this->m_vB1);
    }
};

A C++ traverse routine

Only in very special cases you will need to define a Traverse routine.

HNXGC_TRAVERSE(CFoo) {
        HNXGC_TRAVERSE_PTR(m_pNext);
        HNXGC_TRAVERSE_MULTIPLE_BEGIN
        { m_pNext, m_pPrev };
        HNXGC_TRAVERSE_MULTIPLE_END
        HNXGC_TRAVERSE_CHAIN((CBar*)this);

Casting field from/to struct

In C/C++, given a type of an object, you can get the address of a field inside the object. Vice versa, you can calculate the address of an object from a pointer to the object’s field. Some macros are widely used for these, like CONTAINING_RECORD(), FIELD_OFFSET() in Windows, and container_of, offsetof in Unix.

HnxGC continue support and encourage using these macros. HnxGC provides interior pointer macro to support these macros and enhanced them to object lifetime management.

An interior pointer to a field inside an object

Under the help of interior_cast, you can hold an object via a pointer to a field of the object. See the following example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include "hnxgc.h"

struct Bar { double c[7]; };

struct __attribute__((packed)) Foo {
    char a;
    struct Bar b;
    Foo() { printf("Foo(%p) created.\n", this); }
    ~Foo() { printf("Foo(%p) destructed.\n", this); }
};

gcptr<Bar>::ret getBarFromFoo(gcptr<Foo>::in foo) {
    Bar * bar = &foo->b;
    return interior_cast(foo, bar);
}

gcptr<Foo>::ret getFooFromBar(gcptr<Bar>::in bar) {
    Foo * foo = container_of(bar, Foo, b);
    return interior_cast(bar, foo);
}

int main() {
    gcptr<Foo> foo = gcnew Foo;
    printf("Foo:%p\n", (void*)foo);

    gcptr<Bar> bar = getBarFromFoo(foo);
    foo = nullptr;      // only `bar' holds the object `foo'

    printf("Bar:%p\n", (void*)bar);
    printf("Foo:%p\n", (void*)getFooFromBar(bar));
    return 0;
}
1
2
3
4
5
Foo(0x5067f80) created.
Foo:0x5067f80
Bar:0x5067f81
Foo:0x5067f80
Foo(0x5067f80) destructed.

This example shows that,

  • Interior pointer bar solely holds the object alive (since line 27).
  • Interior pointer, like bar, is used exactly in the same way as other normal pointers, such as doing reference counting to reclaim object immediately.

See also

interior_cast.

Pointing to any interior address, including odd address

There is no restriction on alignment of the address of an interior pointer. An interior pointer can point to any address inside an object, including odd addresses as shown in the above example, bar at a packed struct Foo (line 5 and output #3)

Casting to deeper inside of another interior pointer

The control object parameter passed to interior_cast is not restricted to a gcnew created objects, it can be an interior pointer also (see line 17, 30 of the above example). That is, you can create an interior pointer which is pointing to the deeper inside of another internal pointer.

gcptr<Bar> bar = interior_cast<Bar>(foo, &foo->b);
gcptr<double> p = interior_cast<int>(bar, &bar->c[5]);

Casting an interior pointer back to normal pointer

Nothing special, using interior_cast the same way as casting inside, you can cast an interior pointer back to the beginning of the object.

The above example actually demonstrates this type of usage. Look at the subroutine getFooFromBar(), it accepts bar an interior pointer, and returns foo the original object, line 30, exactly the same value as line 24.

Embedding objects inside another

As shown in the above example, struct Bar is embedded in struct Foo. In fact, GC-enabled class(es) can also be embedded into another GC-enabled class.

Here is an example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include "hnxgc.h"

class Foo; // forward declaration

class Bar {
public:
    HNXGC_ENABLE(Bar)
    gcptr<Foo>::m foo;
};

class Foo {
public:
    HNXGC_ENABLE(Foo)
    int count;
    Bar bar;         // 1 bar embedded in Foo
    Bar bars[7];     // 7 bars embedded in Foo
    Foo() { printf("Foo(%p) created.\n", this); }
    ~Foo() { printf("Foo(%p) destructed.\n", this); }
};

int main() {
    gcptr<Foo> foo = gcnew Foo;
    printf("Foo:%p\n", (void*)foo);

    gcptr<Bar> bar = interior_cast<Bar>(foo, foo->bars + 3);
    foo = nullptr;      // only `bar' holds the object `foo'

    Foo * pFoo = container_of(bar, Foo, bars[3]);

    // make a cycle
    bar->foo = interior_cast<Foo>(bar, pFoo);

    printf("Bar:%p\n", (void*)bar);
    printf("Foo:%p\n", (void*)bar->foo);

    bar = nullptr;

    printf("-- explicitly invoke garbage collection --\n");
    HXSystem_gc();

    return 0;
}
Foo(0x5053c70) created.
Foo:0x5053c70
Bar:0x5053c84
Foo:0x5053c70
-- explicitly invoke garbage collection --
Foo(0x5053c70) destructed.

This time, class Bar and Foo are both GC-enabled class. This Foo instance contains eight Bar instances, each of them has a member pointer foo for holding objects.

Multiple inheritance

Similar to embedded objects, you can use C++ multiple inheritance with garbage collection. Casting between base classes and the derived class can be done by interior_cast and variants.

Suppose class D inherits class A, B and C.

class D : public C, public B, public A {
public:
    HNXGC_ENABLE(D)
    ...
};

You can use C++ casting operators like static_cast, dynamic_cast to convert pointers among class D and its base classes A, B and C.

Besides, you can use interior_cast and its variants gc_static_cast, gc_dynamic_cast, etc, to do casting on GC smart pointers, with the capability to hold objects from reclamation.

// using interior_cast on gcptr<>
gcptr<D> d = ...;
gcptr<B> b = interior_cast<B>(d, d); // cast from derived class to base class
gcptr<D> d2 = interior_cast<D>(b, b); // cast back from Base to Derived class

// same as above, but using gc_static_cast, gc_dynamic_cast on gcptr<>
b = gc_static_cast<B>(d);
d2 = gc_dynamic_cast<D>(b);

Type casting

For your convenience, HnxGC defines some variants of interior_cast, including:

Three GC counterpart of C++ castings

gcptr<byte> pData = gcnew byte[512]; pData[i] = ...;
gcptr<sockaddr_in> pSockaddr = gc_reinterpret_cast<sockaddr_in>(pData);

and a casting from C/C++ native pointer to GC smart pointer

Foo * pFoo = ...;
gcptr<Foo> foo = native_cast<Foo>(pFoo);

Note

For native casting, as shown in the above example, the parameter pFoo must point to a valid managed object. Application program may crash if you cast a stack or malloc-ed address to a GC smart pointer.