先自我介绍一下,小编浙江大学结业,去过华为、字节跳动等大厂,目前阿里P7
深知大多数步伐员,想要提拔技能,每每是自己摸索成长,但自己不成体系的自学结果低效又漫长,而且极易碰到天花板技术故步自封!
因此网络整理了一份《2024年最新Web前端全套学习资料》,初衷也很简朴,就是盼望能够帮助到想自学提拔又不知道该从何学起的朋友。
既有得当小白学习的零底子资料,也有得当3年以上经验的小同伴深入学习提拔的进阶课程,涵盖了95%以上前端开辟知识点,真正体系化!
由于文件比较多,这里只是将部分目次截图出来,全套包罗大厂面经、学习笔记、源码讲义、实战项目、大纲路线、解说视频,并且后续会持续更新
如果你必要这些资料,可以添加V获取:vip1024c (备注前端)
正文
你发现了这个函数许多汇编代码是多余的。因为一开始,我们的编译器就是Debug(调试)模式,没有任何编译器优化的。如果你把编译优化打开,你会得到一个非常精简的代码。
改变 Show Assembly Output For 到选择器到 存档(Xcode7不一样,区别是如果你想获取精简的汇编,必要Edit Scheme成Release模式,另外切换后记得clean下工程)。搜索_addFunction:,你可以看到如下代码:
_addFunction: add r0, r1 bx lr
这非常简洁! 可以看到仅仅两个指令就写完了这个函数。你可能没想到仅用两个指令就完成了~ 当然,你平时写的函数一样平常更长也更有趣点~
如今你已经有一个以返回到调用者分支为结束的函数。那么作为一个相互关系的另一个函数,调用该函数的调用者呢?
调用函数
首先,你必要给addFunction函数添加一个让编译器不做优化的属性。你已经发现如果我们开启优化,那么代码会移撤除不必要的指令,乃至连函数调用都会被移除,或者可能直接将函数作为内联函数使用。
例如,编译器可能直接用add指令代替函数调用。现实上,编译器是非常强大智能的,它可能直接帮你计算好了相加后的值,连add指令都不必要生成。
这个教程,我们不盼望编译器做优化或者将函数内联。回到main.m文件,修改函数成如下:
attribute((noinline))
int addFunction(int a, int b) {
int c = a + b;
return c;
}
继续在下方添加另一个函数如下:
void fooFunction() { int add = addFunction(12, 34); printf(“add = %i”, add); }
fooFunction函数简朴地用addFunction让12和34相加并且打印出值。这里使用的是C语言的printf而不是Objective-C的NSLog函数的原因是后者的汇编结果更加复杂。
再一次生成汇编代码,搜索_fooFunction,你可以看到如下代码:
fooFunction: @ 1: push {r7, lr} @ 2: movs r0, #12 movs r1, #34 @ 3: mov r7, sp @ 4: bl addFunction @ 5: mov r1, r0 @ 6: movw r0, :lower16L.str-(LPC1_0+4)) movt r0, :upper16L.str-(LPC1_0+4)) LPC1_0: add r0, pc @ 7: blx _printf @ 8: pop {r7, pc}
这里引入了一些教程之前没有介绍过的指令,但不消担心,他们并不复杂,我们来看:
- 这个指令跟我们之前的add sp, #12指令做的事变差不多。这里,r7和lr被推入到栈,意味着sp(栈指针)减掉了8字节(栈指针始终指向栈顶,以是在push的时间会变小),因为r7和lr都是4字节。注意到栈指针变小了而且通过一个指令存储了两个值。r7的值必要存储起来的原始是之后函数实验时它会被使用到并重写。lr被存储的原因在函数最后将会知晓;
- 这两个指令(mov)是Move组的成员之一。有时间你会看到movs,或者movw,或者其他,但他们的作用都是用一个值来添补寄存器。你可以将一个寄存器的值移动到另一个寄存器,因此mov r0, r1会将r1寄存器内容添补到r0,r1的值稳固。在这两行代码中,r0和r1是用函数中定义的两个常量赋值的。注意到他们是被加载到r0和r1中,刚好是addFunction的入参。
- 在函数调用边界时,栈指针应该被生存起来,因此作为可存储本地变量的寄存器之一r7被使用了。你会发现剩余的函数代码中并没有使用栈指针或者r7,因此这是个小小的多余处置惩罚。有时间开启了优化也优化不掉。
- 指令bl意味着函数调用。记得函数的入参已经放入相关的寄存器r0及r1了吧。这个指令实验的代码我们称之为分支。因为是指令bl而不是指令b,指令bl全称是『branch with link』,意味着在实验分支代码之前,必要将lr(链接寄存器)置为当前函数的下一个指令。回想下,当addFunction方法返回时,lr就是指向下一个要实验的指令。
- 这是将两个数相加的addFunction分支返回的节点。记得之前阐明过函数的返回值是存放在r0的吧~ 这个值会作为printf函数的第二个参数,因此mov指令用于将r0的内容赋值给r1。
- printf函数的第一个参数是一个字符串。这三条指令加载指向所需的字符串的开始地点的指针到r0寄存器。字符串存储在我们称之为二机制『数据段』的位置。但只有终极二进制被链接时才能知道该数据的详细位置。 字符串可以在main.m生成的目标文件例找到。如果你在生成的汇编代码内搜索『L.str』,便可找到它。前两个指令加载常量的地点,并减去标签的地点(LPC1_0加上4字节)。看到第三个指令这么做的目标就很显着了。r0加上pc(步伐计数器),这样无论L.str在二进制文件的什么位置都能够准确的存放字符串的位置。 下面的图展示了内存分布。L_.str - (LPC1_0 + 4)差值可以任意变动,但是加载r0的代码却不消变化。
- 这条指令是调用printf函数。这里的blx跟bl指令有点差别,x代表交换,当有必要时,处置惩罚器可以改变当前运行模式。处置惩罚器运行模式有点逾越了本教程的范围,ARM处置惩罚器有两种运行模式:ARM和Thumb。Thumb指令集是16位宽,而ARM是32位。Thumb指令比ARM少,使用Thumb意味着更少的代码大小及更好的CPU缓存。通常使用有限的Thumb指令集可以让你从更小的包大小中获益。想了解更多Thumb的知识请戳这里
- 最后一条指令是推出第一条指令推入的值。这次列表中的寄存器的值是用栈中的值添补的,且栈指针增加了。回想下,r7和lr之前是被推入到栈中,那么此时为何是推出来的值存入到了r7和pc中,而不是r7和lr呢? 好的,记得lr是存储当前函数实验完成后的下一个指令地点吧。当你把lr推出栈赋值给pc后,实验将会从本函数调用的地方继续实验。这通常是一个函数返回的实现方式,而不是像addFunction那样切分支的方式。
以上是对ARM指令集大抵的介绍。还有许多其他指令集,但这些是开始理解指令集最重要的的指令。让我们来用伪代码快速回忆一下代码做的事变:
mov r0, r1 => r0 = r1 mov r0, #10 => r0 = 10 ldr r0, [sp] => r0 = *sp str r0, [sp] => *sp = r0 add r0, r1, r2 => r0 = r1 + r2 add r0, r1 => r0 = r0 + r1 push {r0, r1, r2} => r0, r1 r2 入栈 pop {r0, r1, r2} => 栈顶出三个, 并赋值给r0, r1 and r2. b _label => pc = _label bl _label => lr = pc + 4; pc = _label
哇哦~ 如今你可以读懂一些ARM汇编代码了~
Objective-C 汇编
至此,你看到的函数都是C语言的。Objective-C代码要复杂点,不过让我们来检验一下。在ViewController.m代码中添加以下代码实现:
- (int)addValueint)a toValueint)b {
int c = a + b;
return c;
}
让我们再次重复之前精简的汇编方式,搜索addValue:toValue:函数,可以看到:
“-[ViewController addValue:toValue:]”:
adds r0, r3, r2
bx lr
首先你会注意到标署名字。这次便署名字包罗了类名及全部的方法名。
如果你和之前的addFunction汇编代码相比较,你会发现两个入参存储在了r2及r3而不是r0和r1。为什么呢?
OK,因为Objective-C函数在C函数的底子上多传了两个隐式的参数。addValue:toValue:方法语法上和以下方法相同:
int ViewController_addValue_toValue(id self, SEL _cmd, int a, int b) {
int c = a + b;
return c;
}
这就是为什么a和b变量分别存储在r2和r3内了。如今你大概知道了前两个隐式参数的含义了,你总是可以使用self这个变量。
但是,_cmd可能之前你没有见过。像self变量一样,在Objective-C代码中它是可获取的,而且代表着当前函数的selector。你一样平常从不会用到它,这就是你为何没听过的原因了。
为了看清Objective-C函数是怎样被调用的,我们在ViewController中添加如下代码:
int add = [self addValue:12 toValue:34];
NSLog(@“add = %i”, add);
}
生成代码并找到该方法,你可以看到类似下面的代码(Xcode7生成的有点不一样了):
“-[ViewController foo]”: @ 1: push {r7, lr} @ 2: movw r1, :lower16L_OBJC_SELECTOR_REFERENCES_-(LPC1_0+4)) movt r1, :upper16L_OBJC_SELECTOR_REFERENCES_-(LPC1_0+4)) LPC1_0: add r1, pc @ 3: ldr r1, [r1] @ 4: movs r2, #12 movs r3, #34 @ 5: mov r7, sp @ 6: blx objc_msgSend @ 7: mov r1, r0 @ 8: movw r0, :lower16L__unnamed_cfstring-(LPC1_1+4)) movt r0, :upper16L__unnamed_cfstring_-(LPC1_1+4)) LPC1_1: add r0, pc @ 9: blx _NSLog @ 10: pop {r7, pc}
再次,和之前我们看到的C语言函数差不多。分解它:
- 将r7及lr入栈;
- 使用同样加载字符串的方式,加载在名为L_OBJC_SELECTOR_REFERENCES_的标签处的字符串指针到r1。像便署名字一样,它是一个selector的引用。其实selector就是存储在数据段的字符串。
- 如果你在汇编代码里查找L_OBJC_SELECTOR_REFERENCES_,你会发现:L_OBJC_SELECTOR_REFERENCES_:.long L_OBJC_METH_VAR_NAME_,这阐明r1指向的是L_OBJC_METH_VAR_NAME_标签的地点。如果你继续检察该便签,你将找到addValue:toValue:字符串。 指令ldr r1, [r1]表示加载存储在r1指针内的内容并赋值给r1。用伪代码是这么表述的r1 = *r1。再细致想想,r1其实已经指向addValue:toValue:字符串地点。
- 加载常量到r2和r3。
- 生存栈指针。
- 以生存lr指针且可更换模式的方式切分支到objc_msgSend。这个方式是Objective-C语言的核心。它调用它的入参selector的实现。参数终极和传给这个方法的参数一样,r0是self,r1是_cmd,r2和r3是剩下的参数。这就是为何selector要赋值给r1,剩余参数赋值给r2和r3,r0是隐式加载的,因为self变量已经存在了。
- addValue:toValue:方法的返回值照旧r0。这个指令将r0的值赋值给r1,因为r0之后要作为C函数NSLog的参数。
- 加载NSLog必要的字符串给r0,像printf函数一样。
- 以生存lr指针且可更换模式的方式切分支到NSLog。
- 两个值被推出来,一个赋值给r7一个给pc。这个指令将使函数返回。
如你所见,当生成汇编代码时,C函数和Objective-C没有多大差别。两者的主要差别在于,Objective-C隐式的传递了两个参数,且selector是在生存在数据段内的。
Objective-C函数实验过程
你已经大抵看到了objc_msgSend函数,你可能也在Crash日志内见过它。这个函数是Objective-C运行时的核心。运行时是胶合Objective-C应用的代码,包括所有的内存管理方法及类处置惩罚。
每一次Objective-C函数调用,都必要objc_msgSendC函数来派发消息。它会去对应的对象方法列表内搜索方法的实现。objc_msgSend函数署名如下:
id objc_msgSend(id self, SEL _cmd, …)
在函数实验当中的第一个参数是self对象。因此当我们写诸如self.someProperty代码时,self就是这么来的。
第二个参数是少有人知的隐藏参数。你可以试试,在Objective-C方法里写这样一句代码:NSLog(@"%@", NSStringFromSelector(_cmd));,你可以看到当前的方法被打印出来。明白了不?
剩下的参数就是方法所需的参数了。像addValue:toValue:方法有两个参数的方法,初次外,还有另外两个参数。因此,不调用Objective-C函数,你可以直接这样写也可达到同样的结果:
int add = (int)objc_msgSend(self, NSSelectorFromString(@“addValue:toValue:”), 12, 34);
NSLog(@“add = %i”, add);
}
注:objc_msgSend函数的返回值是id范例,但被强转成int范例。这没问题是因为他们的大小都是一样的。如果方法返回差别大小的返回值,现实上是另一个方法被调用了。你可以在这里了解更多信息。如果返回值是浮点型,那么另一个objc_msgSend方法的变种被调用.
回想下上面Objective-C方法生成的等量C函数的署名如下:
int ViewController_addValue_toValue(id self, SEL _cmd, int a, int b)
如今对于这个写法应该没什么惊讶的。可以看出它跟我们objc_msgSend的署名非常匹配!意味着当objc_msgSend方法找到了对应方法实现时,调用的各个参数都精确了。你可以在这里阅读更多关于objc_msgSend方法的信息。
如今,你可以逆向工程
获得了ARM汇编的一些知识,你应该对步伐中一些中断,瓦解或者运行不精确有种感觉了。为何你会想去看汇编代码?因为你找到更多信息看清到底是哪一步导致bug发生。
有时间,你并没有源码,例如,你瓦解发生在第三方库或者系统框架内。若能通过汇编调查将会让你快速找到问题地点。iOS SDK所有的框架都装在这个目次下:
<ath_to_Xcode>/Contents/Developer/Platforms/iPhoneOS.platform/Developer/ SDKs/iPhoneOS6.1.sdk/System/Library/Frameworks
为调查这些库,我发起你买这个软件HopperApp,该软件可以反汇编既而你可以检察。例如,打开UIKit库,你可以看到每个方法做了啥,看清来像这样:
这是http://www.raywenderlich.com/wp-content/uploads/2013/04/05-HopperApp-1.png方法的汇编代码。运用你新得到的ARM汇编知识,你应该可以知道方法做了什么。
第一个selector指针加载到r1,为objc_msgSend方法做预备。注意到并没有动过其他寄存器,那么r0中的self就和shouldAutorotateToInterfaceOrientation方法一样。
同样地,你也发现被调用的函数也是只有一个参数,因为他的名字里只有一个冒号。因为只剩下r2未处置惩罚了,那么传给shouldAutorotateToInterfaceOrientation的第一个参数就是我们必要传给调用函数的参数。
最后,调用函数后,r0没有动过。那么调用函数的返回值,就是当前函数的返回值。
因此你可以推论出这个函数是这么实现的:
- (BOOL)shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation)interfaceOrientation {
return [self _doesTopViewControllerSupportInterfaceOrientation:interfaceOrientation];
}
哇哦!太简朴了!通常一个函数的逻辑比这个复杂一些,但通常你都可以把他们构造起来,并快速的想明白一些代码做了什么。
何去何从?
iOS汇编教程给了你一些洞察ARM内核的方法。你也学会了一些关于C和Objective-C的一些调用约定。
装备了这些知识,当你的应用瓦解在系统库深部时,你就有了理解这些随机代码的方法。或者你只想通过看汇编代码了解你的代码到底是怎样运行的。
如果你对深入研究ARM感爱好,我发起你买一个Raspberry Pi. 这些晓得设备拥有一个和iOS设备非常类似的ARM处置惩罚器,而且有许多文档教你怎样为它编写代码。
另一个值得关注的是NEON。这是在所有除了iPhone 3GS之前的处置惩罚器上的额外的指令集。它提供单指令多数据流(SIMD)能力让你非常高效地处置惩罚数据。运用这些指令的应用一样平常是图像处置惩罚的。如果你必要高效地处置惩罚事变,学习怎样写NEON指令使用内联汇编将会对你有益。这真的非常高端~
转自:http://blog.csdn.net/u010164190/article/details/52551720
css
1,盒模子
2,怎样实现一个最大的正方形
3,一行水平居中,多行居左
4,水平垂直居中
5,两栏布局,左边固定,右边自顺应,左右不重叠
6,怎样实现左右等高布局
7,画三角形
8,link @import导入css
9,BFC理解
js
1,判断 js 范例的方式
2,ES5 和 ES6 分别几种方式声明变量
3,闭包的概念?优缺点?
4,浅拷贝和深拷贝
5,数组去重的方法
6,DOM 事故有哪些阶段?谈谈对事故代理的理解
7,js 实验机制、事故循环
8,介绍下 promise.all
9,async 和 await,
10,ES6 的 class 和构造函数的区别
11,transform、translate、transition 分别是什么属性?CSS 中常用的实现动画方式,
12,介绍一下rAF(requestAnimationFrame)
13,javascript 的垃圾回收机制讲一下,
14,对前端性能优化有什么了解?一样平常都通过那几个方面去优化的?
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提拔。
必要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注前端)
一个人可以走的很快,但一群人才能走的更远!岂论你是正从事IT行业的老鸟或是对IT行业感爱好的新人,都接待加入我们的的圈子(技术交换、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
,ES6 的 class 和构造函数的区别
11,transform、translate、transition 分别是什么属性?CSS 中常用的实现动画方式,
12,介绍一下rAF(requestAnimationFrame)
13,javascript 的垃圾回收机制讲一下,
14,对前端性能优化有什么了解?一样平常都通过那几个方面去优化的?
[外链图片转存中…(img-EIGHqb97-1713676883018)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提拔。
必要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注前端)
[外链图片转存中…(img-yJRL66XR-1713676883019)]
一个人可以走的很快,但一群人才能走的更远!岂论你是正从事IT行业的老鸟或是对IT行业感爱好的新人,都接待加入我们的的圈子(技术交换、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |