Runtime - isa相关

  • Objective-C是一门动态性比较强的编程语言,跟C、C++等语言有着很大的不同
  • Objective-C的动态性是由Runtime API来支撑的
  • Runtime API提供的接口基本都是C语言的,源码由C/C++/汇编语言编写

isa详解

  • 要想学习Runtime,首先要了解它底层的一些常用数据结构,比如isa指针
  • 在arm64架构之前,isa就是一个普通的指针,存储着Class、Meta-Class对象的内存地址
  • 从arm64架构开始,对isa进行了优化,变成了一个共用体(union)结构,还使用位域来存储更多的信息

&可以用来取出特定的位,想取哪一位,哪一位与1取,其他位与0取
| 按位或,想让某一位设为1的话,找到某一位,按位或就可以了

声明一个Person类,有Bool类型的三个属性,分别为:tall、rich、handsome,如果直接用@property声明三个属性,那么需要开辟16个字节空间,代码如下:

Person.h文件
#import <Foundation/Foundation.h>
@interface Person : NSObject

@property (nonatomic,assign,getter=isTall)BOOL tall;
@property (nonatomic,assign,getter=isRich)BOOL rich;
@property (nonatomic,assign,getter=isHandsome)BOOL handsome;
@end
main文件
Person *person = [[Person alloc] init];
person.tall = YES;
person.rich = NO;
person.handsome = YES;
NSLog(@"%zd",class_getInstanceSize([Person class]));
打印结果:16

因为Bool类型都只有0或者1的结果,用1位就可以够用了,所以想实现把这三个属性用三个位存储,代码实现如下:

Person.h文件
#import <Foundation/Foundation.h>
@interface Person : NSObject
- (void)setTall:(BOOL)tall;
- (void)setRich:(BOOL)rich;
- (void)setHandsome:(BOOL)handsome;

- (BOOL)isTall;
- (BOOL)isRich;
- (BOOL)isHandsome;
@end
Person.m文件
#import "Person.h"

//掩码:一般用来按位与(&)运算的,十位进制不方便看,用二位进制
//#define TallMask 1
//#define RichMask 2
//#define HandsomeMask 4

//#define TallMask 0b00000001
//#define RichMask 0b00000010
//#define HandsomeMask 0b00000100
//或者
#define TallMask (1<<0)
#define RichMask (1<<1)
#define HandsomeMask (1<<2)
@interface Person ()
{
    char _tallRichHandsome;//0b 0000 0011,最后一位tall,倒数第二位rich,倒数第三位handsome
}
@end
@implementation Person
//- (instancetype)init{
    //if (self = [super init]) {
       // _tallRichHandsome = 0b00000111;//0b00000011;
    //}
    //return self;
//}
- (void)setTall:(BOOL)tall{
    if (tall){
//        _tallRichHandsome = _tallRichHandsome | TallMask;
        _tallRichHandsome |= TallMask;
    }else{
        _tallRichHandsome &= ~TallMask;//(按位取反是~)
    }
}
- (BOOL)isTall{
    return !!(_tallRichHandsome & TallMask);
}
- (void)setRich:(BOOL)rich{
    if (rich) {
        _tallRichHandsome |= RichMask;
    }else{
        _tallRichHandsome &= ~RichMask;
    }
}
- (BOOL) isRich{
    return !!(_tallRichHandsome & RichMask);
}
- (void)setHandsome:(BOOL)handsome{
    if (handsome) {
        _tallRichHandsome |= HandsomeMask;
    }else{
        _tallRichHandsome &= ~HandsomeMask;
    }
}
- (BOOL)isHandsome{
    return !!(_tallRichHandsome & HandsomeMask);
}
@end
main文件
Person *person = [[Person alloc] init];
person.tall = YES;
person.rich = NO;
person.handsome = YES;
NSLog(@"tall:%d rich:%d handsome:%d",person.isTall,person.isRich,person.isHandsome);

还可以使用位域实现:

Person.h文件
#import <Foundation/Foundation.h>
@interface Person : NSObject
- (void)setTall:(BOOL)tall;
- (void)setRich:(BOOL)rich;
- (void)setHandsome:(BOOL)handsome;

