Jeffrey Liu

Little by little

  • 主页
所有文章 友链

Jeffrey Liu

Little by little

  • 主页

Type-Encoding研究

2018-08-23

Type Encoding

最近在看runtime,对于消息发送机制中的类型编码(Type-Encoding)比较感兴趣,我们知道,当我们调用[receiver sendWithArg:arg1 another:arg2]时,编译器会转换成objc_msgSend(receiver,@selector(sendWithArg:another), arg1, arg2), 假如我们有个方法:

1
- (void)sendMsgWithParams:(NSDictionary *)params;

我们要如何获得这个方法的Type-Encoding呢,很简单:

1
2
3
4
SEL aSel = @selector(sendMsgWithParams:);
Method aMethod = class_getInstanceMethod(self.class, aSel);
const char *typeEncoding = method_getTypeEncoding(aMethod);
NSLog(@"sel:%s, %s",aSel,typeEncoding);

我们会得到这么一串儿: v24@0:8@16, 这就是这个方法的type-encoding,我们来解释一下这串编码:

  1. @encode(void) =” v”,标识返回类型
  2. @encode(id)=”@”, 标识receiver(self)
  3. @encode(SEL)=”:”,标识selector(_cmd)
  4. @encode(NSDictionary *)=”@”, 标识参数params的类型

完整的类型编码表我会在文末放出,那么编码中的数字是什么意思呢?其实是runtime方法调用参数的实际顺序:

1
0: self, 8: @selector(sendMsgWithParams:), 16: params(NSDictionary), 24:void

其实就和objc_msgSend(self, _cmd, arg1, arg2, ...)对应起来了。

@的问题

在type-encoding中,所有NSObject及其子类的类型type-encoding都是@,那么这是不是意味着runtime其实并不会检查具体的NSObject类型,我们来做个试验吧。

还是拿sendMsgWithParams这个方法做例子,这个方法的参数要求是NSDictionary,那么我们传一个NSNumber试试?

1
[self sendMsgWithParams:@(11)];

这个时候编译器会提示一个warning:

Incompatible pointer types sending ‘NSNumber ‘ to parameter of type ‘NSDictionary ‘

但是编译过程不会报错,程序也能够跑起来。

如果我们用id呢?

1
2
id aNum = @(11);
[self sendMsgWithParams:aNum];

这时连warning都没有了,因为编译器并不知道id类型是否是NSDictionary,对于type-encoding来说都是@。

如果直接传数字呢?

1
[self sendMsgWithParams:11];

这时候会出现编译错误:

Implicit conversion of ‘int’ to ‘NSDictionary *’ is disallowed with ARC

说明根据type-encoding是能够监测出int(i)和NSObject(@),比较特殊的是:

1
[self sendMsgWithParams:0];

不会有任何报错,其实这里0被当成了(NSObject *)0,也就是nil了。

小结

Objective-C的类型系统,只能检查基本的数据类型,对于NSObject及其子类,类型系统都是统一当成一个类型处理的,这一特点可以使Objective-C足够动态化,但是带来的弊端也比较明显,就是想依赖方法定义的参数类型来约束传参类型,是比较困难的。必要的时候,应该要对传入的参数做类型检查,避免类型不一致引发的crash。

附上Type-Encoding的编码表:

Code 含义
c char
i int
s short
l long
q long long
C unsigned char
I unsigned int
S unsigned short
L unsigned long
Q unsigned long long
f float
d double
B bool
v void
* char *
@ id, NSObject
# Class
: SEL, selector
[array type] array
{name=type…} structure
(name=type…) union
b bit field of num bits
^ pointer to type
? unknown type(function pointers)
  • objc
  • type-encoding
  • runtime
  • Objective-C

展开全文 >>

xxd命令生成十六进制头文件

2018-08-20

xxd命令是什么?

1
xxd [options] [infile] [outfile]

xxd命令用于二进制或十六进制显示文件的内容,本文将重点讲下xxd -i这个命令的使用。

-i | -include

output in C include file style. A complete static array definition is written (named after the input file), unless xxd reads from stdin.

