What's the methodLists attribute of the structure objc_class for?

stackoverflow上看到这个问题,刚好那时候也在看相关内容,同有疑惑。

不过后来一时没理清,暂时先搁置了下来。今天早上,同事突然问起了这个问题,所以又跟了一下。

具体过程直接用英文写。


I'm now using Xcode 4, and the declaration of struct objc_class is as following:

struct objc_class {
    Class isa;

#if !__OBJC2__
    Class super_class                                        OBJC2_UNAVAILABLE;
    const char *name                                         OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list *ivars                             OBJC2_UNAVAILABLE;
    struct objc_method_list **methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache *cache                                 OBJC2_UNAVAILABLE;
    struct objc_protocol_list *protocols                     OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */


The macro OBJC2_UNAVAILABLE makes me know that struct objc_class, under Objective-C 2.0, is just a pointer to itself, similar to struct objc_object:

typedef struct objc_class *Class;
typedef struct objc_object {
    Class isa;
} *id;

/* OBJC2_UNAVAILABLE: unavailable in objc 2.0, deprecated in Leopard */
#if !defined(OBJC2_UNAVAILABLE)
#   if __OBJC2__
#       define OBJC2_UNAVAILABLE UNAVAILABLE_ATTRIBUTE
#   else
#       define OBJC2_UNAVAILABLE DEPRECATED_IN_MAC_OS_X_VERSION_10_5_AND_LATER
#   endif
#endif


Therefore I failed when I tried to follow the methodLists to observe its changes, which results in EXEC_BAD_ACCESS. 

I realized I should find out the new struct of objc_class, then occasionally the file objc-runtime-new.h came into my eyes with struct class_t:

typedef struct class_t {
    struct class_t *isa;
    struct class_t *superclass;
    Cache cache;
    IMP *vtable;
    class_rw_t *data;
} class_t;


The member vtable is dazzling for C++ programmers, including me. And methodLists sits in data:

typedef struct class_rw_t {
    uint32_t flags;
    uint32_t version;

    const class_ro_t *ro;
    
    method_list_t **methods;
    struct chained_property_list *properties;
    const protocol_list_t ** protocols;

    struct class_t *firstSubclass;
    struct class_t *nextSiblingClass;
} class_rw_t;


So, why method_list_t **, why not method_list_t *?

What's struct objc_method_list **methodLists for?

objc-class.m states:

 * cls->methodLists may be in one of three forms:
 * 1. NULL: The class has no methods.
 * 2. non-NULL, with CLS_NO_METHOD_ARRAY set: cls->methodLists points 
 *    to a single method list, which is the class's only method list.
 * 3. non-NULL, with CLS_NO_METHOD_ARRAY clear: cls->methodLists points to 
 *    an array of method list pointers. The end of the array's block 
 *    is set to -1. If the actual number of method lists is smaller 
 *    than that, the rest of the array is NULL.
 * 
 * Attaching categories and adding and removing classes may change 
 * the form of the class list. In addition, individual method lists 
 * may be reallocated when fixed up.
 *
 * Classes are initially read as #1 or #2. If a category is attached 
 * or other methods added, the class is changed to #3. Once in form #3, 
 * the class is never downgraded to #1 or #2, even if methods are removed.
 * Classes added with objc_addClass are initially either #1 or #3.


For a normal class, #1 or #2 is the proper situation, but when class_addMethod is used on or categories are added to it, the class changes to #3.

It is easy to understand #1 & #2, NULL or a single method list. But how the class changes to #3?

The key function is attachMethodLists in objc_runtime-new.mm, when calling class_addMethod or adding a category, this function will be called.

staticvoid 
attachMethodLists(class_t *cls, method_list_t **addedLists, int addedCount, 
                  BOOL methodsFromBundle, BOOL *inoutVtablesAffected)
{
    rwlock_assert_writing(&runtimeLock);

    // Don't scan redundantly
    BOOL scanForCustomRR = !UseGC && !cls->hasCustomRR();

    // Method list array is NULL-terminated.
    // Some elements of lists are NULL; we must filter them out.

    method_list_t **oldLists = cls->data()->methods;
    int oldCount = 0;
    if (oldLists) {
        while (oldLists[oldCount]) oldCount++;
    }
        
    int newCount = oldCount + 1;  // including NULL terminator
    for (int i = 0; i < addedCount; i++) {
        if (addedLists[i]) newCount++;  // only non-NULL entries get added
    }

    method_list_t **newLists = (method_list_t **)
        _malloc_internal(newCount * sizeof(*newLists));

    // Add method lists to array.
    // Reallocate un-fixed method lists.
    // The new methods are PREPENDED to the method list array.

    newCount = 0;
    int i;
    for (i = 0; i < addedCount; i++) {
        method_list_t *mlist = addedLists[i];
        if (!mlist) continue;

        // Do sth. necessary
        …code omitted
        
        // Fill method list array
        newLists[newCount++] = mlist;
    }

    // Copy old methods to the method list array
    for (i = 0; i < oldCount; i++) {
        newLists[newCount++] = oldLists[i];
    }
    if (oldLists) free(oldLists);

    // NULL-terminate
    newLists[newCount++] = NULL;
    cls->data()->methods = newLists;
}

Below is a draft.






  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值