- (BOOL)isTall;
- (BOOL)isRich;
- (BOOL)isHandsome;
@end
Person.m文件
#import "Person.h"

#define TallMask (1<<0)
#define RichMask (1<<1)
#define HandsomeMask (1<<2)
@interface Person ()
{
//    char _tallRichHandsome;//0b 0000 0011,最后一位tall,倒数第二位rich,倒数第三位handsome
    //位域
    struct {
        char tall : 1;
        char rich : 1;
        char handsome : 1;
    } _tallRichHandsome;//0b0000 0000,结构体里边,先写的变量会自动排到最后边,所以最后边是tall,倒数第二位是rich,倒数第三位是handsome
}
@end
@implementation Person
- (void)setTall:(BOOL)tall{
    _tallRichHandsome.tall = tall;
}
- (BOOL)isTall{
    return !!_tallRichHandsome.tall;
    //_tallRichHandsome.tall的结果是只有一个二进制位的0b1,但是函数的返回值是bool类型的,是需要一个字节,也就是八位的二进制比如0b0000 0000,那么将一个二进制位的强制转成8位的,就会出现问题,其他的位全用1去覆盖了,返回值就变成了-1,所以需要两次取反
}
- (void)setRich:(BOOL)rich{
    _tallRichHandsome.rich = rich;
}
- (BOOL) isRich{
    return !!_tallRichHandsome.rich;
}
- (void)setHandsome:(BOOL)handsome{
    _tallRichHandsome.handsome = handsome;
}
- (BOOL)isHandsome{
    return !!_tallRichHandsome.handsome;
}
@end
main文件
Person *person = [[Person alloc] init];
person.tall = NO;
person.rich = YES;
person.handsome = NO;
NSLog(@"tall:%d rich:%d handsome:%d",person.isTall,person.isRich,person.isHandsome);

结构体和共用体的区别:
结构体内各变量之间内存独立,共用体内各变量共用一块内存。
结构体:

struct Date{
    int year;
    int month;
    int day;
};
结构体内存

共用体

union Date{
    int year;
    int month;
    int day;
};
共用体内存
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        union Date date;
        date.year = 2011;
        date.month = 11;
        date.day = 10;
        NSLog(@"union : year = %d month = %d day = %d",date.year,date.month,date.day);
 打印结果:
 union : year = 10 month = 10 day = 10

        struct Date1 date1;
        date1.year = 2022;
        date1.month = 12;
        date1.day = 30;
        NSLog(@"struct : year = %d month = %d day = %d",date1.year,date1.month,date1.day);
打印结果:
struct : year = 2022 month = 12 day = 30
}
    return 0;
}

如上,Person的属性赋值取值实现也用union写一下,

Person.h文件
#import <Foundation/Foundation.h>
@interface Person : NSObject
//@property (nonatomic,assign,getter=isTall)BOOL tall;
//@property (nonatomic,assign,getter=isRich)BOOL rich;
//@property (nonatomic,assign,getter=isHandsome)BOOL handsome;

- (void)setTall:(BOOL)tall;
- (void)setRich:(BOOL)rich;
- (void)setHandsome:(BOOL)handsome;

- (BOOL)isTall;
- (BOOL)isRich;
- (BOOL)isHandsome;
@end
Person.m文件
#import "Person.h"
#define TallMask (1<<0)
#define RichMask (1<<1)
#define HandsomeMask (1<<2)
@interface Person ()
{
    //位域
    union{
        char bits;
        struct {
            char tall : 1;
            char rich : 1;
            char handsome : 1;
        };//当前的struct只是增加可读性,没有什么实质性作用,因为去掉当前的struct整个的内容,也不影响代码运行效果
    } _tallRichHandsome;
}
@end
@implementation Person