xxd -i 可以把指定的二进制文件转换成C的头文件,那么这样做有啥用呢?

应用场景

在实际开发中,特别是SDK开发者,对于一些图片资源文件,常常需要建立一个bundle来打包这些图片,如果是静态库,提供给第三方时,需要一并带上bundle,有时候一个bundle里可能就几张图,有没有更好的方式不需要用bundle来存这些图呢?

当然是有的,我们就利用xxd -i abc.jpg abc.h命令,将二进制的图片文件转成头文件,那么就不需要用bundle来打包图片了,只需要将这些图片生成的头文件放入项目中即可。

生成的头文件是类似这样的结构:

1
2
3
4
5
6
7
8
9
unsigned char abc_png[] = {
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x3c,
0x08, 0x06, 0x00, 0x00, 0x00, 0x3a, 0xfc, 0xd9, 0x72, 0x00, 0x00, 0x00,
.... //省略无数行
0xf7, 0xf0, 0xd5, 0x19, 0xf4, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e,
0x44, 0xae, 0x42, 0x60, 0x82
};
unsigned int abc_png_len = 1541;

调用方法如下:

1
2
3
4
5
#import "abc.h"
...
NSData *imgData = [NSData dataWithBytesNoCopy:abc_png length:abc_png_len freeWhenDone:NO];
UIImage *image = [UIImage imageWithData:imageData];
...

优缺点

优点:

  • 无需新建bundle来打包图片,使得静态库的使用更为方便

缺点:

  • 每个图片都需要生成一个头文件,当然也可以手动把生成的内容汇总在同一个文件里,但这样可读性会变差

  • 头文件的大小大约是原始PNG图片的十倍左右,对于一些小的icon比较适用。

  • xxd
  • file
  • bundle
  • tools

展开全文 >>

在macOS上搭建TensorFlow环境

2017-06-26

在mac上安装TensorFlow的方法(官方推荐用virtualenv,以Python2.7为例):

  1. 安装pip和virtualenv:

    1
    2
    sudo easy_install pip
    sudo pip install --upgrade virtualenv
  2. 创建TensorFlow的目录(在~目录下创建tensorflow目录):

    1
    virtualenv --system-site-packages ~/tensorflow
  3. 激活virtualenv环境:

    1
    source ~/tensorflow/bin/activate
  4. 终端会变成这样:

    1
    (tensorflow)$
  5. 退出:

    1
    (tensorflow)$ deactivate
  6. 安装TensorFlow,这里需要注意的是macOS Sierra以后由于SIP机制(System Integrity Protection),直接安装会提示”…OSError: [Errno 1] Operation not permitted…”,我们需要通过--user -U将其安装到用户目录下:

    1
    pip install --upgrade tensorflow --user -U
  7. 成功安装会提示(这些都是TensorFlow需要用到的库):

    1
    Successfully installed backports.weakref-1.0rc1 bleach-1.5.0 funcsigs-1.0.2 html5lib-0.9999999 markdown-2.2.0 mock-2.0.0 numpy-1.13.0 pbr-3.1.1 protobuf-3.3.0 setuptools-36.0.1 six-1.10.0 tensorflow-1.2.0 werkzeug-0.12.2 wheel-0.29.0
  8. 这个时候进入python的交互环境测试:

    1
    2
    3
    4
    5
    >>>python
    import tensorflow as tf
    hello = tf.contant('Hello, TensorFlow!')
    sess = tf.Sesssion()
    print(sess.run(hello))
  9. 不出意外的话,TensorFlow会提示一些奇怪的东西:

    1
    The TensorFlow library wasn't compiled to use SSE instructions, but these are available on your machine and could speed up CPU computations" in "Hello, TensorFlow!" program
  10. 按照github上Carmezim的说法,这些warning是告诉我们如果自己编译TensorFlow会使其运行速度更快,我们可以忽略这些警告(例如在我的.zshrc中):

    1
    2
    # in your ~/.zshrc file
    export TF_CPP_MIN_LOG_LEVEL=2

