1、介绍下内存的几大区域?
- 栈区(stack)由编译器自动分配并释放,存放函数的参数值、局部变量等。栈是系统数据结构,对应线程/进程是唯一的。优点是快速高效,缺点是有限制,数据不灵活(先进后出)
- 栈空间分静态分配和动态分配两种。
静态分配是编译器完成的,比如总动变量(auto)的分配。
动态分配是由alloca 函数完成。
栈的动态分配无需释放(是自动的),也就是说没有释放函数
为可移植的程序期间,栈的动态分配操作是不被鼓励的!
- 堆区(heap)由程序员分配和释放,如果程序员不释放,程序结束时,可能会由操作系统回收、比如在iOS中alloc都是存放在堆中。
- 优点是灵活方便,数据适用面广泛,但是效率有一点降低。
堆是函数库内部数据结构,不一定唯一。
不同堆分配的内存无法互相操作
堆空间的分配总是动态的。
虽然程序结束时所有的数据空间都会被释放会系统,但是精确的申请内存、释放内存匹配是良好程序的基本要素。
- 全局区(静态区)(static)全局变量和静态变量的存储是放在一起的,初始化的全局变量和静态变量存放在一块区域,未初始化的全局变量和静态变量在相邻的另一块区域,程序结束后由系统释放。
注意:全局区又可分为未初始化全局区:.bss段 和初始化全局区:data段
举例: int a;未初始化。
int a = 10;已初始化的
- 文字常量区: 存放常量字符串,程序结束后由系统释放
- 代码区: 存放函数的二进制代码
- 代码例子
int a = 10; 全局初始化区
char *p; 全局未初始化区main{
int b; 栈
char s[] = “abc”; 栈
char *p1; 栈
char *p2 = “123456”; p2在栈上,123456在常量区
static int c = 0; 全局(静态)初始化区w1 = (char *)malloc(10);
w2 = (char *)malloc(10);
分配得来的10和20字节的区域都在堆区。
}
2、你是如何组件化解耦的?
3、runtime如何通过selector找到对应的IMP地址?
概述
类对象中有类方法和实例方法的类表,列表中记录着方法的名字、参数和实现,而selector本质就是方法名称,runtime通过这个方法名称就可以在列表中找到该方法对应的实现。
这里声明了一个指向struct objc_method_list 指针的指针,可以包含类方法类表和实例方法列表
具体实现
在寻找IMP的地址时,runtime提供了两种方法
IMP class_getMethodImplementation(Class cla, SEL name);
IMP method_getImplementation(Method m)
类方法(假设有一个类A)
class_getMethodImplementation(objc_getMetaClass(“A”),@selector(methodName));
实例方法
class_getMethodImplementation([A class],@selector(methodName));
通过传入的参数不同,找到不同的方法列表,方法列表中保存着下面方法的结构体,机构体中包含着方法的实现,selector本质就是方法的名称,通过该方法名称,即可在结构体中找到相应的实现。
struct objc_method {SEL method_namechar *method_typesIMP method_IMP}
而对于第二种方法,传入的参数只有method,区分类方法和实例方法在于封装method的函数
类方法
Method class_getClassMethod(Class cls, SEL name)
实例方法
Method class_getInstanceMethod(Class cls, SEL name)
最后调用IMP method_Implementation(Method m) 获取IMP地址
实验
- (void)viewDidLoad {
[super viewDidLoad];
Test *test1 = [[Test alloc] init];
Test *test2 = [[Test alloc] init];
}
- (instancetype)init {
self = [super init];
if (self) {
[self getIMPFromSelector:@selector(aaa)];
[self getIMPFromSelector:@selector(test1)];
[self getIMPFromSelector:@selector(test2)];
}
return self;
}
- (void)test1 {
NSLog(@"test1");
}
+ (void)test2 {
NSLog(@"test2");
}
- (void)getIMPFromSelector:(SEL)aselector {
//first method
IMP instanceIMP1 = class_getMethodImplementation(objc_getClass("Test"), aselector);//实例方法
IMP classIMP1 = class_getMethodImplementation(objc_getMetaClass("Test"), aselector);//类方法
//second method
Method instanceMethod = class_getInstanceMethod(objc_getClass("Test"), aselector);
IMP instanceIMP2 = method_getImplementation(instanceMethod);
Method classMethod1 = class_getClassMethod(objc_getClass("Test"), aselector);
IMP classIMP2 = method_getImplementation(classMethod1);
Method classMethod2 = class_getClassMethod(objc_getMetaClass("Test"), aselector);
IMP classIMP3 = method_getImplementation(classMethod2);
Method instanceMethod3 = class_getInstanceMethod(objc_getMetaClass("Test"), aselector);
IMP classIMP4 = method_getImplementation(instanceMethod3);
NSLog(@"instance1 = %p instance2 = %p class1 = %p class2 = %p class3 = %p class4 = %p",instanceIMP1,instanceIMP2,classIMP1,classIMP2,classIMP3,classIMP4);
这里有一个叫Test的类,在初始化方法中,调用了两次getIMPFromSelector方法,第一个aaa方法是不存在的,test1是实例方法,test2是类方法。
然后实例化两个Test对象,打印数据如下:
instance1 = 0x10a185600 instance2 = 0x0 class1 = 0x10a185600 class2 = 0x0 class3 = 0x0 class4 = 0x0
instance1 = 0x10986c210 instance2 = 0x10986c210 class1 = 0x10a185600 class2 = 0x0 class3 = 0x0 class4 = 0x0
instance1 = 0x10a185600 instance2 = 0x0 class1 = 0x10986c240 class2 = 0x10986c240 class3 = 0x10986c240 class4 = 0x10986c240
instance1 = 0x10a185600 instance2 = 0x0 class1 = 0x10a185600 class2 = 0x0 class3 = 0x0 class4 = 0x0
instance1 = 0x10986c210 instance2 = 0x10986c210 class1 = 0x10a185600 class2 = 0x0 class3 = 0x0 class4 = 0x0
instance1 = 0x10a185600 instance2 = 0x0 class1 = 0x10986c240 class2 = 0x10986c240 class3 = 0x10986c240 class4 = 0x10986c240
该地址0x10a185600一共出现了8次,这是因为在调用class_getMethodImpeletation()方法时,无法找到对应实现时返回的相同的一个地址,无论该方法是类方法还是实例方法,返回的地址是相同的,但是每次运行时返回的地址不相同。
对于另一种方法,如果找不到对应实现,会返回0;
还有一点:class_getClassMethod()方法的第一个参数无论传入objc_getClass()还是objc_getMetaClass(),最终调用method_getImplementation()都可以成功的找到类方法的实现;
而class_getInstanceMethod()的第一个参数如果传入objc_getMetaClass(),再调用method_getImplementation()时无法找到实例方法的实现却可以找到类方法的实现。
4、runloop内部实现逻辑? runLoop详解
A run loop receices events from tso different types of sources.input sources deliver asynchoronous events,usually messages from another thread or from a different application. Timer sources deliver synchronous events,occurring at a scheduled time or repeating interval.Both types of source use an application-specific handler routine to process the event when it arrives.
shows the conceptual structure of a run loop and a variety of sources.The input sources deliver asynchronous events to the corresponding handlers and cause the runUntiDate:method(called on the thread's associated NSRunLoop object) to exit. Timer sources deliver events to their handler routines but do not cause the run loop to exit.
runloop.jpg
翻译
运行循环接收来自不同类型的源的事件。输入源提供异步事件,通常来自另一线程或来自不同应用程序的消息。 计时器源提供同步事件,发生在计划时间或重复间隔。两种类型的源使用特定于应用程序的处理程序来处理事件到达时。
下图显示了运行循环和各种源的概念结构。输入源将异步事件传递给相应的处理程序,并导致runUntiDate:方法(在线程的关联NSRunLoop对象上调用)退出。 计时器源将事件传递到其处理程序例程,但不会导致运行循环退出。
实际上 RunLoop 就是这样一个函数,其内部是一个 do-while 循环。当你调用 CFRunLoopRun() 时,线程就会一直停留在这个循环里;直到超时或被手动停止,该函数才会返回。
苹果用 RunLoop 实现的功能。
- kCFRunLoopDefaultMode: App的默认 Mode,通常主线程是在这个 Mode 下运行的。
- UITrackingRunLoopMode: 界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响。
- UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用。
- GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到。
- kCFRunLoopCommonModes: 这是一个占位的 Mode,没有实际作用。
网友评论