selector

iOS:走近Mach-O文件之Objc代码存储

2020-05-29  本文已影响0人  笑出zhu声

在iOS中Mach-O文件主要有以下三种:

Mach-O文件的格式一般包括一个Mach-O头,一系列的载入命令,一个或多个Section,如下图:

Mach-O文件格式

Section

Section段存放的内容通过MachOView工具查看主要有两大类:__Text__DATA

而不管是__Text还是__DATA下面还都包含若干子项,为了弄清楚每个子项中存放的内容,我们编写一个测试工程,里面尽量包含Objc语音的特性:类,继承,分类,协议,实例方法,类方法等

Person.h

@interface Person : NSObject

- (void)sleep;
- (void)walk;

@end


Person.m

NSString *string1 = @"it is string1";
NSString *string2;

@implementation Person

- (void)sleep {
    NSLog(@"i am sleeping");
}

- (void)walk {
    printf("i am walking");
}

+ (void)grow {
    static NSString *string3 = @"it is string3";
    static NSString *string4;
    
    NSLog(@"%@_%@",string3,string4);
}
@end

Person类增加分类:

Person+other.h

@interface Person (other)

- (void)ill;

@end

Person+other.m

@implementation Person (other)
- (void)ill {
    NSLog(@"i am fall ill");
}
@end

添加StudentProtocol协议:

@protocol StudentProtocol <NSObject>
- (void)study;
+ (void)playgame;
@end

最后实现Person类的子类Student,同时实现StudentProtocol协议:

Student.h

@interface Student : Person <StudentProtocol>

@end

Student.m

@implementation Student
- (void)study {
    NSLog(@"i am studying");
}

+ (void)playgame {
    NSLog(@"i am playing game");
}

@end

为了防止Xcode的dead code 的优化,我们在main函数里面显示调用下上面的类/方法:

int main(int argc, const char * argv[]) {
    
    Person *p = [[Person alloc] init];
    [p sleep];
    [p walk];
    [p ill];
    
    Student *student = [[Student alloc] init];
    [student study];
    [Student playgame];
    return 0;
}

同时,为了保持工程的简洁,我们创建的是Command Line Tool 工程。将生成的可执行文件用MachOView打开:

可执行文件
下面我们看下每个子项存储的内容:

图中所示,__TEXT,__text存放的为具体的方法实现,如Person类的sleep和walk方法,MachOView 已经帮我们翻译成汇编代码。引用的外部符号如NSLog记录在偏移地址0x100001C98的位置,而0x100001C98__TEXT,__stubs中,作为App启动时动态链接的辅助,动态链接的内容我们以后介绍:

__TEXT,__stubs

很好理解,__cstring中保存的是c字符串,比如sleep方法中引用的 "i am sleeping"。那么,__TEXT,__cfstring保存的是什么内容呢?

__cfstring

从上图可知,实际上__TEXT,__cfstring中保存的是Objc字符串,包括类型,值,大小,如从偏移地址00002058开始记录了Objc字符串@"i am sleeping"的信息,类型是__CFConstantStringClassReference,大小为13,保存的值为0x100001CF6,从__cstring中可查到0x100001CF6指向的正是"i am sleeping"。从上面的分析看一个Objc字符串占用的空间比c字符串多个块__TEXT,__cfstring的存储。

__DATA,__objc_classlist中保存了自定义的类,而类的具体信息在__DATA,__objc_data中:

image.png
0x100002718的地址保存了Person类的信息:ISA、父类、缓存、虚表等。

协议的信息保存的很详细,协议没有ISA指针,同时对实例方法,类方法,是不是optional的都进行了记录。

__DATA,__objc_const记录了方法、协议、属性列表和类概要信息。

记录了被使用的类和方法,因为Objc是动态语言,这里只记录了被显式使用的类和方法。

以上是Objc代码在可执行文件中的存储方式,了解代码的存储方式可以让我们更深入的理解Objc的运行时,同时给我们优化可执行文件大小以及解决Appstore对__TEXT段大小限制提供思路:

-Wl,-rename_section,__TEXT,__objc_classname,__RODATA,__objc_classname
-Wl,-rename_section,__TEXT,__objc_methtype,__RODATA,__objc_methtype
-Wl,-rename_section,__TEXT,__objc_methname,__RODATA,__objc_methname
-Wl,-rename_section,__TEXT,__cstring,__RODATA,__cstring
-Wl,-rename_section,__TEXT,__const,__RODATA,__const
上一篇 下一篇

猜你喜欢

热点阅读