0%

字节序

字节间的顺序。主要有两种:大端序和小端序。当然你也可以规定其他的顺序,但这两种顺序是规则最简单的了。

内存:

1
2
内存:   ------------------------------->
内存地址:0 1 2 3

大端序:从左往右读,最左边为最高位。反映在内存即最低地址为最高位,最高地址为最低位。
小端序:从右往左读,最右边为最高位。反映在内存即最低地址为最低位,最高地址为最高位。
可以看到,大端序和小端序最高位到最低位的顺序刚好相反,是翻转的关系。

为什么会有字节序?
因为一串数字可以从左开始读,也可以从右开始读。如果不规定读的顺序,那么就无法得知正确的信息。
比如:一张纸上写着1234
按大端序读,最左边的1就是最高位,结果就是一千两百三十四
按小端序读,最右边4就是最高位,最左边的1反而是最低位,结果就是四千三百二十一
可以看到对于同一串文本,读的顺序不同,得到的结果也会不同。大部分国家都是从左到右读,但也有的国家是从右到左读的。没有好坏,纯属习惯问题。因此数字“一千两百三十四”,对于从左到右读的国家,书写出来就是1234。而对于从右到左读的国家,书写出来就是4321。大小端只是记法不同,表达的数字还是同一个。

CPU读取内存中的数据时,是从低地址向高地址方向进行读取的。所以一般计算机的内部处理都是小端字节序(目前为止我就还没遇到过采用大端存储的机器)。但是,人类还是习惯读写大端字节序。所以,除了计算机的内部处理,其他的场合比如网络传输和文件储存,几乎都是用的大端字节序。

举例:

0x1234
0001 0010 0011 0100 (采用大端序存储)-> 0010 1100 0100 1000(采用小端序存储,根据大端翻转即可)
1234 -> 3412(第一个字节代表的就是0x34,小端从右往左读)

0x01
0000 0000 0000 0000 0000 0000 0000 0001(大端序)
->
1000 0000 0000 0000 0000 0000 0000 0000(小端序)

阅读全文 »

在往图床上丢图片时,有时候总是因为图片太大而要等很久的时间,面对这种情况压缩图片后再上传是一个不错的选择。但是每次都要先打开图片压缩软件,再丢图片压缩,再找到压缩后的图片,再上传到图床,这一系列操作也是繁琐。今天就来解决一下这个问题。

很显然脚本能够帮助我们自动完成这些烦人的操作。我们希望这个脚本能够自动压缩当前文件夹的所有图片,压缩后将图片转移到另一个文件夹方便查找。

说干就干,lets go!

首先,我们需要安装一个图片压缩软件,这个图片压缩软件要能支持命令行操作,这里选择ImageOptim: https://imageoptim.com ,到官网下载后安装即可。

其次,我们需要安装ImageOptim-CLI,有了它就可以用命令行操作图片压缩软件ImageOptim了。推荐使用npm安装特别是M1芯片的Mac,打开终端输入:

1
npm install -g imageoptim-cli

安装完成后查看一下版本确定是否安装成功:

1
2
imageoptim --version
3.0.7

最后,就是写脚本了:目前我们只压缩png,jpg,jpeg,gif

随便打开一个文本编辑器比如记事本,拷贝下面的代码并保存为 compress.sh ,保存路径随意这里为桌面。

阅读全文 »

find

在指定目录下查找文件。

使用该命令时,不设置任何参数,则find命令将在当前目录下查找子目录与文件(即默认递归查找)。并且将查找到的子目录和文件全部进行显示。

语法

1
find path expression

expression:表达式,选项,参数。

find 根据下列规则判断 path 和 expression,在命令列上第一个 - ( ) , ! 之前的部份为 path,之后的是 expression。如果 path 是空字串则使用目前路径,如果 expression 是空字串则使用 -print 为预设 expression。

注意:path最好不要省略。

expression 中可使用的选项有二三十个之多,用到的时候可以查一下。

常见的有:

阅读全文 »

有一个需求需要循环播放一个视频,UI希望的效果是无缝循环播放。当时觉得应该问题不大,但事实却并非如此。如果只是单纯的循环播放,确实简单。但是想要无缝丝滑连贯的循环播放却并不那么简单。

方案一:ijkplayer

项目中用的ijkplayer,刚开始是监听播放完成通知,播放完后重新播放,结果发现每次播完后都要停顿一下才会继续播放。达不到无缝循环的要求。

一顿搜索之后,发现可以通过设置option的loop属性,设置为100就是循环播放100次,设置1就是播放一次,设置0就是无线循环播放。这一次以为稳了,然并卵,还是每次播完后都要停顿一下才会继续播放。

