Objective-C Runtime

Objective-C Runtime是提供Objective-C语言动态特性的运行时的库。

/* Types */
#if !OBJC_TYPES_DEFINED

// 类方法
typedef struct objc_method *Method;
// 实例变量
typedef struct objc_ivar *Ivar;
// 分类
typedef struct objc_category *Category;
// 对象声明的属性
typedef struct objc_property *objc_property_t;

// objc_class结构体
struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY;
    
#if !__OBJC2__
    Class super_class                       OBJC2_UNAVAILABLE;  // 父类
    const char *name                        OBJC2_UNAVAILABLE;  // 类名
    long version                            OBJC2_UNAVAILABLE;  // 类的版本信息,默认为0
    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;

#endif

#ifdef __OBJC__
@class Protocol;
#else
typedef struct objc_object Protocol;
#endif

// 定义方法
struct objc_method_description {
    SEL name;               // 方法的名称
    char *types;            // 方法的参数类型
};

// 定义属性
typedef struct {
    const char *name;           // 属性名称
    const char *value;          // 属性值
} objc_property_attribute_t;

/* 实例方法 */

// 返回指定对象的一个副本
OBJC_EXPORT id object_copy(id obj, size_t size);
// 释放指定对象内存 返回nil
OBJC_EXPORT id object_dispose(id obj);
// 返回对象的类
OBJC_EXPORT Class object_getClass(id obj);
// 设置对象的类
OBJC_EXPORT Class object_setClass(id obj, Class cls);
// 是否是类对象
OBJC_EXPORT BOOL object_isClass(id obj);
// 返回给定对象的类名
OBJC_EXPORT const char *object_getClassName(id obj);
// 返回指向给定对象分配的任何额外字节的指针
OBJC_EXPORT void *object_getIndexedIvars(id obj);
// 返回对象中实例变量的值
OBJC_EXPORT id object_getIvar(id obj, Ivar ivar);
// 设置对象中实例变量的值
OBJC_EXPORT void object_setIvar(id obj, Ivar ivar, id value);
// 在对象中设置实例变量的值 iOS 10 AVAILABLE
OBJC_EXPORT void object_setIvarWithStrongDefault(id obj, Ivar ivar, id value);
// 修改类实例的实例变量的值
OBJC_EXPORT Ivar object_setInstanceVariable(id obj, const char *name, void *value);
// 更改一个实例类中实例变量的值 iOS 10 AVAILABLE
OBJC_EXPORT Ivar object_setInstanceVariableWithStrongDefault(id obj, const char *name, void *value);
// 返回对象实例变量的值
OBJC_EXPORT Ivar object_getInstanceVariable(id obj, const char *name, void **outValue);

/* 类方法 */

