一只代码狗的随笔

知其然知其所以然!

0%

​ 在工作中遇到一个需求,比较两个target的不同——编译资源和资源文件的不同。我们都知道.m文件会参与编译;xib,assets文件会作为包的资源文件。头文件在预处理时使用。那xcode是如何组织这些内容的。本文的主角xcodeproj做了这些。

Read more »

一个c语言编写的文件,需要经过以下四个步骤最后才能成为一个可执行文件。

  1. 预处理
  2. 编译
  3. 汇编
  4. 链接

​ 可以简单的理解为1-3步骤是对代码语言的处理,将高级语言译成机器可以识别的机器语言。步骤4是将每个编译后的文件整合为一个完整的可执行文件,链接的实质是将符号和定义联系在一起。

ELF结构:

​ 下表为ELF文件主体结构,每个ELF文件都是由多个段组成的,一些复杂的elf文件会涵盖很多不同的段,一些简单的ELF文件可能只包括少数的段。这些都是由代码中数据决定的。

Read more »

使用mac的时候,cmd+tab切换应用非常舒服。其中的算法就是LRU算法。LRU全称Least Recently Used,即最近最少使用算法。下面用js实现以下最简单的LRU算法。

Read more »

一、promise介绍

promise原是前端社区中为解决回调陷阱而提出的一种方案。后在ES6中,官方对此进行了统一支持。promise写的代码便于阅读,代码相较优雅。

Read more »

在应用开发过程中会碰到加载大图占用内存过多导致内存溢出的情况。一张图片占用的内存大小是有迹可循的。比如一个RGBA的像素,占用的内存大小为4byte。一张100x100像素的图片解压缩后占用的内存大小大约为100x100x4byte的内存(这里暂时不考虑图片信息之类的内存占用,仅举例说明)。

对于一个手机应用来说,图片显示的区域不会太大。假如图片显示的区域为1000x1000pixel,显示的图片原始数据为1wx1w的图片,那这张图片的像素数量远远超过了屏幕能显示的像素数量。那大部分的像素其实是没有价值的。反而解压缩原始尺寸后的图片占用的内容是非常庞大的。

于是乎就可以想到这么一个方案——在显示图片的时候根据原始图片生成一个满足显示需求的小图,这样就可以减少内存的占用。这也是现在业内使用的主流方案。

在这里有一个问题,生成小图的方式是可以有效的解决之后显示图片所需要的内存。那么在生成小图这个阶段,是不是需要将原始的图片数据先读取到内存中,对原始数据解压缩,然后再生成相应的压缩图片。那在这一步中,图片读取和压缩的过程一定会带来一个内存峰值。也来到本文的一个主题,图片的解压缩存不存一种方式———边读取图片数据边生成采样率低的图片,从而保证处理过的数据的内存可以及时的释放。

Read more »

需求场景

H5页面使用客户端的相册图片。H5调用客户端的相册功能选取图片,图片资源在客户端本地,因此需要客户端将选取后的图片数据返回给H5。

方案设计

一、直接通过桥将数据传给H5应用

将获取的UIImage对象转化为NSData对象,通过桥,直接将NSData对象传给H5应用。

二、拦截H5的网络请求,将图片数据通过网络请求的方式返回给H5应用

客户端图片选取之后,生成一个图片地址,地址中包含特定的图片协议和这个图片的唯一标识符。图片地址返回给H5应用,H5应用取得图片地址后,将图片地址视为一个网络图片地址,通过request请求图片数据。客户端通过NSURLProtocol拦截WebView发起的包含特定图片协议的网络请求,并根据请求的地址匹配本地的图片资源,生成response返回给H5应用。客户端在请求过程中扮演一个本地服务器的角色。

缓存设计

相册选择完图片后,已经获取到了图片数据,对应的图片数据进行缓存,提高读取效率。获取到的UIImage对象转化为NSData数据存储在缓存池中,缓存池的结构为简单的hash表。

拦截器实现

iOS中系统提供了请求的拦截器——NSURLProtocol。子类化NSURLProtocol类,并注册这个urlProtocol即可拦截到当前进程的网络请求。但在 WKWebView 中的请求却完全不遵从这一规则,WKWebView有自己独立的进程,通过IPC进行进程间通信。想要拦截WKWebView的请求则需要通过c类的registerSchemeForCustomProtocol:方法进行注册urlProtocol(WKBrowsingContextController是私有类,只能通过runtime的方式生成获取这个类,且iOS 8.4 以后才被引入,调用私有类和私有方法存在被拒的隐患)。

1
2
3
4
5
6
// 注册protocol scheme为自定义的protocol。
Class cls = [[[WKWebView new] valueForKey:@"browsingContextController"] class];
SEL sel = NSSelectorFromString(@"registerSchemeForCustomProtocol:");
if ([(id)cls respondsToSelector:sel]) {
[(id)cls performSelector:sel withObject:scheme];
}

降级方案

对于iOS 8.4 以下的版本,无法获取到WKBrowsingContextController类,也就导致无法进行网络拦截。在整个图片选择逻辑中,APP通过桥传输的都是图片的地址。H5应用都是直接加载图片地址的数据。因此,对于iOS 8.4以下的版本,APP端不作本地服务器,选择完图片后自行传到远程服务器,将正常的网络图片地址传给H5应用。

前言:什么是函数重载?

