- 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_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