// 返回指定的类
OBJC_EXPORT Class objc_getClass(const char *name);
// 返回指定的元类
OBJC_EXPORT Class objc_getMetaClass(const char *name);
// 返回指定的类
OBJC_EXPORT Class objc_lookUpClass(const char *name);
// 返回指定的类
OBJC_EXPORT Class objc_getRequiredClass(const char *name);
// 返回已注册的类定义的列表
OBJC_EXPORT int objc_getClassList(Class *buffer, int bufferCount);
// 创建并返回一个指向所有已注册类的指针列表
OBJC_EXPORT Class *objc_copyClassList(unsigned int *outCount);
// 返回类的类名
OBJC_EXPORT const char *class_getName(Class cls);
// 是否是元类
OBJC_EXPORT BOOL class_isMetaClass(Class cls);
// 返回类的父类
OBJC_EXPORT Class class_getSuperclass(Class cls);
// 给类指定一个父类
OBJC_EXPORT Class class_setSuperclass(Class cls, Class newSuper);
// 返回类版本号
OBJC_EXPORT int class_getVersion(Class cls);
// 设置类版本号
OBJC_EXPORT void class_setVersion(Class cls, int version);
// 返回实例类的大小
OBJC_EXPORT size_t class_getInstanceSize(Class cls);
// 返回类中指定名称实例成员变量的信息
OBJC_EXPORT Ivar class_getInstanceVariable(Class cls, const char *name);
// 返回类成员变量的信息
OBJC_EXPORT Ivar class_getClassVariable(Class cls, const char *name);
// 返回整个成员变量列表
OBJC_EXPORT Ivar *class_copyIvarList(Class cls, unsigned int *outCount);
// 返回实例方法
OBJC_EXPORT Method class_getInstanceMethod(Class cls, SEL name);
// 返回类方法
OBJC_EXPORT Method class_getClassMethod(Class cls, SEL name);
// 返回类方法实现的指针
OBJC_EXPORT IMP class_getMethodImplementation(Class cls, SEL name);
// 返回类方法实现的指针
OBJC_EXPORT IMP class_getMethodImplementation_stret(Class cls, SEL name);
// 类实例是否响应指定的selector
OBJC_EXPORT BOOL class_respondsToSelector(Class cls, SEL sel);
// 返回所有方法的数组
OBJC_EXPORT Method *class_copyMethodList(Class cls, unsigned int *outCount);
// 返回类是否实现指定的协议
OBJC_EXPORT BOOL class_conformsToProtocol(Class cls, Protocol *protocol);
// 返回类实现的协议列表
OBJC_EXPORT Protocol * __unsafe_unretained
*class_copyProtocolList(Class cls, unsigned int *outCount);
// 返回指定的属性
OBJC_EXPORT objc_property_t class_getProperty(Class cls, const char *name);
// 返回属性列表
OBJC_EXPORT objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount);
// 返回指定变量布局
OBJC_EXPORT const uint8_t *class_getIvarLayout(Class cls);
// 返回指定弱引用的变量布局
OBJC_EXPORT const uint8_t *class_getWeakIvarLayout(Class cls);
// 添加方法 如果返回YES则添加成功
OBJC_EXPORT BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types);
// 替代方法的实现
OBJC_EXPORT IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types);
// 添加变量
OBJC_EXPORT BOOL class_addIvar(Class cls, const char *name, size_t size,uint8_t alignment, const char *types);
// 添加协议
OBJC_EXPORT BOOL class_addProtocol(Class cls, Protocol *protocol);
// 添加属性
OBJC_EXPORT BOOL class_addProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount);
// 替换属性
OBJC_EXPORT void class_replaceProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount);
// 设置变量布局
OBJC_EXPORT void class_setIvarLayout(Class cls, const uint8_t *layout);
// 设置弱属性的变量布局
OBJC_EXPORT void class_setWeakIvarLayout(Class cls, const uint8_t *layout);
// 通过CoreFoundation's自由连接。不能自己调用此方法
OBJC_EXPORT Class objc_getFutureClass(const char *name);
// 创建类实例
OBJC_EXPORT id class_createInstance(Class cls, size_t extraBytes);
// 在指定位置创建类实例
OBJC_EXPORT id objc_constructInstance(Class cls, void *bytes);
// 销毁类实例
OBJC_EXPORT void *objc_destructInstance(id obj);
// 创建新的类
OBJC_EXPORT Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes);
// 注册创建的类
OBJC_EXPORT void objc_registerClassPair(Class cls);
// 用于KVO观察者模式。 不能自己调用此方法
OBJC_EXPORT Class objc_duplicateClass(Class original, const char *name, size_t extraBytes);
// 销毁一个类及其相关联的类
OBJC_EXPORT void objc_disposeClassPair(Class cls);
// 返回方法的名称
OBJC_EXPORT SEL method_getName(Method m);
// 返回一个方法指针
OBJC_EXPORT IMP method_getImplementation(Method m);
// 返回描述方法的字符串
OBJC_EXPORT const char *method_getTypeEncoding(Method m);
// 返回方法接收参数的个数
OBJC_EXPORT unsigned int method_getNumberOfArguments(Method m);
// 返回描述方法返回类型的字符串
OBJC_EXPORT char *method_copyReturnType(Method m);
// 返回描述方法单个参数类型的字符串
OBJC_EXPORT char *method_copyArgumentType(Method m, unsigned int index);
// 返回一个描述方法返回类型
OBJC_EXPORT void method_getReturnType(Method m, char *dst, size_t dst_len);
// 返回描述方法单个参数类型
OBJC_EXPORT void method_getArgumentType(Method m, unsigned int index, char *dst, size_t dst_len);
// 返回指定方法的结构描述
OBJC_EXPORT struct objc_method_description *method_getDescription(Method m);
// 设置方法的指针地址
OBJC_EXPORT IMP method_setImplementation(Method m, IMP imp);
/**
 * 交换两个方法指针地址
 * @note 交换原理如下:
 *  \code
 *  IMP imp1 = method_getImplementation(m1);
 *  IMP imp2 = method_getImplementation(m2);
 *  method_setImplementation(m1, imp2);
 *  method_setImplementation(m2, imp1);
 *  \endcode
 */