又是一顿搜索,发现了ijkplayer可以播放contact文件,所谓contact文件就是按照一定的格式将要播放的视频地址按顺序写入一个文件,把这个文件丢给ijkplayer,ijkplayer就会按照顺序一个个播放,当时就在想是不是这种方式可以无缝播放呢?于是将视频的地址写了1000遍,这样ijkplayer就会播放1000次,达到一个循环播放的假象。然而还是没有什么卵用,效果和上面一样。

总结一下:ijkplayer的播放机制就是播完后会暂停,这一暂停就导致不连贯了,因此可以很明显的看到会停顿一下才继续播放。使用ijkplayer基本上做不到无缝循环播放视频。当然也有可能是我才疏学浅没找到正确的方式。

方案二:AVPlayer

ijkplayer既然不行,那就只能试一下系统的AVPlayer了,还是同样的思路,监听播放完成通知,播放完后重新播放,结果发现效果一样。

方案三:AVQueuePlayer

最后找到一篇文章说是要使用AVPlayer的子类AVQueuePlayer,试了一下发现效果确实很不错,几乎看不出来有停顿的感觉。AVQueuePlayer应该会预加载要播放的资源,这样播放完后就可以立即加载渲染下一个视频的画面了。

阅读全文 »

自从电脑坏了之后就一直没有拿去修,没空是一方面,更多的是担心修不好还被坑维修费。就这样磨磨蹭蹭过了差不多大半年,今年五一放假也没啥去处,于是想干脆去华强北修一修,万一修好了呢。本来是想5月1日去修的,结果起太晚了磨蹭到2点感觉有点晚了,就想着第二天去吧,结果第二天下了一天的雨,也没去成。

5月3日天气有点阴沉,但好歹没下雨了。于是从角落把电脑翻出来发现盖子都有点发霉了,简单的清理了一下便装进背包,向华强北出发。华强北虽然经过过几次,但没去里边修过东西,出发前搜了下华强北修电脑的帖子,广告很多,被骗被坑的回答也很多,很多答主说要慎重,说有的店就是拖着你说修不好让你低价卖给他,有的还会偷换电脑零件,反正啥样的都有。看完之后还是有点忐忑的,倒不是怕偷换零件,毕竟我这个电脑用了6年半了也没啥好零件了,主要是怕漫天要价,拖着不修,扯皮闹心。

不过还是决定去试一下,下午两点到了华强路A出口,一出来就看到几家手机电脑维修的店子,我也不知道哪家好,也没有多问多选,直接走进了第一家。进去之后没有说修电脑的事,当时手机拍照有黑斑,就先问了下手机的问题能不能修,维修的几个人都是年轻人,说能修大概160-180块钱,只需要清洗一下摄像头就可以弄好。

这里说一下维修店的模式,基本上店面都是一个小门面,相当于一个小工作台,这里只会做一些简单的维修比如换个屏,贴个膜,初步检测下设备啥的,如果问题比较大或者需要其他设备,他们会拿到他们所谓的维修间里,两个地方是有一段距离的而且你是不可能跟着去的,顾客只能在店面等着。

话说回来,让他拆下摄像头后我问要洗多久,他说大概要等1个小时。感觉还行便问这里能不能修电脑,他说能修电脑,于是我把电脑给他看了看,告诉他是主板烧坏了,他拿着万用表测了测,说的原因我也记不住,大概的意思是芯片供电模块短路。

我:修好大概要多少钱

他:550

我:有点贵

他:已经是最低价了,这样吧,加上那个洗摄像头的费用一共700怎么样

我:那要修多久啊

阅读全文 »

next主题下给文章添加版权信息

1、修改_config.yml文件

1
2
3
4
5
creative_commons:
license: by-nc-sa
sidebar: false
post: true
language:

post的值改为true表示文章开启添加版权信息。

至此,部署后就可以看到每篇文章下面都会有版权信息了。但是有一些文章我们不想添加版权信息,那么还需要接着设置:

2、打开文件 themes/next/layout/_macro/post.swig

找到creative_commons那一行改为:

1
2
3
{%- if theme.creative_commons.license and theme.creative_commons.post and page.copyright %}
{{ partial('_partials/post/post-copyright.swig') }}
{%- endif %}

就是添加了 page.copyright ,这样就可以单独控制某篇文章是否添加版权信息了。

在文章开头的头部添加 copyright: true 表示当前文章要添加版权信息,false表示不需要。

阅读全文 »

NSTimer