- (void)setTall:(BOOL)tall{
    if (tall) {
        _tallRichHandsome.bits |= TallMask;
    }else{
        _tallRichHandsome.bits &= ~TallMask;
    }
}
- (BOOL)isTall{
    return  !!(_tallRichHandsome.bits & TallMask);
}
- (void)setRich:(BOOL)rich{
    if (rich) {
        _tallRichHandsome.bits |= RichMask;
    }else{
        _tallRichHandsome.bits &= ~RichMask;
    }
}
- (BOOL) isRich{
    return  !!(_tallRichHandsome.bits & RichMask);
}
- (void)setHandsome:(BOOL)handsome{
    if (handsome) {
        _tallRichHandsome.bits |= HandsomeMask;
    }else{
        _tallRichHandsome.bits &= ~HandsomeMask;
    }
}
- (BOOL)isHandsome{
    return  !!(_tallRichHandsome.bits & HandsomeMask);
}
@end
main文件
Person *person = [[Person alloc] init];
person.tall = NO;
person.rich = YES;
person.handsome = NO;
NSLog(@"tall:%d rich:%d handsome:%d",person.isTall,person.isRich,person.isHandsome);

如下是苹果对isa的官方文档:

所以arm64之后,isa占用8个字节,不仅仅只放地址值,还有很多信息等。

isa详解 - 位域
  • nonpointer
    0 ,代表普通的指针,存储着Class、Meta-Class对象的内存地址
    1,代表优化过,使用位域存储更多的信息
  • hsa_assoc
    是否有设置过关联对象,如果没有,释放时会更快
  • has_cxx_dtor
    是否有C++的析构函数(.cxx_destruct),如果没有,释放时会更快
  • shiftcls
    存储着Class、Meta-Class对象的内存地址信息
  • magic
    用于在调试时分辨对象是否完成初始化
  • weakly_referenced
    是否有被弱引用指向过,如果没有,释放时会更快
  • deallocating
    对象是否正在释放
  • extra_rc
    里面存储的值是引用计数器减1
  • has_sidetable_rc
    引用计数器是否过大无法存储在isa中
    如果为1,那么引用计数会存储在一个叫SideTable的类的属性中
位运算补充
#import "ViewController.h"
typedef enum {
    OptionsOne = 1 << 0,    //0b0001
    OptionsTwo = 1 << 1,    //0b0010
    OptionsThree = 1<<2,    //0b0100
    OptionsFour = 1 << 3    //0b1000
}Options;
@interface ViewController ()

@end
@implementation ViewController
- (void)setOptions:(Options)options{
    if (options & OptionsOne) {
        NSLog(@"包含了OptionsOne");
    }
    if (options & OptionsTwo) {
        NSLog(@"包含了OptionsTwo");
    }
    if (options & OptionsThree) {
        NSLog(@"包含了OptionsThree");
    }
    if (options & OptionsFour) {
        NSLog(@"包含了OptionsFour");
    }
}
- (void)viewDidLoad {
    [super viewDidLoad];
    [self setOptions:OptionsOne | OptionsTwo | OptionsFour];

    self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
}
@end

源代码

typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {
    UIViewAutoresizingNone                 = 0,
    UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,
    UIViewAutoresizingFlexibleWidth        = 1 << 1,
    UIViewAutoresizingFlexibleRightMargin  = 1 << 2,
    UIViewAutoresizingFlexibleTopMargin    = 1 << 3,
    UIViewAutoresizingFlexibleHeight       = 1 << 4,
    UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};

由此可见,源代码的实现思路和枚举Options的思路是一致的。

Class的结构

元类对象是一种特殊的类对象,数据结构都是一样的,只是里边存储的数据不一样,都是Class类型。


Class的结构
class_rw_t
  • class_rw_t里面的methods、properties、protocols是二维数组,是可读可写的,包含了类的初始内容、分类的内容
class_ro_t
  • class_ro_t里面的baseMethodList、baseProtocols、ivars、baseProperties是一维数组,是只读的,包含了类的初始内容
method_t
  • method_t是对方法/函数的封装
  • IMP代表函数的具体实现
    typedef id _Nullable (*IMP)(id _Nonnull,SEL _Nonnull, ...);
  • SEL代表方法/函数名,一般叫做选择器,底层结构跟char *类似:可以通过@selector()sel_registerName()获得;可以通过sel_getName()NSStringFromSelector()转成字符串;不同类中相同名字的方法,所对应的方法选择器是相同, typedef struct objc_selector *SEL;
  • types包含了函数返回值、参数编码的字符串,实例化一个Person类,随便写一个对象方法,打断点之后lldb打印出types的值:v 16 @ 0 :8,这里边v,@,:等的意思可以看下边的解释。16为所有参数所占的字节数,@0:从0字节开始, :8 :SEL为8开始,
