protocol_t
struct protocol_t : objc_object {
const char *mangledName;
struct protocol_list_t *protocols;
method_list_t *instanceMethods;
method_list_t *classMethods;
method_list_t *optionalInstanceMethods;
method_list_t *optionalClassMethods;
property_list_t *instanceProperties;
uint32_t size; // sizeof(protocol_t)
uint32_t flags;
// Fields below this point are not always present on disk.
const char **_extendedMethodTypes;
const char *_demangledName;
property_list_t *_classProperties;
...
}
mangledName是协议的名称.在存储的时候由mangledName和_demangledName共同作用生成.
protocols是遵循的上级协议,后面看protocol_list_t这个类型.
instanceMethods是在协议中声明的实例方法
classMethods是类方法
optionalInstanceMethods是@optional修饰的实例方法
optionalClassMethods是@optional修饰的类方法
instanceProperties是声明的属性
_classProperties是@property中用class关键字修饰的属性
可以看到方法和属性都是entsize_list_tt的.
typedef uintptr_t protocol_ref_t; // protocol_t *, but unremapped
struct protocol_list_t {
// count is pointer-sized by accident.
uintptr_t count;
protocol_ref_t list[0]; // variable-size
size_t byteSize() const {
return sizeof(*this) + count*sizeof(list[0]);
}
protocol_list_t *duplicate() const {
return (protocol_list_t *)memdup(this, this->byteSize());
}
typedef protocol_ref_t* iterator;
typedef const protocol_ref_t* const_iterator;
const_iterator begin() const {
return list;
}
iterator begin() {
return list;
}
const_iterator end() const {
return list + count;
}
iterator end() {
return list + count;
}
};
protocol_list_t类似method_list_t那些, 但是它不是继承自entsize_list_tt了.
两个成员count和list.
上面定义了一个protocol_ref_t,实质是protocol_t * .
下面又把protocol_ref_t * 换名迭代器iterator,注意又加了一次 * .
通过begin()和end()来将迭代器置于首位或者末位.
所以begin()返回iterator,也就是protocol_ref_t * ,protocol_ref_t又是protocol_t * ,
最终**begin()才是protocol_t.
runtime的协议
runtime源码中只有两种数据类型继承自objc_object,一个是类objc_class,一个是协议protocol_t.
class有方法,属性,protocol也一样;
协议和类一样,既可以加载静态,也可以在运行时构造.
在运行时,可以使用objc_allocate系列函数创建一个新的class,可以使用class_add系列函数向class添加新的方法,属性;
同样也可以使用objc_allocate系列函数创建新的协议,使用protocol_add系列函数向协议添加新的方法,属性.
这些是class与protocol的相似之处.
@protocol BaseProtocol <NSObject>
- (void)baseMetod;
@end
@interface MyClass : NSObject
@end
@interface MySubClass : MyClass <BaseProtocol>
@end
//main.m
Protocol *p = objc_allocateProtocol("newProtocol");
protocol_addProtocol(p, objc_getProtocol("BaseProtocol"));
objc_registerProtocol(p);
class_addProtocol(MyClass.class, p);
定义了一个类MyClass,没有遵循任何协议.
另外又定义了类MySubClass继承自MyClass.
定义了一个协议BaseProtocol,类MySubClass遵循它.
在main.m中:
runtime的objc_allocate系列方法有两个,objc_allocateClassPair构造类,objc_allocateProtocol构造协议.
这里构造了一个协议newProtocol,
然后protocol_addProtocol(Protocol *proto_gen, Protocol *addition_gen) 是给协议添加上级协议,
proto_gen是需要添加上级的协议,addition_gen是上级协议.
objc_getProtocol是获取一个已经注册的协议,
注意BaseProtocol写出来必须找一个类遵循,不然build之后符号表里没有这个协议,会获取不到.
最后和构造类的objc_registerClassPair一样,需要调用objc_registerProtocol.
运行观察内存:
(lldb) p objc_getProtocol("BaseProtocol")
(Protocol *) $0 = 0x00000001000087d0
(lldb) p (protocol_t *)$0
(protocol_t *) $3 = 0x00000001000087d0
(lldb) p *$3
(protocol_t) $4 = {
objc_object = (isa_storage = "\xc80\U00000004\t\U00000001")
mangledName = 0x0000000100003e67 "BaseProtocol"
protocols = 0x0000000100008368
instanceMethods = 0x0000000100008380
classMethods = nil
optionalInstanceMethods = nil
optionalClassMethods = nil
instanceProperties = nil
size = 96
flags = 0
_extendedMethodTypes = 0x00000001000083a0
_demangledName = 0x0000000000000000
_classProperties = nil
}
(lldb) p $4.instanceMethods
(method_list_t *) $5 = 0x0000000100008380
(lldb) p *$5
(method_list_t) $6 = {
entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 24, count = 1)
}
(lldb) p $6.get(0)
(method_t) $7 = {}
(lldb) p $7.big()
(method_t::big) $8 = {
name = "baseMetod"
types = 0x0000000100003f32 "v16@0:8"
imp = 0x0000000000000000
}
objc_getProtocol("BaseProtocol")拿到了一个protocol_t,说明BaseProtocol通过静态加载的方式被注册.
mangledName是BaseProtocol,
输出instanceMethods到$5,是entsize_list_tt类型的,get(0)然后输出big().
看到name是baseMetod.
观察main.m中的Protocol *p,
(lldb) p (protocol_t *)p
(protocol_t *) $1 = 0x00000001095bf8b0
(lldb) p *$1
(protocol_t) $2 = {
objc_object = (isa_storage = "\xc90\U00000004\t\U00000001\x80\U0000001d\U000000029<")
mangledName = 0x0000000100003c39 "newProtocol"
protocols = 0x00000001095b6250
instanceMethods = nil
classMethods = nil
optionalInstanceMethods = nil
optionalClassMethods = nil
instanceProperties = nil
size = 96
flags = 0
_extendedMethodTypes = 0x0000000000000000
_demangledName = 0x0000000000000000
_classProperties = nil
}
newProtocol是动态构建的,newProtocol是遵循了BaseProtocol的,会存在protocols中,试一下把他找出来.
(lldb) p $2.protocols
(protocol_list_t *) $28 = 0x00000001095b6250
(lldb) p *$3
(protocol_list_t) $4 = (count = 1, list = protocol_ref_t [] @ 0x0000600001e8daf8)
(lldb) p $4.begin()
(protocol_list_t::iterator) $5 = 0x00000001095b6258
(lldb) p *$5
(protocol_ref_t) $6 = 4295002064
(lldb) p (protocol_t *)$7
(protocol_t *) $8 = 0x00000001000087d0
(lldb) p *$8
(protocol_t) $9 = {
objc_object = (isa_storage = "\xc80\U00000004\t\U00000001")
mangledName = 0x0000000100003e67 "BaseProtocol"
protocols = 0x0000000100008368
instanceMethods = 0x0000000100008380
classMethods = nil
optionalInstanceMethods = nil
optionalClassMethods = nil
instanceProperties = nil
size = 96
flags = 0
_extendedMethodTypes = 0x00000001000083a0
_demangledName = 0x0000000000000000
_classProperties = nil
}
$4.begin()获取到iterator,也就是protocol_ref_t *
然后* iterator拿到protocol_ref_t,也就是protocol_t * ,
最后*$8拿到protocol_t.
可以看到mangledName是BaseProtocol.
最关键的是$8也就是protocol_t *和上面objc_getProtocol("BaseProtocol")拿到的地址是一样的,都是0x00000001000087d0,
所以协议在内存中只有一份.
还可以由BaseProtocol继续查看上级协议,
(lldb) p $9.protocols
(protocol_list_t *) $10 = 0x0000000100008368
(lldb) p *$10
(protocol_list_t) $11 = (count = 1, list = protocol_ref_t [] @ 0x0000600001ea7588)
(lldb) p $11.begin()
(protocol_list_t::iterator) $12 = 0x0000000100008370
(lldb) p *$12
(protocol_ref_t) $13 = 4295001968
(lldb) p (protocol_t *)$13
(protocol_t *) $14 = 0x0000000100008770
(lldb) p *$14
(protocol_t) $15 = {
objc_object = (isa_storage = "")
mangledName = 0x0000000100003e74 "NSObject"
protocols = nil
instanceMethods = 0x0000000100008090
classMethods = nil
optionalInstanceMethods = 0x0000000100008260
optionalClassMethods = nil
instanceProperties = 0x0000000100008280
size = 96
flags = 0
_extendedMethodTypes = 0x00000001000082c8
_demangledName = 0x0000000000000000
_classProperties = nil
}
可以看到mangledName是"NSObject",
它没有上级协议,
lldb) p $41.instanceMethods
(method_list_t *) $42 = 0x0000000100008090
(lldb) p *$42
(method_list_t) $43 = {
entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 24, count = 19)
}
(lldb) p $43.get(0).big()
(method_t::big) $44 = {
name = "isEqual:"
types = 0x0000000100003e85 "c24@0:8@16"
imp = 0x0000000000000000
}
(lldb) p $41.instanceProperties
(property_list_t *) $45 = 0x0000000100008280
(lldb) p *$45
(property_list_t) $46 = {
entsize_list_tt<property_t, property_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 16, count = 4)
}
(lldb) p $46.get(0)
(property_t) $47 = (name = "hash", attributes = "TQ,R")
(lldb) p $46.get(1)
(property_t) $48 = (name = "superclass", attributes = "T#,R")
(lldb) p $46.get(2)
(property_t) $49 = (name = "description", attributes = "T@\"NSString\",R,C")
(lldb) p $46.get(3)
(property_t) $50 = (name = "debugDescription", attributes = "T@\"NSString\",R,C")
(lldb) p $41.optionalInstanceMethods
(method_list_t *) $51 = 0x0000000100008260
(lldb) p *$51
(method_list_t) $52 = {
entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 24, count = 1)
}
(lldb) p $52.get(0).big()
(method_t::big) $53 = {
name = "debugDescription"
types = 0x0000000100003e98 "@16@0:8"
imp = 0x0000000000000000
}
这是NSObject协议的一些内容,有20个方法,只有一个是optional,
有4个属性,hash,superclass,description和debugDescription.
也就是下面这些东西:
@protocol NSObject
- (BOOL)isEqual:(id)object;
@property (readonly) NSUInteger hash;
@property (readonly) Class superclass;
- (Class)class OBJC_SWIFT_UNAVAILABLE("use 'type(of: anObject)' instead");
- (instancetype)self;
- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
- (BOOL)isProxy;
- (BOOL)isKindOfClass:(Class)aClass;
- (BOOL)isMemberOfClass:(Class)aClass;
- (BOOL)conformsToProtocol:(Protocol *)aProtocol;
- (BOOL)respondsToSelector:(SEL)aSelector;
- (instancetype)retain OBJC_ARC_UNAVAILABLE;
- (oneway void)release OBJC_ARC_UNAVAILABLE;
- (instancetype)autorelease OBJC_ARC_UNAVAILABLE;
- (NSUInteger)retainCount OBJC_ARC_UNAVAILABLE;
- (struct _NSZone *)zone OBJC_ARC_UNAVAILABLE;
@property (readonly, copy) NSString *description;
@optional
@property (readonly, copy) NSString *debugDescription;
@end
category_ t
struct category_t {
const char *name;
classref_t cls;
WrappedPtr<method_list_t, method_list_t::Ptrauth> instanceMethods;
WrappedPtr<method_list_t, method_list_t::Ptrauth> classMethods;
struct protocol_list_t *protocols;
struct property_list_t *instanceProperties;
// Fields below this point are not always present on disk.
struct property_list_t *_classProperties;
method_list_t *methodsForMeta(bool isMeta) {
if (isMeta) return classMethods;
else return instanceMethods;
}
property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
protocol_list_t *protocolsForMeta(bool isMeta) {
if (isMeta) return nullptr;
else return protocols;
}
};
内容不多,主要是:
名称name
classref_t是class_t*.指向的类cls,
方法列表instanceMethods和classMethods,
协议列表protocols,
属性列表instanceProperties和_classProperties.
category没有allocate,add之类的方法, 只能静态加载,也就是只能读取编译器生成的分类.
分类中的属性与方法
分类不仅不能动态添加,类本身也不持有category_t相关的数据结构,而是通过合并的方式来获取分类的内容.
先跳过类和分类的加载, 看看分类被加载后做了什么.
在runtime-new.mm中有一个UnattachedCategories的类,用于处理分类与类的合并.
调用了一个关键函数
static void
attachCategories(Class cls, const locstamped_category_t *cats_list, uint32_t cats_count,
int flags)
{
//....
}
这个函数是这样说明的,将分类的属性,方法,协议合并到类中,并且先加载分类先合并.
// Attach method lists and properties and protocols from categories to a class.
// Assumes the categories in cats are all loaded and sorted by load order,
// oldest categories first.
这个函数,cls可以是类或元类,cats_list是该类的category_t数组.
当分类被加载之后,就会进入这个函数,首先创建一个分类来观察一下.
///MyClass.h
@interface MyClass : NSObject
@end
///MyClass.m
@implementation MyClass
+ (void)load{
printf("myClass load\n");
}
@end
///MyClass+myCat.h
@interface MyClass (myCat)
@property(nonatomic, assign) NSInteger a;
- (void)catMethod;
@end
///MyClass+myCat.m
@implementation MyClass (myCat)
+ (void)load{
printf("myCat load\n");
}
- (NSInteger)a{
return 0;
}
- (void)setA:(NSInteger)a{}
- (void)catMethod{}
@end
这里给类和分类都实现+load方法,为什么这么做与类与分类的加载有关,这篇先不涉及.
然后在attachCategories函数的for循环中添加:
//...
for (uint32_t i = 0; i < cats_count; i++) {
auto& entry = cats_list[i];
if(strcmp(cls->nonlazyMangledName(), "MyClass") == 0){
printf("这是MyClass\n");
}
//...
现在进入printf断点的时候,entry就是分类'myCat'了.
(lldb) po entry.cat->name
"myCat"
(lldb) p entry.cat->instanceMethods
(WrappedPtr<method_list_t, method_list_t::Ptrauth>) $2 = {
ptr = 0x0000000100008218
}
(lldb) p *$2
(method_list_t) $3 = {
entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 24, count = 3)
}
(lldb) p $3.get(0).big()
(method_t::big) $4 = {
name = "a"
types = 0x0000000100003ed6 "q16@0:8"
imp = 0x0000000100003b30 (KCObjcBuild`-[MyClass(myCat) a])
}
(lldb) p $3.get(1).big()
(method_t::big) $5 = {
name = "setA:"
types = 0x0000000100003ede "v24@0:8q16"
imp = 0x0000000100003b40 (KCObjcBuild`-[MyClass(myCat) setA:])
}
(lldb) p $3.get(2).big()
(method_t::big) $6 = {
name = "catMethod"
types = 0x0000000100003ebb "v16@0:8"
imp = 0x0000000100003b60 (KCObjcBuild`-[MyClass(myCat) catMethod])
}
现在可以看到三个方法,属性a的setter和getter都有,而且对应的imp也有,因为我们都实现了.
如果注释掉
//- (NSInteger)a{
// return 0;
//}
//
//- (void)setA:(NSInteger)a{
//
//}
再运行观察,就只有一个catMethod方法了.不管加不加@dynamic
(method_list_t) $1 = {
entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 24, count = 1)
}
(lldb) p $1.get(0).big()
(method_t::big) $2 = {
name = "catMethod"
types = 0x0000000100003ec5 "v16@0:8"
imp = 0x0000000100003b70 (KCObjcBuild`-[MyClass(myCat) catMethod])
}
关于属性和成员变量
1.类如果声明了@property var,在加载类的时候,会生成property_t添加到ro的baseProperties,同时会生成ivar_t添加到ro的ivars.
2.在gcc时代,声明属性需要声明成员变量_var,然后@property var,然后@synthesize var来生成set和get方法;
后来@property自带了@synthesize的功能,并且也不需要再写一遍_var了,在类中@property直接生成成员变量和set,get方法;
但是如果遵循的协议中有@property var, 那么类可以选择在interface中再声明一遍@property var,或者在implementation中@synthesize var.
3.@synthesize是作用于@property的,可以是类的interface或者扩展,或者遵循的协议;只是声明成员变量,然后添加@synthesize,会报错"Property implementation must have its declaration in interface 'xxxClass' or one of its extensions".
这里指明了需要在interface或者extensions中声明property,extension经常被叫做匿名分类,就是
@interface XXX () //<遵循的协议>
/*
声明属性和方法
**/
@end
4.因为指明了要在interface或者extensions声明属性,所以给分类添加属性然后@synthesize是会报错的.
"@synthesize not allowed in a category's implementation"
@synthesize是@property青春版的一部分,既然@synthesize不行那分类的@property肯定也不会生成set和get.
5.在分类中声明@property, 编译器确实会生成set和get的声明,所以调用set或者get是可以编译通过的,
但是没有实现set和get方法,在mach-o中是没有对应符号的,运行程序loadimage之后也没有这两个selector,
category_t里也就没有对应的method_t.
并且在分类中声明@property ivar,没有生成成员变量,即使自己实现set和get,也是写不出_ivar的.
至于动态绑定就是另一回事了,与成员变量无关.
合并分类
然后再来仔细看看attachCategories函数,
static void
attachCategories(Class cls, const locstamped_category_t *cats_list, uint32_t cats_count, int flags)
{
constexpr uint32_t ATTACH_BUFSIZ = 64;
method_list_t *mlists[ATTACH_BUFSIZ];
property_list_t *proplists[ATTACH_BUFSIZ];
protocol_list_t *protolists[ATTACH_BUFSIZ];
uint32_t mcount = 0;
uint32_t propcount = 0;
uint32_t protocount = 0;
bool fromBundle = NO;
bool isMeta = (flags & ATTACH_METACLASS);
auto rwe = cls->data()->extAllocIfNeeded();
for (uint32_t i = 0; i < cats_count; i++) {
auto& entry = cats_list[i];
method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
if (mlist) {
if (mcount == ATTACH_BUFSIZ) {
prepareMethodLists(cls, mlists, mcount, NO, fromBundle, __func__);
rwe->methods.attachLists(mlists, mcount);
mcount = 0;
}
mlists[ATTACH_BUFSIZ - ++mcount] = mlist;
fromBundle |= entry.hi->isBundle();
}
property_list_t *proplist =
entry.cat->propertiesForMeta(isMeta, entry.hi);
if (proplist) {
if (propcount == ATTACH_BUFSIZ) {
rwe->properties.attachLists(proplists, propcount);
propcount = 0;
}
proplists[ATTACH_BUFSIZ - ++propcount] = proplist;
}
protocol_list_t *protolist = entry.cat->protocolsForMeta(isMeta);
if (protolist) {
if (protocount == ATTACH_BUFSIZ) {
rwe->protocols.attachLists(protolists, protocount);
protocount = 0;
}
protolists[ATTACH_BUFSIZ - ++protocount] = protolist;
}
}
if (mcount > 0) {
prepareMethodLists(cls, mlists + ATTACH_BUFSIZ - mcount, mcount,
NO, fromBundle, __func__);
rwe->methods.attachLists(mlists + ATTACH_BUFSIZ - mcount, mcount);
if (flags & ATTACH_EXISTING) {
flushCaches(cls, __func__, [](Class c){
// constant caches have been dealt with in prepareMethodLists
// if the class still is constant here, it's fine to keep
return !c->cache.isConstantOptimizedCache();
});
}
}
rwe->properties.attachLists(proplists + ATTACH_BUFSIZ - propcount, propcount);
rwe->protocols.attachLists(protolists + ATTACH_BUFSIZ - protocount, protocount);
}
用上ATTACH_BUFSIZ的时候是极少的,可以忽略相关的代码
首先获取类的rw,cls->data()->extAllocIfNeeded();分类中的内容都是添加到rw里的,不过这里不是class_rw_t,是class_rw_t的下级数据结构class_rw_ext_t,当需要写入rw的时候调用cls->data()extAllocIfNeeded()获取它,需要读取rw的时候调用cls->data()即可.
也就是这个东西.
struct class_rw_ext_t {
DECLARE_AUTHED_PTR_TEMPLATE(class_ro_t)
class_ro_t_authed_ptr<const class_ro_t> ro;
method_array_t methods;
property_array_t properties;
protocol_array_t protocols;
const char *demangledName;
uint32_t version;
};
可以看到在for循环中, mlist, proplist, protolist分别往方法methods,属性properties,和协议protocols里添加.
这三个成员都是list_array_tt,通过调用attachLists函数添加.
list_array_tt的原理在这一篇
总之结论上来说,attachLists每次都是往最前面添加,分类的内容会被添加在类原本内容的前面,
在慢速查找时,是从前到后查找的,因此如果方法名相同,分类的方法会覆盖类的方法.
同样的如果方法名相同,后面编译的分类也会覆盖前面编译的分类的方法.
可以看到合并分类的时候并没有给ro添加ivar,这并不是因为ivar不能动态添加,即使在运行时,也可以调用class_addIvar来添加.
这主要与设计有关,swift的extension也不能添加属性.
类扩展
类扩展,extension,也就是匿名分类,只有@interface没有@implementation.
不管是声明在类文件中,还是单独一个文件,都不会产生分类,不会走attachCategories,在编译阶段就被合并到类中.
//MyClass.h
@interface MyClass : NSObject
@end
///MyClass.m
@interface MyClass()
@property(nonatomic, assign) NSInteger bCatpp;
@end
@implementation MyClass
@end
///MyClass+MyEx.h
@interface MyClass ()
@property(nonatomic, assign) NSInteger cCatpp;
@end
在.m添加一个扩展,并且另外创建一个扩展文件
执行 clang -rewrite-objc MyClass.m -o My.cpp
然后拉倒最后可以看到_bCatpp和_cCatpp的定义,以及set和get和
//成员变量_bCatpp和_cCatpp
extern "C" unsigned long OBJC_IVAR_$_MyClass$_bCatpp;
extern "C" unsigned long OBJC_IVAR_$_MyClass$_cCatpp;
struct MyClass_IMPL {
struct NSObject_IMPL NSObject_IVARS;
NSInteger _bCatpp;
NSInteger _cCatpp;
};
//bCatpp的get方法实现
static NSInteger _I_MyClass_bCatpp(MyClass * self, SEL _cmd) { return (*(NSInteger *)((char *)self + OBJC_IVAR_$_MyClass$_bCatpp)); }
//bCatpp的set方法实现
static void _I_MyClass_setBCatpp_(MyClass * self, SEL _cmd, NSInteger bCatpp) { (*(NSInteger *)((char *)self + OBJC_IVAR_$_MyClass$_bCatpp)) = bCatpp; }
//cCatpp的get方法实现
static NSInteger _I_MyClass_cCatpp(MyClass * self, SEL _cmd) { return (*(NSInteger *)((char *)self + OBJC_IVAR_$_MyClass$_cCatpp)); }
//cCatpp的set方法实现
static void _I_MyClass_setCCatpp_(MyClass * self, SEL _cmd, NSInteger cCatpp) { (*(NSInteger *)((char *)self + OBJC_IVAR_$_MyClass$_cCatpp)) = cCatpp; }
//MyClass的方法列表,包含所有的SEL
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[9];
} _OBJC_$_INSTANCE_METHODS_MyClass __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
9,
//这里我编译的时候还写了一个init方法
{{(struct objc_selector *)"init", "@16@0:8", (void *)_I_MyClass_init},
{(struct objc_selector *)"bCatpp", "q16@0:8", (void *)_I_MyClass_bCatpp},
{(struct objc_selector *)"setBCatpp:", "v24@0:8q16", (void *)_I_MyClass_setBCatpp_},
{(struct objc_selector *)"cCatpp", "q16@0:8", (void *)_I_MyClass_cCatpp},
{(struct objc_selector *)"setCCatpp:", "v24@0:8q16", (void *)_I_MyClass_setCCatpp_},
{(struct objc_selector *)"bCatpp", "q16@0:8", (void *)_I_MyClass_bCatpp},
{(struct objc_selector *)"setBCatpp:", "v24@0:8q16", (void *)_I_MyClass_setBCatpp_},
{(struct objc_selector *)"cCatpp", "q16@0:8", (void *)_I_MyClass_cCatpp},
{(struct objc_selector *)"setCCatpp:", "v24@0:8q16", (void *)_I_MyClass_setCCatpp_}}
};