iOS-5.OC对象的本质之clang编译main.m

2,442 阅读2分钟

ios底层文章汇总

1. Clang简介

Clang是一个C语言、C++、Objective-C语言的轻量级编译器

Clang将支持其普通lambda表达式返回类型的简化处理以及更好的处理constexpr关键字。

它与GNU C语言规范几乎完全兼容(当然也有部分不兼容的内容, 包括编译命令选项也会有点差异),并在此基础上增加了额外的语法特性,

比如C函数重载 (通过__attribute__((overloadable))来修饰函数).

2. OC对象的clang编译

2.1 新建控制台工程

2.2 在main.m中定义类HTPerson和对象person


@interface HTPerson : NSObject
@property(nonatomic,strong)NSString *name;
@end

@implementation HTPerson

@end
int main(int argc, const char * argv[]) {
   @autoreleasepool {
       HTPerson *person = [[HTPerson alloc]init];
      
       NSLog(@"Hello, World!");
   }
   return 0;
}

2.3.终端使用clang编译main.m生成main.cpp

clang -rewrite-objc main.m -o main.cpp

UIKit报错问题 加入条件编译

clang -rewrite-objc -fobjc-arc -fobjc-runtime=ios-13.0.0 -isysroot / Applications/Xcode.app/Contents/Developer/Platforms/ iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.0.sdk main.m

Xcode安装的时候顺带安装了xcrun命令,xcrun命令在clang的基础上进行了 一些封装,要更好用一些

  • 模拟器可以使用:
xcrun -sdk iphonesimulator clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp 
  • 手机可以使用
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main- arm64.cpp 

2.4 打开main.cpp并找到HTPerson类

可以观察到HTPerson类编译成了一个struct,在OC中类的本质就是结构体(struct)

  • struct 的成员变量

struct NSObject_IMPL NSObject_IVARS和在HTPerson类中定义的属性_name.

其中struct NSObject_IMPL NSObject_IVARS就是我们常说的isa.

  • struct 的成员函数

    属性name的setter : _I_HTPerson_setName_

    属性name的getter方法 : _I_HTPerson_name-->objc_setProperty

注:属性的所有setter方法都会进入objc_setProperty方法中

#ifndef _REWRITER_typedef_HTPerson
#define _REWRITER_typedef_HTPerson
typedef struct objc_object HTPerson;
typedef struct {} _objc_exc_HTPerson;
#endif

extern "C" unsigned long OBJC_IVAR_$_HTPerson$_name;
struct HTPerson_IMPL {
	struct NSObject_IMPL NSObject_IVARS;
	NSString *_name;
};

// @property(nonatomic,copy)NSString *name;
/* @end */


// @implementation HTPerson


static NSString * _I_HTPerson_name(HTPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_HTPerson$_name)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);

static void _I_HTPerson_setName_(HTPerson * self, SEL _cmd, NSString *name) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct HTPerson, _name), (id)name, 0, 1); }

OC中的所有set方法,都会进objc_setProperty-->reallySetProperty

reallySetProperty源码解析: image.png

从.cpp代码中可以看到,OC代码

HTPerson *person = [HTPerson alloc];

编译器编译后可用C++代码替换为:

HTPerson *person = ((HTPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("HTPerson"), sel_registerName("alloc"));
//HTPerson *person = objc_msgSend(objc_getClass("HTPerson"), sel_registerName("alloc"));

其中self,_cmd是消息底层默认的两个参数id(消息接受者),SEL(消息编号)

objc_msgSend末尾可继续加参数,但是考虑到编译参数问题,我们需要关闭严格核查

2.5 对象方法调用

HTPerson *person = [HTPerson alloc];   
objc_msgSend(person,sel_registerName("sayNB"));
[person sayNB];

注意:

  • 1)直接调用objc_msgSend,需要导入头文件#import <objc/message.h>
  • 2)需要将target --> Build Setting -->搜索msg -- 将enable strict checking of obc_msgSend calls由YES 改为NO,关闭严格核查,否则objc_msgSend的参数会报错

2.6 对象方法调用(继承自父类的方法):objc_msgSendSuper

HTPerson *person = [HTPerson alloc];
HTTeacher *teacher = [HTTeacher alloc];
[person sayHello];

struct objc_super lgsuper;
lgsuper.receiver = person; //消息的接收者 person
lgsuper.super_class = [HTTeacher class]; //指定父类
objc_msgSendSuper(&lgsuper, sel_registerName("sayHello"));