Defining subclasses at run time

  • Hi,

    I'm solving the following problem. I have a huge array of items of the
    same class MyItem. All items need to share a certain global "context"
    variable. Normally, I would wrap access to this variable in a
    class-level (+) method of MyItem, like +[MyItem getContext].

    Now the problem is that I need multiple arrays of MyItems have
    different contexts. The solution could be to add the context pointer
    as an ivar to each MyItem instance, but I would not like to waste
    memory on that (the arrays are huge).

    How do I solve this problem?

    I have an idea that I could spawn new subclasses of MyClass at run
    time, for each new array of MyItems, assigning each class with a
    different class-level context variable, and then instantiating items
    from that subclass. In this way, +[_runtime_subclass_of_MyItem
    getContext] would return a different context value for each array.
    This would reuse the "isa" pointer that's already in any NSObject
    instance.

    Is this possible in Obj-C? Or is there a better idea?

    Thanks!
  • On Jun 11, 2012, at 13:20 , <cocoa-dev-request...> wrote:
    > Date: Mon, 11 Jun 2012 12:35:13 +0300
    > From: Oleg Krupnov <oleg.krupnov...>
    > Message-ID:
    > <CAK83q-B44U6Dh5HX7GdjJ676_VOt_6C-+<DnGXSAbgihPjH3KLw...>
    >
    > I'm solving the following problem. I have a huge array of items of the
    > same class MyItem. All items need to share a certain global "context"
    > variable. Normally, I would wrap access to this variable in a
    > class-level (+) method of MyItem, like +[MyItem getContext].
    >
    > Now the problem is that I need multiple arrays of MyItems have
    > different contexts. The solution could be to add the context pointer
    > as an ivar to each MyItem instance, but I would not like to waste
    > memory on that (the arrays are huge).

    You could subclass NSArray (tricky because it's a class cluster), adding the context variable as an ivar to that; or make your own class containing that context and the actual array, and implementing all the proper methods.

    However, what I used in a similar situation is objc_getAssociatedObject() and objc_setAssociatedObject() in objc/runtime.h. Look them up.

    HTH,
    --
    Rainer Brockerhoff  <rainer...>
    Belo Horizonte, Brazil
    "In the affairs of others even fools are wise
    In their own business even sages err."
    Weblog: http://www.brockerhoff.net/blog
  • On Jun 11, 2012, at 9:50 AM, Rainer Brockerhoff wrote:

    > You could subclass NSArray (tricky because it's a class cluster), adding the context variable as an ivar to that; or make your own class containing that context and the actual array, and implementing all the proper methods.

    I don't think that will help unless there's a pointer from MyItem to its containing array.

    > However, what I used in a similar situation is objc_getAssociatedObject() and objc_setAssociatedObject() in objc/runtime.h. Look them up.

    I'll bet that will add more overhead than the size of a pointer, to each object, so you might as well add the pointer to MyItem.

    My take on this is that creating subclasses on the fly is possible (using the low-level ObjC API) but it seems kind of an overly complex hack, i.e. the kind of thing Ruby fanatics would do ;-)

    Is adding 4 or 8 bytes to each object really a big deal?
    Is there a way you can make the other instance data smaller to compensate for adding a pointer?
    Alternatively, if there are less than 256 'types' of MyItem, you could add a byte field to the class (possibly sneaking it into space that's already there by making use of struct packing) and use that as an index into a global table of 256 kinds.

    —Jens
  • On Jun 11, 2012, at 2:35 AM, Oleg Krupnov <oleg.krupnov...> wrote:
    > Now the problem is that I need multiple arrays of MyItems have
    > different contexts. The solution could be to add the context pointer
    > as an ivar to each MyItem instance, but I would not like to waste
    > memory on that (the arrays are huge).

    First, verify that adding the ivar would in fact cost memory. Objective-C objects are allocated by malloc(). For most sizes, malloc() allocates additional bytes rather than returning exactly the number of bytes you asked for. It's possible that adding the ivar will not push you into the next malloc() size bucket, in which case the extra ivar is free.

    To check this, you can call malloc_size() from malloc/malloc.h to see the actual allocated size of your object. Do it with and without the new ivar and compare the results. If they are the same then just add the context ivar.

    > I have an idea that I could spawn new subclasses of MyClass at run
    > time, for each new array of MyItems, assigning each class with a
    > different class-level context variable, and then instantiating items
    > from that subclass. In this way, +[_runtime_subclass_of_MyItem
    > getContext] would return a different context value for each array.
    > This would reuse the "isa" pointer that's already in any NSObject
    > instance.

    This can work if you aren't afraid of the low-level runtime machinery. Note that Key-Value Observing already uses the trick of changing the isa pointer, so you won't be able to use KVO with instances of MyItem.

    Look up objc_allocateClassPair() and objc_registerClassPair() to create the subclass objects. You can ask for extra storage on the class objects, accessible via object_getIndexedIvars(), to store your context pointer.

    --
    Greg Parker    <gparker...>    Runtime Wrangler
previous month june 2012 next month
MTWTFSS
        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  
Go to today