相关链接:

  1. TensorFlow官方安装教程
  2. 解决mac osx下pip安装ipython权限的问题
  3. Github issue of TensorFlow warning
  • TensorFlow
  • macOS
  • pip
  • Python
  • TensorFlow

展开全文 >>

使用wget下载Xcode

2016-03-29

自从Xcode出现了那次XcodeGhost事件后,便只能通过AppStore上进行更新了,然而由于种种原因,AppStore经常上不去,即使连上了也经常会有坑爹的网络问题,所以如果能下载dmg当然是更好的。

方法也很简单,就是通过wget等命令行下载工具直接下载,支持断点续传,稳定方便,比AppStore好多了。

首先需要做的就是找到下载链接:

https://developer.apple.com/services-account/download?path=/Developer_Tools/Xcode_7.3/Xcode_7.3.dmg

用Chrome打开这个链接会弹出Apple Developer账户登录的信息,然后会重定向回开发者主页,第二次打开这个页面,便会弹出下载对话框,但是用Chrome下载可能会面临网络不稳定的情况,我们右键页面选择检查,打开Chrome调试界面,选择Network, 选择download?path=…开头的连接,如图所示:

调试界面

在Response Headers中,我们可以获得location,即实际的下载链接。在Request Header中,我们找到cookie中的ADCDownloadAuth字段。

只需要有location和ADCDownloadAuth我们就可以用wget进行下载了。

命令如下:

1
wget --header "Cookie:ADCDownloadAuth=ZbmIKnE9W%2FhUDmqp9hYPVdeKuzB3VwGEUrEhZn%2BDQtF4Wg9DJiVonlD2Qi7gb7m3ZRLlBQdx02Oq%0D%0AV9qpH2t4WHYq20FxlxKMm7xFJZwEJpg1zXHh08rCVh6bZFBu7J9nfOmkwt21m4esehS0jzrwu%2Fgf%0D%0As5S2EUVdfyfStzkCL3QnTRmo%0D%0A" http://adcdownload.apple.com/Developer_Tools/Xcode_7.3/Xcode_7.3.dmg

附上下载截图:

下载截图

喝杯咖啡,静静等候就好啦:-)

(这也方法也适用于下载其他Developer工具,只需要知道Request URL。如下载Xcode7.2.1.dmg只需要把Xcode7.3替换成7.2.1即可。)

  • Xcode
  • wget
  • download
  • Xcode

展开全文 >>

如何改变Xcode的编辑器字体

2016-03-16

Xcode本身的字体大小默认只有11,如果在iMac下做开发,人眼看得会很累,那么如何更改Xcode编辑器的字体呢?

  1. 打开Xcode-Preferences-Fonts&Colors,点下方+号,选择从原本的数十个模板里新建一个自己的新模板,比如我的就叫做Midnight Extended,这样可以避免破坏原始的主题
    选择主题

  2. 在SourceEditor中选中任意文本(如PlainText),然后按⌘(cmd)+A选中所有文本,这样下方的”T”按钮应该会从灰色变为可以点击,点击后就可以弹出字体选择框了,我选的是OSX 10.11推出的苹方-简字体,字号选14号
    选择文本

  3. 最终效果如图所示,是不是比默认的好看多了?当然大家也可以根据自己的喜好自由定制~
    最终效果

  • Xcode
  • font
  • Xcode

展开全文 >>

iOS开发中macro的使用

2016-03-11

在iOS开发中,常常会用到宏(Macro)来灵活定义一些常量或是函数,下面就罗列一些宏的常用方法。

  1. #define
    define是最常用的宏命令,用以定义常量或函数,在实际使用中,编译器用宏中#define A B的B替换A

  2. #ifdef/#ifndef
    #ifdef A表示如果A已被定义,而#ifndef A表示如果A未定义。后者常用来解决重复定义的问题,这两个宏都需要在下一行用#endif来结束

  3. #if/#elif/#else/#endif
    #if A或者#if A 1是宏中的条件判断语句,也需要用#endif结束

  4. do {…} while (0)
    使用do {…} while (0)构造的宏不会受到大括号、分号的影响,总是会按照期望的方式运行,而#define A(...) do {} while (0)可以使A(…)失效,即什么也不做

  5. 多行
    宏支持多行,只需要在行末尾用\即可

  • iOS
  • Objective-C
  • Macros
  • iOS开发