Type Encoding
  • iOS中提供了一个叫做@encode的指令,可以将具体的类型表示成字符串编码。
NSLog(@"打印结果=%s",@encode(int));//打印结果:打印结果=I
NSLog(@"打印结果=%s",@encode(id));//打印结果:打印结果=@
NSLog(@"打印结果=%s",@encode(SEL));//打印结果:打印结果=:
方法缓存
  • Class内部结构中有个方法缓存(cache_t),用散列表(哈希表)来缓存曾经调用过的方法,可以提高方法的查找速度

    在往buckets里边存调用过的函数时,并不是下标0开始逐一往里存储,存的时候,就是用@selector(方法名) & _mask = 索引下标,索引下标是几就把这个值存到第几个上,如下图:

    那么存在一种情况是:不同的方法名 & 不同的_mask取得的索引下标可能存在相同的情况,苹果的底层源码是这样处理的,如果索引下标已有存储的值,那么就把&的值存在(索引-1)的下标位置,等到取的时候,方法名&_mask=索引下标,判断不等于key的话就-1,再比较,不相等的话就一直-1下去,直到相等。
    最开始会给散列表一个长度,如果长度不够的话就进行扩容,扩容时,清空之前的缓存。
  • 缓存查找:objc-cache.mm;bucket_t * cache_t::find(cache_key_t k, id receiver)

objc_msgSend

Person.h文件
#import <Foundation/Foundation.h>
@interface Person : NSObject
- (void)personTest;
@end
Person.m文件
#import "Person.h"

@implementation Person
-(void)personTest{
    NSLog(@"%s",__func__);
}
@end
#import <Foundation/Foundation.h>
#import "GoodStudent.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *person = [[Person alloc] init];
        [person personTest];
    }
    return 0;
}

