Table Of Contents

Array (Objective-C)

In this section and the next one, we are going to discuss how to use array (including Cocoa collections) in Objective-C and C++, and some related topics, like “strong referencing an array element(interior pointer)”.

iGC collects cycles of arrays, including Objective-C native array and Cocoa collections: NSArray, NSDictionary and NSSet, and C/C++ scalar-type array, object pointer array, and array of objects new_feature (each element stores the contents of an object, not a reference to an object)

native array

In Objective-C, you can define an array of references as instance variables. Here is an example of ARCGC code:

#include "hnxgc.h"

@interface Foo : NSObject {
    id ids[15];             // array of `id'
    Foo * foos[10];         // array of `Foo *'
}
@end

...

int main() {
    HXSystem_setTraceType(true, kHXLogObjLifeTime);

    Foo * o1 = [[Foo alloc] init];
    Foo * o2 = [[Foo alloc] init];

    o1->ids[0] = o1;                    // cycle 1
    o1->ids[1] = o2; o2->ids[0] = o1;   // cycle 2
    o1->foos[3] = [[Foo alloc] init]; o1->foos[3]->ids[1] = o2; //cycle 3

    o1 = o2 = nil;  // no more refs, but objects are still there

    printf("-- explicitly invoke garbage collection --\n");
    HXSystem_gc();   // all these objects are destructed and freed

    return 0;
}
... 12:25:13.423 [11303] Create object 0x7a350d0(Foo)
... 12:25:13.440 [11303] Create object 0x7a35038(Foo)
... 12:25:13.440 [11303] Create object 0x7a34fa0(Foo)
-- explicitly invoke garbage collection --
... 12:25:13.444 [12d03] Destructing 0x7a34fa0(Foo)
... 12:25:13.444 [12d03] Destructing 0x7a35038(Foo)
... 12:25:13.444 [12d03] Destructing 0x7a350d0(Foo)
... 12:25:13.444 [12d03] Free object 0x7a34fa0(Foo)
... 12:25:13.444 [12d03] Free object 0x7a35038(Foo)
... 12:25:13.444 [12d03] Free object 0x7a350d0(Foo)

Garbage collection successfully reclaims the retain cycle leaks.

NSArray

NSArray and NSMutableArray are “toll-free bridged” with their Core Foundation counterparts. Under Cocoa’s object-ownership conventions, objects added to these Cocoa collections are retained until they are removed from the collection or the collection is deallocated.

Retain cycles of these Cocoa collections need to be reclaimed by garbage collection. iGC specially handles these “toll-free bridged” classes and NSDictionary and NSSet to allow garbage collecting them.

Look at this code:

// ARCGC code
@autoreleasepool {
    Foo * foo = [[Foo alloc] init];

    NSMutableArray *array = [NSMutableArray arrayWithObjects: @"Foo", foo, nil];

    foo->ids[0] = array;
}
printf("-- explicitly invoke garbage collection --\n");
HXSystem_gc();
... 13:46:45.591 [11303] Create object 0x7a7f0d0(Foo)
... 13:46:45.593 [11303] Create object 0x9a552c0(__NSArrayM)
-- explicitly invoke garbage collection --
... 13:46:45.596 [12d03] Destructing 0x9a552c0(__NSArrayM)
... 13:46:45.596 [12d03] Destructing 0x7a7f0d0(Foo)
... 13:46:45.596 [12d03] Free object 0x9a552c0(__NSArrayM)
... 13:46:45.596 [12d03] Free object 0x7a7f0d0(Foo)

The leak are reclaimed successfully by garbage collection in this sample.

NSDictionary and NSSet

NSDictionary and NSSet are “toll-free bridged” classes that are garbage collectable. Here is an example.

// ARCGC code
@autoreleasepool {
    Foo * foo = [[Foo alloc] init];

    NSMutableDictionary * dict = [NSMutableDictionary dictionary];

    [dict setObject:foo forKey:@"abcd"]; foo->ids[5] = dict; // cycle
}

printf("-- explicitly invoke garbage collection --\n");
HXSystem_gc();
... 15:30:43.675 [11303] Create object 0x7aef0d0(Foo)
... 15:30:43.676 [11303] Create object 0x7af32c0(__NSDictionaryM)
-- explicitly invoke garbage collection --
... 15:30:43.676 [12d03] Destructing 0x7af32c0(__NSDictionaryM)
... 15:30:43.676 [12d03] Destructing 0x7aef0d0(Foo)
... 15:30:43.676 [12d03] Free object 0x7af32c0(__NSDictionaryM)
... 15:30:43.676 [12d03] Free object 0x7aef0d0(Foo)