NSTimer是使用最多的一种定时器,虽然但是NSTimer还是有很多的注意事项:

  1. 不注意使用的话有循环引用的隐患。因为NSTimer会强引用target,如果target再强引用NSTimer那么就会发生循环引用。比如:

    1
    2
    3
    4
    5
    - (void)test_normal_timer1 {
    self.normalStrongTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(doSome) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:self.normalStrongTimer forMode:NSRunLoopCommonModes];
    [self.normalStrongTimer fire];
    }

    上面的代码就会发生循环引用。此时即使页面返回,但是因为循环引用,ViewController的dealloc将不会被执行,因此invalidate不能放在dealloc里,只能在其他时机比如viewDidDisappear里调用。所以一般使用weak来弱引用定时器,这样就可以在dealloc里调用invalidate。

  2. 需要在合适的地方invalid定时器,否则定时器会一直强引用target从而延长target的生命周期。

    在开发中很容易忘记invalid定时器,一旦忘记invalid定时器,定时器就会延长target的生命周期,比如页面返回了但实际还没有被销毁,从而产生一些诡异问题。这也是使用NSTimer必须注意的地方。

  3. 使用时必须保证有一个活跃的runloop,并且需要指定mode。因此在子线程中使用不是很方便。

  4. 精度可能不够。

  5. 网上的一个说法:创建和撤销必须在同一个线程上,在多线程环境下使用不便。(这一条存疑,经过验证在子线程创建一个定时器,在另一个子线程invalidate并没有发现有什么问题)

  6. iOS10开始支持block使用,同样在使用block时一定要注意循环引用。

为了从根本上避免上述问题,一个弱引用target的、能够在自身销毁时自动invalid的定时器想必是极好的。解决办法就是封装GCD定时器。

GCD定时器

基本使用:

1.创建定时器源

1
2
3
4
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,
0,
0,
self.privateSerialQueue);

2.设置定时器

1
2
3
4
5
6
7
8
int64_t intervalInNanoseconds = (int64_t)(self.timeInterval * NSEC_PER_SEC);
int64_t toleranceInNanoseconds = (int64_t)(self.tolerance * NSEC_PER_SEC);

dispatch_source_set_timer(self.timer,
dispatch_time(DISPATCH_TIME_NOW, intervalInNanoseconds),
(uint64_t)intervalInNanoseconds,
toleranceInNanoseconds
);
阅读全文 »

HTTP各版本特性

HTTP 0.9

1991年发布

基本上只是一个草稿,设计的初衷只是为了获取简单的HTML对象。只支持GET。没怎么使用就被HTTP 1.0代替了。

HTTP 1.0

1996年5月发布规范 rfc1945

第一个广泛使用的版本。

相比HTTP 0.9

  1. 增加了对多媒体对象的处理,任何格式的内容都可以发送。这使得互联网不仅可以传输文字,还能传输图像、视频、二进制文件。

  2. 增加了一些方法:POST,HEAD。丰富了浏览器与服务器的互动手段。

  3. 新增了HTTP首部。比较重要的有Content-Type ,Content-Length,Content-Encoding

  4. HTTP请求和响应格式的改变。

  5. 其他新增功能。比如状态码的定义。

请求报文:

阅读全文 »

Bug

出现机型:iPhone7Plus iOS15.1。

描述:在开发过程中发现弹出弹窗点击关闭之后界面就卡死了。多次尝试之后,最终发现只有在调试的时候出现,把线一拔不调试的时候一切又正常了。拿另一部iPhone6Plus iOS12.5.4的手机又都不会出现。顿时感觉遇到”薛定谔的bug”了。

分析

项目中对UIAlertController做了一层简单的封装,是在自己的window上present出来的,这样在使用的时候就不用操心谁来present了。

方法接口:

1
+ (void)showActionSheetWithTitle:(nullable NSString *)title message:(nullable NSString *)message cancelButtonTitle:(nullable NSString *)cancelButtonTitle destructiveButtonIndex:(NSInteger)destructiveButtonIndex buttonTitles:(nullable NSArray *)buttonTitles showedPatternHandler:(nullable NSDictionary *(^)(void))showedPatternHandler buttonClickedHandler:(nullable void(^)(NSUInteger buttonIndex))buttonClickedHandler;

内部部分实现:

1
2
3
4
5
6
7
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:preferredStyle];
alertController.alertWindow = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
alertController.alertWindow.backgroundColor = [UIColor clearColor];
alertController.alertWindow.hidden = NO;
...
alertController.alertWindow.rootViewController = [UIViewController new];
[alertController.alertWindow.rootViewController presentViewController:alertController animated:YES completion:nil];

刚开始时,对象持有关系:

阅读全文 »