-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch.xml
116 lines (116 loc) · 95.3 KB
/
search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title><![CDATA[Git学习笔记]]></title>
<url>%2F2837359220.html</url>
<content type="text"><![CDATA[Git简介Git和其他版本控制系统如SVN的一个不同之处就是有暂存区的概念。 术语解释 Workspace:工作区 Index/Stage:暂存区,也叫索引 Repository:仓库区(或版本库) Remote:远程仓库 工作区: 通过git init创建的代码库的所有文件但是不包括.git文件(版本库) 版本库(Repository)工作区有一个隐藏目录.git,这个不算工作区,而是Git的版本库。 暂存区: 通过 git add 添加的修改,都是进入到暂存区,肉眼不可见 通过 git status 可以看到修改的状态。 还有Git为自动创建的第一个分支master,以及指向master的一个指针叫HEAD。 工作原理文件往Git版本库里添加的时候,是分两步执行的: 第一步是用git add把文件添加进去,实际上就是把文件修改添加到暂存区; 第二步是用git commit提交更改,实际上就是把暂存区的所有内容提交到当前分支。 三种状态Git 有三种状态,你的文件可能处于其中之一:已提交(committed)、已修改(modified)和已暂存(staged)。 已提交表示数据已经安全的保存在本地数据库中。 已修改表示修改了文件,但还没保存到数据库中。 已暂存表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中。 由此引入 Git 项目的三个工作区域的概念:Git 仓库、工作目录以及暂存区域。工作目录、暂存区域以及 Git 仓库如下图所示 Git 仓库目录是 Git 用来保存项目的元数据和对象数据库的地方。 这是 Git 中最重要的部分,从其它计算机克隆仓库时,拷贝的就是这里的数据。 工作目录是对项目的某个版本独立提取出来的内容。 这些从 Git 仓库的压缩数据库中提取出来的文件,放在磁盘上供你使用或修改。 暂存区域是一个文件,保存了下次将提交的文件列表信息,一般在 Git 仓库目录中。 有时候也被称作‘索引’,不过一般说法还是叫暂存区域。 用户信息安装完成后,还需要最后一步设置,在命令行输入:12$ git config --global user.name "Your Name"$ git config --global user.email "[email protected]" 注意: git config命令的–global参数,用了这个参数,表示你这台机器上所有的Git仓库都会使用这个配置,当然也可以对某个仓库指定不同的用户名和Email地址。 检查配置信息如果想要检查你的配置,可以使用 git config --list 命令来列出所有 Git 当时能找到的配置。 常用基础命令 初始化一个Git仓库,使用git init命令。出现隐藏.git目录,用ls -ah命令就可以看见。 添加文件到Git仓库,分两步: 使用命令git add <file>,注意,可反复多次使用,添加多个文件; 使用命令git commit -m <message>提交。 要查看哪些文件处于什么状态,可以用 git status 命令。 忽略文件总会有些文件无需纳入 Git 的管理,也不希望它们总出现在未跟踪文件列表。我们可以创建一个名为 .gitignore 的文件,列出要忽略的文件模式。 .gitignore 的格式规范如下: 所有空行或者以 # 开头的行都会被 Git 忽略。 可以使用标准的 glob 模式匹配。 匹配模式可以以(/)开头防止递归。 匹配模式可以以(/)结尾指定目录。 要忽略指定模式以外的文件或目录,可以在模式前加上惊叹号(!)取反。 所谓的 glob 模式是指 shell 所使用的简化了的正则表达式。 提示:GitHub 有一个十分详细的针对数十种项目及编程语言的 .gitignore 文件列表,你可以在 http://github.com/github/gitignore 找到它。 查看已暂存和未暂存的修改如果 git status 命令的输出对于你来说过于模糊,你想知道具体修改了什么地方,可以用 git diff 命令。 请注意,git diff 本身只显示尚未暂存的改动,而不是自上次提交以来所做的所有改动。 所以有时候你一下子暂存了所有更新过的文件后,运行 git diff 后却什么也没有,就是这个原因。 然后用 git diff --cached 查看已经暂存起来的变化:(--staged 和 --cached 是同义词) 跳过使用暂存区域Git 提供了一个跳过使用暂存区域的方式, 只要在提交的时候,给 git commit 加上 -a 选项,Git 就会自动把所有已经跟踪过的文件暂存起来一并提交,从而跳过 git add 步骤。 移除文件 git rm 移除已跟踪文件(同时会删除工作目录中的文件)。 如果删除之前修改过并且已经放到暂存区域的话,则必须要用强制删除选项 -f(注:即 force 的首字母)。 这是一种安全特性,用于防止误删还没有添加到快照的数据,这样的数据不能被 Git 恢复。 另外一种情况是,想让文件保留在磁盘,但是并不想让 Git 继续跟踪。 当你忘记添加 .gitignore 文件,不小心把一个很大的日志文件或一堆 .a 这样的编译生成文件添加到暂存区时,可以使用 --cached 选项:12345678910$ git rm --cached mytext.txt$ git statusOn branch masterYour branch is ahead of 'origin/master' by 1 commit. (use "git push" to publish your local commits)Changes to be committed: (use "git reset HEAD <file>..." to unstage) deleted: mytext.txt git rm 命令后面可以列出文件或者目录的名字,也可以使用 glob 模式。 移动文件1$ git mv file_from file_to 查看提交历史默认不用任何参数的话,git log 会按提交时间列出所有的更新,最近的更新排在最上面。显示最近两次提交内容差异:1$ git log -p -2 每次提交的简略的统计信息:1$ git log --stat 常用选项 --pretty 。 这个选项可以指定使用不同于默认格式的方式展示提交历史。 这个选项有一些内建的子选项供你使用。 oneline 将每个提交放在一行显示,查看的提交数很大时非常有用。 short ,full 和 fuller 可以用,展示的信息或多或少有些不同。 1$ git log --pretty=oneline 1234$ git log --pretty=format:"%h - %an, %ar : %s"ca82a6d - Scott Chacon, 6 years ago : changed the version number085bb3b - Scott Chacon, 6 years ago : removed unnecessary testa11bef0 - Scott Chacon, 6 years ago : first commit 撤消操作有时候我们提交完了才发现漏掉了几个文件没有添加,或者提交信息写错了,可以运行带有 --amend 选项的提交命令尝试重新提交:1$ git commit --amend 这个命令会将暂存区中的文件提交。 如果自上次提交以来你还未做任何修改(例如,在上次提交后马上执行了此命令),那么快照会保持不变,而你所修改的只是提交信息。 例如,提交后发现忘记了暂存某些需要的修改,可以像下面这样操作:123$ git commit -m 'initial commit'$ git add forgotten_file$ git commit --amend 最终你只会有一个提交 - 第二次提交将代替第一次提交的结果。 取消暂存的文件reset 后面不跟参数就是取消所有。1git reset HEAD mytext.txt 撤消对文件的修改1234567891011121314git statusOn branch masterYour branch is up-to-date with 'origin/master'.Changes to be committed: (use "git reset HEAD <file>..." to unstage) new file: 123Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: 123 按照说明输入命令撤销1git checkout -- 123 隐藏(Stash)操作要切换任务做其他的,但不想提交一直在做的工作; 那么可以把当前工作的改变隐藏起来。 要将一个新的存根推到堆栈上,运行 git stash 命令。通过使用 git stash list 命令来查看已存在更改的列表。执行 git stash pop 命令即可从堆栈中删除更改并将其放置在当前工作目录中。 查看远程仓库 如果想查看你已经配置的远程仓库服务器,可以运行 git remote 命令。 它会列出你指定的每一个远程服务器的简写。 指定选项 -v,会显示需要读写远程仓库使用的 Git 保存的简写与其对应的 URL。 添加远程仓库1git remote add <shortname> <url> 从远程仓库中抓取与拉取1git fetch [remote-name] 这个命令会访问远程仓库,从中拉取所有还没有的数据。执行完成后,将会拥有那个远程仓库中所有分支的引用,可以随时合并或查看。 如果使用 clone 命令克隆了一个仓库,命令会自动将其添加为远程仓库并默认以 “origin” 为简写。 所以,git fetch origin 会抓取克隆(或上一次抓取)后新推送的所有工作。 必须注意 git fetch 命令会将数据拉取到本地仓库, 它并不会自动合并或修改当前的工作。如果你有一个分支设置为跟踪一个远程分支,可以使用 git pull 命令来自动的抓取然后合并远程分支到当前分支。 这对你来说可能是一个更简单或更舒服的工作流程;默认情况下,git clone 命令会自动设置本地 master 分支跟踪克隆的远程仓库的 master 分支(或不管是什么名字的默认分支)。 运行 git pull 通常会从最初克隆的服务器上抓取数据并自动尝试合并到当前所在的分支。 推送到远程仓库推送命令: git push [remote-name] [branch-name] 。1$ git push origin master 查看远程仓库查看远程仓库的更多信息: remote show [remote-name] 。1$ git remote show origin 远程仓库的移除与重命名1$ git remote rename old new 更多详细命令请参考https://git-scm.com/book/zh/v2http://www.yiibai.com/git/]]></content>
<categories>
<category>Git</category>
<category>Git学习</category>
</categories>
<tags>
<tag>Git</tag>
<tag>总结</tag>
</tags>
</entry>
<entry>
<title><![CDATA[iOS-内存管理]]></title>
<url>%2F66907237.html</url>
<content type="text"><![CDATA[内存中的五大区域 栈区(stack): 存放函数的参数值、局部变量的值等, 由编译器自动分配释放,通常在函数执行结束后就释放了,其操作方式类似数据结构中的栈。 栈内存分配运算内置于处理器的指令集,效率很高。 分配的内存容量有限,比如iOS中栈区的大小是512k。 堆区(heap) C 语言使用 malloc、calloc、realloc 函数分配的空间,需要使用 free 函数释放 由程序员分配释放,若程序员不释放,会出现内存泄漏 分配方式类似于链表。 堆区的大小由系统决定,包括:系统内存/磁盘交换空间 静态区(BSS段):全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后,由系统释放。 常量区(数据段):常量存储在这里,不允许修改的。 代码区:存放函数体的二进制代码。 栈区职责(存储的内容) 局部变量 方法实参 栈区的特点 存储空间有限 . iphone的栈区大小只有512k(默认) ,非常有限 栈区的地址是连续的 栈区地址按照分配的顺序,由大到小顺序排列 访问速度快. 栈区的内存由系统管理 后进先出/先进后出 栈区的工作原理 开启栈帧 保存实参 保存局部变量 方法完成后弹栈,销毁栈帧,释放空间 堆区的特点堆区的大小由系统决定,包括:系统内存/磁盘交换空间 所有程序共享 存储大数据 程序员管理 堆区的地址是不连续的 速度没有栈区快 堆区的访问速度没有栈区快,因为我们要访问堆区中创建对象的属性, 必须先需要通过变量找到栈区的地址,再通过地址定位到到堆区中的某一个位置, 只有找个这个位置之后,我们才可以访问到存储到这个对象中属性对应的数值.由于有了 这个地址寻找的过程,所有速度没有栈区的快. 全局变量/静态变量/常量保存的内存区域 教科书中 全局变量 和 静态变量 有初始值保存在 数据段(常量区) 没有初始值保存在 BSS段(静态区),设置初始值后,会被移动到 数据段(常量区) Xcode 8 中 全局变量 和 静态变量 无论是否设置初始值,都保存在 BSS段(静态区) 常量 存储在 数据段(常量区)) 不能把 全局变量 定义在头文件中,否则会出现重复定义验证:12345678910111213141516171819202122232425262728293031323334353637#import <Foundation/Foundation.h>NSInteger num1 = 10;NSInteger num2;static NSInteger sNum1 = 10;static NSInteger sNum2;const NSInteger cNum = 10000;//定义一个常量int main(int argc, const char * argv[]) {@autoreleasepool {NSLog(@"第1个全局变量的地址%p", &num1);NSLog(@"第2个全局变量的地址%p", &num2);num2=100;NSLog(@"第2个全局变量的地址%p(初始化后的)", &num2);NSLog(@"##############################################");NSLog(@"第1个静态的全局变量的地址%p", &sNum1);NSLog(@"第2个静态的全局变量的地址%p", &sNum2);sNum2=100;NSLog(@"第2个静态的全局变量的地址%p(初始化后的)", &sNum2);//第2个静态的全局变量的地址0x100001230(初始化后的)NSLog(@"##############################################");NSLog(@"第1个常量的地址%p", &cNum);//第1个常量的地址0x100000e88}return 0;}第1个全局变量的地址0x100001218第2个全局变量的地址0x100001228第2个全局变量的地址0x100001228(初始化后的)#################第1个静态的全局变量的地址0x100001220第2个静态的全局变量的地址0x100001230第2个静态的全局变量的地址0x100001230(初始化后的)##################第1个常量的地址0x100000e88 静态变量 在 BSS 段为 静态变量 分配空间 为 静态变量 设置初始值,如果没有指定初始值,会使用 0 来作为初始值 static 关键字定义静态变量的代码,只会被执行一次! 正确用法 如果只有一个方法使用,将 静态变量 定义在方法内部 如果有多个方法使用,将 静态变量 定义在 .m 中 不要把静态变量定义在头文件中 常量 在 数据段 为常量分配空间 const 关键字保证其后修饰的常量的值不允许被修改 在程序被加载到内存时,就会为常量分配空间并且设置初始值 如果没有指定初始值,会使用 0 作为初始值 正确用法 在 .m 中定义常量并且设置初始值 1const NSInteger cNum = 99; 在 .h 中使用 extern 关键字声明常量在其他位置定义并且已经赋值,外部可以直接使用 1extern const NSInteger cNum; 常量名应该尽量的长以避免出现重名]]></content>
<categories>
<category>iOS</category>
</categories>
<tags>
<tag>iOS</tag>
<tag>内存管理</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Hello World]]></title>
<url>%2F1243066710.html</url>
<content type="text"><![CDATA[Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub. Quick StartCreate a new post1$ hexo new "My New Post" More info: Writing Run server1$ hexo server More info: Server Generate static files1$ hexo generate More info: Generating Deploy to remote sites1$ hexo deploy More info: Deployment]]></content>
</entry>
<entry>
<title><![CDATA[Hexo+Next主题优化]]></title>
<url>%2F3358042383.html</url>
<content type="text"><![CDATA[1. 置主题风格打开 themes/next/_config.yml 文件,搜索 scheme 关键字,将你需用启用的 scheme 前面注释 # 去除即可。123456789# ---------------------------------------------------------------# Scheme Settings# ---------------------------------------------------------------# Schemes#scheme: Muse # 默认 Scheme,这是 NexT 最初的版本,黑白主调,大量留白#scheme: Mist # Muse 的紧凑版本,整洁有序的单栏外观scheme: Pisces # 双栏 Scheme,小家碧玉似的清新#scheme: Gemini # 类似 Pisces 2. 设置菜单项的显示文本和图标NexT 使用的是 Font Awesome 提供的图标, Font Awesome 提供了 600+ 的图标,可以满足绝大的多数的场景,同时无须担心在 Retina 屏幕下图标模糊的问题。 2.1 设置菜单项的显示中文文本:打开 themes/next/languages/zh-Hans.yml 文件,搜索 menu 关键字,修改对应中文或者新增。123456789101112menu: home: 首页 archives: 归档 categories: 分类 tags: 标签 about: 关于 search: 搜索 schedule: 日程表 sitemap: 站点地图 commonweal: 公益404 # 新增menu catalogue: 目录 2.2 设定菜单项的文件目录和对应图标(新版两项合并)打开 themes/next/_config.yml 文件,搜索 menu_icons 关键字,修改对应图标名称或者新增对应 menu 的图标。123456789101112131415161718192021222324252627# ---------------------------------------------------------------# Menu Settings# ---------------------------------------------------------------# When running the site in a subdirectory (e.g. domain.tld/blog), remove the leading slash from link value (/archives -> archives).# Usage: `Key: /link/ || icon`# Key is the name of menu item. If translate for this menu will find in languages - this translate will be loaded; if not - Key name will be used. Key is case-senstive.# Value before `||` delimeter is the target link.# Value after `||` delimeter is the name of FontAwesome icon. If icon (with or without delimeter) is not specified, question icon will be loaded.menu: home: / || home archives: /archives/ || history categories: /categories/ || list tags: /tags/ || tags tools: /categories/工具资源/ || briefcase about: /about/ || user #schedule: /schedule/ || calendar #sitemap: /sitemap.xml || sitemap #commonweal: /404/ || heartbeat# Enable/Disable menu icons.# Icon Mapping:# Map a menu item to a specific FontAwesome icon name.# Key is the name of menu item and value is the name of FontAwesome icon. Key is case-senstive.# When an question mask icon presenting up means that the item has no mapping icon.menu_icons: enable: true 除了 home, archives , /后面都需要手动创建这个页面 2.3 创建菜单项对应文件目录,以分类为例在终端窗口下,定位到 Hexo 站点目录下。使用 hexo new page 新建一个页面,命名为 categories :12$ cd your-hexo-site$ hexo new page categories 编辑刚新建的页面,设置分类123456---title: 分类date: 2014-12-22 12:39:04categories: Testing #分类名type: "categories"--- 3. 头像设置3.1 添加头像打开 themes/next/_config.yml 文件,搜索 Sidebar Avatar 关键字,去掉 avatar 前面的#:1234# Sidebar Avatar# in theme directory(source/images): /images/avatar.jpg# in site directory(source/uploads): /uploads/avatar.jpgavatar: http://example.com/avatar.png 或者使用本地图片,把图片放入 themes/next/source/images 下,修改 avatar:1avatar: /images/avatar.gif 3.2 设置头像边框为圆形框打开位于 themes/next/source/css/_common/components/sidebar/sidebar-author.syl 文件,修改如下:123456789101112.site-author-image { display: block; margin: 0 auto; padding: $site-author-image-padding; max-width: $site-author-image-width; height: $site-author-image-height; border: $site-author-image-border-width solid $site-author-image-border-color; // 修改头像边框 border-radius: 50%; -webkit-border-radius: 50%; -moz-border-radius: 50%;} 3.3 特效:鼠标放置头像上旋转123456789101112131415161718192021.site-author-image { display: block; margin: 0 auto; padding: $site-author-image-padding; max-width: $site-author-image-width; height: $site-author-image-height; border: $site-author-image-border-width solid $site-author-image-border-color; // 修改头像边框 border-radius: 50%; -webkit-border-radius: 50%; -moz-border-radius: 50%; // 设置旋转 transition: 1.4s all;}// 可旋转的圆形头像,`hover`动作.site-author-image:hover { -webkit-transform: rotate(360deg); -moz-transform: rotate(360deg); -ms-transform: rotate(360deg); -transform: rotate(360deg);} 4. 浏览页面的时候显示当前浏览进度打开 themes/next/_config.yml ,搜索关键字 scrollpercent ,把 false 改为 true。12# Scroll percent label in b2t buttonscrollpercent: true 如果想把 top按钮放在侧边栏,打开 themes/next/_config.yml ,搜索关键字 b2t ,把 false 改为 true。12345# Back to top in sidebar b2t: true # Scroll percent label in b2t button scrollpercent: true 5. 侧边栏设置5.1 设置侧边栏社交链接打开 themes/next/_config.yml 文件,搜索关键字 social ,然后添加社交站点名称与地址即可。 123456789101112131415# ---------------------------------------------------------------# Sidebar Settings# ---------------------------------------------------------------# Social Links.# Usage: `Key: permalink || icon`# Key is the link label showing to end users.# Value before `||` delimeter is the target permalink.# Value after `||` delimeter is the name of FontAwesome icon. If icon (with or without delimeter) is not specified, globe icon will be loaded.social: E-Mail: mailto:[email protected] || envelope Google: https://plus.google.com/yourname || google Twitter: https://twitter.com/yourname || twitter FB Page: https://www.facebook.com/yourname || facebook # 等等 5.2 设置侧边栏社交图标打开 themes/next/_config.yml 文件,搜索关键字 social_icons ,添加社交站点名称(注意大小写)图标,Font Awesome图标地。 5.3 RSS在你 Hexo 站点目录下:1$ npm install hexo-generator-feed --save 打开 Hexo 站点下的 _config.yml ,添加如下配置:12345678# feed# Dependencies: https://github.com/hexojs/hexo-generator-feedfeed: type: atom path: atom.xml limit: 20 hub: content: 5.4 友情链接打开 themes/next/_config.yml 文件,搜索关键字 Blog rolls:1234567# Blog rollslinks_title: 友情链接 #标题links_layout: block #布局,一行一个连接#links_layout: inlinelinks: #连接 baidu: http://example.com/ google: http://example.com/ 6. 主页文章添加边框阴影效果打开 themes/next/source/css/_custom/custom.styl ,向里面加代码:12345678// 主页文章添加阴影效果.post { margin-top: 0px; margin-bottom: 60px; padding: 25px; -webkit-box-shadow: 0 0 5px rgba(202, 203, 203, .5); -moz-box-shadow: 0 0 5px rgba(202, 203, 204, .5);} 7. 修改文章间分割线打开 themes/next/source/css/_common/components/post/post-eof.styl ,修改:12345678910.posts-expand { .post-eof { display: block; // margin: $post-eof-margin-top auto $post-eof-margin-bottom; width: 0%; //分割线长度 height: 0px; // 分割线高度 background: $grey-light; text-align: center; }} 8. 代码块自定义样式123456789101112131415// Custom styles.code { color: #ff7600; background: #fbf7f8; margin: 2px;}// 边框的自定义样式.highlight, pre { margin: 5px 0; padding: 5px; border-radius: 3px;}.highlight, code, pre { border: 1px solid #d6d6d6;} 9. 开启版权声明主题配置文件下,搜索关键字 post_copyright , enable 改为 true:12345# Declare license on postspost_copyright: enable: true license: CC BY-NC-SA 4.0 license_url: https://creativecommons.org/licenses/by-nc-sa/4.0/ 10. 自定义文章底部版权声明效果:1234作者:Dragonstyle链接:http://www.dragonstyle.win/2017/09/06/Android-Studio个人设置/來源:简书版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处! 在目录 themes/next/layout/_macro/ 下添加 my-copyright.swig ,内容如下:123456789101112131415161718192021222324252627282930313233{% if page.copyright %}<div class="my_post_copyright"> <script src="//cdn.bootcss.com/clipboard.js/1.5.10/clipboard.min.js"></script> <!-- JS库 sweetalert 可修改路径 --> <script type="text/javascript" src="http://jslibs.wuxubj.cn/sweetalert_mini/jquery-1.7.1.min.js"></script> <script src="http://jslibs.wuxubj.cn/sweetalert_mini/sweetalert.min.js"></script> <link rel="stylesheet" type="text/css" href="http://jslibs.wuxubj.cn/sweetalert_mini/sweetalert.mini.css"> <p><span>本文标题:</span>{{ page.title }}</a></p> <p><span>文章作者:</span>{{ theme.author }}</a></p> <p><span>发布时间:</span>{{ page.date.format("YYYY年MM月DD日 - HH:mm:ss") }}</p> <p><span>最后更新:</span>{{ page.updated.format("YYYY年MM月DD日 - HH:mm:ss") }}</p> <p><span>原始链接:</span><a href="{{ url_for(page.path) }}" title="{{ page.title }}">{{ page.permalink }}</a> <span class="copy-path" title="点击复制文章链接"><i class="fa fa-clipboard" data-clipboard-text="{{ page.permalink }}" aria-label="复制成功!"></i></span> </p> <p><span>许可协议:</span><i class="fa fa-creative-commons"></i> <a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/" target="_blank" title="Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0)">署名-非商业性使用-禁止演绎 4.0 国际</a> 转载请保留原文链接及作者。</p> </div><script> var clipboard = new Clipboard('.fa-clipboard'); clipboard.on('success', $(function(){ $(".fa-clipboard").click(function(){ swal({ title: "", text: '复制成功', html: false, timer: 500, showConfirmButton: false }); }); })); </script>{% endif %} 在目录 themes/next/source/css/_common/components/post/ 下添加 my-post-copyright.styl,内容如下:123456789101112131415161718192021222324252627282930313233343536373839404142434445.my_post_copyright { width: 85%; max-width: 45em; margin: 2.8em auto 0; padding: 0.5em 1.0em; border: 1px solid #d3d3d3; font-size: 0.93rem; line-height: 1.6em; word-break: break-all; background: rgba(255,255,255,0.4);}.my_post_copyright p{margin:0;}.my_post_copyright span { display: inline-block; width: 5.2em; color: #333333; // title color font-weight: bold;}.my_post_copyright .raw { margin-left: 1em; width: 5em;}.my_post_copyright a { color: #808080; border-bottom:0;}.my_post_copyright a:hover { color: #0593d3; // link color text-decoration: underline;}.my_post_copyright:hover .fa-clipboard { color: #000;}.my_post_copyright .post-url:hover { font-weight: normal;}.my_post_copyright .copy-path { margin-left: 1em; width: 1em; +mobile(){display:none;}}.my_post_copyright .copy-path:hover { color: #808080; cursor: pointer;} 修改 themes/next/layout/_macro/post.swig ,在代码如下:12345{% if theme.wechat_subscriber.enabled and not is_index %} <div> {% include 'wechat-subscriber.swig' %} </div> {% endif %} 之前添加增加如下代码:12345<div> {% if not is_index %} {% include 'my-copyright.swig' %} {% endif %}</div> 修改 themes/next/source/css/_common/components/post/post.styl 文件,在最后一行增加代码:1@import "my-post-copyright" 设置新建文章自动开启 copyright,即新建文章自动显示自定义的版权声明,设置 your site/scaffolds/post.md文件12345678---title: {{ title }}date: {{ date }}tags:type: "categories"categories:copyright: true #新增,开启--- 11. 在右上角或者左上角实现fork me on github选择样式GitHub Ribbons,修改图片跳转链接,并复制文本框中的代码,将如下地方代码换为自己Github主页:打开 themes/next/layout/_layout.swig 文件,把代码复制到<div class="headband"></div>下面。 12. 修改文章底部的那个带#号的标签打开 themes/next/layout/_macro/post.swig 文件,搜索 rel="tag"># ,将 # 换成 <i class="fa fa-tag"></i>12345<div class="post-tags"> {% for tag in post.tags %} <a href="{{ url_for(tag.path) }}" rel="tag"><i class="fa fa-tag"></i> {{ tag.name }}</a> {% endfor %}</div> 13. 添加顶部加载条打开 themes/next/_config.yml ,搜索关键字 pace ,设置为 true ,可以更换加载样式:12345678910111213141516171819# Progress bar in the top during page loading.pace: true# Themes list:#pace-theme-big-counter#pace-theme-bounce#pace-theme-barber-shop#pace-theme-center-atom#pace-theme-center-circle#pace-theme-center-radar#pace-theme-center-simple#pace-theme-corner-indicator#pace-theme-fill-left#pace-theme-flash#pace-theme-loading-bar#pace-theme-mac-osx#pace-theme-minimal# For example# pace_theme: pace-theme-center-simplepace_theme: pace-theme-flash #替换更换样式 14. 本地搜索在你站点的根目录下1$ npm install hexo-generator-searchdb --save 打开 Hexo 站点的 _config.yml,添加配置12345search: path: search.xml field: post format: html limit: 10000 打开 themes/next/_config.yml ,搜索关键字 local_search ,设置为 true:123456789# Local search# Dependencies: https://github.com/flashlab/hexo-generator-searchlocal_search: enable: true # if auto, trigger search by changing input # if manual, trigger search by pressing enter key or search button trigger: auto # show top n results per article, show all results by setting to -1 top_n_per_article: 1 15. 修改网页底部 在图标库中找到你自己喜欢的图标, 修改桃心,打开 themes/next_config.yml ,搜索关键字 authoricon,替换图标名 12# icon between year and author @Footerauthoricon: id-card 隐藏网页底部 Hexo 强力驱动 打开主题配置文件,搜索关键字 copyright ,如下:12# Footer `powered-by` and `theme-info` copyrightcopyright: false 16. 博文置顶打开 Hexo 站点下 node_modules/hexo-generator-index/lib/generator.js 文件。代码全部替换为:(next 5.1以后主题已自带此功能)12345678910111213141516171819202122232425262728'use strict';var pagination = require('hexo-pagination');module.exports = function(locals){ var config = this.config; var posts = locals.posts; posts.data = posts.data.sort(function(a, b) { if(a.top && b.top) { // 两篇文章top都有定义 if(a.top == b.top) return b.date - a.date; // 若top值一样则按照文章日期降序排 else return b.top - a.top; // 否则按照top值降序排 } else if(a.top && !b.top) { // 以下是只有一篇文章top有定义,那么将有top的排在前面(这里用异或操作居然不行233) return -1; } else if(!a.top && b.top) { return 1; } else return b.date - a.date; // 都没定义按照文章日期降序排 }); var paginationDir = config.pagination_dir || 'page'; return pagination('', posts, { perPage: config.index_generator.per_page, layout: ['index', 'archive'], format: paginationDir + '/%d/', data: { __index: true } });}; 打开文章添加top字段,设置数值,数值越大文章越靠前:1234567---layout: layouttitle: 标签1date: 2017-08-18 15:41:18tags: 标签1top: 100--- 17. 统计功能,统计功能,显示文章字数统计,阅读时长,总字数在站点的根目录下:1$ npm i --save hexo-wordcount 打开 themes/next/_config.yml ,搜索关键字 post_wordcount:1234567891011# Post wordcount display settings# Dependencies: https://github.com/willin/hexo-wordcountpost_wordcount: item_text: true #字数统计 wordcount: true #预览时间 min2read: true #总字数,显示在页面底部 totalcount: true separated_meta: true 18. 修改文章内文本连接样式打开 themes/next/source/css/_custom/custom.styl,添加代码:1234567891011// 文章内链接文本样式.post-body p a{ color: #0593d3; border-bottom: none; border-bottom: 1px solid #0593d3; &:hover { color: #fc6423; border-bottom: none; border-bottom: 1px solid #fc6423; }} 19. 每篇文章末尾统一添加“本文结束”标记在路径 /themes/next/layout/_macro 中新建 passage-end-tag.swig 文件,并添加以下内容:12345<div> {% if not is_index %} <div style="text-align:center;color: #ccc;font-size:14px;">------ 本文结束------</div> {% endif %}</div> 打开 themes/next/layout/_macro/post.swig 文件,添加:12345<div> {% if not is_index %} {% include 'passage-end-tag.swig' %} {% endif %} </div> 然后打开主题配置文件 _config.yml,在末尾添加:123# 文章末尾添加“本文结束”标记passage_end_tag:enabled: true 20. 文章顶部显示更新时间打开主题配置文件 _config.yml ,搜索关键字 updated_at 设置为 true :123456# Post meta display settingspost_meta: item_text: true created_at: true updated_at: true categories: true 编辑文章,增加关键字updated(next可以根据文章改变时间自动更改)12345---layout: layouttitle: 关于date: 2017-08-18 15:41:18updated: 2017-09-05 20:18:54 #手动添加更新时间 21. 修改访问URL路径默认情况下访问URL路径为:domain/2017/08/18/关于本站,修改为 domain/About/关于本站。编辑 Hexo 站点下的 _config.yml 文件,修改其中的 permalink 字段:1permalink: :category/:title/ 22. 给代码块添加复制功能 下载插件clipboard.js 。 打开 themes/next/source/lib/ ,新建文件夹 clipboard。 把下载 clipboard.js下的 src 文件夹下的文件拖动到 clipboard文件夹下。 打开 themes/next/source/js/src/ ,新建文件 custom.js ,代码如下: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102//此函数用于创建复制按钮function createCopyBtns() { var $codeArea = $("figure table"); //查看页面是否具有代码区域,没有代码块则不创建 复制按钮 if ($codeArea.length > 0) { //复制成功后将要干的事情 function changeToSuccess(item) { $imgOK = $("#copyBtn").find("#imgSuccess"); if ($imgOK.css("display") == "none") { $imgOK.css({ opacity: 0, display: "block" }); $imgOK.animate({ opacity: 1 }, 1000); setTimeout(function() { $imgOK.animate({ opacity: 0 }, 2000); }, 2000); setTimeout(function() { $imgOK.css("display", "none"); }, 4000); }; }; //创建 全局复制按钮,仅有一组。包含:复制按钮,复制成功响应按钮 //值得注意的是:1.按钮默认隐藏,2.位置使用绝对位置 position: absolute; (position: fixed 也可以,需要修改代码) $(".post-body").before('<div id="copyBtn" style="opacity: 0; position: absolute;top:0px;display: none;line-height: 1; font-size:1.5em"><span id="imgCopy" ><i class="fa fa-paste fa-fw"></i></span><span id="imgSuccess" style="display: none;"><i class="fa fa-check-circle fa-fw" aria-hidden="true"></i></span>'); //创建 复制 插件,绑定单机时间到 指定元素,支持JQuery var clipboard = new Clipboard('#copyBtn', { target: function() { //返回需要复制的元素内容 return document.querySelector("[copyFlag]"); }, isSupported: function() { //支持复制内容 return document.querySelector("[copyFlag]"); } }); //复制成功事件绑定 clipboard.on('success', function(e) { //清除内容被选择状态 e.clearSelection(); changeToSuccess(e); }); //复制失败绑定事件 clipboard.on('error', function(e) { console.error('Action:', e.action); console.error('Trigger:', e.trigger); }); //鼠标 在复制按钮上滑动和离开后渐变显示/隐藏效果 $("#copyBtn").hover( function() { $(this).stop(); $(this).css("opacity", 1); }, function() { $(this).animate({ opacity: 0 }, 2000); } ); }}//感应鼠标是否在代码区$("figure").hover( function() { //-------鼠标活动在代码块内 //移除之前含有复制标志代码块的 copyFlag $("[copyFlag]").removeAttr("copyFlag"); //在新的(当前鼠标所在代码区)代码块插入标志:copyFlag $(this).find(".code").attr("copyFlag", 1); //获取复制按钮 $copyBtn = $("#copyBtn"); if ($copyBtn.lenght != 0) { //获取到按钮的前提下进行一下操作 //停止按钮动画效果 //设置为 显示状态 //修改 复制按钮 位置到 当前代码块开始部位 //设置代码块 左侧位置 $copyBtn.stop(); $copyBtn.css("opacity", 0.8); $copyBtn.css("display", "block"); $copyBtn.css("top", parseInt($copyBtn.css("top")) + $(this).offset().top - $copyBtn.offset().top + 3); $copyBtn.css("left", -$copyBtn.width() - 3); } }, function() { //-------鼠标离开代码块 //设置复制按钮可见度 2秒内到 0 $("#copyBtn").animate({ opacity: 0 }, 2000); });//页面载入完成后,创建复制按钮$(document).ready(function() { createCopyBtns();}); 打开 themes/next/layout/_custom/ ,新建文件 custom.swig ,代码如下: 12<script type="text/javascript" src="/lib/clipboard/clipboard.js"></script><script type="text/javascript" src="/js/src/custom.js"></script> 修改文件 themes/next/layout/_layout.swig ,在标签 </body>上面插入代码: 1{% include '_custom/custom.swig' %} 23. 新建404界面在站点根目录下,输入 hexo new page 404 ,默认在 Hexo 站点下/source/404/index.md打开新建的404界面,在顶部插入一行,写上 permalink: /404 ,这表示指定该页固定链接为 http://"主页"/404.html。123456---title: #404 Not Found:该页无法显示date: 2017-09-06 15:37:18comments: falsepermalink: /404--- 如果你不想编辑属于自己的404界面,可以显示腾讯公益404界面,代码如下:123456789101112131415161718<!DOCTYPE HTML><html><head> <meta http-equiv="content-type" content="text/html;charset=utf-8;"/> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /> <meta name="robots" content="all" /> <meta name="robots" content="index,follow"/> <link rel="stylesheet" type="text/css" href="https://qzone.qq.com/gy/404/style/404style.css"></head><body> <script type="text/plain" src="http://www.qq.com/404/search_children.js" charset="utf-8" homePageUrl="/" homePageName="回到我的主页"> </script> <script src="https://qzone.qq.com/gy/404/data.js" charset="utf-8"></script> <script src="https://qzone.qq.com/gy/404/page.js" charset="utf-8"></script></body></html> 24. 静态资源压缩在站点目录下:1$ npm install gulp -g 安装gulp插件:12345npm install gulp-minify-css --savenpm install gulp-uglify --savenpm install gulp-htmlmin --savenpm install gulp-htmlclean --savenpm install gulp-imagemin --save 在 Hexo 站点下添加 gulpfile.js文件,文件内容如下:123456789101112131415161718192021222324252627282930313233343536373839404142434445var gulp = require('gulp');var minifycss = require('gulp-minify-css');var uglify = require('gulp-uglify');var htmlmin = require('gulp-htmlmin');var htmlclean = require('gulp-htmlclean');var imagemin = require('gulp-imagemin');// 压缩css文件gulp.task('minify-css', function() { return gulp.src('./public/**/*.css') .pipe(minifycss()) .pipe(gulp.dest('./public'));});// 压缩html文件gulp.task('minify-html', function() { return gulp.src('./public/**/*.html') .pipe(htmlclean()) .pipe(htmlmin({ removeComments: true, minifyJS: true, minifyCSS: true, minifyURLs: true, })) .pipe(gulp.dest('./public'))});// 压缩js文件gulp.task('minify-js', function() { return gulp.src(['./public/**/.js','!./public/js/**/*min.js']) .pipe(uglify()) .pipe(gulp.dest('./public'));});// 压缩 public/demo 目录内图片gulp.task('minify-images', function() { gulp.src('./public/demo/**/*.*') .pipe(imagemin({ optimizationLevel: 5, //类型:Number 默认:3 取值范围:0-7(优化等级) progressive: true, //类型:Boolean 默认:false 无损压缩jpg图片 interlaced: false, //类型:Boolean 默认:false 隔行扫描gif进行渲染 multipass: false, //类型:Boolean 默认:false 多次优化svg直到完全优化 })) .pipe(gulp.dest('./public/uploads'));});// 默认任务gulp.task('default', [ 'minify-html','minify-css','minify-js','minify-images']); 只需要每次在执行 generate 命令后执行 gulp 就可以实现对静态资源的压缩,压缩完成后执行 deploy 命令同步到服务器:123hexo ggulphexo d 25. 本地站点推送到GitHub上在站点更目录下:1$ npm install hexo-deployer-git --save 在 Hexo 站点的 _config.yml 中配置 deploy:123456# Deployment## Docs: https://hexo.io/docs/deployment.htmldeploy: type: git repo: <repository url> #your github.io.git branch: master 1$ hexo clean 1$ hexo d --g hexo g # 生成本地 public 静态文件,hexo d # 部署到 Github 上,也可以缩写成:hexo g –d 。 26. 添加文章书写样式26.1 文字增加背景色块打开 themes/next/source/css/_custom 下的 custom.styl 文件,添加属性样式:1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556// 颜色块-黄span#inline-yellow {display:inline;padding:.2em .6em .3em;font-size:80%;font-weight:bold;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:0;background-color: #f0ad4e;}// 颜色块-绿span#inline-green {display:inline;padding:.2em .6em .3em;font-size:80%;font-weight:bold;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:0;background-color: #5cb85c;}// 颜色块-蓝span#inline-blue {display:inline;padding:.2em .6em .3em;font-size:80%;font-weight:bold;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:0;background-color: #2780e3;}// 颜色块-紫span#inline-purple {display:inline;padding:.2em .6em .3em;font-size:80%;font-weight:bold;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:0;background-color: #9954bb;} 在你需要编辑的文章地方。放置如下代码:1234<span id="inline-blue"> 站点配置文件 </span><span id="inline-purple"> 主题配置文件 </span><span id="inline-yellow"> 站点配置文件 </span><span id="inline-green"> 主题配置文件 </span> 26.2 下载样式打开 themes/next/source/css/_custom/custom.styl 文件,添加属性样式:1234567891011121314a#download {display: inline-block;padding: 0 10px;color: #000;background: transparent;border: 2px solid #000;border-radius: 2px;transition: all .5s ease;font-weight: bold;&:hover {background: #000;color: #fff;}} 在你需要编辑的文章地方。放置如下代码:1<a id="download" href="https://git-scm.com/download/win"><i class="fa fa-download"></i><span> Download Now</span> </a> 26.3 在文档中增加图标, Font Awesome 提供图标1<i class="fa fa-pencil"></i>支持Markdown 27. 实现点击出现桃心效果 复制网页代码 新建 love.js 文件并且将代码复制进去,然后保存。 将 love.js文件放到路径 /themes/next/source/js/src 里面 然后打开 \themes\next\layout\_layout.swig 文件,在末尾(在前面引用会出现找不到的bug)添加以下代码:12<!-- 页面点击小红心 --><script type="text/javascript" src="/js/src/love.js"></script> 28. 添加热度 next主题集成leanCloud,根据next官方文档设置阅读次数统计(LeanCloud) ,然后打开 /themes/next/layout/_macro/post.swig ,在画红线的区域添加 ℃: 然后打开 /themes/next/languages/zh-Hans.yml ,将 visitors 汉化为热度就可以了: 12345678910111213post: created: 创建于 modified: 更新于 sticky: 置顶 posted: 发表于 in: 分类于 read_more: 阅读全文 untitled: 未命名 toc_empty: 此文章未包含目录 visitors: 热度 wordcount: 字数统计 min2read: 阅读时长 totalcount: 博客全站字数 29. 添加 README.md 文件每个项目下一般都有一个 README.md 文件,但是使用 hexo 部署到仓库后,项目下是没有 README.md 文件的。 在 Hexo 目录下的 source 根目录下添加一个 README.md 文件,修改站点配置文件 _config.yml ,将 skip_render 参数的值设置为:1skip_render: README.md 保存退出即可。再次使用 hexo d 命令部署博客的时候就不会在渲染 README.md 这个文件了。 30. 文章加密访问打开 themes/next/layout/_partials/head.swig文件,在 之前插入代码: 12345678910 <script> (function(){ if('{{ page.password }}'){ if (prompt('请输入文章密码') !== '{{ page.password }}'){ alert('密码错误'); history.back(); } } })();</script> 在文章上应用:12345678910---title: 2017观看影视date: 2017-09-25 16:10:03type:top:comments:categories: [影音, 影视]tags: [影音, 电影, 电视剧, 动画]password: 123456--- 31. 添加jiathis分享在主题配置文件中,做如下配置:1234567# Share# This plugin is more useful in China, make sure you known how to use it.# And you can find the use guide at official webiste: http://www.jiathis.com/.# Warning: JiaThis does not support https.jiathis: true ##uid: Get this uid from http://www.jiathis.com/#add_this_id: 如果你想自定义话,打开 themes/next/layout/_partials/share/jiathis.swig 根据官网代码修改。 32. 修改打赏字体不闪动修改文件 next/source/css/_common/components/post/post-reward.styl,然后注释其中的函数 wechat:hover 和 alipay:hover ,如下:123456789101112/* 注释文字闪动函数 #wechat:hover p{ animation: roll 0.1s infinite linear; -webkit-animation: roll 0.1s infinite linear; -moz-animation: roll 0.1s infinite linear;} #alipay:hover p{ animation: roll 0.1s infinite linear; -webkit-animation: roll 0.1s infinite linear; -moz-animation: roll 0.1s infinite linear;}*/ 33. 自定义鼠标样式打开 themes/next/source/css/_custom/custom.styl ,在里面写下如下代码:1234567// 鼠标样式 * { cursor: url("http://om8u46rmb.bkt.clouddn.com/sword2.ico"),auto!important } :active { cursor: url("http://om8u46rmb.bkt.clouddn.com/sword1.ico"),auto!important } 其中 url 里面必须是 ico 图片,ico 图片可以上传到网上(我是使用七牛云图床),然后获取外链,复制到 url 里就行了。 34. 网站标题栏背景颜色当使用Pisces主题时,网站标题栏背景颜色是黑色的,感觉不好看,可以在 source/css/_schemes/Pisces/_brand.styl 中修改:123456789101112.site-meta { padding: 20px 0; color: white; background: $blue-dodger; //修改为自己喜欢的颜色 +tablet() { box-shadow: 0 0 16px rgba(0,0,0,0.5); } +mobile() { box-shadow: 0 0 16px rgba(0,0,0,0.5); }} 但是,我们一般不主张这样修改源码的,在 next/source/css/_custom 目录下面专门提供了 custom.styl 供我们自定义样式的,因此也可以在 custom.styl 里面添加:1234// Custom styles..site-meta { background: $blue; //修改为自己喜欢的颜色} 35. 修改内容区域的宽度我们用Next主题是发现在电脑上阅读文章时内容两边留的空白较多,这样在浏览代码块时经常要滚动滚动条才能阅读完整,体验不是很好,下面提供修改内容区域的宽度的方法。NexT 对于内容的宽度的设定如下: 700px,当屏幕宽度 < 1600px 900px,当屏幕宽度 >= 1600px 移动设备下,宽度自适应 如果你需要修改内容的宽度,同样需要编辑样式文件。在Mist和Muse风格可以用下面的方法: 编辑主题的 source/css/_variables/custom.styl 文件,新增变量:12345// 修改成你期望的宽度$content-desktop = 700px// 当视窗超过 1600px 后的宽度$content-desktop-large = 900px 当你使用Pisces风格时可以用下面的方法:123header{ width: 90%; }.container .main-inner { width: 90%; }.content-wrap { width: calc(100% - 260px); } 36. 修改Logo字体在 themes/next/source/css/_custom/custom.styl 中添加如下代码:12345678@font-face { font-family: Zitiming; src: url('/fonts/Zitiming.ttf');}.site-title { font-size: 40px !important; font-family: 'Zitiming' !important;} 其中字体文件在 themes/next/source/fonts 目录下,里面有个 .gitkeep 的隐藏文件,打开写入你要保留的字体文件,比如我的是就是写入 Zitiming.ttf ,具体字库自己从网上下载即可。 37. 添加背景图在 themes/next/source/css/_custom/custom.styl 中添加如下代码:1234567body{ background:url(/images/bg.jpg); background-size:cover; background-repeat:no-repeat; background-attachment:fixed; background-position:center;} 38. 各版块透明度修改38.1 内容板块透明博客根目录 themes\next\source\css\_schemes\Pisces\_layout.styl 文件 .content-wrap 标签下 background: white修改为:1background: rgba(255,255,255,0.7); //0.7是透明度 38.2 菜单栏背景博客根目录 themes\next\source\css\_schemes\Pisces\_layout.styl 文件 .header-inner 标签下 background: white修改为:1background: rgba(255,255,255,0.7); //0.7是透明度 38.3 站点概况背景博客根目录 themes\next\source\css\_schemes\Pisces\_sidebar.styl 文件 .sidebar-inner 标签下 background: white修改为:1background: rgba(255,255,255,0.7); //0.7是透明度 然后修改博客根目录 themes\next\source\css\_schemes\Pisces\_layout.styl 文件 .sidebar 标签下 background: $body-bg-color修改为:1background: rgba(255,255,255,0.7); //0.7是透明度 38.4 按钮背景博客根目录 themes\next\source\css\_common\components\post\post-button.styl 同上修改对应位置为 background: transparent; 39. 添加网易云音乐在网易云音乐(网页版)中搜索我们想要插入的音乐,然后点击生成外链播放器然后根据你得设置生成相应的html代码,将获得的html代码插入到你想要插入的位置。 我放在了侧边栏,在 themes/next/layout/_custom/sidebar.swig 文件中增加生成的HTML代码:1234<div id="music163player"> <iframe frameborder="no" border="0" marginwidth="0" marginheight="0" width=280 height=86 src="//music.163.com/outchain/player?type=2&id=38358214&auto=0&height=66"> </iframe></div> 可以根据自己实际情况修改宽高等样式。 参考自: http://www.jianshu.com/p/3ff20be857http://blog.csdn.net/qq_33699981/article/details/72716951http://blog.csdn.net/heqiangflytosky/article/details/54863185http://ibruce.info/2013/11/22/hexo-your-blog/]]></content>
<categories>
<category>技术</category>
<category>个人博客</category>
</categories>
<tags>
<tag>Hexo</tag>
<tag>Next主题</tag>
<tag>Hexo+Next</tag>
</tags>
</entry>
<entry>
<title><![CDATA[输入事件来源]]></title>
<url>%2F3383943387.html</url>
<content type="text"><![CDATA[输入事件来源Run loop接收输入事件来自两种不同的来源: 1.输入源(input source) 2.定时源(timer source) 两种源都使用程序的某一特定的处理例程来处理到达的事件。 当你创建输入源,你需要将其分配给run loop中的一个或多个模式。模式只会在特定事件影响监听的源。大多数情况下,run loop运行在默认模式下,但是你也可以使其运行在自定义模式。若某一源在当前模式下不被监听,那么任何其生成的消息只在run loop运行在其关联的模式下才会被传递。 1.输入源(input source)传递异步事件,通常消息来自于其他线程或程序。输入源传递异步消息给相应的处理例程,并调用runUntilDate:方法来退出(在线程里面相关的NSRunLoop对象调用)。 1.1基于端口的输入源基于端口的输入源由内核自动发送。 Cocoa和Core Foundation内置支持使用端口相关的对象和函数来创建的基于端口的源。例如,在Cocoa里面你从来不需要直接创建输入源。你只要简单的创建端口对象,并使用NSPort的方法把该端口添加到run loop。端口对象会自己处理创建和配置输入源。 在Core Foundation,你必须人工创建端口和它的run loop源。我们可以使用端口相关的函数(CFMachPortRef,CFMessagePortRef,CFSocketRef)来创建合适的对象。下面的例子展示了如何创建一个基于端口的输入源,将其添加到run loop并启动:1234567891011121314151617181920212223void createPortSource() { CFMessagePortRef port = CFMessagePortCreateLocal(kCFAllocatorDefault, CFSTR("com.someport"),myCallbackFunc, NULL, NULL); CFRunLoopSourceRef source = CFMessagePortCreateRunLoopSource(kCFAllocatorDefault, port, 0); CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopCommonModes); while (pageStillLoading) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; CFRunLoopRun(); [pool release]; } CFRunLoopRemoveSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode); CFRelease(source); } 1.2自定义输入源自定义的输入源需要人工从其他线程发送。 为了创建自定义输入源,必须使用Core Foundation里面的CFRunLoopSourceRef类型相关的函数来创建。你可以使用回调函数来配置自定义输入源。Core Fundation会在配置源的不同地方调用回调函数,处理输入事件,在源从run loop移除的时候清理它。 除了定义在事件到达时自定义输入源的行为,你也必须定义消息传递机制。源的这部分运行在单独的线程里面,并负责在数据等待处理的时候传递数据给源并通知它处理数据。消息传递机制的定义取决于你,但最好不要过于复杂。创建并启动自定义输入源的示例如下:1234567891011121314151617181920212223void createCustomSource(){ CFRunLoopSourceContext context = {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context); CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode); while (pageStillLoading) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; CFRunLoopRun(); [pool release]; } CFRunLoopRemoveSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode); CFRelease(source);} 1.3Cocoa上的Selector源除了基于端口的源,Cocoa定义了自定义输入源,允许你在任何线程执行selector方法。和基于端口的源一样,执行selector请求会在目标线程上序列化,减缓许多在线程上允许多个方法容易引起的同步问题。不像基于端口的源,一个selector执行完后会自动从run loop里面移除。 当在其他线程上面执行selector时,目标线程须有一个活动的run loop。对于你创建的线程,这意味着线程在你显式的启动run loop之前是不会执行selector方法的,而是一直处于休眠状态。 NSObject类提供了类似如下的selector方法:1- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)argwaitUntilDone:(BOOL)wait modes:(NSArray *)array; 2.定时源(timer source)定时源在预设的时间点同步方式传递消息,这些消息都会发生在特定时间或者重复的时间间隔。定时源则直接传递消息给处理例程,不会立即退出run loop。 需要注意的是,尽管定时器可以产生基于时间的通知,但它并不是实时机制。和输入源一样,定时器也和你的run loop的特定模式相关。如果定时器所在的模式当前未被run loop监视,那么定时器将不会开始直到run loop运行在相应的模式下。类似的,如果定时器在run loop处理某一事件期间开始,定时器会一直等待直到下次run loop开始相应的处理程序。如果run loop不再运行,那定时器也将永远不启动。]]></content>
<categories>
<category>RunLoop</category>
</categories>
<tags>
<tag>RunLoop</tag>
</tags>
</entry>
<entry>
<title><![CDATA[自动释放池]]></title>
<url>%2F1488828979.html</url>
<content type="text"><![CDATA[自动释放池作用 自动释放对象的 所有 autorelease 的对象,在出了作用域之后,会被自动添加到最近创建的自动释放池中 自动释放池被销毁或者耗尽时,会向池中所有对象发送 release 消息,释放池中对象 自动释放池,在 ARC & MRC 程序中,同样有效 App启动后,苹果在主线程 RunLoop 里注册了两个 Observer管理和维护AutoreleasePool,其回调都是 _wrapRunLoopWithAutoreleasePoolHandler(),打印currentRunLoop可以看到。 12<CFRunLoopObserver 0x6080001246a0 [0x101f81df0]>{valid = Yes, activities = 0x1, repeats = Yes, order = -2147483647, callout = _wrapRunLoopWithAutoreleasePoolHandler (0x1020e07ce), context = <CFArray 0x60800004cae0 [0x101f81df0]>{type = mutable-small, count = 0, values = ()}}<CFRunLoopObserver 0x608000124420 [0x101f81df0]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 2147483647, callout = _wrapRunLoopWithAutoreleasePoolHandler (0x1020e07ce), context = <CFArray 0x60800004cae0 [0x101f81df0]>{type = mutable-small, count = 0, values = ()}} 第一个 Observer 监视的事件是 Entry(即将进入Loop),其回调内会调用 _objc_autoreleasePoolPush() 创建自动释放池。其 order 是-2147483647,优先级最高,保证创建释放池发生在其他所有回调之前。 第二个 Observer 监视了两个事件,这个 Observer 的 order 是 2147483647,优先级最低,保证其释放池子发生在其他所有回调之后: BeforeWaiting(准备进入休眠)时调用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 释放旧的池并创建新池; Exit(即将退出Loop) 时调用 _objc_autoreleasePoolPop() 来释放自动释放池。 主线程的其他操作通常均在这个AutoreleasePool之内(main函数中),以尽可能减少内存维护操作(当然你如果需要显式释放【例如循环】时可以自己创建AutoreleasePool否则一般不需要自己创建)。 常见面试题:1. 自动释放池是什么时候创建的?什么时候销毁的? 创建,运行循环检测到事件并启动后,就会创建自动释放池 销毁:一次完整的运行循环结束之前,会被销毁 2. 以上代码是否有问题?如果有,如何解决?12345for (long i = 0; i < largeNumber; ++i) { NSString *str = [NSString stringWithFormat:@"hello - %ld", i]; str = [str uppercaseString]; str = [str stringByAppendingString:@" - world"];} 解决方法:引入自动释放池 1> 外面加自动释放池(快?):能够保证for循环结束后,内部产生的自动释放对象,都会被销毁,需要等到 for 结束后,才会释放内存 2> 里面加自动释放池(慢?):能够每一次 for 都释放产生的自动释放对象! 12345678910111213141516171819202122232425262728293031- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { NSLog(@"start"); CFAbsoluteTime start = CFAbsoluteTimeGetCurrent(); [self answer1]; NSLog(@"外 %f", CFAbsoluteTimeGetCurrent() - start); start = CFAbsoluteTimeGetCurrent(); [self answer2]; NSLog(@"内 %f", CFAbsoluteTimeGetCurrent() - start);}- (void)answer1 { @autoreleasepool { for (long i = 0; i < largeNumber; ++i) { NSString *str = [NSString stringWithFormat:@"hello - %ld", i]; str = [str uppercaseString]; str = [str stringByAppendingString:@" - world"]; } }}- (void)answer2 { for (long i = 0; i < largeNumber; ++i) { @autoreleasepool { NSString *str = [NSString stringWithFormat:@"hello - %ld", i]; str = [str uppercaseString]; str = [str stringByAppendingString:@" - world"]; } }} 实际测试结果,是运行循环放在内部的速度更快! 日常开发中,如果遇到局部代码内存峰值很高,可以引入运行循环及时释放延迟释放对象]]></content>
<categories>
<category>RunLoop</category>
</categories>
<tags>
<tag>RunLoop</tag>
<tag>自动释放池</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Runloop]]></title>
<url>%2F3570446412.html</url>
<content type="text"><![CDATA[RunloopRunLoop对象 CFRunLoopRef 是在 CoreFoundation 框架内的,它提供了纯 C 函数的 API,所有这些 API 都是线程安全的。 NSRunLoop 是基于 CFRunLoopRef 的封装,提供了面向对象的 API,但是这些 API 不是线程安全的。 CFRunLoopRef 的代码是开源的,你可以在这里 http://opensource.apple.com/tarballs/CF 下载到整个 CoreFoundation 的源码。 获得RunLoop对象12345678910111213//Foundation[NSRunLoop currentRunLoop]; // 获得当前线程的RunLoop对象[NSRunLoop mainRunLoop]; // 获得主线程的RunLoop对象//Core FoundationCFRunLoopGetCurrent(); // 获得当前线程的RunLoop对象CFRunLoopGetMain(); // 获得主线程的RunLoop对象//NSRunLoop <--> CFRunLoopRef 相互转化NSLog(@"NSRunLoop <--> CFRunloop == %p--%p",CFRunLoopGetMain() , [NSRunLoop mainRunLoop].getCFRunLoop);#【打印结果】:内存地址相同0000-00-13 00:30:16.527 MultiThreading[57703:1217113] NSRunLoop <--> CFRunloop == 0x60000016a680--0x60000016a680 作用 保持程序的持续运行(如:程序一启动就会开启一个主线程(中的 runloop 是自动创建并运行),runloop 保证主线程不会被销毁,也就保证了程序的持续运行)。 处理App中的各种事件(如:touches 触摸事件、NSTimer 定时器事件、Selector事件(选择器 performSelector))。 节省CPU资源,提高程序性能(有事情就做事情,没事情就休息 (其资源释放))。 负责渲染屏幕上的所有UI。 附:CFRunLoop.c 源码1234567891011121314151617181920212223#【用DefaultMode启动,具体实现查看 CFRunLoopRunSpecific Line2704】#【RunLoop的主函数,是一个死循环 dowhile】void CFRunLoopRun(void) { /* DOES CALLOUT */ int32_t result; do { /* 参数一:CFRunLoopRunSpecific 具体处理runloop的运行情况 参数二:CFRunLoopGetCurrent() 当前runloop对象 参数三:kCFRunLoopDefaultMode runloop的运行模式的名称 参数四:1.0e10 runloop默认的运行时间,即超时为10的九次方 参数五:returnAfterSourceHandled 回调处理 */ result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false); CHECK_FOR_FORK(); //【判断】:如果runloop没有停止 且 没有结束则继续循环,相反侧退出。 } while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);}#【直观表现】RunLoop 其实内部就是do-while循环,在这个循环内部不断地处理各种任务(`比如Source、Timer、Observer`),通过判断result的值实现的。所以 可以看成是一个死循环。如果没有RunLoop,UIApplicationMain 函数执行完毕之后将直接返回,就是说程序一启动然后就结束; Runloop 开启&退出验证 Runloop 是在UIApplicationMain 中开启。1234567891011121314151617# int 类型返回值UIKIT_EXTERN int UIApplicationMain(int argc, char *argv[], NSString * __nullable principalClassName, NSString * __nullable delegateClassName);int main(int argc, char * argv[]) { @autoreleasepool { NSLog(@"开始"); int number = UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); NSLog(@"结束"); return number; }}#【验证结果】:只会打印开始,并不会打印结束。----#【Runloop 的退出条件】。App退出;线程关闭;设置最大时间到期; 【注解】:说明在UIApplicationMain函数内部开启了一个和主线程相关的RunLoop (保证主线程不会被销毁),导致 UIApplicationMain 不会返回,一直在运行中,也就保证了程序的持续运行。 Runloop和线程关系 每条线程都有唯一的一个与之对应的RunLoop对象,保存在一个字典里。 创建子线程RunLoop,通过[NSRunLoop currentRunLoop]在子线程内部获取,不获取则不会创建,方法调用时,会先查看字典,有则返回,没有则创建并存入字典中。 主线程的RunLoop已经自动创建,子线程的RunLoop需要主动创建。 RunLoop在第一次获取时创建,在线程结束时销毁。 CFRunLoopRef源码1234567891011121314151617181920212223242526# 1. 主线程相关联的RunLoop创建 // 创建字典 CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks); // 创建主线程 根据传入的主线程创建主线程对应的RunLoop CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np()); // 保存主线程 将主线程-key和RunLoop-Value保存到字典中 CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);# 2. 创建与子线程相关联的RunLoop // 从字典中获取子线程的runloop CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t)); __CFUnlock(&loopsLock); if (!loop) { // 如果子线程的runloop不存在,那么就为该线程创建一个对应的runloop CFRunLoopRef newLoop = __CFRunLoopCreate(t); __CFLock(&loopsLock); loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t)); // 把当前子线程和对应的runloop保存到字典中 if (!loop) { CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop); loop = newLoop; } // don't release run loops inside the loopsLock, because CFRunLoopDeallocate may end up taking it __CFUnlock(&loopsLock); CFRelease(newLoop); } 模拟 RunLoop 实现12345678910111213141516171819202122232425262728293031#import <objc/message.h>Person *person;void callFunc(int type) { NSLog(@"正在执行 %d...%@", type, person); UInt32 result = ((UInt32 (*)(id, SEL, int, NSString *))objc_msgSend)(person, @selector(hahaha:name:), type, @"zhangsan"); NSLog(@"耗时 %u 秒", result);}int main(int argc, const char * argv[]) { @autoreleasepool { int result = 0; person = [[Person alloc] init]; while (YES) { printf("请输入选择项,0表示退出:"); scanf("%d", &result); if (result == 0) { printf("88\n"); break; } else { callFunc(result); } } } return 0;} 运行循环与时钟1234567891011121314151617181920212223@interface ViewController ()@property (nonatomic, strong) NSTimer *timer;@end@implementation ViewController- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(fire) userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];}- (void)fire { static int num = 0; /// 耗时操作 for (int i = 0; i < 1000 * 1000; ++i) { [NSString stringWithFormat:@"hello - %d", i]; } NSLog(@"%d", num++);}@end 运行测试,会发现卡顿非常严重 将时钟添加到其他线程工作 1234567891011121314151617- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(startTimer) object:nil]; [self.thread start];}- (void)startTimer { @autoreleasepool { NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(fire) userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; _timerRf = CFRunLoopGetCurrent(); CFRunLoopRun(); NSLog(@"come here"); }} 注意:主线程的运行循环是默认启动的,但是子线程的运行循环是默认不工作的,这样能够保证线程执行完毕后,自动被销毁 停止运行循环 123456789- (IBAction)stop { if (_timerRf == NULL) { return; } CFRunLoopStop(_timerRf); _timerRf = NULL;} RunLoop应用 NSTimer ImageView显示:控制方法在特定的模式下可用 PerformSelector 常驻线程:在子线程中开启一个runloop AutoreleasePool 自动释放池 UI更新 NSTimerNSTimer 其实就是 CFRunLoopTimerRef,他们之间是 toll-free bridged 的。一个 NSTimer 注册到 RunLoop 后,RunLoop 会为其重复的时间点注册好事件。例如 10:00, 10:10, 10:20 这几个时间点。RunLoop为了节省资源,并不会在非常准确的时间点回调这个Timer。Timer 有个属性叫做 Tolerance (宽容度),标示了当时间点到后,容许有多少最大误差。如果某个时间点被错过了,例如执行了一个很长的任务,则那个时间点的回调也会跳过去,不会延后执行。就比如等公交,如果 10:10 时我忙着玩手机错过了那个点的公交,那我只能等 10:20 这一趟了。 创建两种方式: 1234/// 一种是timerWithXXX,+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo/// 一种是scheduedTimerWithXXX+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo 区别 后者除了创建一个定时器外会自动以NSDefaultRunLoopModeMode添加到当前线程RunLoop中 不添加到RunLoop中的NSTimer是无法正常工作的。 注意事项 注意timer添加到runloop时应该设置为什么mode 注意timer在不需要时,一定要调用invalidate方法释放定时器 NSTimer不是一种实时机制,可能存在误差 UITableView 与 NSTimer 冲突1234567891011121314/// 1. 更改RunLoop运行Mode(NSRunLoopCommonModes)[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];/// 2. 将NSTimer放到新的线程中NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(newThread) object:nil]; [thread start];- (void)newThread{ @autoreleasepool{ //在当前Run Loop中添加timer,模式是默认的NSDefaultRunLoopMode timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(incrementCounter:) userInfo: nil repeats:YES]; //开始执行新线程的Run Loop,如果不启动run loop,timer的事件是不会响应的 [[NSRunLoop currentRunLoop] run]; } } CADisplayLink是一个执行频率(fps)和屏幕刷新相同(可以修改preferredFramesPerSecond改变刷新频率)的定时器,它也需要加入到RunLoop才能执行。与NSTimer类似,CADisplayLink同样是基于CFRunloopTimerRef实现,底层使用mk_timer(可以比较加入到RunLoop前后RunLoop中timer的变化)。和NSTimer相比它精度更高(尽管NSTimer也可以修改精度),不过和NStimer类似的是如果遇到大任务它仍然存在丢帧现象。通常情况下CADisaplayLink用于构建帧动画,看起来相对更加流畅,而NSTimer则有更广泛的用处。 PerformSelecter performSelecter:afterDelay: 实际上其内部会创建一个 Timer 并添加到当前线程的 RunLoop 中 performSelector:onThread: 实际上其会创建一个Timer加到对应的线程 UI更新当在操作 UI 时,比如改变了 Frame、更新了 UIView/CALayer 的层次时,或者手动调用了 UIView/CALayer 的 setNeedsLayout/setNeedsDisplay方法后,这个 UIView/CALayer 就被标记为待处理,并被提交到一个全局的容器去。 苹果注册了一个 Observer 监听 BeforeWaiting(即将进入休眠) 和 Exit (即将退出Loop) 事件,回调去执行一个很长的函数:_ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv()函数遍历所有待处理的 UIView/CAlayer 以执行实际的绘制和调整,并更新 UI 界面。 GCD和RunLoop的关系GCD 提供的某些接口也用到了 RunLoop, 例如 dispatch_async()。 当调用 dispatch_async(dispatch_get_main_queue(), block) 时,libDispatch 会向主线程的 RunLoop 发送消息,RunLoop会被唤醒,并从消息中取得这个 block,并在回调 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__() 里执行这个 block。但这个逻辑仅限于 dispatch 到主线程,dispatch 到其他线程仍然是由 libDispatch 处理的。。 更多RunLoop使用利用Observer对RunLoop进行监视 用perfromSelector在默认模式下设置图片,防止UITableView滚动卡顿 UITableView+FDTemplateLayoutCell利用Observer在界面空闲状态下计算出UITableViewCell的高度并进行缓存 PerformanceMonitor关于iOS实时卡顿监控 常见面试题问题1:runloop是来做什么的?runloop和线程有什么关系?主线程默认开启了runloop么?子线程呢? runloop: 从字面意思看:运行循环、跑圈,其实它内部就是do-while循环,在这个循环内部不断地处理各种任务(比如Source、Timer、Observer)事件。runloop和线程的关系:一个线程对应一个RunLoop,主线程的RunLoop默认创建并启动,子线程的RunLoop需手动创建且手动启动(调用run方法)。RunLoop只能选择一个Mode启动,如果当前Mode中没有任何Source(Sources0、Sources1)、Timer,那么就直接退出RunLoop。 问题4:苹果是如何实现Autorelease Pool的? 解答: Autorelease Pool作用:缓存池,可以避免我们经常写relase的一种方式。其实就是延迟release,将创建的对象,添加到最近的autoreleasePool中,等到autoreleasePool作用域结束的时候,会将里面所有的对象的引用计数器 - autorelease.]]></content>
<categories>
<category>iOS</category>
<category>RunLoop</category>
</categories>
<tags>
<tag>RunLoop</tag>
</tags>
</entry>
<entry>
<title><![CDATA[RunLoop内部实现]]></title>
<url>%2F1981037120.html</url>
<content type="text"><![CDATA[RunLoop内部实现内部逻辑根据苹果在文档里的说明,RunLoop 内部的逻辑大致如下: RunLoop的事件队列,官方描述看这里 通知观察者run loop已经启动 通知观察者任何即将要开始的定时器 通知观察者任何即将启动的非基于端口的源 启动任何准备好的非基于端口的源 如果基于端口的源准备好并处于等待状态,立即启动;并进入步骤9。 通知观察者线程进入休眠 将线程置于休眠直到任一下面的事件发生: 某一事件到达基于端口的源 定时器启动 Run loop设置的时间已经超时 run loop被显式唤醒 通知观察者线程将被唤醒。 处理未处理的事件 如果用户定义的定时器启动,处理定时器事件并重启run loop。进入步骤2 如果输入源启动,传递相应的消息 如果run loop被显式唤醒而且时间还没超时,重启run loop。进入步骤2 通知观察者run loop结束。 其内部代码整理如下 :123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110/// 用DefaultMode启动void CFRunLoopRun(void) { CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);} /// 用指定的Mode启动,允许设置RunLoop超时时间int CFRunLoopRunInMode(CFStringRef modeName, CFTimeInterval seconds, Boolean stopAfterHandle) { return CFRunLoopRunSpecific(CFRunLoopGetCurrent(), modeName, seconds, returnAfterSourceHandled);} /// RunLoop的实现int CFRunLoopRunSpecific(runloop, modeName, seconds, stopAfterHandle) { /// 首先根据modeName找到对应mode CFRunLoopModeRef currentMode = __CFRunLoopFindMode(runloop, modeName, false); /// 如果mode里没有source/timer/observer, 直接返回。 if (__CFRunLoopModeIsEmpty(currentMode)) return; /// 1. 通知 Observers: RunLoop 即将进入 loop。 __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopEntry); /// 内部函数,进入loop __CFRunLoopRun(runloop, currentMode, seconds, returnAfterSourceHandled) { Boolean sourceHandledThisLoop = NO; int retVal = 0; do { /// 2. 通知 Observers: RunLoop 即将触发 Timer 回调。 __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeTimers); /// 3. 通知 Observers: RunLoop 即将触发 Source0 (非port) 回调。 __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeSources); /// 执行被加入的block __CFRunLoopDoBlocks(runloop, currentMode); /// 4. RunLoop 触发 Source0 (非port) 回调。 sourceHandledThisLoop = __CFRunLoopDoSources0(runloop, currentMode, stopAfterHandle); /// 执行被加入的block __CFRunLoopDoBlocks(runloop, currentMode); /// 5. 如果有 Source1 (基于port) 处于 ready 状态,直接处理这个 Source1 然后跳转去处理消息。 if (__Source0DidDispatchPortLastTime) { Boolean hasMsg = __CFRunLoopServiceMachPort(dispatchPort, &msg) if (hasMsg) goto handle_msg; } /// 通知 Observers: RunLoop 的线程即将进入休眠(sleep)。 if (!sourceHandledThisLoop) { __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeWaiting); } /// 7. 调用 mach_msg 等待接受 mach_port 的消息。线程将进入休眠, 直到被下面某一个事件唤醒。 /// 一个基于 port 的Source 的事件。 /// 一个 Timer 到时间了 /// RunLoop 自身的超时时间到了 /// 被其他什么调用者手动唤醒 __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort) { mach_msg(msg, MACH_RCV_MSG, port); // thread wait for receive msg } /// 8. 通知 Observers: RunLoop 的线程刚刚被唤醒了。 __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopAfterWaiting); /// 收到消息,处理消息。 handle_msg: /// 9.1 如果一个 Timer 到时间了,触发这个Timer的回调。 if (msg_is_timer) { __CFRunLoopDoTimers(runloop, currentMode, mach_absolute_time()) } /// 9.2 如果有dispatch到main_queue的block,执行block。 else if (msg_is_dispatch) { __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg); } /// 9.3 如果一个 Source1 (基于port) 发出事件了,处理这个事件 else { CFRunLoopSourceRef source1 = __CFRunLoopModeFindSourceForMachPort(runloop, currentMode, livePort); sourceHandledThisLoop = __CFRunLoopDoSource1(runloop, currentMode, source1, msg); if (sourceHandledThisLoop) { mach_msg(reply, MACH_SEND_MSG, reply); } } /// 执行加入到Loop的block __CFRunLoopDoBlocks(runloop, currentMode); if (sourceHandledThisLoop && stopAfterHandle) { /// 进入loop时参数说处理完事件就返回。 retVal = kCFRunLoopRunHandledSource; } else if (timeout) { /// 超出传入参数标记的超时时间了 retVal = kCFRunLoopRunTimedOut; } else if (__CFRunLoopIsStopped(runloop)) { /// 被外部调用者强制停止了 retVal = kCFRunLoopRunStopped; } else if (__CFRunLoopModeIsEmpty(runloop, currentMode)) { /// source/timer/observer一个都没有了 retVal = kCFRunLoopRunFinished; } /// 如果没超时,mode里没空,loop也没被停止,那继续loop。 } while (retVal == 0); } /// 10. 通知 Observers: RunLoop 即将退出。 __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);} 可以看到,实际上 RunLoop 就是这样一个函数,其内部是一个 do-while 循环。当你调用 CFRunLoopRun() 时,线程就会一直停留在这个循环里;直到超时或被手动停止,该函数才会返回。 这张图更详细描述了上面Runloop的核心流程: 注意: 是_黄色_区域的消息处理中并不包含source0,因为它在循环开始之初就会处理 CFRunLoopPerformBlock尽管在上图中作为唤醒机制有所体现,但事实上执行只是入队,等待下次RunLoop运行才会执行,而如果需要立即执行则必须调用CFRunLoopWakeUp。 底层实现从上面代码第7步可以看到,RunLoop 的核心是基于 mach port 的,其进入休眠时调用的函数是 mach_msg()。 12345678/// 7. 调用 mach_msg 等待接受 mach_port 的消息。线程将进入休眠, 直到被下面某一个事件唤醒。/// 一个基于 port 的Source 的事件。/// 一个 Timer 到时间了/// RunLoop 自身的超时时间到了/// 被其他什么调用者手动唤醒__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort) { mach_msg(msg, MACH_RCV_MSG, port); // thread wait for receive msg} Mach 是 Darwin 的核心,可以说是内核的核心,提供了进程间通信(IPC)、处理器调度等基础服务。在 Mach 中,进程、线程间的通信是以消息的方式来完成的,消息在两个 Port 之间进行传递(这也正是 Source1 之所以称之为 Port-based Source 的原因,因为它就是依靠系统发送消息到指定的Port来触发的)。消息的发送和接收使用<mach/message.h>中的mach_msg()函数 为了实现消息的发送和接收,mach_msg() 函数实际上是调用了一个 Mach 陷阱 (trap),即函数mach_msg_trap(),陷阱这个概念在 Mach 中等同于系统调用。当你在用户态调用 mach_msg_trap() 时会触发陷阱机制,切换到内核态;内核态中内核实现的 mach_msg() 函数会完成实际的工作,如下图:例如你在模拟器里跑起一个 iOS 的 App,然后在 App 静止时点击暂停,你会看到主线程调用栈是停留在 mach_msg_trap() 这个地方 关于具体 mach port 发送信息可以查看:NSHipster这篇文章,或中文翻译 。]]></content>
<categories>
<category>RunLoop</category>
</categories>
<tags>
<tag>RunLoop</tag>
</tags>
</entry>
<entry>
<title><![CDATA[RunLoop相关类]]></title>
<url>%2F2615412398.html</url>
<content type="text"><![CDATA[RunLoop相关类 CFRunloopRef【RunLoop本身】 CFRunloopModeRef【Runloop的运行模式】 CFRunloopSourceRef【Runloop要处理的事件源】 CFRunloopTimerRef【Timer事件】 CFRunloopObserverRef【Runloop的观察者(监听者)】 内部关系如图:可以得出以下几点: CFRunLoopModeRef代表的是RunLoop的运行模式。 一个 RunLoop 包含若干个 Mode,每个 Mode 又包含若干个 Source/Timer/Observer。 每次调用 RunLoop 的主函数时,只能指定其中一个 Mode,这个Mode被称作 CurrentMode。 如果需要切换 Mode,只能退出 Loop,再重新指定一个 Mode 进入。这样做主要是为了分隔开不同组的 Source/Timer/Observer,让其互不影响。 注意:Source/Timer/Observer 被统称为 mode item,一个 item 可以被同时加入多个 mode。但一个 item 被重复加入同一个 mode 时是不会有效果的。如果一个 mode 中一个 item 都没有,则 RunLoop 会直接退出,不进入循环。 ModeRunLoop 有五种运行模式,其中常见的有1.2两种 kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行 UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响 UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用 GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到 kCFRunLoopCommonModes: 这是一个占位用的Mode,作为标记kCFRunLoopDefaultMode和UITrackingRunLoopMode用,并不是一种真正的Mode CFRunLoopMode 和 CFRunLoop 的结构大致如下:12345678910111213141516struct __CFRunLoopMode { CFStringRef _name; // Mode Name, 例如 @"kCFRunLoopDefaultMode" CFMutableSetRef _sources0; // Set CFMutableSetRef _sources1; // Set CFMutableArrayRef _observers; // Array CFMutableArrayRef _timers; // Array ...}; struct __CFRunLoop { CFMutableSetRef _commonModes; // Set CFMutableSetRef _commonModeItems; // Set CFRunLoopModeRef _currentMode; // Current Runloop Mode CFMutableSetRef _modes; // Set ...}; CFRunLoop对外暴露的管理 Mode 接口只有下面2个:12CFRunLoopAddCommonMode(CFRunLoopRef runloop, CFStringRef modeName);CFRunLoopRunInMode(CFStringRef modeName, ...); Mode 暴露的管理 mode item 的接口有下面几个:123456CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef modeName);CFRunLoopAddObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef modeName);CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode);CFRunLoopRemoveSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef modeName);CFRunLoopRemoveObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef modeName);CFRunLoopRemoveTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode); _commonModes:一个 mode 可以标记为 Common 属性,主线程的 RunLoop 里有两个预置的 Mode:kCFRunLoopDefaultMode 和 UITrackingRunLoopMode都已经被标记为Common属性,当然你也可以通过调用 CFRunLoopAddCommonMode() 方法将自定义mode 放到 kCFRunLoopCommonModes 组合。 commonModeItems:存放的source, observer, timer等,在每次 runLoop 运行的时候都会被同步到具有 Common 标记的 Modes 里。如:[[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes] 就是把timer放到commonModeItems 里。 更多系统或框架 Mode查看这里 SourceCFRunLoopSourceRef 是事件产生的地方。Source有两个版本:Source0 和 Source1。 Source0 (负责App内部事件,由App负责管理触发,例如UITouch事件) 只包含了一个回调(函数指针),它不能主动触发事件。使用时,你需要先调用CFRunLoopSourceSignal(source),将这个 Source 标记为待处理,然后手动调用 CFRunLoopWakeUp(runloop) 来唤醒 RunLoop,就会处理并调用事件处理方法。 Source1 包含了一个 mach_port 和一个回调(函数指针),可以监听系统端口和其他线程相互发送消息,能主动唤醒 RunLoop(由操作系统内核进行管理,例如CFMessagePort消息)。 【Port-Based Sources】:基于端口的源 (对应的是source1):与内核端口相关,只需要简单的创建端口对象,并使用 NSPort 的方法将端口对象加入到runloop,端口对象会处理创建以及配置输入源对应,Source1和Timer都属于端口事件源,不同的是所有的Timer都共用一个端口Mode Timer Port,而每个Source1都有不同的对应端口 【Custom Input Sources】:自定义源:使用CFRunLoopSourceRef 类型相关的函数 (线程) 来创建自定义输入源。 【Perform Selector Sources】:performSelector:OnThread:delay: TimerCFRunLoopTimerRef 是基于时间的触发器,上层对应NSTimer,它和 NSTimer 是toll-free bridged 的,可以混用。其包含一个时间长度和一个回调(函数指针)。当其加入到 RunLoop 时,RunLoop会注册对应的时间点,当时间点到时,RunLoop会被唤醒以执行那个回调。 Observer12345678910struct __CFRunLoopObserver { CFRuntimeBase _base; pthread_mutex_t _lock; CFRunLoopRef _runLoop; CFIndex _rlCount; CFOptionFlags _activities; /* immutable */ CFIndex _order; /* immutable */ CFRunLoopObserverCallBack _callout; /* immutable */ CFRunLoopObserverContext _context; /* immutable, except invalidation */} CFRunLoopObserverRef相当于消息循环中的一个监听器,随时通知外部当前RunLoop的运行状态(它包含一个函数指针_callout将当前状态及时告诉观察者)。具体的Observer状态如下 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ //创建监听者 /* 第一个参数 CFAllocatorRef allocator:分配存储空间 CFAllocatorGetDefault()默认分配 第二个参数 CFOptionFlags activities:要监听的状态 kCFRunLoopAllActivities 监听所有状态 第三个参数 Boolean repeats:YES:持续监听 NO:不持续 第四个参数 CFIndex order:优先级,一般填0即可 第五个参数 :回调 两个参数observer:监听者 activity:监听的事件 */ /* 所有事件 typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity{ kCFRunLoopEntry = (1UL << 0), // 即将进入Loop kCFRunLoopBeforeTimers = (1UL << 1), // 即将处理 Timer kCFRunLoopBeforeSources = (1UL << 2), // 即将处理 Source kCFRunLoopBeforeWaiting = (1UL << 5), // 即将进入休眠 kCFRunLoopAfterWaiting = (1UL << 6), // 刚从休眠中唤醒 kCFRunLoopExit = (1UL << 7), // 即将退出Loop }; */ CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) { switch (activity) { case kCFRunLoopEntry: NSLog(@"RunLoop进入"); break; case kCFRunLoopBeforeTimers: NSLog(@"RunLoop要处理Timers了"); break; case kCFRunLoopBeforeSources: NSLog(@"RunLoop要处理Sources了"); break; case kCFRunLoopBeforeWaiting: NSLog(@"RunLoop要休息了"); break; case kCFRunLoopAfterWaiting: NSLog(@"RunLoop醒来了"); break; case kCFRunLoopExit: NSLog(@"RunLoop退出了"); break; default: break; } }); // 给RunLoop添加监听者 /* 第一个参数 CFRunLoopRef rl:要监听哪个RunLoop,这里监听的是主线程的RunLoop 第二个参数 CFRunLoopObserverRef observer 监听者 第三个参数 CFStringRef mode 要监听RunLoop在哪种运行模式下的状态 */ CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode); /* CF的内存管理(Core Foundation) 凡是带有Create、Copy、Retain等字眼的函数,创建出来的对象,都需要在最后做一次release GCD本来在iOS6.0之前也是需要我们释放的,6.0之后GCD已经纳入到了ARC中,所以我们不需要管了 */ CFRelease(observer);} Call out在开发过程中几乎所有的操作都是通过Call out进行回调的(无论是Observer的状态通知还是Timer、Source的处理),而系统在回调时通常使用如下几个函数进行回调(换句话说你的代码其实最终都是通过下面几个函数来负责调用的,即使你自己监听Observer也会先调用下面的函数然后间接通知你,所以在调用堆栈中经常看到这些函数): 12345678910111213141516171819202122232425262728293031323334353637383940414243{ /// 1. 通知Observers,即将进入RunLoop /// 此处有Observer会创建AutoreleasePool: _objc_autoreleasePoolPush(); __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopEntry); do { /// 2. 通知 Observers: 即将触发 Timer 回调。 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopBeforeTimers); /// 3. 通知 Observers: 即将触发 Source (非基于port的,Source0) 回调。 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopBeforeSources); __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block); /// 4. 触发 Source0 (非基于port的) 回调。 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(source0); __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block); /// 6. 通知Observers,即将进入休眠 /// 此处有Observer释放并新建AutoreleasePool: _objc_autoreleasePoolPop(); _objc_autoreleasePoolPush(); __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopBeforeWaiting); /// 7. sleep to wait msg. mach_msg() -> mach_msg_trap(); /// 8. 通知Observers,线程被唤醒 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopAfterWaiting); /// 9. 如果是被Timer唤醒的,回调Timer __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__(timer); /// 9. 如果是被dispatch唤醒的,执行所有调用 dispatch_async 等方法放入main queue 的 block __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(dispatched_block); /// 9. 如果如果Runloop是被 Source1 (基于port的) 的事件唤醒了,处理这个事件 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__(source1); } while (...); /// 10. 通知Observers,即将退出RunLoop /// 此处有Observer释放AutoreleasePool: _objc_autoreleasePoolPop(); __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopExit);} 例如在控制器的touchBegin中打入断点查看堆栈(由于UIEvent是Source0,所以可以看到一个Source0的Call out函数CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION调用):]]></content>
<categories>
<category>RunLoop</category>
</categories>
<tags>
<tag>RunLoop</tag>
</tags>
</entry>
<entry>
<title><![CDATA[常驻线程]]></title>
<url>%2F1985083902.html</url>
<content type="text"><![CDATA[常驻线程线程创建出来就处于等待状态(有或无任务),想用它的时候就用它执行任务,不想用的时候就处于等待状态。 聊天发送语音消息,可能会专门开一个子线程来处理; 在后台记录用户的停留时间或某个按钮点击次数,这些用主线程做可能不太方便,可能会开启一个子线程后台默默收集; 解决:给子线程开启一个RunLoop123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869#import "ViewController.h"@interface ViewController ()@property(nonatomic,strong)NSThread *thread;@end@implementation ViewController- (void)viewDidLoad { [super viewDidLoad];}-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ // 创建子线程并开启 NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(show) object:nil]; self.thread = thread; [thread start];}-(void)show{ // 注意:打印方法一定要在RunLoop创建开始运行之前,如果在RunLoop跑起来之后打印,RunLoop先运行起来,已经在跑圈了就出不来了,进入死循环也就无法执行后面的操作了。 // 但是此时点击Button还是有操作的,因为Button是在RunLoop跑起来之后加入到子线程的,当Button加入到子线程RunLoop就会跑起来 NSLog(@"%s",__func__); // 1.创建子线程相关的RunLoop,在子线程中创建即可,并且RunLoop中要至少有一个Timer 或 一个Source 保证RunLoop不会因为空转而退出,因此在创建的时候直接加入 // 添加Source [NSMachPort port] 添加一个端口 [[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode]; // 添加一个Timer NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(test) userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; //创建监听者 CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) { switch (activity) { case kCFRunLoopEntry: NSLog(@"RunLoop进入"); break; case kCFRunLoopBeforeTimers: NSLog(@"RunLoop要处理Timers了"); break; case kCFRunLoopBeforeSources: NSLog(@"RunLoop要处理Sources了"); break; case kCFRunLoopBeforeWaiting: NSLog(@"RunLoop要休息了"); break; case kCFRunLoopAfterWaiting: NSLog(@"RunLoop醒来了"); break; case kCFRunLoopExit: NSLog(@"RunLoop退出了"); break; default: break; } }); // 给RunLoop添加监听者 CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode); // 2.子线程需要开启RunLoop [[NSRunLoop currentRunLoop]run]; CFRelease(observer);}- (IBAction)btnClick:(id)sender { [self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:NO];}-(void)test{ NSLog(@"%@",[NSThread currentThread]);}@end 注意:创建子线程相关的RunLoop,在子线程中创建即可,并且RunLoop中要至少有一个Timer 或 一个Source 保证RunLoop不会因为空转而退出,因此在创建的时候直接加入,如果没有加入Timer或者Source,或者只加入一个监听者,运行程序会崩溃]]></content>
<categories>
<category>RunLoop</category>
</categories>
<tags>
<tag>RunLoop</tag>
<tag>常驻线程</tag>
</tags>
</entry>
</search>