在终端cd 到main文件所在的位置,输入xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp生成c++文件,拖入到工程中不进行编译,会发现 [person personTest];代码在c++文件中是
((void (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("personTest"))去掉一些影响学习的强转符号,就变成了objc_msgSend(person,sel_registerName("personTest"));代码,sel_registerName("personTest")是#import <objc/runtime.h>中的方法


在上图中可以看出sel_registerName("")方法需要一个c语言的字符串参数,返回值是SEL,所以sel_registerName("personTest") == @selector(personTest)

Person *person = [[Person alloc] init];
[person personTest];
NSLog(@"%p %p",sel_registerName("personTest"),@selector((personTest)));
打印结果:0x100003f96 0x100003f96

地址是一样的,说明sel_registerName("personTest") == @selector(personTest)

现在再调用一下[Person initialize];方法,在终端中刚刚的位置用xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp重新生成一下,再看c++的文件,

((void (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("personTest"));
((void (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("initialize"));

同样的,去掉一些强转的符号:

objc_msgSend(person, sel_registerName("personTest"));等同于
objc_msgSend(person, @selector(personTest));
消息接收者(receiver):person
消息名称:personTest
objc_msgSend)(objc_getClass("Person"), sel_registerName("initialize"));等同于
objc_msgSend)([Person class], @selector(initialize));
消息接收者(receiver):[Person class]
消息名称:initialize
//所以OC的方法调用也被称为:消息机制,给方法调用者发送消息
objc_msgSend执行流程
  • OC中的方法调用,其实都是转换为objc_msgSend函数的调用
  • objc_msgSend的执行流程可以分为3大阶段:
    消息发送
    动态方法解析
    消息转发
objc_msgSend执行流程 - 源码跟读

objc_msgSend底层代码是用汇编实现的,(底层原理代码,如果调用频次比较高的方法,很多都是用汇编编写,效率更高)

objc_msgSend执行流程01 - 消息发送
  • 如果是从class_rw_t中查找方法:已经排序的,用二分查找,没有排序的,遍历查找
  • receiver通过isa指针找到receiverClass
  • receiverClass通过superclass指针找到superclass
objc_msgSend执行流程02 - 动态方法解析
Person.h文件
#import <Foundation/Foundation.h>
@interface Person : NSObject
- (void)test;
@end

Person.m文件(第一种写法)
#import "Person.h"
#import <objc/runtime.h>
@implementation Person
//- (void)test{
//    NSLog(@"%s",__func__);
//}

- (void)other{
    NSLog(@"%s",__func__);
}
struct method_t {
    SEL sel;
    char *types;
    IMP imp;
};
+ (BOOL)resolveInstanceMethod:(SEL)sel{

    if (sel == @selector(test)) {
        //获取其他方法
        struct method_t *method = (struct method_t *) class_getInstanceMethod(self, @selector(other));
        //动态添加test方法的实现
        class_addMethod(self, sel, method->imp, method->types);
        //返回YES代表有动态添加方法
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}
@end

Person.m文件(第二种写法)
#import "Person.h"
#import <objc/runtime.h>
@implementation Person
//- (void)test{
//    NSLog(@"%s",__func__);
//}
- (void)other{
    NSLog(@"%s",__func__);
}
+ (BOOL)resolveInstanceMethod:(SEL)sel{
    if (sel == @selector(test)) {
        //获取其他方法
        Method method = class_getInstanceMethod(self, @selector(other));
        //动态添加test方法的实现
        class_addMethod(self, sel, method_getImplementation(method), method_getTypeEncoding(method));
        //返回YES代表有动态添加方法
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}
@end

如果person类里边是类方法,不是对象方法,写法如下:

Person.h文件
#import <Foundation/Foundation.h>
@interface Person : NSObject
+ (void)test;
@end

Person.m文件
#import "Person.h"
#import <objc/runtime.h>
@implementation Person
void c_other(id self,SEL _cmd){
    NSLog(@"c_other - %@ - %@",self,NSStringFromSelector(_cmd));
}
+ (BOOL)resolveClassMethod:(SEL)sel{
    if (sel == @selector(test)) {
        //第一个参数是object_getClass(self)
        class_addMethod(object_getClass(self), sel, (IMP)c_other,"v16@0:8");
    }
    return [super resolveClassMethod:sel];
}
@end

那么如果没有实现+ (BOOL)resolveInstanceMethod:(SEL)sel方法或者+ (BOOL)resolveClassMethod:(SEL)sel方法,或者在这两个方法里没有动态添加方法,那么就进入第三个流程:消息转发

objc_msgSend的执行流程03 - 消息转发

消息转发:将消息转发给别人


  • 开发者可以在forwardInvocation:方法中自定义任何逻辑
  • 以上方法都有对象方法、类方法两个版本(前面可以是加号+,也可以是减号-)
    上图中所示逻辑代码实现如下:
Cat.h文件
#import <Foundation/Foundation.h>
@interface Cat : NSObject
- (void)test;
@end

Cat.m文件
#import "Cat.h"
@implementation Cat
-(void)test{
    NSLog(@"%s",__func__);  
}
@end
Person.h文件
#import <Foundation/Foundation.h>
@interface Person : NSObject
- (void)test;
@end

Person.m文件
#import "Person.h"
#import <objc/runtime.h>
#import "Cat.h"
@implementation Person
-(id)forwardingTargetForSelector:(SEL)aSelector{
    if (aSelector == @selector(test)) {
        //objc_msgSend([[Cat alloc] init],aSelector)
        return [[Cat alloc] init];
    }
    return [super forwardingTargetForSelector:aSelector];
}
@end

但是如果-(id)forwardingTargetForSelector:(SEL)aSelector方法不实现或者返回值为nil,则- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector方法

Person.m文件
#import "Person.h"
#import <objc/runtime.h>
#import "Cat.h"
@implementation Person
//方法签名:返回值类型、参数类型
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    if (aSelector == @selector(test)) {
        return [NSMethodSignature signatureWithObjCTypes:"v16@0:8"];
    }
    return [super methodSignatureForSelector:aSelector];
}

//NSInvocation封装了一个方法调用,包括:方法调用者、方法名、方法参数
//anInvocation.target 方法调用者
//anInvocation.selector方法名
//[anInvocation getArgument:NULL atIndex:0]
- (void)forwardInvocation:(NSInvocation *)anInvocation{
//    anInvocation.target = [[Cat alloc] init];
//    [anInvocation invoke];
    [anInvocation invokeWithTarget:[[Cat alloc] init]];
     开发者可以在```forwardInvocation:```方法中自定义任何逻辑的意思是:不一定非要用anInvocation返回一个对象,也可以直接只打印,比如在这个方法里只写一句NSLog(@"abc------");代码,那么person在调用test方法之后,只会打印一句abc------。
}
@end

那么如果test方法有一个参数的情况下,用方法签名的实现:

Person.h文件
#import <Foundation/Foundation.h>
@interface Person : NSObject
- (void)test:(int)age;
@end

Person.m文件
#import "Person.h"
#import <objc/runtime.h>
#import "Cat.h"
@implementation Person
//方法签名:返回值类型、参数类型
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    if (aSelector == @selector(test)) {
        return [NSMethodSignature signatureWithObjCTypes:"v20@0:8i16"];
        //或者直接写成 return [NSMethodSignature signatureWithObjCTypes:"v@:i"];
    }
    return [super methodSignatureForSelector:aSelector];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation{
    //参数顺序:receiver、selector、other arguments
    int age;
    [anInvocation getArgument:&age atIndex:2];
    NSLog(@"%d",age + 10);
}
@end

下面处理一下类方法:

Cat.h文件
#import <Foundation/Foundation.h>
@interface Cat : NSObject
+ (void)test;
@end

Cat.m文件
#import "Cat.h"

@implementation Cat
+ (void)test{
    NSLog(@"%s",__func__);  
}
@end
Person.h文件
#import <Foundation/Foundation.h>
@interface Person : NSObject
+(void)test;
@end

Person.m文件
#import "Person.h"
#import <objc/runtime.h>
#import "Cat.h"
@implementation Person

+ (id)forwardingTargetForSelector:(SEL)aSelector{
    if (aSelector == @selector(test)) return [Cat class];
    return [super forwardingTargetForSelector:aSelector];
}
@end

或者+ (id)forwardingTargetForSelector:(SEL)aSelector方法不实现或者返回值为nil

Person.m文件
#import "Person.h"
#import <objc/runtime.h>
#import "Cat.h"
@implementation Person
+ (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    if (aSelector == @selector(test)) return [NSMethodSignature signatureWithObjCTypes:"v@:"];
    return [super methodSignatureForSelector:aSelector];
}

+ (void)forwardInvocation:(NSInvocation *)anInvocation{
    NSLog(@"123......");
}
@end

dynamic:提醒编译器不要自动生成setter方法和getter方法的实现、不要自动生成成员变量。正常情况下,如果不写dynamic关键字,代码如下:

Person.h文件
#import <Foundation/Foundation.h>
@interface Person : NSObject
@property (nonatomic,assign) int age;
@end

Person.m文件
#import "Person.h"
#import <objc/runtime.h>
@implementation Person

@end
main文件
Person *person = [[Person alloc] init];
person.age = 10;
NSLog(@"age is = %d",person.age);
打印结果:age is = 10

如果用dynamic修饰,代码如下:

Person.h文件
#import <Foundation/Foundation.h>
@interface Person : NSObject
@property (nonatomic,assign) int age;
@end

Person.m文件
#import "Person.h"
#import <objc/runtime.h>
@implementation Person
@dynamic age;
@end
main文件
Person *person = [[Person alloc] init];
person.age = 10;
NSLog(@"age is = %d",person.age);
打印结果:直接崩掉interview-cache[83420:7465918] -[Person setAge:]: unrecognized selector sent to instance 0x108f71930
LLVM的中间代码(IR)

OC - > 中间代码(.ll)-> 汇编、机器代码

  • Objective-C在变为机器代码之前,会被LLVM编译器转换为中间代码(Intermediate Representation)
  • 可以使用以下命令行指令生成中间代码
    clang -emit-llvm -S main.m
  • 语法简介
    @ - 全局变量
    % - 局部变量
    alloca - 在当前执行的函数的堆栈帧中分配内存,当该函数返回到其调用者时,将自动释放内存
    i32 - 32位4字节的整数
    align - 对齐
    load - 读出,store写入
    icmp - 两个整数值比较,返回布尔值
    br - 选择分支,根据条件来转向label,不根据条件跳转的话类似goto
    label - 代码标签
    call - 调用函数
    具体可以参考官方文档:https://llvm.org/docs/LangRef.html
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容