在C++和java语言中都有函数重载是指在同一作用域内,可以有一组具有相同函数名,不同参数列表的函数,这组函数被称为重载函数。重载函数通常用来命名一组功能相似的函数,这样做减少了函数名的数量,对于程序的可读性有很大的好处。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
void foo(int i) {
cout<<"print a integer :"<<i<<endl;
}

void foo(string str) {
cout<<"print a string :"<<str<<endl;
}

int main() {
foo(12);
foo("hello world!");
return 0;
}

main函数中foo(12)调用的是foo(int)函数,foo(“hello world”)调用foo(string str)函数。

C++,Java中是如何实现方法重载

C++和Java允许重载方法,这些方法在代码中都有相同的名字,参数列表不同。如果仅仅将方法名最为符号,那链接器定然不能识别。之所以能使用重载是因为他们的编译器将方法名和参数列表组合编码作为这个方法的符号。这种编码过程称为重整(mangling)。重整后的方法因为参数列表不同,编码后得到的符号也不相同。例如foo(int)编码为__Z3fooi, foo(string)编码为__Z3fooSs。

OC中方法有重载吗?

试想一下在一个类中添加如下两个方法。

1
2
- (void)foo:(int)i;
- (void)foo:(NSString *)str;

毫无疑问会收到报错。错误内容为Duplicate declaration of method,方法名重复了。显然OC不支持这样的写法。

理解OC中的方法调用

1
[self foo]

通过clang转为c++代码为

1
((void (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("foo"));

foo方法的调用,实际是调用了objc_msgSend这个函数,self,sel作为函数的参数。

再来看下上述两个方法。两者的sel都是foo:。对于一个类,其内部有一个方法的列表。msgSend函数的实现为根据sel从方法列表中获取imp。因此上述两个方法的SEL存在重复,msgSend函数不能区分,找到对应的imp。因此,在OC语言中不存在上述的方法重载。

OCLint是iOS中代码质量控制的工具,支持自定义规则,适用于团队协作开发,规范代码。

Read more »

有一个需求:

![screenshot](/Users/remain/Library/Group Containers/Q79WDW8YH9.com.evernote.Evernote/Evernote/quick-note/13630831-personal-app.yinxiang.com/quick-note-nJN2un/attachment–jsgTib/screenshot.png)

A,B, C三个视图,视图的size确定,B业务逻辑隐藏/显示。视图之间的间距确定。B视图隐藏时,A,C视图显示如下:

![screenshot](/Users/remain/Library/Group Containers/Q79WDW8YH9.com.evernote.Evernote/Evernote/quick-note/13630831-personal-app.yinxiang.com/quick-note-nJN2un/attachment–Jg14UK/screenshot.png)

建立A-B约束,和A-C约束,A,B,C等宽。修改A-B,A-C约束优先级实现。

视图优先级的使用

![screenshot](/Users/remain/Library/Group Containers/Q79WDW8YH9.com.evernote.Evernote/Evernote/quick-note/13630831-personal-app.yinxiang.com/quick-note-nJN2un/attachment–74l5Ia/screenshot.png)

label_A和label_B设置间距。要求B的内容显示完整,A的内容可以显示不全。默认先布局A。修改View的resistancePriority的修改布局顺序

​ LLDB是个开源的内置于XCode的具有REPL(read-eval-print-loop)特征的Debugger,也是iOS开发中,配合断点使用的调试命令工具。

基本操作命令

​ 在lldb中输入help能看到所有的命令,通过help 可以看到相应命令的具体用法。以下介绍几种常用的命令。

  • 打印输出

p – Evaluate an expression on the current thread. Displays any

​ returned value with LLDB’s default formatting.

po – Evaluate an expression on the current thread. Displays any

​ returned value with formatting controlled by the type’s author.

p和po两个命令是日常打印输出最常用的。po,p是expression的缩写。在命令行中输入help po,可以看到po和p命令的详细信息,

'po' is an abbreviation for 'expression -O —',

'p' is an abbreviation for 'expression —'

po输出的是对象的description方法的返回值,也可以重写对象的description方法来改变输出。而p输出的是LLDB的默认格式。例如下图NSArray的打印输出。po和p命令同时也能执行代码。根接下来的expr命令功能相同。

  • 动态执行代码

expression – Evaluate an expression on the current thread. Displays
any returned value with LLDB’s default formatting.

expr命令可以执行输入的代码块(call命令的效果相同,都是调用__lldb_expr函数)。通过输入的代码块,我们可以动态的改变变量的值,可以调用一个方法…。使用Option+Enter键可以输入多行代码。

如上图,动态的执行obj=nil这段代码,将obj的值置为nil。需要补充的是,expr命令接收的代码不能直接使用枚举值,例如NSRoundUp需要转化为(NSRoundingMode)1。此外,expr命令不能执行return value。

display – Evaluate an expression at every stop (see ‘help target
stop-hook’.)

display作用的与expr相同,都是执行一段代码,而display命令在每一次到断点的时候都会执行一遍输入的代码。

  • 查看堆栈信息

(lldb) help bt

​ —Show the current thread’s call stack. Any numeric argument displays at

​ most that many frames. The argument ‘all’ displays all threads. Expects

​ ‘raw’ input (see ‘help raw-input’.)

Syntax: bt [ | all]

‘bt’ is an abbreviation for ‘_regexp-bt’

​ 此外,在lldb中只要获取到对象的地址,就能使用expr,call命令调用对象的方法。因为,oc是方法的调用都是msg_send函数。