OBJC_EXPORT void method_exchangeImplementations(Method m1, Method m2);
// 返回实例变量的名称
OBJC_EXPORT const char *ivar_getName(Ivar v);
// 返回实例变量类型的字符串
OBJC_EXPORT const char *ivar_getTypeEncoding(Ivar v);
// 返回实例变量的偏移量
OBJC_EXPORT ptrdiff_t ivar_getOffset(Ivar v);
// 返回
OBJC_EXPORT const char *property_getName(objc_property_t property);
// 返回属性的属性字符串
OBJC_EXPORT const char *property_getAttributes(objc_property_t property);
// 返回属性的属性数组
OBJC_EXPORT objc_property_attribute_t *property_copyAttributeList(objc_property_t property, unsigned int *outCount);
// 返回指定属性名称的属性的值
OBJC_EXPORT char *property_copyAttributeValue(objc_property_t property, const char *attributeName);
// 返回协议的名称
OBJC_EXPORT Protocol *objc_getProtocol(const char *name);
// 返回runtime已知的所有协议的数组
OBJC_EXPORT Protocol * __unsafe_unretained *objc_copyProtocolList(unsigned int *outCount);
// 判断一个协议是否遵循了另一个协议
OBJC_EXPORT BOOL protocol_conformsToProtocol(Protocol *proto, Protocol *other);
// 判断两个协议是否相等
OBJC_EXPORT BOOL protocol_isEqual(Protocol *proto, Protocol *other);
// 返回协议的名称
OBJC_EXPORT const char *protocol_getName(Protocol *p);
// 返回执行协议指定方法结构的描述
OBJC_EXPORT struct objc_method_description protocol_getMethodDescription(Protocol *p, SEL aSel, BOOL isRequiredMethod, BOOL isInstanceMethod);
// 返回满足指定协议方法描述的数组
OBJC_EXPORT struct objc_method_description *protocol_copyMethodDescriptionList(Protocol *p, BOOL isRequiredMethod, BOOL isInstanceMethod, unsigned int *outCount);
// 返回指定协议的属性
OBJC_EXPORT objc_property_t protocol_getProperty(Protocol *proto, const char *name, BOOL isRequiredProperty, BOOL isInstanceProperty);
/**
 * 返回声明协议所需要的属性的数组.
 * @note Identical to
 * \code
 * protocol_copyPropertyList2(proto, outCount, YES, YES);
 * \endcode
 */
OBJC_EXPORT objc_property_t *protocol_copyPropertyList(Protocol *proto, unsigned int *outCount);
OBJC_EXPORT objc_property_t *protocol_copyPropertyList2(Protocol *proto, unsigned int *outCount, BOOL isRequiredProperty, BOOL isInstanceProperty)
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0);
// 返回协议所采用的协议的数组
OBJC_EXPORT Protocol * __unsafe_unretained *protocol_copyProtocolList(Protocol *proto, unsigned int *outCount);
// 创建一个新的协议,添加到objc_registerProtocol方法中
OBJC_EXPORT Protocol *objc_allocateProtocol(const char *name);
// 注册新协议
OBJC_EXPORT void objc_registerProtocol(Protocol *proto);
// 添加一个方法到协议中,此协议必须正在建设中
OBJC_EXPORT void protocol_addMethodDescription(Protocol *proto, SEL name, const char *types, BOOL isRequiredMethod, BOOL isInstanceMethod);
// 把已经建设的协议添加到正在建设的协议中去
OBJC_EXPORT void protocol_addProtocol(Protocol *proto, Protocol *addition);
// 添加一个属性到协议中去,此协议必须正在建设中
OBJC_EXPORT void protocol_addProperty(Protocol *proto, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount, BOOL isRequiredProperty, BOOL isInstanceProperty);
// 返回所有加载在Objective-C框架和动态库上的名称
OBJC_EXPORT const char **objc_copyImageNames(unsigned int *outCount);
// 返回一个来自类动态库的名称
OBJC_EXPORT const char *class_getImageName(Class cls);
// 返回库中所有类的名称
OBJC_EXPORT const char **objc_copyClassNamesForImage(const char *image,unsigned int *outCount);
// 返回指定sel的方法的名称
OBJC_EXPORT const char *sel_getName(SEL sel);
// 注册一个Objective-C运行时系统的方法名称
OBJC_EXPORT SEL sel_getUid(const char *str);
// 注册一个sel方法,并映方法名称到sel中并返回sel值
OBJC_EXPORT SEL sel_registerName(const char *str);
// 判断两个sel是否相同
OBJC_EXPORT BOOL sel_isEqual(SEL lhs, SEL rhs);
// 当发现突变的foreach迭代过程中时插入编译器
OBJC_EXPORT void objc_enumerationMutation(id obj);
// 设置当前突变的处理程序
OBJC_EXPORT void objc_setEnumerationMutationHandler(void (*handler)(id));
// 设置函数调用objc_msgForward
OBJC_EXPORT void objc_setForwardHandler(void *fwd, void *fwd_stret);
// 创建一个当调用此方法时调用指定块的函数指针
OBJC_EXPORT IMP imp_implementationWithBlock(id block);
// 返回一个使用imp_implementationWithBlock创建的与块相关的函数指针
OBJC_EXPORT id imp_getBlock(IMP anImp);
// 移除与函数指针相关联的块
OBJC_EXPORT BOOL imp_removeBlock(IMP anImp);
// 加载弱指针引用的对象并返回它
OBJC_EXPORT id objc_loadWeak(id *location);
// 存储_weak变量的新值
OBJC_EXPORT id objc_storeWeak(id *location, id obj);
// 设置关联
OBJC_EXPORT void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);
// 获取关联值
OBJC_EXPORT id objc_getAssociatedObject(id object, const void *key);
// 移除关联对象
OBJC_EXPORT void objc_removeAssociatedObjects(id object);
// 发送一个具有简单返回值的消息到一个实例类上
OBJC_EXPORT void objc_msgSend(void /* id self, SEL op, ... */ );
// 发送一个具有结构返回值的消息到一个实例类上
OBJC_EXPORT void objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */ );
// 发送一个具有返回数据结构值的消息到一个实例类上
OBJC_EXPORT void objc_msgSend_stret(void /* id self, SEL op, ... */ );
// 发送一个具有返回数据结构值的消息到一个实例父类上
OBJC_EXPORT void objc_msgSendSuper_stret(void /* struct objc_super *super, SEL op, ... */ );
// 发送一个具有返回浮点类型值的消息到一个实例类上
OBJC_EXPORT void objc_msgSend_fpret(void /* id self, SEL op, ... */ );
OBJC_EXPORT void objc_msgSend_fp2ret(void /* id self, SEL op, ... */ );
// 调用指定方法的实现
OBJC_EXPORT void method_invoke(void /* id receiver, Method m, ... */ );
// 调用返回数据结构的指定方法的实现
OBJC_EXPORT void method_invoke_stret(void /* id receiver, Method m, ... */ );