The leak is reclaimed.

Note

Although these “toll-free bridged” Objective-C Cocoa collections are garbage collected, Core Foundation classes are not yet garbage collectable. You can use CFDictionary as usual, but cycles still leak.

Array (C++)

iGC collects cycles of C/C++ array, including:

  • scalar-type array
  • array of pointers
  • array of objects (each element stores the contents of an object, not a reference to an object).

Create a scalar-type array

Like in C/C++, you can use gcnew T[n] style to create an array of n instances of type T. It returns a GC smart pointer to the first element. You can use the pointer as a C/C++ pointer, like p[i] or *(p + i), to access elements.

gcptr<char> pText = gcnew char[100];
gcptr<double> pValues = gcnew double[5];

strcpy(pText, "Hello world");
pValues[3] = 1.67;

Similarly, you can create array of other types, including struct and class.

gcptr<CGPoint> p = gcnew CGPoint[128];
p[3].x = 100;
(p + 3)->y = 50;

Reference and hold an element of an array new_feature

You can use interior_cast to create an effective reference to an element of an array. Like other effective references in the system, it holds the array from reclamation. This kind of references are used like others. See the following code:

gcptr<int> p = gcnew int[20];
for (int i = 0; i < 20; i++) p[i] = i;

p = interior_cast(p, &p[3]);    // a pointer to the 4th element
for (int i = 0; i < 5; i++) {
    printf("%u ", p[i]);
}
printf("\n");

p = interior_cast(p, &p[3]);    // to the 4th element of the sub-array
for (int i = 0; i < 5; i++) {
    printf("%u ", p[i]);
}
printf("\n");
3 4 5 6 7
6 7 8 9 10

The helper function interior_cast accepts two parameters, an object reference and a pointer to an address that the return value will point to. The interior_cast returns a smart pointer pointing to where the second parameter specified, and holding keep the object, specified by the first parameter, alive from reclamation.

As shown in the above example, the object passed in as the first parameter of interior_cast can also be an interior pointer.

Create an array of managed pointers

If you intend to create an array of gcptr<> under management, then the type of gcptr<> should be gcptr<T>::mm, because there isn’t a class associated to perform automatic pointer layout registration.

gcptr<gcptr<int>::mm> p = gcnew gcptr<int>::mm[20];  // an array of pointers

p[3] = gcnew int;       // element 4 points to an object of int
p[5] = gcnew int[7];    // element 6 points to a new array of int

For a pointer array not under management, just use standard C/C++ way to define it. For example,

int main() {
    gcptr<Foo> p[30];   // array in stack
    p[2] = gcnew Foo;

    gcptr<Foo> *p2 = new gcptr<Foo>[30];  // unmanaged array
    p2[7] = gcnew Foo;
    delete [] p2;       // need manually delete it
}

Create an array of objects new_feature

Many other GC or RC implementations can not support array of objects, because they connect each object implicitly with a standalone managed memory block (which is released independently).

In HnxGC, object is object, memory is memory, they are not always equal. Objects can be deeply nested in another object in a single managed memory block. Typical cases include an array of objects, Multiple inheritance, compound structure, etc.

You can create an array of GC enabled objects similar to scalar type or “C” structs. For example:

class Book {
public:
    HNXGC_ENABLE(Book);
    char * m_strName;
    gcptr<Person> m_pAuthor;
    size_t m_nWords;
};

int main() {
    gcptr<Book> p = gcnew Book[100];    // create 100 instances of Book

    p[7].m_strName = "The Secrets of Forest";
    p[7].m_pAuthor = gcnew Person("Joe");
    ...

}

In the Book array, each element is a full-fledged managed Book object, e.g. the m_pAuthor in each element can hold objects alive. The array occupies 100 * sizeof(Book) = 100 * 12 = 1200 bytes in a 32-bit environment. It is not 100 * sizeof(void *) = 400 bytes.

Comparing to Java’s an array of references to objects, it has advantages:

  • You can create a large number of objects at once, quickly without individual memory allocations;
  • Accessing elements of array is fast without indirect memory access and less chances of memory cache miss.
  • Objects as array elements are always there and ready, no need to check NULL elements.

Footnotes

new_feature indicates a distinct feature provided by iGC and HnxGC.