展开全文 >>

使用iOS自带CommonCrypto库计算MD5

2016-03-04

开发中常常会用到MD5计算,实际上iOS 5.0就已经内置了加密和哈希计算的库,这里就简单讲下用其中的CommonCrypto进行MD5计算。

Objective-C代码:

1
2
3
4
5
6
7
8
9
10
11
12
#import <CommonCrypto/CommonCrypto.h>
+(NSString *)MD5:(NSString *)str
{
const char* cstr = [str UTF8String];
unsigned char result[CC_MD5_DIGEST_LENGTH];
CC_MD5(cstr, (CC_LONG)strlen(cstr), result);

return [NSString stringWithFormat:
@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
result[0], result[1], result[2], result[3], result[4], result[5], result[6], result[7],
result[8], result[9], result[10], result[11], result[12], result[13], result[14], result[15]];
}

此外,CommonCrypto还支持SHA,Hmac等。

  • iOS
  • CommonCrypto
  • MD5
  • iOS开发

展开全文 >>

Mac SVN版本太旧解决办法

2016-02-24

Mac(El Captain)自带的SVN版本是1.7.20,而最新(2016.2.24)的SVN是1.9.3,用brew install svn装了最新的SVN,然而用svn --version出来的还是1.7.20。

解决办法就是将homebrew安装的svn路径加入到PATH中,具体方法如下:

  1. 找到brew安装svn的bin目录:/usr/local/Cellar/subversion/1.9.3/bin

  2. 在~/.bash_profile中将其加入到PATH中(若新机器没有bash_profile则新建一个):

    1
    export PATH="/usr/local/Cellar/subversion/1.9.3/bin:$PATH"
  3. 编译bash_profile: source ~/.bash_profile

  4. 查看svn位置: which -a svn(-a可以看到PATH中所有svn的位置)、
  5. 检查版本号: svn --version

通过其他方式安装的svn也可以通过类似方法解决。

  • mac
  • bugfix

展开全文 >>

如何重置App Store

2016-02-23

Mac的App Store有时会出现无法下载软件或是已购项目中显示该下载项目错误之类的提示,解决方法如下:

  1. 在终端内输入:

    1
    defaults write com.apple.appstore.ShowDebugMenu -bool true
  2. 重新启动AppStore,菜单栏最右会出现Debug栏,选择Reset Application以及Clear Cookies

  3. 重新开始下载项目

  • mac
  • bugfix

展开全文 >>

Mac常用App推荐

2016-02-21

这是去年整理的一篇Mac App推荐,不定期更新。

文本编辑类

  1. Mou

    Markdown编辑器

  2. MacVim

    vim编辑器

  3. MacTeX

    LaTeX for Mac,编辑器可以用TeXShop

  4. TeXStudio

    非常好用的LaTeX IDE

  5. Atom

    Github推荐的文本编辑器

  6. XMind

    最好用的思维导图软件

more >>
  • mac
  • tools

展开全文 >>

« Prev 12Next »
© 2018 Jeffrey Liu
Hexo Theme Yilia by Litten
  • 所有文章
  • 友链

tag:

  • xxd
  • file
  • bundle
  • Xcode
  • font
  • wget
  • download
  • mac
  • TensorFlow
  • macOS
  • pip
  • Python
  • iOS
  • Objective-C
  • Macros
  • objc
  • type-encoding
  • runtime
  • 吐槽
  • CommonCrypto
  • MD5

    缺失模块。
    1、请确保node版本大于6.2
    2、在博客根目录(注意不是yilia根目录)执行以下命令:
    npm i hexo-generator-json-content --save

    3、在根目录_config.yml里添加配置:

      jsonContent:
        meta: false
        pages: false
        posts:
          title: true
          date: true
          path: true
          text: false
          raw: false
          content: false
          slug: false
          updated: false
          comments: false
          link: false
          permalink: false
          excerpt: false
          categories: false
          tags: true
    

  • Charlene
  • Meeple Life