// 关联策略
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
    OBJC_ASSOCIATION_ASSIGN = 0,           // 表示弱引用关联,非线程安全,通常是数据类型
    OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, // 表示强(strong)引用关联对象,非线程安全
    OBJC_ASSOCIATION_COPY_NONATOMIC = 3,   // 表示关联对象copy,非线程安全
    OBJC_ASSOCIATION_RETAIN = 01401,       // 表示强(strong)引用关联对象,是线程安全的
    OBJC_ASSOCIATION_COPY = 01403          // 表示关联对象copy,是线程安全的
};

// 编码类型
#define _C_ID       '@' // 代表对象类型
#define _C_CLASS    '#' // 代表类对象 (Class)
#define _C_SEL      ':' // 代表方法selector (SEL)
#define _C_CHR      'c' // 代表char类型
#define _C_UCHR     'C' // 代表unsigned char类型
#define _C_SHT      's' // 代表short类型
#define _C_USHT     'S' // 代表unsigned short类型
#define _C_INT      'i' // 代表int类型
#define _C_UINT     'I' // 代表unsigned int类型
#define _C_LNG      'l' // 代表long类型,在64位处理器上也是按照32位处理
#define _C_ULNG     'L' // 代表unsigned long类型
#define _C_LNG_LNG  'q' // 代表long long类型
#define _C_ULNG_LNG 'Q' // 代表unsigned long long类型
#define _C_FLT      'f' // 代表float类型
#define _C_DBL      'd' // 代表double类型
#define _C_BFLD     'b' //
#define _C_BOOL     'B' // 代表C++中的bool或者C99中的_Bool
#define _C_VOID     'v' // 代表void类型
#define _C_UNDEF    '?' // 代表未知类型
#define _C_PTR      '^' // 代表指针类型
#define _C_CHARPTR  '*' // 代表字符串类型 (char *)
#define _C_ATOM     '%' //
#define _C_ARY_B    '[' // 代表array
#define _C_ARY_E    ']'
#define _C_UNION_B  '(' // 代表UNION
#define _C_UNION_E  ')'
#define _C_STRUCT_B '{' // 代表结构体
#define _C_STRUCT_E '}'
#define _C_VECTOR   '!' // 代表矢量类型
#define _C_CONST    'r' // 代表常量类型

例子1 Runtime

    // 获取属性列表&&获取属性名称
    unsigned int count;
    objc_property_t *propertyList = class_copyPropertyList([self class], &count);
    for (int i = 0; i < count ; i++) {
        const char *propertyName = property_getName(propertyList[i]);
        NSString *str = [NSString stringWithUTF8String:propertyName];
        NSLog(@"property_getName==%@",str);
    }
    
    // 获取方法列表&&获取方法名称
    Method *methodList = class_copyMethodList([self class], &count);
    for (int i = 0; i < count; i++) {
        Method method = methodList[i];
        //获取方法的参数列表
        int arguments = method_getNumberOfArguments(method);
        NSLog(@"method_getNumberOfArguments===%d",arguments);
        NSLog(@"method_getName===%@",NSStringFromSelector(method_getName(method)));
    }
    
    // 获取类成员变量列表&&获取成员变量名称
    Ivar *ivarList = class_copyIvarList([self class], &count);
    for (int i = 0; i < count; i++) {
        Ivar ivar = ivarList[i];
        NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];
        NSLog(@"ivar_getName===%@",ivarName);
    }
    
    // 获取类协议列表&&获取协议名称
    __unsafe_unretained Protocol **protocolList = class_copyProtocolList([self class], &count);
    for (unsigned int i = 0; i < count; i++) {
        Protocol *protocol = protocolList[i];
        NSString *protocolStr = [NSString stringWithUTF8String:protocol_getName(protocol)];
        NSLog(@"protocol_getName===%@",protocolStr);
    }
    
    // 是不是元类
    BOOL isMetaClass = class_isMetaClass(self.class);
    NSLog(@"ViewController is the meta class? %d", isMetaClass);
    
    // 是不是类对象
    ViewController *obj = [ViewController new];
    BOOL isClass = object_isClass(obj);
    NSLog(@"ViewController is the class? %d", isClass);
    
    // 获取类名称
    const char *name = class_getName(self.class);
    NSLog(@"class_getName===%@",[NSString stringWithCString:name encoding:NSUTF8StringEncoding]);
    
    // 设置关联&&获取关联
    static char *key = "key";
    objc_setAssociatedObject(self, &key, @"hello world", OBJC_ASSOCIATION_COPY_NONATOMIC);
    NSString *associated = objc_getAssociatedObject(self, &key);
    NSLog(@"objc_getAssociatedObject===%@",associated);
    
    /*----------------------Method Swizzling------------------------*/
    // 获取实例方法
    Method originalMethod = class_getInstanceMethod([self class], @selector(original:));
    Method exchangeMethod = class_getInstanceMethod([self class], @selector(exchange:));
    
    // 获取方法指针
    IMP exchangeImp = method_getImplementation(exchangeMethod);
    IMP originalImp = method_getImplementation(originalMethod);
    
    // 获取描述方法的参数类型
    const char *originalType = method_getTypeEncoding(originalMethod);
    const char *exchangeType = method_getTypeEncoding(exchangeMethod);
    
    // 添加方法
    BOOL didAddMethod = class_addMethod([self class],
                                        @selector(original:),
                                        exchangeImp,
                                        exchangeType);
    
    if (didAddMethod) {
        // 替换方法
        class_replaceMethod([self class],
                            @selector(exchange:),
                            originalImp,
                            originalType);
    }else{
        // Method Swizzling
        method_exchangeImplementations(exchangeMethod, originalMethod);
    }
    /*----------------------Method Swizzling------------------------*/

例子2 Runtime message

   // 初始化 [[MsgSend1 alloc] init];
    MsgSend1 *msg = ((MsgSend1 * (*)(id,SEL))objc_msgSend)((id)[MsgSend1 class], @selector(alloc));
    msg = ((MsgSend1 * (*)(id,SEL))objc_msgSend)((id)msg, @selector(init));
    
    // 发送无参无返回值消息
    ((void (*)(id,SEL))objc_msgSend)((id)msg, @selector(noArgumentsAndNoReturnValue));
    
    // 发送有参数无返回值得消息
    ((void (*)(id,SEL,NSString *))objc_msgSend)((id)msg, @selector(hasArguments:),@"带参数但是无返回值");
    
    // 带返回值不带参数消息
    NSString *retValue = ((NSString * (*)(id,SEL))objc_msgSend)((id)msg,@selector(noArgumentsButReturnValue));
    NSLog(@"%@",retValue);
    
    // 带参数带返回值消息
    int returnValue = ((int (*)(id,SEL,NSString *,int))objc_msgSend)((id)msg,@selector(hasValue1:Value2:),@"参数",999);
    NSLog(@"return value is %d",returnValue);
    
    // 动态添加方法再调用  其中i代表返回类型int,@代表参数,:代表SEL
    class_addMethod(msg.class, NSSelectorFromString(@"cStyleFunc"), (IMP)cStyleFunc,"i@:");
    returnValue = ((int (*)(id,SEL,const void *,const void *))objc_msgSend)((id)msg,NSSelectorFromString(@"cStyleFunc"),"one","two");
    NSLog(@"return value is %d",returnValue);
    
    // 带返回浮点值的消息
    //objc_msgSend
    //float returnFloatValue =  ((float (*)(id,SEL))objc_msgSend)((id)msg,@selector(returnFloatType));
    //objc_msgSend_fpret
    float returnFloatValue =  ((float (*)(id,SEL))objc_msgSend_fpret)((id)msg,@selector(returnFloatType));
    NSLog(@"return float is %f",returnFloatValue);
    
    // 带结构体返回值的消息
    //objc_msgSend_stret
    CGRect frame = ((CGRect (*)(id,SEL))objc_msgSend_stret)((id)self,@selector(returnTypeIsStruct));
    NSLog(@"return frame is %@",NSStringFromCGRect(frame));

你可能感兴趣的:(Objective-C Runtime)