From 83fba1de9a13587f86c9d104b30bd907b6487880 Mon Sep 17 00:00:00 2001
From: DogeCoding
Date: Mon, 15 Jan 2018 02:02:32 +0800
Subject: [PATCH 1/8] First commit
---
placeholder | 0
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 placeholder
diff --git a/placeholder b/placeholder
new file mode 100644
index 0000000..e69de29
From 1da7d8f720b0e24aefd43746cffbf792b30b54de Mon Sep 17 00:00:00 2001
From: DogeCoding
Date: Mon, 15 Jan 2018 02:02:33 +0800
Subject: [PATCH 2/8] Site updated: 2018-01-15 02:02:32
---
.../index.html" | 522 ++++
.../index.html" | 516 ++++
.../index.html" | 545 ++++
.../index.html" | 496 +++
.../index.html" | 560 ++++
.../index.html" | 748 +++++
.../index.html" | 554 ++++
.../index.html" | 575 ++++
.../index.html" | 663 ++++
.../index.html" | 557 ++++
.../index.html" | 528 ++++
.../index.html" | 561 ++++
.../index.html | 521 ++++
.../index.html | 605 ++++
.../index.html" | 549 ++++
.../index.html" | 507 ++++
.../index.html" | 556 ++++
.../index.html" | 805 +++++
.../index.html" | 546 ++++
.../index.html" | 671 +++++
.../index.html" | 547 ++++
.../index.html" | 527 ++++
.../index.html" | 508 ++++
.../index.html" | 519 ++++
.../index.html" | 512 ++++
2017/08/02/title: Dispatch/index.html | 530 ++++
.../index.html" | 511 ++++
.../index.html" | 509 ++++
.../index.html" | 522 ++++
.../index.html" | 503 ++++
.../index.html" | 523 ++++
2017/08/30/title: Brew Instruction/index.html | 552 ++++
404.html | 9 +
CNAME | 1 +
about/index.html | 425 +++
archives/2017/05/index.html | 345 +++
archives/2017/07/index.html | 549 ++++
archives/2017/07/page/2/index.html | 555 ++++
archives/2017/07/page/3/index.html | 561 ++++
archives/2017/07/page/4/index.html | 391 +++
archives/2017/08/index.html | 561 ++++
archives/2017/08/page/2/index.html | 357 +++
archives/2017/index.html | 561 ++++
archives/2017/page/2/index.html | 553 ++++
archives/2017/page/3/index.html | 555 ++++
archives/2017/page/4/index.html | 561 ++++
archives/2017/page/5/index.html | 457 +++
archives/index.html | 561 ++++
archives/page/2/index.html | 553 ++++
archives/page/3/index.html | 555 ++++
archives/page/4/index.html | 561 ++++
archives/page/5/index.html | 457 +++
atom.xml | 874 ++++++
baidu_verify_pl2cC4IAHr.html | 425 +++
categories/Algorithm/index.html | 588 ++++
categories/Algorithm/page/2/index.html | 384 +++
categories/CPP/index.html | 452 +++
categories/Network/index.html | 384 +++
categories/OS/index.html | 412 +++
categories/Shell/index.html | 418 +++
categories/iOS/index.html | 594 ++++
categories/iOS/page/2/index.html | 588 ++++
categories/iOS/page/3/index.html | 424 +++
categories/index.html | 1426 +++++++++
content.json | 1 +
css/fonts/fontawesome/FontAwesome.otf | Bin 0 -> 134808 bytes
css/fonts/fontawesome/fontawesome-webfont.eot | Bin 0 -> 165742 bytes
css/fonts/fontawesome/fontawesome-webfont.svg | 2671 +++++++++++++++++
css/fonts/fontawesome/fontawesome-webfont.ttf | Bin 0 -> 165548 bytes
.../fontawesome/fontawesome-webfont.woff | Bin 0 -> 98024 bytes
.../fontawesome/fontawesome-webfont.woff2 | Bin 0 -> 77160 bytes
css/fonts/roboto/Roboto-Bold.eot | Bin 0 -> 20966 bytes
css/fonts/roboto/Roboto-Bold.ttf | Bin 0 -> 127744 bytes
css/fonts/roboto/Roboto-Bold.woff | Bin 0 -> 62876 bytes
css/fonts/roboto/Roboto-Bold.woff2 | Bin 0 -> 49976 bytes
css/fonts/roboto/Roboto-Light.eot | Bin 0 -> 20940 bytes
css/fonts/roboto/Roboto-Light.ttf | Bin 0 -> 126792 bytes
css/fonts/roboto/Roboto-Light.woff | Bin 0 -> 62316 bytes
css/fonts/roboto/Roboto-Light.woff2 | Bin 0 -> 49380 bytes
css/fonts/roboto/Roboto-Medium.eot | Bin 0 -> 21364 bytes
css/fonts/roboto/Roboto-Medium.ttf | Bin 0 -> 127488 bytes
css/fonts/roboto/Roboto-Medium.woff | Bin 0 -> 62980 bytes
css/fonts/roboto/Roboto-Medium.woff2 | Bin 0 -> 50224 bytes
css/fonts/roboto/Roboto-Regular.eot | Bin 0 -> 21320 bytes
css/fonts/roboto/Roboto-Regular.ttf | Bin 0 -> 126072 bytes
css/fonts/roboto/Roboto-Regular.woff | Bin 0 -> 61736 bytes
css/fonts/roboto/Roboto-Regular.woff2 | Bin 0 -> 49236 bytes
css/fonts/roboto/Roboto-Thin.eot | Bin 0 -> 21659 bytes
css/fonts/roboto/Roboto-Thin.ttf | Bin 0 -> 127584 bytes
css/fonts/roboto/Roboto-Thin.woff | Bin 0 -> 61628 bytes
css/fonts/roboto/Roboto-Thin.woff2 | Bin 0 -> 48524 bytes
css/style.css | 11 +
img/alipay.jpg | Bin 0 -> 236290 bytes
img/avatar.jpg | Bin 0 -> 4320 bytes
img/brand.jpg | Bin 0 -> 33276 bytes
img/cc.png | Bin 0 -> 444 bytes
img/img-err.png | Bin 0 -> 2646 bytes
img/img-loading.png | Bin 0 -> 2104 bytes
img/wechat.jpg | Bin 0 -> 306192 bytes
index.html | 872 ++++++
js/main.js | 506 ++++
js/main.min.js | 1 +
js/search.js | 141 +
js/search.min.js | 1 +
page/2/index.html | 834 +++++
page/3/index.html | 843 ++++++
page/4/index.html | 419 +++
placeholder | 0
search/index.html | 426 +++
tags/Bug/index.html | 438 +++
tags/LeetCode/index.html | 438 +++
tags/OC/index.html | 574 ++++
tags/Reading-Notes/index.html | 472 +++
tags/STL/index.html | 472 +++
tags/Swift/index.html | 506 ++++
tags/Tool/index.html | 506 ++++
tags/UI/index.html | 404 +++
tags/index.html | 1565 ++++++++++
.../\345\211\221\346\214\207Offer/index.html" | 506 ++++
.../index.html" | 404 +++
"tags/\350\275\254\350\275\275/index.html" | 472 +++
121 files changed, 47428 insertions(+)
create mode 100644 "2017/05/13/title: \345\212\250\346\200\201\350\247\204\345\210\222\357\274\210Dynamic Programming\357\274\211/index.html"
create mode 100644 "2017/07/06/title: \345\211\221\346\214\207Offer \342\200\224\342\200\224 \345\244\215\346\235\202\351\223\276\350\241\250\347\232\204\345\244\215\345\210\266/index.html"
create mode 100644 "2017/07/17/title: STL\345\270\270\350\247\201\347\224\250\346\263\225 \342\200\224 queue/index.html"
create mode 100644 "2017/07/17/title: \345\270\270\347\224\250\345\256\217\345\256\232\344\271\211/index.html"
create mode 100644 "2017/07/17/title: \346\217\222\344\273\266\347\256\241\347\220\206\342\200\224\342\200\224Vundle/index.html"
create mode 100644 "2017/07/17/title: \346\274\253\350\260\210iOS\347\263\273\345\210\227\344\271\213\357\274\232\345\206\205\345\255\230\347\256\241\347\220\206/index.html"
create mode 100644 "2017/07/17/title: \346\274\253\350\260\210iOS\347\263\273\345\210\227\344\271\213\357\274\232\345\244\232\347\272\277\347\250\213/index.html"
create mode 100644 "2017/07/21/title: STL\345\270\270\350\247\201\347\224\250\346\263\225 \342\200\224 map/index.html"
create mode 100644 "2017/07/21/title: STL\345\270\270\350\247\201\347\224\250\346\263\225 \342\200\224 vector/index.html"
create mode 100644 "2017/07/23/title: CocoaPods \345\256\211\350\243\205\345\217\212\344\270\200\350\210\254\344\275\277\347\224\250/index.html"
create mode 100644 "2017/07/23/title: OC\344\270\255ARC forbids explicit message send of '...'\351\224\231\350\257\257 /index.html"
create mode 100644 "2017/07/23/title: Objective-C\345\243\260\346\230\216\345\234\250\345\244\264\346\226\207\344\273\266\345\222\214\345\256\236\347\216\260\346\226\207\344\273\266\344\270\255\347\232\204\345\214\272\345\210\253 /index.html"
create mode 100644 2017/07/23/title: Remove Duplicates from Sorted Array /index.html
create mode 100644 2017/07/23/title: Search in Rotated Sorted Array /index.html
create mode 100644 "2017/07/23/title: self\344\270\216super\347\232\204\345\214\272\345\210\253 /index.html"
create mode 100644 "2017/07/23/title: xcode\345\270\270\347\224\250\345\277\253\346\215\267\351\224\256\345\217\212\345\205\266\344\273\226\345\212\237\350\203\275/index.html"
create mode 100644 "2017/07/23/title: \343\200\212TCP-IP\350\257\246\350\247\243\343\200\213\347\254\224\350\256\260\342\200\224\347\254\2541\347\253\240 \346\246\202\350\277\260/index.html"
create mode 100644 "2017/07/23/title: \345\270\270\350\247\201\346\216\222\345\272\217\347\256\227\346\263\225\346\200\273\347\273\223 /index.html"
create mode 100644 "2017/07/23/title: \346\234\211\351\231\220\347\212\266\346\200\201\346\234\272\345\234\250iOS\344\270\255\347\232\204\345\272\224\347\224\250/index.html"
create mode 100644 "2017/07/23/title: \350\247\206\351\242\221\345\274\200\345\217\221/index.html"
create mode 100644 "2017/07/23/title: \350\247\243\345\206\263Arduino CH34x\347\263\273\345\210\227\345\234\250macOS Sierra\344\270\255\346\211\276\344\270\215\345\210\260\344\270\262\345\217\243\347\232\204\351\227\256\351\242\230/index.html"
create mode 100644 "2017/07/25/title: iOS \344\277\235\346\214\201\347\225\214\351\235\242\346\265\201\347\225\205\347\232\204\346\212\200\345\267\247/index.html"
create mode 100644 "2017/07/25/title: \345\211\221\346\214\207Offer \342\200\224\342\200\224 \346\273\221\345\212\250\347\252\227\345\217\243\347\232\204\346\234\200\345\244\247\345\200\274/index.html"
create mode 100644 "2017/07/27/title: \345\271\266\345\217\221(concurrency)\345\222\214\345\271\266\350\241\214(parallelism)/index.html"
create mode 100644 "2017/08/01/title: \345\211\221\346\214\207Offer \342\200\224\342\200\224 \346\234\200\345\260\217\347\232\204k\344\270\252\346\225\260/index.html"
create mode 100644 2017/08/02/title: Dispatch/index.html
create mode 100644 "2017/08/03/title: Dispatch \345\234\250swift\344\270\255\347\232\204\344\275\277\347\224\250/index.html"
create mode 100644 "2017/08/03/title: \351\224\231\350\257\257\345\244\204\347\220\206/index.html"
create mode 100644 "2017/08/04/title: \345\211\221\346\214\207Offer \342\200\224\342\200\224 \344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\347\232\204\347\254\254k\344\270\252\347\273\223\347\202\271/index.html"
create mode 100644 "2017/08/20/title: Advanced Swift \347\254\224\350\256\260 \342\200\224\342\200\224 Tips/index.html"
create mode 100644 "2017/08/30/title: Advanced Swift \347\254\224\350\256\260 \342\200\224\342\200\224 \345\205\247\345\273\272\351\233\206\345\220\210\351\241\236\345\236\213/index.html"
create mode 100644 2017/08/30/title: Brew Instruction/index.html
create mode 100644 404.html
create mode 100644 CNAME
create mode 100644 about/index.html
create mode 100644 archives/2017/05/index.html
create mode 100644 archives/2017/07/index.html
create mode 100644 archives/2017/07/page/2/index.html
create mode 100644 archives/2017/07/page/3/index.html
create mode 100644 archives/2017/07/page/4/index.html
create mode 100644 archives/2017/08/index.html
create mode 100644 archives/2017/08/page/2/index.html
create mode 100644 archives/2017/index.html
create mode 100644 archives/2017/page/2/index.html
create mode 100644 archives/2017/page/3/index.html
create mode 100644 archives/2017/page/4/index.html
create mode 100644 archives/2017/page/5/index.html
create mode 100644 archives/index.html
create mode 100644 archives/page/2/index.html
create mode 100644 archives/page/3/index.html
create mode 100644 archives/page/4/index.html
create mode 100644 archives/page/5/index.html
create mode 100644 atom.xml
create mode 100644 baidu_verify_pl2cC4IAHr.html
create mode 100644 categories/Algorithm/index.html
create mode 100644 categories/Algorithm/page/2/index.html
create mode 100644 categories/CPP/index.html
create mode 100644 categories/Network/index.html
create mode 100644 categories/OS/index.html
create mode 100644 categories/Shell/index.html
create mode 100644 categories/iOS/index.html
create mode 100644 categories/iOS/page/2/index.html
create mode 100644 categories/iOS/page/3/index.html
create mode 100644 categories/index.html
create mode 100644 content.json
create mode 100644 css/fonts/fontawesome/FontAwesome.otf
create mode 100644 css/fonts/fontawesome/fontawesome-webfont.eot
create mode 100644 css/fonts/fontawesome/fontawesome-webfont.svg
create mode 100644 css/fonts/fontawesome/fontawesome-webfont.ttf
create mode 100644 css/fonts/fontawesome/fontawesome-webfont.woff
create mode 100644 css/fonts/fontawesome/fontawesome-webfont.woff2
create mode 100644 css/fonts/roboto/Roboto-Bold.eot
create mode 100644 css/fonts/roboto/Roboto-Bold.ttf
create mode 100644 css/fonts/roboto/Roboto-Bold.woff
create mode 100644 css/fonts/roboto/Roboto-Bold.woff2
create mode 100644 css/fonts/roboto/Roboto-Light.eot
create mode 100644 css/fonts/roboto/Roboto-Light.ttf
create mode 100644 css/fonts/roboto/Roboto-Light.woff
create mode 100644 css/fonts/roboto/Roboto-Light.woff2
create mode 100644 css/fonts/roboto/Roboto-Medium.eot
create mode 100644 css/fonts/roboto/Roboto-Medium.ttf
create mode 100644 css/fonts/roboto/Roboto-Medium.woff
create mode 100644 css/fonts/roboto/Roboto-Medium.woff2
create mode 100644 css/fonts/roboto/Roboto-Regular.eot
create mode 100644 css/fonts/roboto/Roboto-Regular.ttf
create mode 100644 css/fonts/roboto/Roboto-Regular.woff
create mode 100644 css/fonts/roboto/Roboto-Regular.woff2
create mode 100644 css/fonts/roboto/Roboto-Thin.eot
create mode 100644 css/fonts/roboto/Roboto-Thin.ttf
create mode 100644 css/fonts/roboto/Roboto-Thin.woff
create mode 100644 css/fonts/roboto/Roboto-Thin.woff2
create mode 100644 css/style.css
create mode 100644 img/alipay.jpg
create mode 100644 img/avatar.jpg
create mode 100644 img/brand.jpg
create mode 100644 img/cc.png
create mode 100644 img/img-err.png
create mode 100644 img/img-loading.png
create mode 100644 img/wechat.jpg
create mode 100644 index.html
create mode 100644 js/main.js
create mode 100644 js/main.min.js
create mode 100644 js/search.js
create mode 100644 js/search.min.js
create mode 100644 page/2/index.html
create mode 100644 page/3/index.html
create mode 100644 page/4/index.html
delete mode 100644 placeholder
create mode 100644 search/index.html
create mode 100644 tags/Bug/index.html
create mode 100644 tags/LeetCode/index.html
create mode 100644 tags/OC/index.html
create mode 100644 tags/Reading-Notes/index.html
create mode 100644 tags/STL/index.html
create mode 100644 tags/Swift/index.html
create mode 100644 tags/Tool/index.html
create mode 100644 tags/UI/index.html
create mode 100644 tags/index.html
create mode 100644 "tags/\345\211\221\346\214\207Offer/index.html"
create mode 100644 "tags/\345\244\232\347\272\277\347\250\213/index.html"
create mode 100644 "tags/\350\275\254\350\275\275/index.html"
diff --git "a/2017/05/13/title: \345\212\250\346\200\201\350\247\204\345\210\222\357\274\210Dynamic Programming\357\274\211/index.html" "b/2017/05/13/title: \345\212\250\346\200\201\350\247\204\345\210\222\357\274\210Dynamic Programming\357\274\211/index.html"
new file mode 100644
index 0000000..c36dac1
--- /dev/null
+++ "b/2017/05/13/title: \345\212\250\346\200\201\350\247\204\345\210\222\357\274\210Dynamic Programming\357\274\211/index.html"
@@ -0,0 +1,522 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 动态规划(Dynamic Programming) | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
动态规划(Dynamic Programming)
+
+
2017-05-13
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
(以下简称DP)
+
+
基本思想 将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。适合用DP求解的问题,经分解得到的子问题一般不是相互独立的,如果使用分治法求解,有些子问题会被重复计算多次,可以用一个表来记录所有已解决的子问题的答案,不管该子问题以后是否被用到,只要它被计算过,就将其结果填入表中。
+
DP算法适用于解最优化问题。通常可以按以下步骤设计:
+
+找出最优解的性质,并刻画其结构特征;
+递归地定义最优值;
+以自底向上的方式计算出最优值;
+根据计算最优值时得到的信息,构造最优解
+
+
+
基本要素
+最优子结构 问题的最优解包含了其子问题的最优解。
+
+重叠子问题 在用递归算法自顶向下求解问题时,每次产生的子问题并不总是新问题,有些子问题被计算多次。DP算法正式利用了这种子问题的重叠性质,对每一个子问题只解一次,而后将其解保存在一个表格中,当再次需要解此问题时,只是简单地用常数时间查看一下结果。通常不同的子问题个数随问题的大小呈多项式增长,因此用DP算法通常只需要多项式时间,从而获得较高的解题效率。
+
+
+
最长公共子序列 问题描述 一个给定序列的子序列是在该序列中删去若干元素后得到的序列。确切的说,若给定序列X = {x1, x2, ..., xm},则另一序列Z = {z1, z2, ..., zk},X的子序列是指存在一个严格递增下标序列{i1, i2, ..., ik}使得对于所有j = 1, 2, ..., k有zj = xij。例如,序列Z = {B, C, D, B}是序列X = {A, B, C, B, D, A, B}的子序列,相应的递增下标序列为{2, 3, 5, 7}。 给定两个序列X和Y,当另一序列Z既是X的子序列又是Y的子序列时,称Z是序列X和Y的公共子序列。 例如,若X = {A, B, C, B, D, A, B},Y = {B, D, C, A, B, A},序列{B, C, A}是X和Y的一个公共子序列,但它不是X和Y的最长公共子序列。序列{B, C, B, A}也是X和Y的一个公共子序列,它的长度为4,而且它是X和Y的最长公共子序列,因为X和Y没有长度大于4的公共子序列。
+
最长公共子序列问题 给定两个序列X = {x1, x2, ..., xm}和Y = {y1, y2, ..., ym},找出X和Y的最长公共子序列。
+
按照DP算法设计的各个步骤求解
+最长公共子序列结构 设序列X = {x1, x2, ..., xm}和Y = {y1, y2, ..., yn}的最长公共子序列为Z = {z1, z2, ..., zk},则
+
+若xm = yn,则zk = xm = yn,且Z
+
+
+子问题的递归结构
+
+计算最优值
+构造最长公共子序列
+算法的改进
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 谢谢大爷~
+
+
+
+
+
+
+
+
+
+
+
+ 微信
+
+ 支付宝
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git "a/2017/07/06/title: \345\211\221\346\214\207Offer \342\200\224\342\200\224 \345\244\215\346\235\202\351\223\276\350\241\250\347\232\204\345\244\215\345\210\266/index.html" "b/2017/07/06/title: \345\211\221\346\214\207Offer \342\200\224\342\200\224 \345\244\215\346\235\202\351\223\276\350\241\250\347\232\204\345\244\215\345\210\266/index.html"
new file mode 100644
index 0000000..2bb53ab
--- /dev/null
+++ "b/2017/07/06/title: \345\211\221\346\214\207Offer \342\200\224\342\200\224 \345\244\215\346\235\202\351\223\276\350\241\250\347\232\204\345\244\215\345\210\266/index.html"
@@ -0,0 +1,516 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 剑指Offer —— 复杂链表的复制 | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
剑指Offer —— 复杂链表的复制
+
+
2017-07-06
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+题目来源牛客网:复杂链表的复制
+
+
题目描述 输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
+
数据结构 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct RandomListNode {
int label;
struct RandomListNode *next, *random;
RandomListNode(int x) :
label(x), next(NULL), random(NULL) {
}
};
*/
class Solution {
public :
RandomListNode* Clone (RandomListNode* pHead) {
}
};
+
解题思路 一、递归思想:把大问题转化若干子问题 此题转化为一个头结点和除去头结点剩余部分,剩余部分操作和原问题一致
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
RandomListNode* Clone (RandomListNode* pHead)
{
if (pHead==NULL )
return NULL ;
RandomListNode* pClonedHead=new RandomListNode(pHead->label);
pClonedHead->next = pHead->next;
pClonedHead->random = pHead->random;
pClonedHead->next=Clone (pHead->next);
return pClonedHead;
}
+
二、
+复制每个节点,如:复制节点A得到A1,将A1插入节点A后面
+遍历链表,A1->random = A->random->next;
+将链表拆分成原链表和复制后的链表
+
+
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
RandomListNode* Clone(RandomListNode* pHead)
{
if (!pHead) return NULL;
RandomListNode *currNode = pHead;
while (currNode){
RandomListNode *node = new RandomListNode(currNode-> label);
node ->next = currNode-> next;
currNode -> next = node;
currNode = node-> next;
}
currNode = pHead;
while (currNode){
RandomListNode *node = currNode-> next;
if (currNode-> random){
node ->random = currNode->random -> next;
}
currNode = node-> next;
}
RandomListNode *pCloneHead = pHead-> next;
RandomListNode *tmp;
currNode = pHead;
while (currNode-> next){
tmp = currNode-> next;
currNode ->next =tmp-> next;
currNode = tmp;
}
return pCloneHead;
}
+
三、哈希表法 时间空间复杂度都是O(n)
+
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
RandomListNode* Clone (RandomListNode* pHead)
{
if (pHead==NULL )
return NULL ;
unordered_multimap<RandomListNode*,RandomListNode*> table;
RandomListNode* pClonedHead=new RandomListNode(pHead->label);
pClonedHead->next=NULL ;
pClonedHead->random=NULL ;
table.insert(make_pair(pHead,pClonedHead));
RandomListNode* pNode=pHead->next;
RandomListNode* pClonedNode=pClonedHead;
while (pNode!=NULL )
{
RandomListNode* pClonedTail=new RandomListNode(pNode->label);
pClonedTail->next=NULL ;
pClonedTail->random=NULL ;
pClonedNode->next=pClonedTail;
pClonedNode=pClonedTail;
table.insert(make_pair(pNode,pClonedTail));
pNode=pNode->next;
}
pNode=pHead;
pClonedNode=pClonedHead;
while (pNode!=NULL )
{
if (pNode->random!=NULL )
{
pClonedNode->random=table.find(pNode->random)->second;
}
pNode=pNode->next;
pClonedNode=pClonedNode->next;
}
return pClonedHead;
}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 谢谢大爷~
+
+
+
+
+
+
+
+
+
+
+
+ 微信
+
+ 支付宝
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git "a/2017/07/17/title: STL\345\270\270\350\247\201\347\224\250\346\263\225 \342\200\224 queue/index.html" "b/2017/07/17/title: STL\345\270\270\350\247\201\347\224\250\346\263\225 \342\200\224 queue/index.html"
new file mode 100644
index 0000000..81320a4
--- /dev/null
+++ "b/2017/07/17/title: STL\345\270\270\350\247\201\347\224\250\346\263\225 \342\200\224 queue/index.html"
@@ -0,0 +1,545 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ STL常见用法 — queue | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
STL常见用法 — queue
+
+
2017-07-17
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+代码敲得少,STL总是用的不熟,便自己整理一些用法,有眼缘的顺手用着吧。
+
+
+
简介 队列(queue)是一种特殊的线性表,是一种先进先出(First In First Out)的数据结构,允许在队列末尾插入元素,队列头取出元素,在STL中是用list或者deque实现,封闭头部即可。
+
用法 头文件
+
构造函数 1
template <class T , class Container = deque <T> > class queue ;
+
队列适配器默认用deque容器实现,也可以指定使用list容器来实现
+
1
2
3
queue <Elem> q;
queue <Elem, list <Elem> > q;
queue <Elem> q1(q2);
+
成员函数
+
+
+成员函数
+实现操作
+
+
+
+
+ Elem& back()
+返回队列最后一个元素
+
+
+ bool empty()const
+如果队列为空,返回true,否则返回false
+
+
+ Elem& front()
+返回队列第一个元素
+
+
+ void pop()
+移除队列中的第一个元素
+
+
+ void push(const Elem& e)
+在队列末尾插入元素e
+
+
+ size_type size()const
+返回队列中的元素数目
+
+
+
+
用法演示 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#include <queue>
using namespace std ;
int main ()
{
int n, m, size;
queue <int > q;
q.push(1 );
q.push(2 );
while (!q.empty())
{
n = q.front();
m = q.back();
size = q.size();
q.pop();
printf ("%d %d %d\n" , n, m, size);
}
return 0 ;
}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 谢谢大爷~
+
+
+
+
+
+
+
+
+
+
+
+ 微信
+
+ 支付宝
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git "a/2017/07/17/title: \345\270\270\347\224\250\345\256\217\345\256\232\344\271\211/index.html" "b/2017/07/17/title: \345\270\270\347\224\250\345\256\217\345\256\232\344\271\211/index.html"
new file mode 100644
index 0000000..ec0a442
--- /dev/null
+++ "b/2017/07/17/title: \345\270\270\347\224\250\345\256\217\345\256\232\344\271\211/index.html"
@@ -0,0 +1,496 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 常用宏定义 | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
常用宏定义
+
+
2017-07-17
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
iOS开发高级:使用宏定义macros
+
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
#ifdef DEBUG
# define DLog(...) NSLog(__VA_ARGS__)
#else
# define DLog(...)
#endif
#define kScreenWidth ([UIScreen mainScreen].bounds.size.width)
#define kScreenHeight ([UIScreen mainScreen].bounds.size.height)
#define RGB(r, g, b, a) [UIColor colorWithRed:r/255.0f green:g/255.0f blue:b/255.0f alpha:a]
#define HEXCOLOR(c) [UIColor colorWithRed:((c>>16)&0xFF)/255.0f green:((c>>8)&0xFF)/255.0f blue:(c&0xFF)/255.0f alpha:1.0f]
#define BACKGROUND_COLOR [UIColor colorWithRed:242.0/255.0 green:236.0/255.0 blue:231.0/255.0 alpha:1.0]
#define CLEARCOLOR [UIColor clearColor]
#define LOADIMAGE(file,type) [UIImage imageWithContentsOfFile:[[NSBundle mainBundle]pathForResource:file ofType:type]]
#define NavigationBar_HEIGHT 44
#define IOS_VERSION [[[UIDevice currentDevice] systemVersion] floatValue]
#define CurrentSystemVersion [[UIDevice currentDevice] systemVersion]
#if TARGET_OS_IPHONE
#endif
#if TARGET_IPHONE_SIMULATOR
#endif
#define VIEWWITHTAG(_OBJECT, _TAG) [_OBJECT viewWithTag : _TAG]
#define BACK(block) dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block)
#define MAIN(block) dispatch_async(dispatch_get_main_queue(),block)
#define USER_DEFAULT [NSUserDefaults standardUserDefaults]
+
+
+
+
+
+
+
+ Last updated: 2018-01-13 13:07:28
+
+
+
+
+
http://codingdoge.cn/2017/07/17/title: 常用宏定义/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 谢谢大爷~
+
+
+
+
+
+
+
+
+
+
+
+ 微信
+
+ 支付宝
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git "a/2017/07/17/title: \346\217\222\344\273\266\347\256\241\347\220\206\342\200\224\342\200\224Vundle/index.html" "b/2017/07/17/title: \346\217\222\344\273\266\347\256\241\347\220\206\342\200\224\342\200\224Vundle/index.html"
new file mode 100644
index 0000000..a430ac6
--- /dev/null
+++ "b/2017/07/17/title: \346\217\222\344\273\266\347\256\241\347\220\206\342\200\224\342\200\224Vundle/index.html"
@@ -0,0 +1,560 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 插件管理——Vundle | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
插件管理——Vundle
+
+
2017-07-17
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+都说Vim是程序员写给自己的编辑器,其中的情结可想而知。
+
+
vim因为其庞大而强劲的插件受到无比的推崇,而插件的查找和管理便成了一个问题。
+
Vundle 便是一个Github 上为了解决这个问题的项目(致敬贡献者们),使用步骤如下:
+
安装Vundle,在终端输入以下代码即可 git clone http://github.com/gmarik/vundle.git ~/.vim/bundle/vundle
+
+
+~/.vim 来自哪? 在mac 中Vim 配置文件.vimrc 在/usr/share/vim/下,一般是没有权限更改的,但是这个.vimrc是全局配置文件,我们只要更改用户配置文件即可
+怎么查看/usr/ ?
+显示隐藏文件 ,自行百度,终端中敲入代码即可。
+Finder 下或者桌面 上的Go,文件夹输入/usr/。mac 下的Vim用户配置文件默认是没有的,需要我们自行创建 终端下输入1
2
3
> mkdir ~/.vim
> touch ~/.vimrc
>
+
+
+
+
+
+~/即为用户根目录。
+
+
+
在.vimrc文件中加一句Bundle plugin_name.vimrc示例:1
2
3
4
5
6
7
8
9
10
11
set nocompatible
filetype off
set rtp+=~/.vim /bundle/vundle/
call vundle#rc()
Bundle 'gmarik/vundle'
Bundle 'vim-plugin-foo'
Bundle 'vim-plugin-bar'
filetype plugin indent on
+
执行Vundle安装命令
+
Vundle的其它命令
+
+
+:BundleList
+列举出列表中(.vimrc中)配置的所有插件
+
+
+
+
+:BundleInstall
+安装列表中全部插件
+
+
+:BundleInstall!
+更新列表中全部插件
+
+
+:BundleSearch foo
+查找foo插件
+
+
+:BundleSearch! foo
+刷新foo插件缓存
+
+
+:BundleClean
+清除列表中没有的插件
+
+
+:BundleClean!
+清除列表中没有的插件
+
+
+
+
+参考Git时代的VIM不完全使用教程 使用Vundle来管理vim的插件 zhongcq 的VIM配置
+
+
+
+
+
+
+
+
+ Last updated: 2018-01-15 01:19:40
+
+
+
+
+
http://codingdoge.cn/2017/07/17/title: 插件管理——Vundle/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 谢谢大爷~
+
+
+
+
+
+
+
+
+
+
+
+ 微信
+
+ 支付宝
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git "a/2017/07/17/title: \346\274\253\350\260\210iOS\347\263\273\345\210\227\344\271\213\357\274\232\345\206\205\345\255\230\347\256\241\347\220\206/index.html" "b/2017/07/17/title: \346\274\253\350\260\210iOS\347\263\273\345\210\227\344\271\213\357\274\232\345\206\205\345\255\230\347\256\241\347\220\206/index.html"
new file mode 100644
index 0000000..28cbe9d
--- /dev/null
+++ "b/2017/07/17/title: \346\274\253\350\260\210iOS\347\263\273\345\210\227\344\271\213\357\274\232\345\206\205\345\255\230\347\256\241\347\220\206/index.html"
@@ -0,0 +1,748 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 漫谈iOS系列之:内存管理 | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
漫谈iOS系列之:内存管理
+
+
2017-07-17
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
引用计数 推荐一篇来自@杨萧玉的引用计数原理Blog
+
+
+简介 iOS中对内存管理的机制(堆内存),每一个对象都有一个与之关联的引用计数(Reference Counting)。当一个对象“被拥有”的时候引用计数+1,当一个对象引用计数为零时该对象被释放。
+比拟 比如上班,最早进入办公室的人需要开灯,之后进入办公室的人需要照明, 下班离开办公室的人不需要照明,最后离开办公室的人需要关灯。 这样对应的引用计数就是:第一个人进入办公室开灯,引用计数是1。之后进入办公室需要照明引用计数是2。下班一个人离开办公室引用计数变成了1,最后一个离开了办公室,引用计数变成了0 。
+引用计数如何储存
+TaggedPointer一篇极好的文章 总体来说,我的理解是如果一个对象使用了Tagged Pointer 技术(比如NSString ,NSNumber 等),指针里面会直接存数据内容,不会再作为“指针”指向其它地址,从Runtime来理解就是不会使用isa指针,也就不会继承苹果的内存管理方式(Reference Counting)。 判断当前对象是否在使用 TaggedPointer 是看标志位是否为1:
+
+
+
+
1
2
3
4
5
6
7
8
9
10
11
12
13
#if SUPPORT_MSB_TAGGED_POINTERS
# define TAG_MASK (1ULL<<63)
#else
# define TAG_MASK 1
inline bool
objc_object::isTaggedPointer()
{
#if SUPPORT_TAGGED_POINTERS
return ((uintptr_t )this & TAG_MASK);
#else
return false ;
#endif
}
+
+isa 指针 指针的内存空间很大,有时候可以优化指针,在指针中存储一部分内容。下面列出不同架构下的64位环境中isa指针结构:
+
+
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
union isa_t
{
isa_t () { }
isa_t (uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
#if SUPPORT_NONPOINTER_ISA
# if __arm64__
# define ISA_MASK 0x00000001fffffff8ULL
# define ISA_MAGIC_MASK 0x000003fe00000001ULL
# define ISA_MAGIC_VALUE 0x000001a400000001ULL
struct {
uintptr_t indexed : 1 ;
uintptr_t has_assoc : 1 ;
uintptr_t has_cxx_dtor : 1 ;
uintptr_t shiftcls : 30 ;
uintptr_t magic : 9 ;
uintptr_t weakly_referenced : 1 ;
uintptr_t deallocating : 1 ;
uintptr_t has_sidetable_rc : 1 ;
uintptr_t extra_rc : 19 ;
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
};
# elif __x86_64__
# define ISA_MASK 0x00007ffffffffff8ULL
# define ISA_MAGIC_MASK 0x0000000000000001ULL
# define ISA_MAGIC_VALUE 0x0000000000000001ULL
struct {
uintptr_t indexed : 1 ;
uintptr_t has_assoc : 1 ;
uintptr_t has_cxx_dtor : 1 ;
uintptr_t shiftcls : 44 ;
uintptr_t weakly_referenced : 1 ;
uintptr_t deallocating : 1 ;
uintptr_t has_sidetable_rc : 1 ;
uintptr_t extra_rc : 14 ;
# define RC_ONE (1ULL<<50)
# define RC_HALF (1ULL<<13)
};
# else
# error unknown architecture
# endif
#endif
};
+
只有arm64 架构的设备支持优化,下面列出了isa指针中变量对应的含义:
+
+
+
+变量名
+含义
+
+
+
+
+indexed
+0 表示普通的isa指针,1 表示使用优化,存储引用计数
+
+
+has_assoc
+表示该对象是否包含 associated object,如果没有,则析构时会更快
+
+
+has_cxx_dtor
+表示该对象是否有 C++ 或 ARC 的析构函数,如果没有,则析构时更快
+
+
+shiftcls
+类的指针
+
+
+magic
+固定值为 0xd2,用于在调试时分辨对象是否未完成初始化
+
+
+weakly_referenced
+表示该对象是否有过weak对象,如果没有,则析构时更快
+
+
+deallocating
+表示该对象是否正在析构
+
+
+has_sidetable_rc
+表示该对象的引用计数值是否过大无法存储在isa指针
+
+
+extra_rc
+存储引用计数值减一后的结果
+
+
+
+
+散列表 散列表来存储引用计数具体是用DenseMap类来实现,实现中有锁保证其安全性。
+
+
+获取引用计数 在MRC环境下可以使用retainCount方法获取某个对象的引用计数。 在ARC环境下可以使用Core Foundation 库的CFGetRetainCount((__bridge CFTypeRef)(obj))方法和Runtime的_objc_rootRetainCount()方法来获取引用计数,也可以使用KVC技术来获取valueForKey:@"retainCount"。注意以上方法不是线程安全的。
+注意 NSString 定义的对象是保存在字符串常量区,没有用引用计数管理内存,如果输出其retainCount,为-1。
+
+
+
+
+
+ retainCount
+ 注意其中的Do not use this method 。
+
+
+
+
MRC(Manual Reference Counting)
+
MRC从字面上理解就是手动管理引用计数,也就是手动管理内存。相关的内存管理方法有retain,release,autorelease,其中retain方法是对引用计数+1,相应的release是对引用计数-1,autorelease是将对象加入自动释放池,下文会讲到。
+
+示例代码
+1
2
3
4
5
6
7
// 以预定Person 类为例
Person * person = ; // 申请对象,此时引用计数=1
; //此时引用记数+1,现为2
; //引用计数-1,此时引用计数=1
; //引用计数-1,此时引用计数=0,内存被释放
; // 将对象加入自动释放池
Person *person = ; // 也可以在创建对象时将其加入自动释放池
+
+
+
按道理来说创建一个对象,然后release后该对象引用计数为零,但是实际情况中并不会出现这种现象,release后再输出其引用计数还是为1,在我的理解中有两种可能:
+
+该对象在引用计数为1的时候进行release后,对象已经被释放,此时再调用retainCount毫无意义,因为该对象已经不存在了,为了防止某些错误保护这个retainCount方法所以编译器不会报错,但是输出值为释放前的值;
+编译器为我们做了各种优化,也许是记录retainCount为零消耗过大或者没有意义。
+
+
+
+
+ 重写了`dealloc`方便查看对象是否被释放
+
+
+
+
+
+
+ 输出其`retainCount`然后释放
+
+
+
+
+
+
+ 可以看到并不会出现引用计数为零的情况,但是该对象确实被释放了
+
+
+
+
+小知识: 指针错误 :访问了一块坏的内存(已经被回收的,不可用的内存)。僵尸对象 :所占内存已经被回收的对象,僵尸对象不能再被使用。(打开僵尸对象检测)空指针 :没有指向任何东西的指针(存储的东西是0, null, nil),给空指针发送消息不会报错。注意 :不能使用[p retaion]让僵尸对象起死复生。
+
+
在MRC管理时代有一个黄金法则:
+
+谁创建谁负责。如果你通过alloc,new,copy来创建了一个对象,那么你就必须调用release或者autorelease方法;
+谁retain,谁release。只要你调用了retain,无论这个对象时如何生成的,你都要调用release;
+
+
ARC
+
原理 前段编译器会为“拥有的”每一个对象加入相应的release语句,如果对象的所有权修饰符是__strong,那么它就是被拥有的。如果再某个方法内创建了一个对象,前端编译器会在方法末尾自动插入release语句已销毁它。而类拥有的对象(实例变量/属性)会在dealloc方法内被释放。
+
+
+
+
+ 编译器所为
+
+
编译器为我们做的,我们可以手动完成达到优化 比如:__autoreleasing在ARC中主要用在参数传递返回值(out-parameters)和引用传递参数(pass-by-reference)的情况下,有这种指针(NSError **)的函数参数如果不加修饰符,编译器会默认将他们认定为__autoreleasing类型。 比如常用的NSError的使用:
+
1
2
3
4
5
NSError *__autoreleasing error ;
if (![data writeToFile:filename options:NSDataWritingAtomic error :&error ])
{
NSLog(@"Error: %@" , error );
}
+
如果你把error定义为了strong型,编译器会隐式地做如下事情,保证最终传入函数的参数依然是个__autoreleasing类型的引用。
+
1
2
3
4
5
6
7
NSError *error ;
NSError *__autoreleasing tempError = error ;
if (![data writeToFile:filename options:NSDataWritingAtomic error :&tempError])
{
error = tempError;
NSLog(@"Error :%@" , error );
}
+
所以为了提高效率,避免这种情况,我们一般在定义error的时候将其老老实实地声明为__autoreleasing类型。
+
循环引用
+
平常我们容易造成循环引用的三种情况:
+
+NSTimer 先看NSTimer使用的代码:
+
+
1
_timer = [NSTimer scheduledTimerWithTimeInterval: 1.0 target: self selector: @selector (runTimer) userInfo: nil repeats: YES];
+
其中_timer是实例变量被self保留,_timer的target是self,self被_timer保留,引发循环引用。
+
+
+
+
+ 循环引用
+ 解除方法就是使target中的对象不是viewController从而断开引用,iOS10之前我们可以写个类别重新封装target来实现,iOS10之后系统给了新方法:
+
1
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval )interval repeats:(BOOL )repeats block:(void (^)(NSTimer *timer))block;
+
+不再需要target,而是传入一个block,在block里面进行循环调用方法
+关于block怎么解决循环引用请看下面
+
+
+block
+简介 block和其他语言的闭包或lambda表达式是一回事,block的使用很像函数指针,不过与函数最大的不同是:block可以访问函数以外、词法作用域以内的外部变量的值。换句话说,block不仅实现函数的功能,还能携带函数的执行环境。
+
+
+
+
block基本语法
+
1
2
3
4
5
6
7
8
9
long (^sum ) (int , int ) = nil;
sum = ^ long (int a, int b) {
return a + b;
};
long s = sum (1 , 2 );
+
定义一个实例函数,该函数返回block:
+
1
2
3
4
5
6
7
8
- (long (^)(int , int )) sumBlock {
int base = 100 ;
return [[ ^ long (int a, int b) {
return base + a + b;
} copy] autorelease];
}
[self sumBlock ](1 ,2 );
+
根据在内存中的位置将block分为三种类型:
+
* `NSGlobalBlock`: 类似函数,位于text段;
+* `NSStackBlock`: 位于栈内存,函数返回后block将无效;
+* `NSMallocBlock`: 位于堆内存。
+block其实包含两个部分内容:
+
+block执行的代码,这是在编译的时候已经生成好的;
+一个包含block执行时需要的所有外部变量值的数据结构。 block将使用到的、作用域附近到的变量的值建立一份快照拷贝到栈上。
+
+
+
+
+ block的数据结构
+
+
+
+
对于 block 外的变量引用,block 默认是将其复制到其数据结构中来实现访问的:
+
+
+
+
+ 传入外部变量
+ 对于用 __block 修饰的外部变量引用,block 是复制其引用地址来实现访问的:
+
+
+
+
+ 用__block修饰
+
+
初步了解了block后看看它怎么构成循环引用并怎么解决的吧
+
1
2
3
4
5
6
7
8
9
10
11
12
13
typedef void (^block)();
@property (copy , nonatomic ) block myBlock;
@property (copy , nonatomic ) NSString *blockString;
- (void )testBlock {
self .myBlock = ^() {
NSString *localString = self .blockString;
};
}
+
看了前面关于block的一些介绍应该容易看出来,当我们往block中传入数据时是保存在了block的堆中,如上述代码中引用了self相当于对self进行了一次retain,而self本身持有block于是造成了循环引用,同时在block中release``self没有用,因为在block中操作作用范围仅仅来block的函数栈,影响不到堆中的self,解决方法如下:
+
1
2
3
4
5
__weak typeof (self ) weakSelf = self ;
self .myBlock = ^(){
__strong typeof (weakSelf) = strongSelf;
NSString *localString = strongSelf;
}
+
其中传入一个若引用就不会造成循环引用,然后在block的函数栈中用一个强指针来接受传进来的弱指针,防止弱指针被提前释放产生野指针。
+
+参考文章: Cooper – 正确使用Block避免Cycle Retain和Crash 唐巧 – 谈Objective-C block的实现 Dev Talking – Objective-C中的Block
+
+
+delegate 我们对代理的写法一般都是:1
@property (nonatomic , weak ) id <TestDelegate> delegate;
+
+
+
如果使用strong的话很明显会造成循环引用(delegate调用self的一些东西),今天被面试官问道如果使用delegate出现了循环引用怎么解决,我说用weak,他说换一个,然后就懵住了,只回答了思路,找到互相引用的对象(可以用Instruments)然后断开引用。
+
Autorelease
+简介 很好理解,字面意思上看就是自动释放,我们可以通过使用autorelease让编译器帮我们在某个时刻自动释放内存。在MRC时我们使用NSAutorelease类来使用自动释放机制,代码如下:
+
+
1
2
3
NSAutoreleasePool pool = [[NSAutoreleasePool alloc] init];
[pool release];
+
也可以直接使用[obj autorelease]。 现在基本上都是ARC环境,这个时候我们使用的是autoreleasepool(自动释放池),比如常见的:
+
1
2
3
4
5
6
7
8
9
10
11
12
13
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain (argc, argv, nil , NSStringFromClass ([AppDelegate class ]));
}
}
int main(int argc, const char *argv[]) {
@autoreleasepool {
}
return 0 ;
}
+
它的作用是把我们在{}中申请的对象在事件处理完时自动释放掉,其中的原理推荐阅读Qi Tang 的iOS 中的 Autorelease Pool 。 前面说到的事件处理完时其实就是一次runloop结束时。
+
+
+
+
+ runloop和autorelease
+
+
+程序运行 -> 开启事件循环 -> 发生触摸事件 -> 创建自动释放池 -> 处理触摸事件 -> 事件对象加入自动释放池 -> 一次事件循环结束, 销毁自动释放池
+
+
+
1
2
3
4
5
6
7
8
9
10
NSArray *urls = <# An array of file URLs #>;
for (NSURL *url in urls) {
@autoreleasepool {
NSError *error;
NSString *fileContents = [NSString stringWithContentsOfURL:url
encoding:NSUTF8StringEncoding error:&error];
}
}
+
+参考文章: sunnyxx —— 黑幕背后的Autorelease Jerry4me —— iOS中autorelease的那些事儿 tutuge —— @autoreleasepool-内存的分配与释放
+
+
修饰词
+
+
+
+
+
+
+ Last updated: 2018-01-14 22:05:07
+
+
+
+
+
http://codingdoge.cn/2017/07/17/title: 漫谈iOS系列之:内存管理/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 谢谢大爷~
+
+
+
+
+
+
+
+
+
+
+
+ 微信
+
+ 支付宝
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git "a/2017/07/17/title: \346\274\253\350\260\210iOS\347\263\273\345\210\227\344\271\213\357\274\232\345\244\232\347\272\277\347\250\213/index.html" "b/2017/07/17/title: \346\274\253\350\260\210iOS\347\263\273\345\210\227\344\271\213\357\274\232\345\244\232\347\272\277\347\250\213/index.html"
new file mode 100644
index 0000000..411cf3a
--- /dev/null
+++ "b/2017/07/17/title: \346\274\253\350\260\210iOS\347\263\273\345\210\227\344\271\213\357\274\232\345\244\232\347\272\277\347\250\213/index.html"
@@ -0,0 +1,554 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 漫谈iOS系列之:多线程 | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
漫谈iOS系列之:多线程
+
+
2017-07-17
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
线程基本概念 线程(thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。 同一进程中的多条线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等等。但同一进程中的多个线程有各自的调用栈(call stack),自己的寄存器环境(register context),自己的线程本地存储(thread-local storage)。线程的状态 :
+
+新生态(New Thread)
+可运行态(Runnable)
+阻塞/非运行态(Not Runnable)
+死亡态(Dead)
+
+
+
+
+
+
+
+
+
死锁 : 是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。
+
死锁条件 :
+
+互斥条件:所谓互斥就是进程在某一时间内独占资源。
+请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
+不剥夺条件:进程已获得资源,在末使用完之前,不能强行剥夺。
+循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
+
+
创建线程的开销 多线程的代价及上下文切换
+
pThread POSIX线程(POSIX Threads,常被缩写为Pthreads)是POSIX的线程标准,跨平台,适用于多种操作系统(类Unix操作系统中,都使用Pthreads作为操作系统的线程,Windows操作系统也有其移植版pthreads-win32),可移植性强,是一套纯C语言的通用API,且线程的生命周期需要程序员自己管理,使用难度较大,所以在实际开发中通常不使用。 Pthreads API中大致共有100个函数调用,全都以”pthread_”开头,并可以分为四类:
+
+线程管理,例如创建线程,等待(join)线程,查询线程状态等。
+互斥锁(Mutex):创建、摧毁、锁定、解锁、设置属性等操作。
+条件变量(Condition Variable):创建、摧毁、等待、通知、设置与查询属性等操作。
+使用了互斥锁的线程间的同步管理。
+
+
pThread在实际开发中基本不使用,所以大概了解下就好了。
+
NSThread GCD dispatch_barrier_async&dispatch_barrier_sync 在队列中,barrier块必须单独执行,不能与其他block并行。这只对并发队列有意义,并发队列如果发现接下来要执行的block是个barrier block,那么就一直要等到当前所有并发的block都执行完毕,才会单独执行这个barrier block代码块,等到这个barrier block执行完毕,再继续正常处理其他并发block。
+
+async, sync两者区别在于async将自己的任务插入队列后, 不用等待自己的任务结束, 继续把后面的任务插入队列, 然后等待自己的任务运行结束才执行后面的任务, sync将自己的任务插入队列后,需要等待自己的任务运行结束才能将后面的任务插入队列。
+
+
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
#import <Foundation/Foundation.h>
@interface Person : NSObject
@property (nonatomic , copy ) NSString *name;
@end
#import "Person.h"
@interface Person ()
@end
static NSString *_name;
static dispatch_queue_t _concurrentQueue;
@implementation Person
- (instancetype )init
{
if (self = [super init]) {
_concurrentQueue = dispatch_queue_create("com.person.syncQueue" , DISPATCH_QUEUE_CONCURRENT);
}
return self ;
}
- (void )setName:(NSString *)name
{
dispatch_barrier_async(_concurrentQueue, ^{
_name = [name copy ];
});
}
- (NSString *)name
{
__block NSString *tempName;
dispatch_sync (_concurrentQueue, ^{
tempName = _name;
});
return tempName;
}
@end
+
NSOperation NSOperation默认是非并发的,当你调用-[NSOperation start]方法时,该方法会等任务结束才会返回; 并发的NSOperation是指,当你调用-[NSOperation start]后,NSOperation会在非当前线程(建立一个NSThread,或是dispatch async等)执行任务,并在任务结束之前就返回;
+
需要注意的是,并发行为都需要你自己实现,若要实现并发,你需要做很多额外的工作:
+
+你需要创建一个subclass;
+除了重载main方法,实现并发你还需要至少重载;start,isConcurrent,isExecuting,isFinished四个方法;
+在start里,创建Thread或者调用一个异步函数;
+更新isExecuting,并且发送相应KVO消息;
+任务结束后,你还得更新isExecuting和isFinished,发送相应KVO消息。 实现一个并发的NSOperation比较少见,具体如何实现,可以读读文档: NSOperation Class Reference
+
+
大多数情况下NSOperation都设计成非并发,这样实现起来会简单很多; 并且,一般会配合NSOperationQueue使用,由NSOperationQueue来负责执行NSOperation,而非直接调用-[NSOperation start]。
+
若有复杂任务需要并发执行,一般也是拆成多个NSOperation,由NSOperationQueue来并发的执行多个NSOperation。
+
+参考:关于iOS多线程,你看我就够了 dispatch_barrier_sync和dispatch_barrier_async iOS开发:深入理解GCD 第二篇(dispatch_group、dispatch_barrier、基于线程安全的多读单写) NSOperation的并发和非并发有什么区别呀?
+
+
+
+
+
+
+
+
+ Last updated: 2018-01-13 13:08:06
+
+
+
+
+
http://codingdoge.cn/2017/07/17/title: 漫谈iOS系列之:多线程/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 谢谢大爷~
+
+
+
+
+
+
+
+
+
+
+
+ 微信
+
+ 支付宝
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git "a/2017/07/21/title: STL\345\270\270\350\247\201\347\224\250\346\263\225 \342\200\224 map/index.html" "b/2017/07/21/title: STL\345\270\270\350\247\201\347\224\250\346\263\225 \342\200\224 map/index.html"
new file mode 100644
index 0000000..73812db
--- /dev/null
+++ "b/2017/07/21/title: STL\345\270\270\350\247\201\347\224\250\346\263\225 \342\200\224 map/index.html"
@@ -0,0 +1,575 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ STL常见用法 — map | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
STL常见用法 — map
+
+
2017-07-21
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
STL常见用法 —
+代码敲得少,STL总是用的不熟,便自己整理一些用法,有眼缘的顺手用着吧。
+
+
+
简介 maps是一个关联容器,用来存储key-value式的元素,提供一对一hash。内部的实现,自建一颗红黑树,这棵树具有对数据自动排序的功能。比如一个班级中,每个学生的学号和他的姓名就存在一对一映射的关系。
+
+第一个值是关键字(key),每个关键字只能在map中出现一次
+第二个值为关键字的值(value)
+
+
+
+
+
+
+
+
+
用法 头文件 1
2
3
4
5
6
7
8
#include <map>
template<
class Key,
class T,
class Compare = std::less<Key>,
class Allocator = std::allocator<std::pair<const Key, T> >
> class map;
+
构造函数 1
2
3
4
5
namespace pmr {
template <class Key, class T, class Compare = std::less<Key>>
using map = std::map<Key, T, Compare,
std::pmr::polymorphic_allocator<std::pair<const Key,T>>>
}
+
成员函数
+
+
+成员函数
+实现操作
+
+
+
+
+begin
+返回一个起始的迭代器
+
+
+end
+返回一个末尾的迭代器
+
+
+empty
+检查容器是否为空
+
+
+size
+返回容器内元素个数
+
+
+clear
+清除容器内容
+
+
+insert
+插入元素或节点
+
+
+erase
+删除元素
+
+
+swap
+交换内容
+
+
+count
+返回指定key的元素个数
+
+
+find
+通过key查找元素
+
+
+
+
用法演示 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <map>
void mapTest()
{
std::map<int, int> m; // 构造函数
m.insert(pair<int, int>(1, 10)); // 插入元素
m.insert(pair<int, int>(2, 30));
m.insert(pair<int, int>(4, 50));
map<int, int>::iterator it; // 迭代器
it = m.find(3); // 通过key查找元素
if (it == m.end()) // 如果返回值为尾部迭代器则无此元素
{
cout << "Not find" << endl;
m.insert(pair<int, int>(3, 20));
}
for (it = m.begin(); it != m.end(); it++) // 通过迭代器遍历map容器
{
cout << it->first << " " << it->second << endl; // 通过迭代器访问元素的key-value
}
}
+
+参考:C/C++ - Map (STL) 用法與心得完全攻略
+
+
+
+
+
+
+
+
+ Last updated: 2018-01-15 01:04:04
+
+
+
+
+
http://codingdoge.cn/2017/07/21/title: STL常见用法 — map/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 谢谢大爷~
+
+
+
+
+
+
+
+
+
+
+
+ 微信
+
+ 支付宝
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git "a/2017/07/21/title: STL\345\270\270\350\247\201\347\224\250\346\263\225 \342\200\224 vector/index.html" "b/2017/07/21/title: STL\345\270\270\350\247\201\347\224\250\346\263\225 \342\200\224 vector/index.html"
new file mode 100644
index 0000000..3bd06e4
--- /dev/null
+++ "b/2017/07/21/title: STL\345\270\270\350\247\201\347\224\250\346\263\225 \342\200\224 vector/index.html"
@@ -0,0 +1,663 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ STL常见用法 — vector | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
STL常见用法 — vector
+
+
2017-07-21
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+代码敲得少,STL总是用的不熟,便自己整理一些用法,有眼缘的顺手用着吧。
+
+
+
简介 vector是线性容器,它的元素严格的按照线性序列排序,和动态数组很相似,和数组一样,它的元素存储在一块连续的存储空间中,这也意味着我们不仅可以使用迭代器(iterator)访问元素,还可以使用指针的偏移方式访问,和常规数组不一样的是,vector能够自动存储元素,可以自动增长或缩小存储空间。
+
用法 头文件
+
构造函数 1
2
3
4
5
6
7
8
template <class T , class Allocator = std : :allocator<T> > class vector ;
namespace pmr {
template <class T >
using vector = std ::vector <T, std ::pmr::polymorphic_allocator<T>>;
}
+
+
+std::vector is a sequence container that encapsulates dynamic size arrays.
+std::pmr::vector is an alias template that uses a polymorphic allocator
+
+
+
成员函数
+
+
+成员函数
+实现操作
+
+
+
+
+c.assign(beg,end)
+将[beg; end)区间中的数据赋值给c
+
+
+c.assign(n,elem)
+将n个elem的拷贝赋值给c
+
+
+c.at(idx)
+传回索引idx所指的数据,如果idx越界,抛出out_of_range
+
+
+c.back()
+传回最后一个数据,不检查这个数据是否存在
+
+
+c.begin()
+传回迭代器重的可一个数据
+
+
+c.capacity()
+返回容器中数据个数
+
+
+c.clear()
+移除容器中所有数据
+
+
+c.empty()
+判断容器是否为空
+
+
+c.end()
+指向迭代器中的最后一个数据地址
+
+
+c.erase(pos)
+删除pos位置的数据,传回下一个数据的位置
+
+
+c.erase(beg,end)
+删除[beg,end)区间的数据,传回下一个数据的位置
+
+
+c.front()
+传回第一个数据
+
+
+get_allocator
+使用构造函数返回一个拷贝
+
+
+c.insert(pos,elem)
+在pos位置插入一个elem拷贝,传回新数据位置
+
+
+c.insert(pos,n,elem)
+在pos位置插入n个elem数据。无返回值
+
+
+c.insert(pos,beg,end)
+在pos位置插入在[beg,end)区间的数据。无返回值
+
+
+c.max_size()
+返回容器中最大数据的数量
+
+
+c.pop_back()
+删除最后一个数据
+
+
+c.push_back(elem)
+在尾部加入一个数据
+
+
+c.rbegin()
+传回一个逆向队列的第一个数据
+
+
+c.rend()
+传回一个逆向队列的最后一个数据的下一个位置
+
+
+c.resize(num)
+重新指定队列的长度
+
+
+c.reserve()
+保留适当的容量
+
+
+c.size()
+返回容器中实际数据的个数
+
+
+c1.swap(c2)
+将c1和c2元素互换
+
+
+swap(c1,c2)
+同上操作
+
+
+vector c
+创建一个空的vector
+
+
+vector c1(c2)
+复制一个vector
+
+
+vector c(n)
+创建一个vector,含有n个数据,数据均已缺省构造产生
+
+
+vector c(n, elem)
+创建一个含有n个elem拷贝的vector。
+
+
+vector c(beg,end)
+创建一个以[beg;end)区间的vector
+
+
+c.~ vector ()
+销毁所有数据,释放内存
+
+
+
+
用法演示
+使用reverse将元素翻转: 需要头文件#include<algorithm>
+
+
1
reverse(vec .begin(),vec.end())
+
将元素翻转(在vector中,如果一个函数中需要两个迭代器,一般后一个都不包含)
+
+使用sort排序: 需要头文件#include, sort(vec.begin(),vec.end());(默认是按升序排列,即从小到大). 可以通过重写排序比较函数按照降序比较,如下:
+
+
定义排序比较函数:
+
1
2
3
4
bool Comp (const int &a,const int &b)
{
return a>b;
}
+
调用时:sort(vec.begin(),vec.end(),Comp),这样就降序排序。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 谢谢大爷~
+
+
+
+
+
+
+
+
+
+
+
+ 微信
+
+ 支付宝
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git "a/2017/07/23/title: CocoaPods \345\256\211\350\243\205\345\217\212\344\270\200\350\210\254\344\275\277\347\224\250/index.html" "b/2017/07/23/title: CocoaPods \345\256\211\350\243\205\345\217\212\344\270\200\350\210\254\344\275\277\347\224\250/index.html"
new file mode 100644
index 0000000..16018dc
--- /dev/null
+++ "b/2017/07/23/title: CocoaPods \345\256\211\350\243\205\345\217\212\344\270\200\350\210\254\344\275\277\347\224\250/index.html"
@@ -0,0 +1,557 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ CocoaPods 安装及一般使用 | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
CocoaPods 安装及一般使用
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
CocoaPods is a dependency manager for Swift and Objective-C Cocoa projects.CocoaPods can help you scale your projects elegantly.
+
INSTALL CocoaPods是基于ruby建立的,要确保你的电脑里装有Ruby,不过Mac都是自带Ruby的,你可以用rvm 来管理你的ruby.
+
+ RVM 实用指南 · Ruby China 然后我们使用Rubygem来安装cocoapods. 由于GFW的原因我们不能访问cocoapods.org,使用淘宝的Ruby镜像来代替:
+
+删除自带的Ruby镜像$ gem sources --remove https://rubygems.org/
+添加淘宝的镜像$ gem sources -a https://gems.ruby-china.org/ (如果这个镜像不能用,就用https://gems.ruby-china.org/ )
+可以用$ gem sources -l来检验。成功即显示以下结果:
+
+
+
+
+ FF76DDA8-EC18-438C-B921-8603D4688C1D
+
+
+
+
+
+安装CocoaPods$ sudo gem install cocoapods
+配置CocoaPods$ pod setup
+
+
如果安装失败的话,根据报错去解决问题,比如gem没更新,ruby版本等。 常见问题:While executing gem ... (Errno::EPERM) 1
Operation not permitted - /usr/bin/xcodeproj
+
安装Cocoapods, 更新gem出现的问题。 - SegmentFault ios - Cannot install cocoa pods after uninstalling, results in error - Stack Overflow
+
Using CocoaPods
+使用之前你要确保你所想用的库存在CocoaPods中:(拿AFNetworking举例)$ pod search AFNetworking 第一次搜索会需要建立索引,比较慢一些。
+搜索完成后会列举出结果和版本:
+
+
+
+
+
+ 37122577-4CDC-48C9-9B25-4B943D983810
+
+
+然后通过创建Podfile 文件来添加依赖关系
+
+先cd进你项目所在的目录(简介里面可以直接复制路径)
+利用vim创建Podfile文件$ vim Podfile
+然后输入:1
2
3
4
5
platform :ios, '10.0'
target 'TargetName' do
pod 'AFNetworking' , '~> 3.0'
end
+
+
+
+文字的意思是,当前AFNetworking支持的iOS最高版本是iOS 10.0,’TargetName’为你项目的名称,要下载的AFNetworking版本是3.0 保存退出。
+
+运行$ pod install 完成后会出现 提示使用’XXX.xcworkspace’文件来代替之前的’XXX.xcodeproj’文件打开项目。 打开项目后会发现 里面有了我们想要加进来的库,可以#import进来了。
+
+
+
+增加新的库 如果使用过程中我还想添加其他的库怎么办,只要在Podfile里面接着添加,然后终端再执行pod install就可以了。
+更新CocoaPods中的库 第三方库们都有人在维护升级,我们需要隔断时间就要更新下我们工程中第三方库的版本。只需要终端输入命令pod update就可以了。
+删除CocoaPods中的某些库 当我们需要去掉某个第三方库时,只需要在Podfile删除该引入该库的语句,然后执行pod update或者pod install就可以了。
+升级CocoaPodssudo gem install cocoapods
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 谢谢大爷~
+
+
+
+
+
+
+
+
+
+
+
+ 微信
+
+ 支付宝
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git "a/2017/07/23/title: OC\344\270\255ARC forbids explicit message send of '...'\351\224\231\350\257\257 /index.html" "b/2017/07/23/title: OC\344\270\255ARC forbids explicit message send of '...'\351\224\231\350\257\257 /index.html"
new file mode 100644
index 0000000..942018e
--- /dev/null
+++ "b/2017/07/23/title: OC\344\270\255ARC forbids explicit message send of '...'\351\224\231\350\257\257 /index.html"
@@ -0,0 +1,528 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ OC中ARC forbids explicit message send of '...'错误 | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
OC中ARC forbids explicit message send of '...'错误
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+转自CSDN hahahacff 有所整理
+
+
ARC forbids explicit message send of’retainCount’
+同’release’等等
+
+
+
+
+
+
+很显然,是ARC 的问题。 错误原因:在创建工程的时候点选了“Use Automatic Reference Counting” 选项,但是又调用了对象的retainCount 方法
+
+
ARC是什么? ARC是iOS 5推出的新功能,全称叫ARC(AutomaticReferenceCounting)。 简单地说,就是代码中自动加入了retain/release,原先需要手动添加的用来处理内存管理的引用计数的代码可以自动地由编译器完成了。该机制在iOS 5/ Mac OS X 10.7 开始导入,利用 Xcode4.2可以使用该机制。简单地理解ARC,就是通过指定的语法,让编译器(LLVM3.0)在编译代码时,自动生成实例的引用计数管理部分代码。有一点,ARC并不是GC,它只是一种代码静态分析(StaticAnalyzer)工具。
+
解决方法
+选择要项目,双击中间的工程名称,进入build setting
+
+
+
+
+
+
+将中间的Objective-C Automatic Reference Counting改为no
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 谢谢大爷~
+
+
+
+
+
+
+
+
+
+
+
+ 微信
+
+ 支付宝
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git "a/2017/07/23/title: Objective-C\345\243\260\346\230\216\345\234\250\345\244\264\346\226\207\344\273\266\345\222\214\345\256\236\347\216\260\346\226\207\344\273\266\344\270\255\347\232\204\345\214\272\345\210\253 /index.html" "b/2017/07/23/title: Objective-C\345\243\260\346\230\216\345\234\250\345\244\264\346\226\207\344\273\266\345\222\214\345\256\236\347\216\260\346\226\207\344\273\266\344\270\255\347\232\204\345\214\272\345\210\253 /index.html"
new file mode 100644
index 0000000..c292728
--- /dev/null
+++ "b/2017/07/23/title: Objective-C\345\243\260\346\230\216\345\234\250\345\244\264\346\226\207\344\273\266\345\222\214\345\256\236\347\216\260\346\226\207\344\273\266\344\270\255\347\232\204\345\214\272\345\210\253 /index.html"
@@ -0,0 +1,561 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Objective-C声明在头文件和实现文件中的区别 | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Objective-C声明在头文件和实现文件中的区别
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+转自codecloud (有整理)
+
+
调试程序的时候,突然想到这个问题,百度一下发现有不少这方面的问答,粗略总结一下:
+属性写在.h文件中和在.m文件中有什么区别?
+
+
Objective-C中有分类和类扩展的概念,而实现文件中的类声明实际上就是类扩展.
+
+@interface部分为类扩展(extension)
+
+
其被设计出来就是为了解决两个问题的
+
+定义类私有方法的地方,也就是下面说到的区别一
+实现public readonly,private readwrite的property(意思是在h头文件中定义一个属性对外是readonly的,但在类的内部希望是可读写的,所以可以在m源文件中的@interface部分重新定义此属性为readwrite,此时此属性对外是只读的,对内是读写的).
+
+
此外,也可在此部分申明变量和属性,但申明的变量,属性和方法均为私有的,只能够被当前类访问,相当于private。
+
区别一: 属性在.h文件中和在.m中声明是有区别的。区别就是,在.h文件中声明的属性,外部类可以通过“类实例.属性”来调用,但在.m中声明的则不可以,获取和设置的方法,只能是通过setValue:forKey和valueForKey来实现。
+
成员变量,有三种权限,就是大家都知道的@private、@protected、@public ,写在.m文件中时,相当于是@private权限,子类无法访问,验证了一下,做权限修改也无效。而写在.h文件中,默认是@protected权限,子类可以访问,可以做权限修改。因为访问权限指针对.h文件。.h文件中成员变量,外部类对其的调用,跟C++一样,用->来调用。
+
区别二 这样可以提高编译效率,避免重复编译。 因为分开的话只需要编译一次生成对应的.obj文件后,再次应用该类的地方,这个类就不会被再次编译,从而大大提高了效率。这样可以提高编译效率,避免重复编译.
+
+怎么去解释呢…其实这是一个面向对象的思想,所谓”提高”的比较对象,应该是直接将方法写到具体函数里的实现方式. h为编译器提供一个索引、声明,连接obj对象和主程序. 编译器在编译的时候,如果需要,则去查找h,找到了h,再找对应的obj,就可以找到类的方法了. 但是如果直接写入到同一个文件(例如hpp),主程序没有索引,也不清楚具体的类实现了没有,只能一次次重复的编译相同的代码,这样实际上没有把这个类该有的东西抽象出来. 对于函数声明在头文件中,在实现文件中实现,也是避免重复编译,函数可以多次声明,但只能实现一次.
+
+
头文件相对于实现文件的作用在于:
+头文件可以预先告诉编译器一些必要的声明,让编译器顺利进行下去,在连接实现以前.未必出现实际的定义.
+头文件的意义在:
+* 使得程序简明,清晰.
+* 避免了重复编写相同的声明代码.
+
+.c和 .h文件没有必然的联系.
+
+
关于头文件和实现文件的编译连接的过程 其实要理解C文件与头文件有什么不同之处,首先需要弄明白编译器的工作过程,一般说来编译器会做以下几个过程:
+
+预处理阶段
+词法与语法分析阶段
+编译阶段,首先编译成纯汇编语句,再将之汇编成跟CPU相关的二进制码,生成各个目标文件
+连接阶段,将各个目标文件中的各段代码进行绝对地址定位,生成跟特定平台相关的可执行文件,当然,最后还可以用objcopy生成纯二进制 码,也就是去掉了文件格式信息.
+
+
编译器在编译时是以C文件为单位进行的,也就是说如果你的项目中一个C文件都没有,那么你的项目将无法编译,连接器是以目标文件为单位,它将一个或多个目标文件进行函数与变量的重定位,生成最终的可执行文件,在PC上的程序开发,一般都有一个main函数,这是各个编译器的约定,当然,你如果自己写连接器脚本的话,可以不用main函数作为程序入口!!!
+
有了这些基础知识,再言归正传,为了生成一个最终的可执行文件,就需要一些目标文件,也就是需要C文件,而这些C文件中又需要一个main函数作为可执行程序的入口,那么我们就从一个C文件入手,假定这个C文件内容如下:
+
1
2
3
4
5
6
7
8
#include \<stdio.h>
#include “mytest.h”
int main (int argc,char **argv)
{
test = 25 ;
printf (“test……………..%d\n”,test);
}
+
头文件内容如下:int test;
+
现在以这个例子来讲解编译器的工作:
+预处理阶段: 编译器以C文件作为一个单元,首先读这个C文件,发现第一句与第二句是包含一个头文件,就会在所有搜索路径中寻找这两个文件,找到之后,就会将相应头文件中再去处理宏,变量,函数声明,嵌套的头文件包含等,检测依赖关系,进行宏替换,看是否有重复定义与声明的情况发生,最后将那些文件中所有的东东全部扫描进这个当前的C文件中,形成一个中间“C文件”
+
+编译阶段: 在上一步中相当于将那个头文件中的test变量扫描进了一个中间C文件,那么test变量就变成了这个文件中的一个全局变量,此时就将所有这个中间C文件的所有变量,函数分配空间,将各个函数编译成二进制码,按照特定目标文件格式生成目标文件,在这种格式的目标文件中进行各个全局变量,函数的符号描述,将这些二进制码按照一定的标准组织成一个目标文件
+
+连接阶段: 将上一步成生的各个目标文件,根据一些参数,连接生成最终的可执行文件,主要的工作就是重定位各个目标文件的函数,变量等,相当于将个目标文件中的二进制码按一定的规范合到一个文件中
+
+
+
再回到C文件与头文件各写什么内容的话题上: 理论上来说C文件与头文件里的内容,只要是C语言所支持的,无论写什么都可以的,比如你在头文件中写函数体,只要在任何一个C文件包含此头文件就可以将这个函数编译成目标文件的一部分(编译是以C文件为单位的,如果不在任何C文件中包含此头文件的话,这段代码就形同虚设),你可以在C文件中进行函数声明,变量声明,结构体声明,这也不成问题!!! 那为何一定要分成头文件与C文件呢?又为何一般都在头件中进行函数,变量声明,宏声明,结构体声明呢?而在C文件中去进行变量定义,函数实现呢?? 原因如下:
+
+如果在头文件中实现一个函数体,那么如果在多个C文件中引用它,而且又同时编译多个C文件,将其生成的目标文件连接成一个可执行文件,在每个引用此头文件的C文件所生成的目标文件中,都有一份这个函数的代码,如果这段函数又没有定义成局部函数,那么在连接时,就会发现多个相同的函数,就会报错
+
+如果在头文件中定义全局变量,并且将此全局变量赋初值,那么在多个引用此头文件的C文件中同样存在相同变量名的拷贝,关键是此变量被赋了初值,所以编译器就会将此变量放入DATA段,最终在连接阶段,会在DATA段中存在多个相同的变量,它无法将这些变量统一成一个变量,也就是仅为此变量分配一个空间,而不是多份空间,假定这个变量在头文件没有赋初值,编译器就会将之放入BSS段,连接器会对BSS段的多个同名变量仅分配一个存储空间
+
+如果在C文件中声明宏,结构体,函数等,那么我要在另一个C文件中引用相应的宏,结构体,就必须再做一次重复的工作,如果我改了一个C文件中的一个声明,那么又忘了改其它C文件中的声明,这不就出了大问题了,程序的逻辑就变成了你不可想象的了,如果把这些公共的东东放在一个头文件中,想用它的C文件就只需要引用一个就OK了!!!这样岂不方便,要改某个声明的时候,只需要动一下头文件就行了
+
+在头文件中声明结构体,函数等,当你需要将你的代码封装成一个库,让别人来用你的代码,你又不想公布源码,那么人家如何利用你的库呢?也就是如何利用你的库中的各个函数呢??一种方法是公布源码,别人想怎么用就怎么用,另一种是提供头文件,别人从头文件中看你的函数原型,这样人家才知道如何调用你写的函数,就如同你调用printf函数一样,里面的参数是怎样的??你是怎么知道的??还不是看人家的头文件中的相关声明啊!!!当然这些东东都成了C标准,就算不看人家的头文件,你一样可以知道怎么使用.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 谢谢大爷~
+
+
+
+
+
+
+
+
+
+
+
+ 微信
+
+ 支付宝
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/2017/07/23/title: Remove Duplicates from Sorted Array /index.html b/2017/07/23/title: Remove Duplicates from Sorted Array /index.html
new file mode 100644
index 0000000..ae31d2e
--- /dev/null
+++ b/2017/07/23/title: Remove Duplicates from Sorted Array /index.html
@@ -0,0 +1,521 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Remove Duplicates from Sorted Array | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Remove Duplicates from Sorted Array
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+题目来源LeetCode: LeetCode OJ
+
+
+
题目描述 Given a sorted array, remove the duplicates in place such that each element appear only once and return the new length. Do not allocate extra space for another array, you must do this in place with constant memory. For example, Given input array nums = [1,1,2], Your function should return length = 2, with the first two elements of nums being 1 and 2 respectively. It doesn’t matter what you leave beyond the new length.
+
题目解释 给出一个sorted array(意思是指已经排好序了?),处理后数组里每一个元素只能出现一次,返回处理后的数组长度。 不能使用额外的数组空间,只能用已经给出的确定的内存空间。
+
分析 因为不太懂sorted array具体指的什么,第一次做的时候以为数组是随机的,相同元素出现的位置是随机的,然后题目也没给出limit time,随手就写了一个O(n^3)
+
1
2
3
4
5
6
7
8
9
10
for (int i = 0 ; i < num ; i++){
truefor (int j = i+1 ; j < num ; j++){
truetrueif (array [i] == array [j]){
for (int k = j; k < num -1 ; k++)
array [k] = array [k+1 ];
num --;
j--;
}
}
}
+
自然是T了。然后就把sorted array当做已经排好序的数组,那就容易多了,算法也都是O(1),一看代码就明白,水题,直接上代码。
+
way1 1
2
3
4
5
6
7
if (nums.empty()) return 0 ;
int index = 0 ;
for (int i = 1 ; i < nums.size(); i++) {
if (nums[index ] != nums[i])
nums[++index ] = nums[i];
}
return index + 1 ;
+
way2 STL 1
return distance(nums .begin(), unique(nums .begin(), nums.end()))
+
+std::distance template typename iterator_traits::difference_type distance (InputIterator first, InputIterator last); Return distance between iterators Calculates the number of elements between first and last.c++ reference
+
+std::unique equality (1) template ForwardIterator unique (ForwardIterator first, ForwardIterator last); predicate (2) template ForwardIterator unique (ForwardIterator first, ForwardIterator last,
+BinaryPredicate pred);
+Remove consecutive duplicates in range Removes all but the first element from every consecutive group of equivalent elements in the range [first,last).c++ reference
+
+
+
相关题目 RemoveDuplicatesfromSortedArrayII
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 谢谢大爷~
+
+
+
+
+
+
+
+
+
+
+
+ 微信
+
+ 支付宝
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/2017/07/23/title: Search in Rotated Sorted Array /index.html b/2017/07/23/title: Search in Rotated Sorted Array /index.html
new file mode 100644
index 0000000..8ee366a
--- /dev/null
+++ b/2017/07/23/title: Search in Rotated Sorted Array /index.html
@@ -0,0 +1,605 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Search in Rotated Sorted Array | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Search in Rotated Sorted Array
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+题目来源LeetCode: LeetCode OJ
+
+
+
+题目描述: Suppose a sorted array is rotated at some pivot unknown to you beforehand. (i.e., 0 1 2 4 5 6 7 might become 4 5 6 7 0 1 2). You are given a target value to search. If found in the array return its index, otherwise return -1. You may assume no duplicate exists in the array.
+
+
题目解释 一个排好序的数组,不知道以哪个点为中心旋转了(部分有序),你的任务是查找给定的数是否存在数组中。在的话返回下标,不在的话返回-1.
+
分析 查找首先想到O(log(n))的二分查找,但是二分查找的前提是有序数组。题目里一个有序数组旋转后变成了部分有序。通过比较两端大小找到增序部分。
+
+eg: 4 5 6 0 1 2 3 first = 4, mid = 0, last = 3, 通过比较first,mid,last找到增序部分。 在这个例子中为1 2 3, 然后判断target是否在这个增序子序列中, 如果在则直接用二分查找, 不在则在另一部分(例子中为4 5 6 0)中继续分解。
+
+
Code int search(const vector<int>& nums, int target)
+{
+ int first = 0;
+ int last = nums.size();
+ while(first != last)
+ {
+ const int mid = (first+last)>>1; // 使用位运算加速
+ if(nums[mid] == target)
+ return mid;
+ if(nums[first] <= nums[mid]) // 找到增序子序列
+ if(nums[first]<=target && target<nums[mid]) // 找到target在哪个部分
+ last = mid;
+ else
+ first = mid+1;
+ else
+ if(nums[mid]<target && target<=nums[last-1])
+ first = mid+1;
+ else
+ last = mid;
+ }
+ return -1;
+}
+
+
拓展 当数组为无序时的二分查找
+
分析 一种是先排序再二分,一种是结合快排思想,每次选择一个关键字,比他大的放右边,比他小的放左边,然后再比较他和需要查找的数的关系,再选择区间进行迭代。如果需要返回查找数的下标,则添加一个纪录下标的数组,这样排好序后也能知道当前数在原始数组中的位置。
+
+初始数组3 1 2 5 4 7 0 6 mid = key = 3 进行一次快排填坑 得到数组0 1 2 3 4 7 5 6 比较mid与target 如果target>mid则迭代mid后半部分 如果target<mid则迭代mid前半部分 直到找到target
+
+
Code int BinarySearch(vector<int>& nums, int target)
+{
+ int num = nums.size();
+ int index[num]; // index数组纪录下标 以便能找到在数组的初始位置
+ for(int i = 0; i < num; i++) // 初始化index数组
+ index[i] = i;
+ int l, r, m, sl, sr, mIndex;
+ l = 0, r = num-1;
+ while(l<=r) // 开始迭代
+ {
+ mIndex = index[l], m = nums[l];
+ sl = l, sr = r;
+ while(sl<sr) // 快排思想,左右填坑,并用index记录位置
+ {
+ while(sl<sr && m<nums[sr])
+ sr--;
+ nums[sl] = nums[sr];
+ index[sl] = index[sr];
+ while(sl<sr && m>nums[sl])
+ sl++;
+ nums[sr] = nums[sl];
+ index[sr] = index[sl];
+ }
+ nums[sl] = m;
+ index[sl] = mIndex;
+ if(m == target)
+ return mIndex;
+ if(target > m) // 判断target在哪个区间
+ l = sl+1;
+ else
+ r = sl-1;
+ }
+ return -1;
+}
+
+
相关题目 Search in Rotated Sorted Array II
+
相关题目分析 因为允许出现重复数字,但是数组还是部分有序的,所以跳过重复数字即可
+
Code int search(const vector<int>& nums, int target)
+{
+ int first = 0;
+ int last = nums.size();
+ while(first != last)
+ {
+ const int mid = (first+last)>>1;
+ if(nums[mid] == target)
+ return mid;
+ if(nums[first] < nums[mid])
+ if(nums[first]<=target && target<nums[mid])
+ last = mid;
+ else
+ first = mid+1;
+ else if(nums[first] > nums[mid])
+ {
+ if(nums[mid]<target && target<=nums[last-1])
+ first = mid+1;
+ else
+ last = mid;
+ }
+ else // 特判相等时跳过
+ first++;
+ }
+ return -1;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 谢谢大爷~
+
+
+
+
+
+
+
+
+
+
+
+ 微信
+
+ 支付宝
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git "a/2017/07/23/title: self\344\270\216super\347\232\204\345\214\272\345\210\253 /index.html" "b/2017/07/23/title: self\344\270\216super\347\232\204\345\214\272\345\210\253 /index.html"
new file mode 100644
index 0000000..6c770d8
--- /dev/null
+++ "b/2017/07/23/title: self\344\270\216super\347\232\204\345\214\272\345\210\253 /index.html"
@@ -0,0 +1,549 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ self与super的区别 | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
self与super的区别
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+原文CSDN evilotus 有所整理
+
+在ObjC 中的类实现中经常看到这两个关键字”self ”和”super ”,以以前oop 语言的经验,拿c++ 为例,self 相当于this ,super 相当于调用父类的方法,这么看起来是很容易理解的。但是它们真正是如何调用的呢? 你知道吗?
+
+
+
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
@interface Person:NSObject
{
trueNSString* name;
}
(void) setName:(NSString) yourName;
@end //Person
@interface PersonMe:Person
{
trueNSUInteger age;
}
(void) setAge:(NSUInteger) age;
(void) setName:(NSString*) yourName andAge:(NSUInteger) age;
@end // PersonMe
@implementation PersonMe
(void) setName:(NSString*) yourName andAge:(NSUInteger) age
{
true[self setAge:age];
true[super setName:yourName];
}
@end // PersonMe
int main(int argc, char* argv[]) {
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]
PersonMe* me = [[PersonMe alloc] init];
[me setName: @"asdf" andAge: 18];
[me release];
[pool drain];
return 0;
}
+
上面有简单的两个类,在子类PersonMe 中调用了自己类中的setAge 和父类中的setName ,这些代码看起来很好理解,没什么问题。 然后我在setName:andAge 的方法中加入两行:
+
1
2
3
4
5
6
7
8
9
10
11
NSLog(@"self ' class is %@", [self class]);
NSLog(@"super' class is %@", [super class]);
```
这样在调用时,会打出来这两个的**class**,先猜下吧,会打印出什么? 按照以前*oop*语言的经验,这里应该会输出:***self ' s class is PersonMe super ' s class is Person***
但是编译运行后,可以发现结果是:
``` //
self 's class is PersonMe
super ' s class is PersonMe
+
self 的class 和预想的一样,怎么super 的class 也是PersonMe ?
+
真相 self 是类的隐藏的参数,指向当前当前调用方法的类,另一个隐藏参数是_cmd ,代表当前类方法的selector 。这里只关注这个self 。super 是个啥?super 并不是隐藏的参数,它只是一个“编译器指示符” ,它和self 指向的是相同的消息接收者,拿上面的代码为例,不论是用[self setName] 还是[super setName] ,接收“setName” 这个消息的接收者都是PersonMe* me 这个对象。不同的是,super 告诉编译器,当调用setName 的方法时,要去调用父类的方法,而不是本类里的。
+
当使用self 调用方法时,会从当前类 的方法列表中开始找,如果没有,就从父类 中再找;而当使用superv时,则从 父类**的方法列表中开始找。然后调用父类的这个方法。
+
One more step 这种机制到底底层是如何实现的?其实当调用类方法的时候,编译器会将方法调用转成一个C函数方法调用,apple的objcRuntimeRef上说:
+
+Sending Messages When it encounters a method invocation, the compiler might generate a call to any of several functions to perform the actual message dispatch, depending on the receiver, the return value, and the arguments. You can use these functions to dynamically invoke methods from your own plain C code, or to use argument forms not permitted by NSObject’s perform… methods. These functions are declared in /usr/include/objc/objc-runtime.h.
+
+objc_msgSend sends a message with a simple return value to an instance of a class.
+objc_msgSend_stret sends a message with a data-structure return value to an instance of a class.
+objc_msgSendSuper sends a message with a simple return value to the superclass of an instance of a class.
+objc_msgSendSuper_stret sends a message with a data-structure return value to the superclass of an instance of a class.
+
+
+
可以看到会转成调用上面4个方法中的一个,由于_stret系列的和没有_stret的那两个类似,先只关注objc_msgSend和objc_msgSendSuper两个方法。
+
当使用[self setName]调用时,会使用objc_msgSend的函数,先看下objc_msgSend的函数定义:
+
1
id objc_msgSend(id theReceiver, SEL theSelector, ...)
+
第一个参数是消息接收者,第二个参数是调用的具体类方法的selector,后面是selector方法的可变参数。我们先不管这个可变参数,以[self setName:]为例,编译器会替换成调用objc_msgSend的函数调用,其中theReceiver是self,theSelector是 @selector(setName:),这个selector是从当前self的class的方法列表开始找的setName,当找到后把对应的 selector传递过去。
+
而当使用[super setName]调用时,会使用objc_msgSendSuper函数,看下objc_msgSendSuper的函数定义:
+
1
id objc_msgSendSuper(struct objc_super *super, SEL op, ...)
+
第一个参数是个objc_super的结构体,第二个参数还是类似上面的类方法的selector,先看下objc_super这个结构体是什么东西:
+
struct objc_super
+{
+ id receiver;
+ Class superClass;
+};
+
+
可以看到这个结构体包含了两个成员,一个是receiver,这个类似上面objc_msgSend的第一个参数receiver,第二个成员是记 录写super这个类的父类是什么,拿上面的代码为例,当编译器遇到PersonMe里setName:andAge方法里的[super setName:]时,开始做这几个事:
+
+构建objc_super的结构体,此时这个结构体的第一个成员变量receiver就是PersonMe* me,和self相同。而第二个成员变量superClass就是指类Person,因为PersonMe的超类就是这个Person。
+
+调用objc_msgSendSuper的方法,将这个结构体和setName的sel传递过去。函数里面在做的事情类似这样:从objc_super结构体指向的superClass的方法列表开始找setName的selector,找到后再以 objc_super->receiver去调用这个selector,可能也会使用objc_msgSend这个函数,不过此时的第一个参数 theReceiver就是objc_super->receiver,第二个参数是从objc_super->superClass中找到 的selector
+
+
+
里面的调用机制大体就是这样了,以上面的分析,回过头来看开始的代码,当输出[self class]和[super class]时,是个怎样的过程。
+
当使用[self class]时,这时的self是PersonMe,在使用objc_msgSend时,第一个参数是receiver也就是self,也是 PersonMe* me这个实例。第二个参数,要先找到class这个方法的selector,先从PersonMe这个类开始找,没有,然后到PersonMe的父类 Person中去找,也没有,再去Person的父类NSObject去找,一层一层向上找之后,在NSObject的类中发现这个class方法,而 NSObject的这个class方法,就是返回receiver的类别,所以这里输出PersonMe。
+
当使用[super class]时,这时要转换成objc_msgSendSuper的方法。先构造objc_super的结构体吧,第一个成员变量就是self, 第二个成员变量是Person,然后要找class这个selector,先去superClass也就是Person中去找,没有,然后去Person 的父类中去找,结果还是在NSObject中找到了。然后内部使用函数objc_msgSend(objc_super->receiver, @selector(class)) 去调用,此时已经和我们用[self class]调用时相同了,此时的receiver还是PersonMe* me,所以这里返回的也是PersonMe。
+
Furthor more 在类的方法列表寻找一个方法时,还牵涉到一个概念类对象的isa指针和objc的meta-class概念,这里就不再详细介绍。
+
+
+
+
+
+
+
+ Last updated: 2018-01-15 00:47:12
+
+
+
+
+
http://codingdoge.cn/2017/07/23/title: self与super的区别 /
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 谢谢大爷~
+
+
+
+
+
+
+
+
+
+
+
+ 微信
+
+ 支付宝
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git "a/2017/07/23/title: xcode\345\270\270\347\224\250\345\277\253\346\215\267\351\224\256\345\217\212\345\205\266\344\273\226\345\212\237\350\203\275/index.html" "b/2017/07/23/title: xcode\345\270\270\347\224\250\345\277\253\346\215\267\351\224\256\345\217\212\345\205\266\344\273\226\345\212\237\350\203\275/index.html"
new file mode 100644
index 0000000..2f732e4
--- /dev/null
+++ "b/2017/07/23/title: xcode\345\270\270\347\224\250\345\277\253\346\215\267\351\224\256\345\217\212\345\205\266\344\273\226\345\212\237\350\203\275/index.html"
@@ -0,0 +1,507 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ xcode常用快捷键及其他功能 | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
xcode常用快捷键及其他功能
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
快捷键
+
Command+R生成并运行程序 Command+B只生成程序而不运行它 Command+T新建选项卡
+
标记
+
// MARK: - \ // TODO: \ // FIXME: \
+
小功能
+
Product>Clean删除生成的中间文件
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 谢谢大爷~
+
+
+
+
+
+
+
+
+
+
+
+ 微信
+
+ 支付宝
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git "a/2017/07/23/title: \343\200\212TCP-IP\350\257\246\350\247\243\343\200\213\347\254\224\350\256\260\342\200\224\347\254\2541\347\253\240 \346\246\202\350\277\260/index.html" "b/2017/07/23/title: \343\200\212TCP-IP\350\257\246\350\247\243\343\200\213\347\254\224\350\256\260\342\200\224\347\254\2541\347\253\240 \346\246\202\350\277\260/index.html"
new file mode 100644
index 0000000..f0d794b
--- /dev/null
+++ "b/2017/07/23/title: \343\200\212TCP-IP\350\257\246\350\247\243\343\200\213\347\254\224\350\256\260\342\200\224\347\254\2541\347\253\240 \346\246\202\350\277\260/index.html"
@@ -0,0 +1,556 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 《TCP/IP详解》笔记—第1章 概述 | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
《TCP/IP详解》笔记—第1章 概述
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+一个互连网就是一组通过相同协议族互连在一起的网络。
+
+
分层 TCP/IP通常被认为是一个四层协议系统:
+
+
+
+
+ WX20170320-171317@2x
+
+
网络层和运输层之间的区别是最为关键的:网络层(IP)提供点到点的服务,而运输层(TCP和UDP)提供端到端的服务。
+
在TCP/IP协议族中,网络层IP提供的是一种不可靠的服务。也就是说,它只是尽可能快地把分组从源结点送到目的结点,但是并不提供任何可靠性保证。而另一方面,TCP在不可靠的IP层上提供了一个可靠的运输层。为了提供这种可靠的服务,TCP采用了超时重传、发送和接收端到端的确认分组等机制。
+
路由器为不同类型的物理网络提供连接。
+
一个主机也可以有多个接口,但一般不称作路由器,除非它的功能只是单纯地把分组从一个接口传送到另一个接口。
+
互联网的目的之一是在应用程序中隐藏所有的物理细节。
+
连接网络的另一个途径是使用网桥。网桥是在链路层上对网络进行互连,而路由器则是在网络层上对网络进行互连。
+
互联网的地址
+
+
+
+
+ 47F26D90-176D-4C0A-A315-6A9E199E79F6
+
+
这些 32 位的地址通常写成四个十进制的数,其中每个整数对应一个字节。这种表示方法称作“点分十进制表示法(Dotted decimal notation)”。
+
+
+
+
+ 70807FBE-712D-4A5C-97F2-BBCA12C1F11D
+
+
封装 TCP传给IP的数据单元称作TCP报文段或简称为TCP段(TCP segment)。IP传给网络接口层的数据单元称作IP数据报(IP datagram)。通过以太网传输的比特流称作帧(Frame)。
+
分用 当目的主机收到一个以太网数据帧时,数据就开始从协议栈中由底向上升,同时去掉各层协议加上的报文首部。每层协议盒都要去检查报文首部中的协议标识,以确定接收数据的上层协议。这个过程称作分用(Demultiplexing)。
+
客户-服务器模型 大部分网络应用程序在编写时都假设一端是客户,另一端是服务器,其目的是为了让服务器为客户提供一些特定的服务。 一般来说,TCP服务器是并发的,而UDP服务器是重复的。
+
习题
+请计算最多有多少个A类、B类和C类网络号。
+用匿名FTP(见27.3节)从主机nic.merit.edu上获取文件nsfnet_statistics_history.netcount。该文件包含在NSFNET网络上登记的国内和国外的网络数。画一坐标系,横坐标代表年,纵坐标代表网络总数的对数值。纵坐标的最大值是习题1.1的结果。如果数据显示一个明显的趋势,请估计按照当前的编址体制推算,何时会用完所有的网络地址(3.10节讨论解决该难题的建议)。
+获取一份主机需求RFC 拷贝[Braden 1989a],阅读有关应用于TCP/IP协议族每一层的稳健性原则。这个原则的参考对象是什么?
+获取一份最新的赋值RFC 拷贝。“quote of the day”协议的有名端口号是什么?哪个RFC 对该协议进行了定义?
+如果你有一个接入TCP/IP互联网的主机帐号,它的主IP地址是多少?这台主机是否接入了Internet?它是多接口主机吗?
+获取一份RFC 1000的拷贝,了解RFC这个术语从何而来。
+与Internet 协会联系,isoc@isoc.org或者+170 3648 9888 ,了解有关加入的情况。
+用匿名FTP从主机is.internic.net处获取文件about-internic/information-about-the-internic。
+
+
部分习题答案
+答案是:27-2(126)+214-2(16 382)+221-2(2 097 150)=2 113 658。每一部分都减去2是因为全0或全1网络ID是非法的。
+图D-1显示了直到1993年8月的有关数据。如果网络数继续呈指数增长的话,虚线估计了2000年可能达到的最大的网络数。
+“自由地接收,保守地发送。”
+
+
+资料来源于 即时通讯网 ,仅做学习参考
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 谢谢大爷~
+
+
+
+
+
+
+
+
+
+
+
+ 微信
+
+ 支付宝
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git "a/2017/07/23/title: \345\270\270\350\247\201\346\216\222\345\272\217\347\256\227\346\263\225\346\200\273\347\273\223 /index.html" "b/2017/07/23/title: \345\270\270\350\247\201\346\216\222\345\272\217\347\256\227\346\263\225\346\200\273\347\273\223 /index.html"
new file mode 100644
index 0000000..fb86a76
--- /dev/null
+++ "b/2017/07/23/title: \345\270\270\350\247\201\346\216\222\345\272\217\347\256\227\346\263\225\346\200\273\347\273\223 /index.html"
@@ -0,0 +1,805 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 常见排序算法总结 | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
常见排序算法总结
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
排序算法一般分为:
+
+内部排序(In-place sort) 不占用额外内存或者占用常数内存,如:插入排序、选择排序、冒泡排序、堆排序、快速排序。
+外部排序(Out-place sort) 因为排序数据大,可用内存一次不能容纳所有排序记录,排序过程中需要访问外存,如:归并排序、计数排序、基数排序、桶排序。
+
+
也分为:
+
+稳定的排序(stable sort) 插入排序、冒泡排序、归并排序、计数排序、基数排序、桶排序。
+不稳定的排序(unstable sort) 选择排序、快速排序、堆排序。
+算法的稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,ri=rj,且ri在rj之前,而在排序后的序列中,ri仍在rj之前,则称这种排序算法是稳定的;否则称为不稳定的。
+
+不稳定算法的改进:只需要在每个输入元素加一个index,表示初始时的数组索引,当不稳定的算法排好序后,对于相同的元素对index排序即可。
+
+
+
+
插入排序
+
+
+
+
+
+
+
最优复杂度:当输入数组就是排好序的时候,复杂度为O(n),而快速排序在这种情况下会产生O(n^2)的复杂度。 最差复杂度:当输入数组为倒序时,复杂度为O(n^2)。
+
+插入排序比较适合用于“少量元素的数组”。插入排序比较适合用于“少量元素的数组”。
+
+
1.直接插入排序(Straight Insertion Sort)
+
工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。具体算法描述如下:
+
+从第一个元素开始,该元素可以认为已经被排序
+取出下一个元素,在已经排序的元素序列中从后向前扫描
+如果该元素(已排序)大于新元素,将该元素移到下一位置
+重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
+将新元素插入到该位置后
+重复步骤2~5
+
+
直接插入排序示例:
+
+
+
+
+
+
+
+如果排序时碰到相等的元素,比较后会把相等的元素放在后面,所以这两个相等的元素之间的前后顺序没有改变,排序是稳定的。
+
+
算法的实现:
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void InsertSort (int a[], int n)
{
for (int i = 1 ; i < n; i++)
{
if (a[i] < a[i-1 ])
{
int j = i-1 ;
int temp = a[i];
a[i] = a[i-1 ];
while (x < a[j] && j >= 0 )
{
a[j+1 ] = a[j];
j--;
}
a[j+1 ] = temp;
}
}
}
+
+如果比较操作的代价比交换操作大的话,可以采用二分查找法来减少比较操作的数目。直接插入排序算法的优化算法有:二分插入排序,2-路插入排序。
+
+
2.希尔排序(Shell’s Sort)
+
希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。它是基于插入排序的以下两点性质而提出改进方法的:
+
+插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率
+但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位
+
+
基本原理是先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。算法具体描述如下:
+
+选择一个步长,其设计者Donald Shell最初建议步长选择为n/2 并且对步长取半直到步长达到1;
+以步长为间隔对序列进行排序;
+重复步骤2直到步长为1。
+
+
希尔排序的示例:
+
+
+
+
+
+
+
+希尔排序是不稳定的。
+
+
算法的实现:
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
void ShellInsertSort (int a[], int n)
{
for (int gap=n>>1 ; gap>0 ; gap>>=1 )
{
for (int i=gap; i<n; i++)
{
int temp = a[i];
int j;
for (j=i-gap; j>=0 &&a[j]>temp; j-=gap)
a[j+gap] = a[j];
a[j+gap] = temp;
}
}
}
+
可能希尔排序最重要的地方在于当用较小步长排序后,以前用的较大步长仍然是有序的。已知的最好步长序列是由Sedgewick提出的(1, 5, 19, 41, 109,…),该序列的项来自两个算式[1] 。这项研究也表明“比较在希尔排序中是最主要的操作,而不是交换。”用这样步长序列的希尔排序比插入排序要快,甚至在小数组中比快速排序和堆排序还快,但是在涉及大量数据时希尔排序还是比快速排序慢。
+
选择排序
+
+
+
+
+
+
+
简单选择排序(Simple Selection Sort)
+
基本原理是在要排序的一组数中,选出最小(或者最大)的一个数与第1个位置的数交换;然后在剩下的数当中再找最小(或者最大)的与第2个位置的数交换,依次类推,直到第n-1个元素(倒数第二个数)和第n个元素(最后一个数)比较为止。算法的具体描述如下:
+
+第一趟,从n 个记录中找出关键码最小的记录与第一个记录交换;
+第二趟,从第二个记录开始的n-1 个记录中再选出关键码最小的记录与第二个记录交换;
+
+以此类推…..
+
+第i趟,则从第i 个记录开始的n-i+1 个记录中选出关键码最小的记录与第i 个记录交换,
+
+
+
直到整个序列按关键码有序。
+
简单选择排序的示例:
+
+
+
+
+
+ 算法实现:
+
1
2
3
4
5
6
7
8
9
10
11
void SelectSort(int a[], int n)
{
trueint min = a[0 ];
truefor (int i = 0 ; i < n; i++)
true{
truetruefor (int j = i; j < n; j++)
truetruetrueif (a[j] < min )
truetruetruetruemin = a[j];
truetruea[i] = min ;
true}
}
+
+选择排序是稳定的。
+
+
选择排序的交换操作介于0 和(n-1) 次之间。选择排序的比较操作为n(n-1) 次之间。选择排序的赋值操作介于0 和3(n-1) 次之间。 比较次数O(n^2 ) ,比较次数与关键字的初始状态无关,总的比较次数N=(n-1)+(n-2)+…+1=n(n-1)/2 。交换次数O(n) ,最好情况是,已经有序,交换0 次;最坏情况是,逆序,交换n-1 次。交换次数比冒泡排序较少,由于交换所需CPU时间比比较所需的CPU时间多,n 值较小时,选择排序比冒泡排序快。 原地操作几乎是选择排序的唯一优点,当空间复杂度要求较高时,可以考虑选择排序;实际适用的场合非常罕见。
+
简单排序的改进——二元选择排序 简单选择排序,每趟循环只能确定一个元素排序后的定位。我们可以考虑改进为每趟循环确定两个元素(当前趟最大和最小记录)的位置,从而减少排序所需的循环次数。改进后对n个数据进行排序,最多只需进行[n/2]趟循环即可。具体实现如下:
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void SelectSort(int r[],int n) {
int i ,j , min ,max , tmp;
for (i=1 ;i <= n/2 ;i++) {
min = i; max = i ;
for (j= i+1 ; j<= n-i; j++) {
if (r[j] > r[max ]) {
max = j ; continue ;
}
if (r[j]< r[min ]) {
min = j ;
}
}
tmp = r[i-1 ]; r[i-1 ] = r[min ]; r[min ] = tmp;
tmp = r[n-i]; r[n-i] = r[max ]; r[max ] = tmp;
}
}
+
堆排序(Heap Sort)
+
+
+
+
+
+
+
+
堆排序是指利用堆这种数据结构所设计的一种排序算法。其基本原理如下: 堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。 若以一维数组存储一个堆,则堆对应一棵完全二叉树,且所有非叶结点的值均不大于(或不小于)其子女的值,根结点(堆顶元素)的值是最小(或最大)的。如:
+
+大顶堆序列:(96, 83, 27, 38, 11, 09)
+小顶堆序列:(12, 36, 24, 85, 47, 30, 53, 91)
+
+
+
+
+
+
+
+
+
初始时把n个数的序列看作是一棵顺序存储的二叉树(一维数组存储二叉树),调整它们的存储序,使之成为一个堆,将堆顶元素输出,得到n个元素中最小(或最大)的元素,这时堆的根节点的数最小(或者最大)。然后对前面(n-1)个元素重新调整使之成为堆,输出堆顶元素,得到n个元素中次小(或次大)的元素。依此类推,直到只有两个节点的堆,并对它们作交换,最后得到有n个节点的有序序列。称这个过程为堆排序。 因此,实现堆排序需解决两个问题:
+
+如何将n 个待排序的数建成堆;
+输出堆顶元素后,怎样调整剩余n-1 个元素,使其成为一个新堆。
+
+
首先讨论第二个问题:输出堆顶元素后,对剩余n-1元素重新建成堆的调整过程。调整小顶堆的方法:
+
+设有m 个元素的堆,输出堆顶元素后,剩下m-1 个元素。将堆底元素送入堆顶((最后一个元素与堆顶进行交换),堆被破坏,其原因仅是根结点不满足堆的性质。
+将根结点与左、右子树中较小元素的进行交换。
+若与左子树交换:如果左子树堆被破坏,即左子树的根结点不满足堆的性质,则重复方法 2.
+若与右子树交换,如果右子树堆被破坏,即右子树的根结点不满足堆的性质。则重复方法 2.
+继续对不满足堆性质的子树进行上述交换操作,直到叶子结点,堆被建成。
+
+
称这个自根结点到叶子结点的调整过程为筛选。如图:
+
+
+
+
+
+
+
再讨论对n 个元素初始建堆的过程。 建堆方法:对初始序列建堆的过程,就是一个反复进行筛选的过程。
+
+n个结点的完全二叉树,则最后一个结点是第个结点的子树。
+筛选从第个结点为根的子树开始,该子树成为堆。
+之后向前依次对各结点为根的子树进行筛选,使之成为堆,直到根结点。
+
+
如图建堆初始过程:无序序列:(49,38,65,97,76,13,27,49)
+
+
+
+
+
+
+
算法的实现:
+
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
* 已知H[s…m]除了H[s] 外均满足堆的定义
* 调整H[s],使其成为大顶堆.即将对第s个结点为根的子树筛选,
*
* @param H是待调整的堆数组
* @param s是待调整的数组元素的位置
* @param length是数组的长度
*
*/
void HeapAdjust(int H[],int s, int length )
{
int tmp = H[s];
int child = 2 *s+1 ;
while (child < length )
{
if (child+1 <length && H[child]<H[child+1 ])
++child;
if (H[s]<H[child])
{
H[s] = H[child];
s = child;
child = 2 *s+1 ;
}
else
break ;
H[s] = tmp;
}
}
* 初始堆进行调整
* 将H[0..length-1]建成堆
* 调整完之后第一个元素是序列的最小的元素
*/
void BuildingHeap(int H[], int length )
{
for (int i = (length -1 ) / 2 ; i >= 0 ; i--)
HeapAdjust(H,i,length );
}
* 堆排序算法
*/
void HeapSort(int H[],int length )
{
BuildingHeap(H, length );
for (int i = length - 1 ; i > 0 ; --i)
{
int temp = H[i]; H[i] = H[0 ]; H[0 ] = temp;
HeapAdjust(H,0 ,i);
}
}
+
+堆排序是不稳定的。
+
+
设树深度为k,
+
+
+
+
+
+ 次,交换记录至多k 次。所以,在建好堆后,排序过程中的筛选次数不超过下式:
+
+
+
+
+
+ 而建堆时的比较次数不超过4n 次,因此堆排序最坏情况下,时间复杂度也为:O(nlogn )。
+
交换排序 冒泡排序(Bubble Sort)
+
+
+
+
+
+
+
+
冒泡排序重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。 冒泡排序示例:
+
+
+
+
+
+ 算法的实现:
+
1
2
3
4
5
6
7
8
9
10
11
void bubbleSort(int a[], int n)
{
for(int i =0
for(int j = 0
if(a[j] > a[j+1])
{
int tmp = a[j];
a[j] = a[j+1];
a[j+1] = tmp
}
}
+
+冒泡排序是稳定的。
+
+
冒泡算法的改进 对冒泡排序常见的改进方法是加入一标志性变量exchange,用于标志某一趟排序过程中是否有数据交换,如果进行某一趟排序时并没有进行数据交换,则说明数据已经按要求排列好,可立即结束排序,避免不必要的比较过程。本文再提供以下两种改进算法:
+
+设置一标志性变量pos,用于记录每趟排序中最后一次进行交换的位置。由于pos位置之后的记录均已交换到位,故在进行下一趟排序时只要扫描到pos位置即可。 改进后算法如下:
+
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void Bubble_1 ( int r[], int n)
{
int i = n -1 ;
while (i > 0 )
{
int pos= 0 ;
for (int j = 0 ; j < i; j++)
if (r[j] > r[j+1 ])
{
pos = j;
int tmp = r[j];
r[j] = r[j+1 ];
r[j+1 ] = tmp;
}
i = pos;
}
}
+
+传统冒泡排序中每一趟排序操作只能找到一个最大值或最小值,我们考虑利用在每趟排序中进行正向和反向两遍冒泡的方法一次可以得到两个最终值(最大者和最小者) , 从而使排序趟数几乎减少了一半。 改进后的算法为:
+
+
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
void Bubble_2 (int r[], int n)
{
int low = 0
int high = n-1
int tmp, j;
while (low < high)
{
for (j = low
if (r[j] > r[j+1])
{
tmp = r[j];
r[j]=r[j+1];
r[j+1]=tmp;
}
--high
for (j = high
if (r[j] < r[j-1])
{
tmp = r[j];
r[j]=r[j-1];
r[j-1]=tmp;
}
++low
}
}
+
快速排序(Quick Sort)
+
+
+
+
+
+
+
+
快速排序又称划分交换排序(partition-exchange sort),使用分治法(Divide and conquer)策略来把一个序列(list)分为两个子序列(sub-lists)。其算法描述为:
+
+从数列中挑出一个元素,称为”基准”(pivot)。
+重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(相同的数可以到任一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
+递归地(recursively)把小于基准值元素的子数列和大于基准值元素的子数列排序。
+
+
快速排序的示例:
+
+一趟排序的过程
+
+
+
+
+
+
+
+
+排序的全过程
+
+
+
+
+
+
+
+
+
+
算法的实现(递归):
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void quickSort_recursive(int a[], int low, int high)
{
int first = low;
int last = high;
int key = a[first ];
if (low >= high)
return ;
while (first < last )
{
while ((first < last )&&(key <= a[last ]))
last --;
a[first ] = a[last ];
while ((first < last )&&(key >= a[first ]))
first ++;
a[last ] = a[first ];
}
a[first ] = key ;
quickSort_recursive(a, low, first -1 );
quickSort_recursive(a, first +1 , high);
}
+
+快速排序是不稳定的。 快排迭代算法:维护一个栈,存放划分的起点终点
+
+
在平均状况下,排序n个项目要Ο(nlogn)次比较。在最坏状况下(正序或逆序)则需要Ο(n^2 )次比较,取决于其递归树的高度。事实上,快速排序通常明显比其他Ο(nlogn)算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来。快速排序是一个不稳定的排序方法。
+
归并排序(Merge Sort)
+
+
+
+
+
+
+
+
归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。
+
递归法
+将序列每相邻两个数字进行归并操作,形成floor(n/2)个序列,排序后每个序列包含两个元素
+将上述序列再次归并,形成floor(n/4)个序列,每个序列包含四个元素
+重复步骤2,直到所有元素排序完毕
+
+
算法实现:
+
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
void merge_sort_recursive(int arr[], int reg [], int start, int end )
{
if (start >= end )
return ;
int len = end - start, mid = (len >> 1 ) + start;
int start1 = start, end1 = mid;
int start2 = mid + 1 , end2 = end ;
merge_sort_recursive(arr, reg , start1, end1);
merge_sort_recursive(arr, reg , start2, end2);
int k = start;
while (start1 <= end1 && start2 <= end2)
reg [k++] = arr[start1] < arr[start2] ? arr[start1++] : arr[start2++];
while (start1 <= end1)
reg [k++] = arr[start1++];
while (start2 <= end2)
reg [k++] = arr[start2++];
for (k = start; k <= end ; k++)
arr[k] = reg [k];
}
void merge_sort(int arr[], const int len)
{
trueint reg [len];
truemerge_sort_recursive(arr, reg , 0 , len - 1 );
}
+
两路归并的递归算法 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void MSort (ElemType *r, ElemType *rf,int s, int t)
{
ElemType *rf2;
if (s==t) r[s] = rf[s];
else
{
int m=(s+t)/2 ;
MSort(r, rf2, s, m);
MSort(r, rf2, m+1 , t);
Merge(rf2, rf, s, m+1 ,t);
}
}
void MergeSort_recursive (ElemType *r, ElemType *rf, int n)
{
MSort(r, rf,0 , n-1 );
}
+
迭代法
+申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列;
+设定两个指针,最初位置分别为两个已经排序序列的起始位置;
+比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置;
+重复步骤3直到某一指针到达序列尾;
+将另一序列剩下的所有元素直接复制到合并序列尾;
+
+
算法实现:
+
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
void merge_sort_iteration(int arr[], int len )
{
int * a = arr;
int * b = new int [len ];
for (int seg = 1 ; seg < len ; seg += seg)
{
for (int start = 0 ; start < len ; start += seg + seg)
{
int low = start, mid = min(start + seg, len ), high = min(start + seg + seg, len );
int k = low;
int start1 = low, end1 = mid ;
int start2 = mid , end2 = high;
while (start1 < end1 && start2 < end2)
b[k++] = a[start1] < a[start2] ? a[start1++] : a[start2++];
while (start1 < end1)
b[k++] = a[start1++];
while (start2 < end2)
b[k++] = a[start2++];
}
T* temp = a;
a = b;
b = temp;
}
if (a != arr)
{
for (int i = 0 ; i < len ; i++)
b[i] = a[i];
b = a;
}
delete[] b;
}
+
+归并排序是稳定的。
+
+
总结 各种排序的稳定性,时间复杂度和空间复杂度总结:
+
+
+
+
+
+
+
+参考资料有: 真实的归宿——八大排序算法 xiazdong——九大排序算法再总结 wikipedia
+
+
+
+
+
+
+
+
+ Last updated: 2018-01-15 00:45:17
+
+
+
+
+
http://codingdoge.cn/2017/07/23/title: 常见排序算法总结 /
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 谢谢大爷~
+
+
+
+
+
+
+
+
+
+
+
+ 微信
+
+ 支付宝
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git "a/2017/07/23/title: \346\234\211\351\231\220\347\212\266\346\200\201\346\234\272\345\234\250iOS\344\270\255\347\232\204\345\272\224\347\224\250/index.html" "b/2017/07/23/title: \346\234\211\351\231\220\347\212\266\346\200\201\346\234\272\345\234\250iOS\344\270\255\347\232\204\345\272\224\347\224\250/index.html"
new file mode 100644
index 0000000..d2f6fb1
--- /dev/null
+++ "b/2017/07/23/title: \346\234\211\351\231\220\347\212\266\346\200\201\346\234\272\345\234\250iOS\344\270\255\347\232\204\345\272\224\347\224\250/index.html"
@@ -0,0 +1,546 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 有限状态机在iOS中的应用 | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
有限状态机在iOS中的应用
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
有限状态机(Finite-State Machine, FSM),是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。
+
其实我们平常和很多状态机都打过交道,比如正则表达式、网络协议(如TCP协议状态机)、游戏设计、字符串匹配等等,可能大多数时候我们都没意识到,接下来我们简略了解下状态机。
+
基本概念
+状态(state): 指的是对象在其生命周期中的一种状况,处于某个特定状态中的对象必然会满足某些条件、执行某些动作或者是等待某些事件,而且状态是相对稳定的,如果没有外部条件的触发,一个状态会一直持续下去。
+事件(event): 指的是对状态机来讲是有意义的那些事情。事件通常会引起状态的变迁,促使状态机从一种状态切换到另一种状态。
+转换(transition): 指的是两个状态之间的一种关系,表明对象将在第一个状态中执行一定的动作,并将在某个事件发生的同时某个特定条件满足时进入第二个状态。
+动作(action): 指的是状态机中可以执行的那些操作,当事件被满足或者状态变迁时执行动作,动作不是必需的。
+
+
如下图状态表:
+
+
+
+
+
+
+
+关于状态机的详细知识可以参考:UML状态图的实用C/C++设计 ERLANG
+
+
在iOS中的使用 背景 假设我们要设计一款网络视频播放器,有基本的播放、暂停功能,当缓冲好时可以进行播放,当URL错误或者视频资源错误时播放失败,我们发现,当我们去描述一个事物以及它的功能时,总是离不开它的状态,如这个播放器,我们可以定义它有播放失败、缓冲中、将要播放…等等状态。
+
设计 我们可以用状态机去实现这样的功能。
+
+
1
2
3
4
5
6
7
8
enum VideoPlayerState {
case failed, // 播放失败
buffering, // 缓冲中
readyToPlay, // 将要播放
playing, // 播放中
paused, // 播放暂停
finished // 播放完毕
}
+
+然后定义当状态发生变换后,针对某个状态我希望它去执行一些逻辑里的动作
+
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var state: VideoPlayerState = .paused {
didSet {
switch state {
case .failed :
popReminderView()
case .buffering :
case .readyToPlay :
case .playing :
case .paused :
case .finished :
}
}
}
+
+在写我们的业务逻辑时,相应的去更改播放器的状态
+
+
1
2
3
4
5
6
networkRequestCompletion () {
state = .readyToPlay
}
+
这样,通过state我们能很清晰的知道现在播放器是什么样应该做什么事,在我们的业务逻辑中,当状态变化时通过didSet我们能很方便的去响应对应状态下应该执行的行为。
+
总结 整篇文章质量或许不是很高,因为还没有大量的代码实践让我去有一个更深更全的体会,但是希望能带给我自己和读者们一点抛砖引玉的效果。我们在编码、设计过程中,多去思考一些,什么地方用什么样的模式更好,比如状态机,来使我们的代码更解耦,易维护,高扩展。
+
+这里有一篇关于Objective-c状态机的实现 ,更抽象,其中用到的枚举值自动转字符串通用方案很有意思,同时可以参考这篇《iOS开发高级:使用宏定义macros 》关于宏定义的使用。 这篇《iOS APP 架构漫谈二 》列举了很具体的运用场景,可以参考。 同时参考的文章有:
+
+关于状态记的开源库推荐:
+
+
+
+
+
+
+
+
+
+ Last updated: 2018-01-13 13:06:26
+
+
+
+
+
http://codingdoge.cn/2017/07/23/title: 有限状态机在iOS中的应用/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 谢谢大爷~
+
+
+
+
+
+
+
+
+
+
+
+ 微信
+
+ 支付宝
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git "a/2017/07/23/title: \350\247\206\351\242\221\345\274\200\345\217\221/index.html" "b/2017/07/23/title: \350\247\206\351\242\221\345\274\200\345\217\221/index.html"
new file mode 100644
index 0000000..f739b74
--- /dev/null
+++ "b/2017/07/23/title: \350\247\206\351\242\221\345\274\200\345\217\221/index.html"
@@ -0,0 +1,671 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 视频开发 | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
视频开发
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
下面會介紹視頻的一些基本知識,和在iOS上實現視頻播放和緩存的幾種方案。
+
+
+软解码和硬解码 GPU解码就是所谓的硬解码,CPU解码就是软解码。iOS提供的播放器类使用的是硬解码,所以视频播放对CPU不会有很大的压力,但是支持的播放格式比较单一,一般就是MP4、MOV、M4V这几个。
+HTTP Live Streaming HTTP Live Streaming(缩写是 HLS)是一个由苹果公司提出的基于HTTP的流媒体网络传输协议。它的工作原理是把整个流分成一个个小的基于HTTP的文件来下载,每次只下载一些。当媒体流正在播放时,客户端可以选择从许多不同的备用源中以不同的速率下载同样的资源,允许流媒体会话适应不同的数据速率。支持的视频流编码为H.264。我们在视频网站上看到的M3U8后缀的播放链接就是使用HLS协议的视频。HLS优点,1、看完一段缓存一段,防止只看一段视频但是把整个视频文件都缓存下来的用户,减少服务器压力和节省流量。2、根据用户网速切换不同的码率,兼顾流程性和清晰度。
+
+
+
播放 实现视频播放的两个方案。
+
+
一、自己实现对数据编码解码 可以在一些开源播放器上进行二次开发,如Bilibili的ijkplayer ,或者直接对FFmpeg 开发,优点在整个播放过程可控,为后续进行缓存、流量控制、码率切换等开发提供了基础,缺点是复杂,要求高,工程量大。
+
+
二、AVFoundation Media Assets, Playback and Editing. 使用Apple自有框架。
+
AVAsset
+AVAsset is an abstract, immutable class used to model timed audiovisual media such as videos and sounds. An asset may contain one or more tracks that are intended to be presented or processed together, each of a uniform media type, including but not limited to audio, video, text, closed captions, and subtitles.
+
+
Audiovisual media的资源类,通常通过AVURLAsset用URL来实例化,可以用Atom Inspector (一个Apple提供的用来查看视频信息的工具)来观察一个视频的属性,再去AVAsset中对应其属性。
+
+
+
+AVAsset属性
+视频文件属性
+
+
+
+
+duration: CMTime
+duration, timescale时长和时间尺度
+
+
+preferredRate: Float
+默认速度
+
+
+preferredVolume: Float
+默认音量
+
+
+creationDate: AVMetadataItem?
+视频创建时间
+
+
+tracks: [AVAssetTrack]
+轨道
+
+
+trackGroups: [AVAssetTrackGroup]
+轨道组
+
+
+lyrics: String?
+当前语言环境合适的歌词
+
+
+metadata: [AVMetadataItem]
+元数据
+
+
+
+
AVPlayer
+An AVPlayer is a controller object used to manage the playback and timing of a media asset. It provides the interface to control the player’s transport behavior such as its ability to play, pause, change the playback rate, and seek to various points in time within the media’s timeline. You can use an AVPlayer to play local and remote file-based media, such as QuickTime movies and MP3 audio files, as well as audiovisual media served using HTTP Live Streaming.
+
+
AVPlayer是一个控制对象用于管理媒体asset的播放,它提供了相关的接口控制播放器的行为,比如:播放、暂停、改变播放的速率、跳转到媒体时间轴上的某一个点(简单理解就是实现拖动功能显示对应的视频位置内容)。我们能够使用AVPlayer播放本地和远程的媒体文件(使用 HTTP Live Streaming),比如: QuickTime movies 和 MP3 audio files,所以AVPlayer可以满足音视频播放的基本需求。
+
+
+
+
+ AVFoundation的层次
+
+
AVPlayerItem
+AVPlayerItem models the timing and presentation state of an asset played by an AVPlayer object. It provides the interface to seek to various times in the media, determine its presentation size, identify its current time, and much more.
+
+
AVPlayerItem是一个负责处理AVAsset的资源并通过AVPlayer来播放的载体,提供了seek、确定显示大小、ID、时间等的接口。
+
AVPlayerLayer
+AVPlayerLayer is a subclass of CALayer to which an AVPlayer object can direct its visual output. It can be used as the backing layer for a UIView or NSView or can be manually added to the layer hierarchy to present your video content on screen.
+
+
负责AVPlayer的视频输出展示。
+
+
+
+
+
+ 依赖关系图
+
+
简单使用 1
2
3
4
5
6
7
8
9
10
11
class AVPlayerTestView: UIView {
let view: UIView? = nil
func initPlayerView() {
guard let url = URL.init(string: "http://meipu1.video.meipai.com/5e81c08e-2850-4fbd-bfc4-4ded297f9f1c.mp") else { return }
let asset = AVAsset.init(url: url)
let item = AVPlayerItem.init(asset: asset)
let player = AVPlayer.init(playerItem: item)
let playerLayer = AVPlayerLayer.init(layer: player)
view?.layer.addSublayer(playerLayer)
}
}
+
设置好一个AVPlayer的依赖关系和输出图层后,AVPlayerItem会根据你的URL去请求数据,自己内部做缓冲然后播放。我们需要做的是用KVO监听AVPlayerItem内部几个关键属性的状态,然后做出我们的处理。
+
+
+
+AVPlayerItem属性
+状态
+
+
+
+
+status: AVPlayerItemStatus
+
+
+
+.unknown: AVPlayerItemStatus
+未知状态
+
+
+.readyToPlay: AVPlayerItemStatus
+准备好去播放
+
+
+.failed: AVPlayerItemStatus
+资源无法被播放
+
+
+loadedTimeRanges: [NSValue]
+加载了的资源的时间范围(一般用来更新缓冲UI)
+
+
+playbackBufferEmpty: Bool
+没有缓冲数据
+
+
+playbackLikelyToKeepUp: Bool
+有足够的缓冲大致能播放无卡顿
+
+
+
+
+General State Observations: You can use Key-value observing (KVO) to observe state changes to many of the player’s dynamic properties, such as its currentItem or its playback rate. You should register and unregister for KVO change notifications on the main thread. This avoids the possibility of receiving a partial notification if a change is being made on another thread. AVFoundation invokes observeValue(forKeyPath:of:change:context:) on the main thread, even if the change operation is made on another thread.
+
+基本状态观察者:你能够使用KVO来观察player动态属性的状态改变,比如像: currentItem 或者它的播放速度。我们应该在主线程注册和去除KVO,这能够避免如果在其它线程发送改变而导致接收局部通知,当发生通知,AVFoundation将在主线程调用observeValue(forKeyPath:of:change:context:) 方法,即使是在其他线程发生。
+
+
KVO能够很好的观察生成的状态,但是并不能够观察播放时间的改变,所以AVPlayer提供了两个方法来观察时间的改变:
+
1
2
3
4
5
6
7
8
9
10
11
/*
@param interval
调用block的时间间隔
@param queue
推荐使用串行队列,放在主线程就行了,并行队列会产生不明确的结果
*/
func addPeriodicTimeObserver(forInterval interval: CMTime, queue: DispatchQueue?, using block: @escaping (CMTime) -> Void) -> Any {
// 可以在里面去设置控制状态,刷新进度UI
}
func addBoundaryTimeObserver(forTimes times: [NSValue], queue: DispatchQueue?, using block: @escaping () -> Void) -> Any
+
+Tips:
+
+创建多个AVPlayerLayer只有最近的layer才会显示视频帧
+可以创建多个AVPlayerItem来替换AVPlayer的当前item,func replaceCurrentItem(with item: AVPlayerItem?)
+监听后要注意控制监听的生命周期
+
+
+
缓存 Apple自有的框架是没有提供缓存功能的,AVPlayer也没有提供直接获取其下载数据的接口,所以想做缓存只能自己来完整的实现。下面有几个方案。
+
一、自己实现的播放器 这种情况大多是根据下载来的数据解码播放,下载的时候做下缓存就好了
+
二、自带播放器+LocalServer 在iOS本地开启Local Server服务,然后MPMoviePlayerController请求本地Local Server服务。本地Local Server服务再不停的去对应的视频地址获取视频流。本地Local Server请求的时候,就可以把视频流缓存在本地。Demo来源:Code4App
+
三、AVPlayer+AVMutableComposition+AVAssetExportSession 原理是直接给AVPlayer传URL,让其内部自己去处理数据下载,然后通过AVMutableComposition和AVAssetExportSession从AVAsset提取视频的数据进行缓存。
+
AVMutableComposition
+AVMutableComposition is a mutable subclass of AVComposition you use when you want to create a new composition from existing assets. You can add and remove tracks, and you can add, remove, and scale time ranges.
+
+
作用是从现有的AVAsset中创建出一个新的AVComposition,使用者能够从别的asset中提取他们的音频轨道或视频轨道,并且把它们添加到新建的composition中。
+
AVAssetExportSession
+An AVAssetExportSession object transcodes the contents of an AVAsset source object to create an output of the form described by a specified export preset.
+
+
作用是把AVAsset解码输出到本地文件中。
+
关键需要把原先的AVAsset(AVURLAsset)实现的数据提取出来后拼接成另一个AVAsset(AVComposition)的数据然后解码输出,由于通过网络url下载下来的视频没有保存视频的原始数据(苹果没有暴露接口给我们获取),下载后播放的avasset不能使用AVAssetExportSession输出到本地文件,要曲线地把下载下来的视频通过重构成另外一个AVAsset实例才能输出。
+
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
NSString *documentDirectory = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
NSString *myPathDocument = [documentDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.mp4",[_source.videoUrl MD5]]];
NSURL *fileUrl = [NSURL fileURLWithPath:myPathDocument];
if (asset != nil) {
AVMutableComposition *mixComposition = [[AVMutableComposition alloc]init];
AVMutableCompositionTrack *firstTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
[firstTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, asset.duration) ofTrack:[[asset tracksWithMediaType:AVMediaTypeVideo]objectAtIndex:0] atTime:kCMTimeZero error:nil];
AVMutableCompositionTrack *audioTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
[audioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, asset.duration) ofTrack:[[asset tracksWithMediaType:AVMediaTypeAudio]objectAtIndex:0] atTime:kCMTimeZero error:nil];
AVAssetExportSession *exporter = [[AVAssetExportSession alloc]initWithAsset:mixComposition presetName:AVAssetExportPresetHighestQuality];
exporter.outputURL = fileUrl;
if (exporter.supportedFileTypes) {
exporter.outputFileType = [exporter.supportedFileTypes objectAtIndex:0] ;
exporter.shouldOptimizeForNetworkUse = YES;
[exporter exportAsynchronouslyWithCompletionHandler:^{
}];
}
}
+
四、AVPlayer+AVAssetResourceLoader AVAssetResourceLoadingRequest
+An AVAssetResourceLoader object mediates resource requests from an AVURLAsset object with a delegate object that you provide. When a request arrives, the resource loader asks your delegate if it is able to handle the request and reports the results back to the asset.AVAssetResourceLoader协调来自AVURLAsset的资源请求,你需要实现它的delegate。当收到一个请求时,ResourceLoader询问你的delegate是否能处理并将结果返回给asset。
+
+
+
+
+
+
+ AVPlayer和AVAssetResourceLoader的层次结构
+
+
AVAssetResourceLoader通过你提供的委托对象去调节AVURLAsset所需要的加载资源。而很重要的一点是,AVAssetResourceLoader仅在AVURLAsset不知道如何去加载这个URL资源时才会被调用,就是说你提供的委托对象在AVURLAsset不知道如何加载资源时才会得到调用。一般我们可以更改URL的scheme用来隐藏真实的URL。如:
+
+参考iOS开发系列–音频播放、录音、视频播放、拍照、视频录制 AVplayer实现播放本地和网络视频(Swift3.0) iOS视频流开发(2)—视频播放 iOS音频播放 (九):边播边缓存 iOS音视频实现边下载边播放 AVFoundation(二):核心AVAsset AVFoundation编程指南2-用AVPlayer播放视频 AV Foundation系列(五)媒体组合
+
+
+
+
+
+
+
+
+ Last updated: 2018-01-13 13:06:57
+
+
+
+
+
http://codingdoge.cn/2017/07/23/title: 视频开发/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 谢谢大爷~
+
+
+
+
+
+
+
+
+
+
+
+ 微信
+
+ 支付宝
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git "a/2017/07/23/title: \350\247\243\345\206\263Arduino CH34x\347\263\273\345\210\227\345\234\250macOS Sierra\344\270\255\346\211\276\344\270\215\345\210\260\344\270\262\345\217\243\347\232\204\351\227\256\351\242\230/index.html" "b/2017/07/23/title: \350\247\243\345\206\263Arduino CH34x\347\263\273\345\210\227\345\234\250macOS Sierra\344\270\255\346\211\276\344\270\215\345\210\260\344\270\262\345\217\243\347\232\204\351\227\256\351\242\230/index.html"
new file mode 100644
index 0000000..0d93f55
--- /dev/null
+++ "b/2017/07/23/title: \350\247\243\345\206\263Arduino CH34x\347\263\273\345\210\227\345\234\250macOS Sierra\344\270\255\346\211\276\344\270\215\345\210\260\344\270\262\345\217\243\347\232\204\351\227\256\351\242\230/index.html"
@@ -0,0 +1,547 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 解决Arduino CH34x系列在macOS Sierra中找不到串口的问题 | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
解决Arduino CH34x系列在macOS Sierra中找不到串口的问题
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+毕设做Arduino开发,一开始买了淘宝上慧净自己改的Arduino UNO国产板子,回来插在mac上读不出串口,安装了它附带的驱动还是读不出,同学买了原装板说一插上就能读出串口,都没自己装驱动,为了省麻烦,直接跟商家换了原装板,这时候麻烦来了,板子在我电脑上读不出串口,在别人电脑上能读出来,别人的在我电脑上也读不出来…很崩溃,然后从各个角度debug,下面一个个步骤来,基本能解决。
+
+
+安装Arduino IDE 上面是官网IDE下载的连接,大概是因为GFW的原因下载速度奇慢,大家也可以去搜Arduino中文社区,里面有好人做了*度网盘的下载种子,速度能快点。 IDE一般自带驱动,如果在串口里没发现,可以试试在
+
+
+
+
+ D7389412-ED23-427D-82E7-4E1C26114D67
+
+
+
这个系统报告里看看USB下面能不能读出
+
也可以在bash里输入
+
+
如果有类似的即可
+
+
+
+
+ 2C66575E-D0DD-4DEA-9F34-8F2E1CF4DA90
+
+
+官方驱动下载 如果都找不到的话可以去沁恒官方下载CH341SER驱动,安装后再查看一遍(安装驱动完会要求重启).
+
+更改SIP设置
+
+Apple在10.11中全面启用了名为 System Integrity Protection (SIP) 的系统完整性保护技术. 受此影响, 未经签名的第三方kext及经过修改的原版kext将无法正常加载, 大部分系统文件即使在root用户下也无法直接进行修改.
+
+
+
+
前面安装驱动不成功大部分是因为驱动文件冲突导致,所以在此之前先删除之前安装的驱动文件:
+
1
2
3
sudo rm -rf /System/Library/Extensions/usb.kext
sudo rm -rf /Library/Extensions/usbserial.kext
sudo rm -rf _private_var_db_receipts/com.wch .*
+
然后参考CH340 CH341 serial adapters fix for El Capitan OS X 可以通过以下步骤修改SIP设置来安装第三方kext:
+
+重启OS X 并且立即按住 Command+ R 来来进入恢复模式
+在恢复模式中, 菜单栏上面找到终端(Terminal)并打开
+在终端中键入命令csrutil enable —without kext
+看到成功的提示信息之后, 输入reboot重启系统
+注: —without kext 中的 - 有两条 Command 就是 ⌘ 图标
+
+
+
+
通过以上的操作之后, 采用CH340/1 系列芯片的Arduino开发板又可以被识别出来了. 如果还是无法识别, 请重新安装一次CH341SER驱动.
+
+最后如果还不行(我的就是到这里还不行),请看看你的板子是否接触不良,反复摩擦尝试看能不能读出串口,我之前也试过这个办法,但是没有用,后面偶然也是必然下反应过来,反反复复插USB拔出查看串口状态,终于解决了(无良商家害人)
+
+
+参考:如何重置 Mac 上的 NVRAM
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 谢谢大爷~
+
+
+
+
+
+
+
+
+
+
+
+ 微信
+
+ 支付宝
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git "a/2017/07/25/title: iOS \344\277\235\346\214\201\347\225\214\351\235\242\346\265\201\347\225\205\347\232\204\346\212\200\345\267\247/index.html" "b/2017/07/25/title: iOS \344\277\235\346\214\201\347\225\214\351\235\242\346\265\201\347\225\205\347\232\204\346\212\200\345\267\247/index.html"
new file mode 100644
index 0000000..0866eb5
--- /dev/null
+++ "b/2017/07/25/title: iOS \344\277\235\346\214\201\347\225\214\351\235\242\346\265\201\347\225\205\347\232\204\346\212\200\345\267\247/index.html"
@@ -0,0 +1,527 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ iOS 保持界面流畅的技巧 | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
iOS 保持界面流畅的技巧
+
+
2017-07-25
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+iOS 保持界面流畅的技巧
+
+
CPU 资源消耗原因和解决方案 对象创建 对象的创建会分配内存、调整属性、甚至还有读取文件等操作,比较消耗 CPU 资源。尽量用轻量的对象代替重量的对象,可以对性能有所优化。比如 CALayer 比 UIView 要轻量许多,那么不需要响应触摸事件的控件,用 CALayer 显示会更加合适。如果对象不涉及 UI 操作,则尽量放到后台线程去创建,但可惜的是包含有 CALayer 的控件,都只能在主线程创建和操作。通过 Storyboard 创建视图对象时,其资源消耗会比直接通过代码创建对象要大非常多,在性能敏感的界面里,Storyboard 并不是一个好的技术选择。
+
尽量推迟对象创建的时间,并把对象的创建分散到多个任务中去。尽管这实现起来比较麻烦,并且带来的优势并不多,但如果有能力做,还是要尽量尝试一下。如果对象可以复用,并且复用的代价比释放、创建新对象要小,那么这类对象应当尽量放到一个缓存池里复用。
+
对象调整 对象的调整也经常是消耗 CPU 资源的地方。这里特别说一下 CALayer:CALayer 内部并没有属性,当调用属性方法时,它内部是通过运行时 resolveInstanceMethod 为对象临时添加一个方法,并把对应属性值保存到内部的一个 Dictionary 里,同时还会通知 delegate、创建动画等等,非常消耗资源。UIView 的关于显示相关的属性(比如 frame/bounds/transform)等实际上都是 CALayer 属性映射来的,所以对 UIView 的这些属性进行调整时,消耗的资源要远大于一般的属性。对此你在应用中,应该尽量减少不必要的属性修改。
+
当视图层次调整时,UIView、CALayer 之间会出现很多方法调用与通知,所以在优化性能时,应该尽量避免调整视图层次、添加和移除视图。
+
对象销毁 对象的销毁虽然消耗资源不多,但累积起来也是不容忽视的。通常当容器类持有大量对象时,其销毁时的资源消耗就非常明显。同样的,如果对象可以放到后台线程去释放,那就挪到后台线程去。这里有个小 Tip:把对象捕获到 block 中,然后扔到后台队列去随便发送个消息以避免编译器警告,就可以让对象在后台线程销毁了。
+
1
2
3
4
5
NSArray *tmp = self.array;
self.array = nil;
dispatch_async(queue, ^{
[tmp class];
});
+
布局计算 视图布局的计算是 App 中最为常见的消耗 CPU 资源的地方。如果能在后台线程提前计算好视图布局、并且对视图布局进行缓存,那么这个地方基本就不会产生性能问题了。
+
不论通过何种技术对视图进行布局,其最终都会落到对 UIView.frame/bounds/center 等属性的调整上。上面也说过,对这些属性的调整非常消耗资源,所以尽量提前计算好布局,在需要时一次性调整好对应属性,而不要多次、频繁的计算和调整这些属性。
+
Autolayout Autolayout 是苹果本身提倡的技术,在大部分情况下也能很好的提升开发效率,但是 Autolayout 对于复杂视图来说常常会产生严重的性能问题。随着视图数量的增长,Autolayout 带来的 CPU 消耗会呈指数级上升。具体数据可以看这个文章:http://pilky.me/36/。 如果你不想手动调整 frame 等属性,你可以用一些工具方法替代(比如常见的 left/right/top/bottom/width/height 快捷属性),或者使用 ComponentKit、AsyncDisplayKit 等框架。
+
文本计算 如果一个界面中包含大量文本(比如微博微信朋友圈等),文本的宽高计算会占用很大一部分资源,并且不可避免。如果你对文本显示没有特殊要求,可以参考下 UILabel 内部的实现方式:用 [NSAttributedString boundingRectWithSize:options:context:] 来计算文本宽高,用 -[NSAttributedString drawWithRect:options:context:] 来绘制文本。尽管这两个方法性能不错,但仍旧需要放到后台线程进行以避免阻塞主线程。
+
如果你用 CoreText 绘制文本,那就可以先生成 CoreText 排版对象,然后自己计算了,并且 CoreText 对象还能保留以供稍后绘制使用。
+
文本渲染 屏幕上能看到的所有文本内容控件,包括 UIWebView,在底层都是通过 CoreText 排版、绘制为 Bitmap 显示的。常见的文本控件 (UILabel、UITextView 等),其排版和绘制都是在主线程进行的,当显示大量文本时,CPU 的压力会非常大。对此解决方案只有一个,那就是自定义文本控件,用 TextKit 或最底层的 CoreText 对文本异步绘制。尽管这实现起来非常麻烦,但其带来的优势也非常大,CoreText 对象创建好后,能直接获取文本的宽高等信息,避免了多次计算(调整 UILabel 大小时算一遍、UILabel 绘制时内部再算一遍);CoreText 对象占用内存较少,可以缓存下来以备稍后多次渲染。
+
图片的解码 当你用 UIImage 或 CGImageSource 的那几个方法创建图片时,图片数据并不会立刻解码。图片设置到 UIImageView 或者 CALayer.contents 中去,并且 CALayer 被提交到 GPU 前,CGImage 中的数据才会得到解码。这一步是发生在主线程的,并且不可避免。如果想要绕开这个机制,常见的做法是在后台线程先把图片绘制到 CGBitmapContext 中,然后从 Bitmap 直接创建图片。目前常见的网络图片库都自带这个功能。
+
图像的绘制 图像的绘制通常是指用那些以 CG 开头的方法把图像绘制到画布中,然后从画布创建图片并显示这样一个过程。这个最常见的地方就是 [UIView drawRect:] 里面了。由于 CoreGraphic 方法通常都是线程安全的,所以图像的绘制可以很容易的放到后台线程进行。一个简单异步绘制的过程大致如下(实际情况会比这个复杂得多,但原理基本一致):
+
1
2
3
4
5
6
7
8
9
10
11
- (void)display {
dispatch_async(backgroundQueue, ^{
CGContextRef ctx = CGBitmapContextCreate(...);
// draw in context...
CGImageRef img = CGBitmapContextCreateImage(ctx);
CFRelease(ctx);
dispatch_async(mainQueue, ^{
layer.contents = img;
});
});
}
+
GPU 资源消耗原因和解决方案 相对于 CPU 来说,GPU 能干的事情比较单一:接收提交的纹理(Texture)和顶点描述(三角形),应用变换(transform)、混合并渲染,然后输出到屏幕上。通常你所能看到的内容,主要也就是纹理(图片)和形状(三角模拟的矢量图形)两类。
+
纹理的渲染 所有的 Bitmap,包括图片、文本、栅格化的内容,最终都要由内存提交到显存,绑定为 GPU Texture。不论是提交到显存的过程,还是 GPU 调整和渲染 Texture 的过程,都要消耗不少 GPU 资源。当在较短时间显示大量图片时(比如 TableView 存在非常多的图片并且快速滑动时),CPU 占用率很低,GPU 占用非常高,界面仍然会掉帧。避免这种情况的方法只能是尽量减少在短时间内大量图片的显示,尽可能将多张图片合成为一张进行显示。
+
当图片过大,超过 GPU 的最大纹理尺寸时,图片需要先由 CPU 进行预处理,这对 CPU 和 GPU 都会带来额外的资源消耗。目前来说,iPhone 4S 以上机型,纹理尺寸上限都是 4096x4096,更详细的资料可以看这里:iosres.com。所以,尽量不要让图片和视图的大小超过这个值。
+
视图的混合 (Composing) 当多个视图(或者说 CALayer)重叠在一起显示时,GPU 会首先把他们混合到一起。如果视图结构过于复杂,混合的过程也会消耗很多 GPU 资源。为了减轻这种情况的 GPU 消耗,应用应当尽量减少视图数量和层次,并在不透明的视图里标明 opaque 属性以避免无用的 Alpha 通道合成。当然,这也可以用上面的方法,把多个视图预先渲染为一张图片来显示。
+
图形的生成 CALayer 的 border、圆角、阴影、遮罩(mask),CASharpLayer 的矢量图形显示,通常会触发离屏渲染(offscreen rendering),而离屏渲染通常发生在 GPU 中。当一个列表视图中出现大量圆角的 CALayer,并且快速滑动时,可以观察到 GPU 资源已经占满,而 CPU 资源消耗很少。这时界面仍然能正常滑动,但平均帧数会降到很低。为了避免这种情况,可以尝试开启 CALayer.shouldRasterize 属性,但这会把原本离屏渲染的操作转嫁到 CPU 上去。对于只需要圆角的某些场合,也可以用一张已经绘制好的圆角图片覆盖到原本视图上面来模拟相同的视觉效果。最彻底的解决办法,就是把需要显示的图形在后台线程绘制为图片,避免使用圆角、阴影、遮罩等属性。
+
+参考:iOS 保持界面流畅的技巧
+
+
+
+
+
+
+
+
+ Last updated: 2018-01-15 00:35:13
+
+
+
+
+
http://codingdoge.cn/2017/07/25/title: iOS 保持界面流畅的技巧/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 谢谢大爷~
+
+
+
+
+
+
+
+
+
+
+
+ 微信
+
+ 支付宝
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git "a/2017/07/25/title: \345\211\221\346\214\207Offer \342\200\224\342\200\224 \346\273\221\345\212\250\347\252\227\345\217\243\347\232\204\346\234\200\345\244\247\345\200\274/index.html" "b/2017/07/25/title: \345\211\221\346\214\207Offer \342\200\224\342\200\224 \346\273\221\345\212\250\347\252\227\345\217\243\347\232\204\346\234\200\345\244\247\345\200\274/index.html"
new file mode 100644
index 0000000..2e5b183
--- /dev/null
+++ "b/2017/07/25/title: \345\211\221\346\214\207Offer \342\200\224\342\200\224 \346\273\221\345\212\250\347\252\227\345\217\243\347\232\204\346\234\200\345\244\247\345\200\274/index.html"
@@ -0,0 +1,508 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 剑指Offer —— 滑动窗口的最大值 | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
剑指Offer —— 滑动窗口的最大值
+
+
2017-07-25
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+题目来源:滑动窗口的最大值
+
+
+
题目描述 给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。
+
解题思路 用一个双端队列维护当前滑动窗口的状态,队首是当前窗口最大值的下标,当窗口滑动进入一个新值k时,k从队尾依次向前比较,比k小的全部出队,保障了k的权重应有的位置
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
vector<int> maxInWindows(const vector<int> &num, unsigned int size) {
truevector<int> ans;
truedeque<int> q;
trueif (num.size() < size || size == 0)
true return ans;
truefor (int i = 0; i < size; i++) {
true while(!q.empty() && num[i] > num[q.back()])
true q.pop_back();
true q.push_back(i);
true}
truefor (int i = size; i < num.size(); i++) {
true ans.push_back(num[q.front()]);
true while (!q.empty() && num[i] >= num[q.back()])
true q.pop_back();
true while (!q.empty() && q.front() <= i-size)
true q.pop_front();
true q.push_back(i);
true}
trueans.push_back(num[q.front()]);
truereturn ans;
}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 谢谢大爷~
+
+
+
+
+
+
+
+
+
+
+
+ 微信
+
+ 支付宝
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git "a/2017/07/27/title: \345\271\266\345\217\221(concurrency)\345\222\214\345\271\266\350\241\214(parallelism)/index.html" "b/2017/07/27/title: \345\271\266\345\217\221(concurrency)\345\222\214\345\271\266\350\241\214(parallelism)/index.html"
new file mode 100644
index 0000000..41bbadb
--- /dev/null
+++ "b/2017/07/27/title: \345\271\266\345\217\221(concurrency)\345\222\214\345\271\266\350\241\214(parallelism)/index.html"
@@ -0,0 +1,519 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 并发(concurrency)和并行(parallelism) | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
并发(concurrency)和并行(parallelism)
+
+
2017-07-27
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+“并发”指的是程序的结构,“并行”指的是程序运行时的状态
+
+
并行(parallelism) 这个概念很好理解。所谓并行,就是同时执行的意思,无需过度解读。判断程序是否处于并行的状态,就看同一时刻是否有超过一个“工作单位”在运行就好了。所以,单线程永远无法达到并行状态。
+
要达到并行状态,最简单的就是利用多线程和多进程。
+
并发(concurrency) 要理解“并发”这个概念,必须得清楚,并发指的是程序的“结构”。当我们说这个程序是并发的,实际上,这句话应当表述成“这个程序采用了支持并发的设计”。好,既然并发指的是人为设计的结构,那么怎样的程序结构才叫做支持并发的设计?
+
正确的并发设计的标准是:使多个操作可以在重叠的时间段内进行(two tasks can start, run, and complete in overlapping time periods)。
+
这句话的重点有两个。我们先看“(操作)在重叠的时间段内进行”这个概念。它是否就是我们前面说到的并行呢?是,也不是。并行,当然是在重叠的时间段内执行,但是另外一种执行模式,也属于在重叠时间段内进行。这就是协程 。
+
使用协程时,程序的执行看起来往往是这个样子:
+
+
+
+
+
+
+
task1, task2 是两段不同的代码,比如两个函数,其中黑色块代表某段代码正在执行。注意,这里从始至终,在任何一个时间点上都只有一段代码在执行,但是,由于 task1 和 task2 在重叠的时间段内执行,所以这是一个支持并发的设计。与并行不同,单核单线程能支持并发。
+
+Different concurrent designs enable different ways to parallelize
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 谢谢大爷~
+
+
+
+
+
+
+
+
+
+
+
+ 微信
+
+ 支付宝
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git "a/2017/08/01/title: \345\211\221\346\214\207Offer \342\200\224\342\200\224 \346\234\200\345\260\217\347\232\204k\344\270\252\346\225\260/index.html" "b/2017/08/01/title: \345\211\221\346\214\207Offer \342\200\224\342\200\224 \346\234\200\345\260\217\347\232\204k\344\270\252\346\225\260/index.html"
new file mode 100644
index 0000000..1707495
--- /dev/null
+++ "b/2017/08/01/title: \345\211\221\346\214\207Offer \342\200\224\342\200\224 \346\234\200\345\260\217\347\232\204k\344\270\252\346\225\260/index.html"
@@ -0,0 +1,512 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 剑指Offer —— 最小的k个数 | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
剑指Offer —— 最小的k个数
+
+
2017-08-01
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
题目描述 输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。(很典型的题目了)
+
解题思路
+插入或者冒泡排序,优化点在记录已排好的个数(O(n*k))
+最小堆(O(nlogk))
+快排思想(O(n))
+
+
way1 不上代码了。
+
way2 上个建堆的算法,通用、解耦、易测试😂
+
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
void HeapAdjust(int H[],int s, int length)
{
int tmp = H[s];
int child = 2*s+1; //左孩子结点的位置。(i+1 为当前调整结点的右孩子结点的位置)
while (child < length) {
if(child+1 <length && H[child]<H[child+1]) { // 如果右孩子大于左孩子(找到比当前待调整结点大的孩子结点)
++child ;
}
if(H[s]<H[child]) { // 如果较大的子结点大于父结点
H[s] = H[child]; // 那么把较大的子结点往上移动,替换它的父结点
s = child; // 重新设置s ,即待调整的下一个结点的位置
child = 2*s+1;
} else { // 如果当前待调整结点大于它的左右孩子,则不需要调整,直接退出
break;
}
H[s] = tmp; // 当前待调整的结点放到比其大的孩子结点位置上
}
print(H,length);
}
/**
* 初始堆进行调整
* 将H[0..length-1]建成堆
* 调整完之后第一个元素是序列的最小的元素
*/
void BuildingHeap(int H[], int length)
{
//最后一个有孩子的节点的位置 i= (length -1) / 2
for (int i = (length -1) / 2 ; i >= 0; i--)
{
cout << "i: " << i << endl;
HeapAdjust(H,i,length);
}
}
/**
* 堆排序算法
*/
void HeapSort(int H[],int length)
{
//初始堆
BuildingHeap(H, length);
//从最后一个元素开始对序列进行调整
for (int i = length - 1; i > 0; --i)
{
//交换堆顶元素H[0]和堆中最后一个元素
int temp = H[i]; H[i] = H[0]; H[0] = temp;
//每次交换堆顶元素和堆中最后一个元素之后,都要对堆进行调整
HeapAdjust(H,0,i);
}
}
+
way3 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
int Partition(vector<int> &input, int begin, int end)
{
trueint first = begin;
trueint last = end;
trueint pivot = input[first];
truewhile (first < last)
true{
truetruewhile (first < last && input[last] >= pivot) last--;
truetrueinput[first] = input[last];
truetruewhile (first < last && input[first] <= pivot) first++;
truetrueinput[last] = input[first];
true}
trueinput[first] = pivot;
truereturn first;
}
vector<int> GetLeastNumbers_Solution(vector<int> &input, int k)
{
trueint len=input.size();
truevector<int> res;
trueif(len==0||k>len||k<=0) return res;
trueif(len==k) return input;
trueint start=0;
trueint end=len-1;
trueint index=Partition(input,start,end);
truewhile(index!=(k-1))
true{
truetrueif(index>k-1)
truetrue{
truetruetrueend=index-1;
truetruetrueindex=Partition(input,start,end);
truetrue}
truetrueelse
truetrue{
truetruetruestart=index+1;
truetruetrueindex=Partition(input,start,end);
truetrue}
true}
truefor (int i = 0; i < k; i++)
truetrueres.push_back(input[i]);
truereturn res;
}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 谢谢大爷~
+
+
+
+
+
+
+
+
+
+
+
+ 微信
+
+ 支付宝
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/2017/08/02/title: Dispatch/index.html b/2017/08/02/title: Dispatch/index.html
new file mode 100644
index 0000000..faafd37
--- /dev/null
+++ b/2017/08/02/title: Dispatch/index.html
@@ -0,0 +1,530 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Dispatch | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Dispatch
+
+
2017-08-02
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Execute code concurrently on multicore hardware by submitting work to dispatch queues managed by the system.
+
+
在多核硬件上,通过提交任务到由这个系统管理的派遣队列上,并发的执行代码。
+
一种把保证回调会在主线程执行的方法:
+
1
2
3
4
5
6
7
8
#ifndef dispatch_main_async_safe
#define dispatch_main_async_safe(block)\
if (strcmp(dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL), dispatch_queue_get_label(dispatch_get_main_queue())) == 0) {\
block();\
} else {\
dispatch_async(dispatch_get_main_queue(), block);\
}
#endif
+
1
2
3
4
5
6
7
8
9
10
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
for(id obj in array)
dispatch_group_async(group, queue, ^{
[self doSomethingIntensiveWith:obj];
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_release(group);
[self doSomethingWith:array];
+
1
2
3
4
5
6
7
8
9
10
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
for(id obj in array)
dispatch_group_async(group, queue, ^{
[self doSomethingIntensiveWith:obj];
});
dispatch_group_notify(group, queue, ^{
[self doSomethingWith:array];
});
dispatch_release(group);
+
dispatch_barrier_sync和dispatch_barrier_async
+
+
+common
+different
+
+
+
+
+等待在它前面插入队列的任务先执行完
+dispatch_barrier_sync将自己的任务插入到队列的时候,需要等待自己的任务结束之后才会继续插入被写在它后面的任务,然后执行它们
+
+
+等待他们自己的任务执行完再执行后面的任务
+dispatch_barrier_async将自己的任务插入到队列之后,不会等待自己的任务结束,它会继续把后面的任务插入到队列,然后等待自己的任务结束后才执行后面任务
+
+
+
+
+参考:GCD入门(一): 基本概念和Dispatch Queue 深入理解GCD 底层并发 API GCD 中 dispatch_once 的性能与实现 读 Concurrency Programming Guide 笔记(一) iOS并发编程
+
+
+
+
+
+
+
+
+ Last updated: 2018-01-13 12:59:21
+
+
+
+
+
http://codingdoge.cn/2017/08/02/title: Dispatch/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 谢谢大爷~
+
+
+
+
+
+
+
+
+
+
+
+ 微信
+
+ 支付宝
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git "a/2017/08/03/title: Dispatch \345\234\250swift\344\270\255\347\232\204\344\275\277\347\224\250/index.html" "b/2017/08/03/title: Dispatch \345\234\250swift\344\270\255\347\232\204\344\275\277\347\224\250/index.html"
new file mode 100644
index 0000000..54f22b3
--- /dev/null
+++ "b/2017/08/03/title: Dispatch \345\234\250swift\344\270\255\347\232\204\344\275\277\347\224\250/index.html"
@@ -0,0 +1,511 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Dispatch 在swift中的使用 | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Dispatch 在swift中的使用
+
+
2017-08-03
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
创建队列 当group里所有事件都完成GCD API有两种方式发送通知,第一种是dispatch_group_wait,会阻塞当前进程,等所有任务都完成或等待超时。第二种方法是使用dispatch_group_notify,异步执行闭包,不会阻塞。
+
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
func downloadPhotosWithCompletion(completion: BatchPhotoDownloadingCompletionClosure?) {
dispatch_async(GlobalUserInitiatedQueue) { // 因为dispatch_group_wait会租塞当前进程,所以要使用dispatch_async将整个方法要放到后台队列才能够保证主线程不被阻塞
var storedError: NSError!
var downloadGroup = dispatch_group_create() // 创建一个dispatch group
for address in [OverlyAttachedGirlfriendURLString,
SuccessKidURLString,
LotsOfFacesURLString]
{
let url = NSURL(string: address)
dispatch_group_enter(downloadGroup) // dispatch_group_enter是通知dispatch group任务开始了,dispatch_group_enter和dispatch_group_leave是成对调用,不然程序就崩溃了。
let photo = DownloadPhoto(url: url!) {
image, error in
if let error = error {
storedError = error
}
dispatch_group_leave(downloadGroup) // 保持和dispatch_group_enter配对。通知任务已经完成
}
PhotoManager.sharedManager.addPhoto(photo)
}
dispatch_group_wait(downloadGroup, DISPATCH_TIME_FOREVER) // dispatch_group_wait等待所有任务都完成直到超时。如果任务完成前就超时了,函数会返回一个非零值,可以通过返回值判断是否超时。也可以用DISPATCH_TIME_FOREVER表示一直等。
dispatch_async(GlobalMainQueue) { // 这里可以保证所有图片任务都完成,然后在main queue里加入完成后要处理的闭包,会在main queue里执行。
if let completion = completion { // 执行闭包内容
completion(error: storedError)
}
}
}
}
+
Serial Queues 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private let serialQueue = DispatchQueue(label: “serialQueue”)
private var dictionary: [String: Any] = [:]
public func set(_ value: Any, forKey key: String) {
serialQueue.sync {
dictionary[key] = value
}
}
public func object(forKey key: String) -> Any? {
var result: Any?
serialQueue.sync {
result = storage[key]
}
// returns after serialQueue is finished operation
// beacuse serialQueue is run synchronously
return result
}
+
Concurrent Queues 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private let concurrentQueue = DispatchQueue(label: "concurrentQueue", attributes: .concurrent)
private var dictionary: [String: Any] = [:]
public func set(_ value: Any?, forKey key: String) {
concurrentQueue.async {
self.storage[key] = value
}
}
public func object(forKey key: String) -> Any? {
var result: Any?
concurrentQueue.sync {
result = storage[key]
}
// returns after concurrentQueue is finished operation
// beacuse concurrentQueue is run synchronously
return result
}
+
Concurrent Queue with Barrier 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private let concurrentQueue = DispatchQueue(label: "concurrentQueue", attributes: .concurrent)
private var dictionary: [String: Any] = [:]
public func set(_ value: Any?, forKey key: String) {
// .barrier flag ensures that within the queue all reading is done
// before the below writing is performed and
// pending readings start after below writing is performed
concurrentQueue.async(flags: .barrier) {
self.storage[key] = value
}
}
public func object(forKey key: String) -> Any? {
var result: Any?
concurrentQueue.sync {
result = storage[key]
}
// returns after concurrentQueue is finished operation
// beacuse concurrentQueue is run synchronously
return result
}
+
延时 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
typealias Task = (_ cancel: Bool) -> Void
func delay(time: TimeInterval, task: @escaping ()->()) -> Task? {
func dispatch_later(block: @escaping ()->()) {
DispatchQueue .main .asyncAfter (deadline: DispatchTime.now()+time, execute: block)
}
var closure : (()->()) ? = task
var result : Task ?
let delayedClosure : Task = { cancel in
if let internalClosure = closure {
if cancel == false {
DispatchQueue .main .async {
internalClosure ()
}
}
}
closure = nil
result = nil
}
result = delayedClosure
dispatch_later {
if let delayedClosure = result {
delayedClosure (false )
}
}
return result
}
func cancel (task: Task?) {
task ?(true )
}
+
网络场景异步开启下载、取消下载,开始下载前保证没有遗留下载任务 1
2
3
4
5
6
7
8
9
10
11
12
13
fileprivate let barrierQueue = DispatchQueue (label: "com.meitu.meipu.videoCache.barrierQueue" )
func downloader (url: URL) {
barrierQueue.sync { [weak self ] in
}
}
func cancel () {
barrierQueue.async(group: nil , qos: .default , flags: .barrier) {
}
}
+
+参考:GCD 在 Swift 3 中的玩儿法 GCD 使用指南 Swift 3 中的 GCD 与 Dispatch Queue Swift 3學習指南:重新認識GCD應用 GCD精讲(Swift 3)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 谢谢大爷~
+
+
+
+
+
+
+
+
+
+
+
+ 微信
+
+ 支付宝
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git "a/2017/08/03/title: \351\224\231\350\257\257\345\244\204\347\220\206/index.html" "b/2017/08/03/title: \351\224\231\350\257\257\345\244\204\347\220\206/index.html"
new file mode 100644
index 0000000..948d065
--- /dev/null
+++ "b/2017/08/03/title: \351\224\231\350\257\257\345\244\204\347\220\206/index.html"
@@ -0,0 +1,509 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 错误处理 | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
错误处理
+
+
2017-08-03
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
错误处理(Error handling)是响应错误以及从错误中恢复的过程。Swift 提供了在运行时对可恢复错误的抛出、捕获、传递和操作的一等公民支持。
+
Swift 中有4种处理错误的方式
+
+把函数抛出的错误传递给调用此函数的代码
+用do-catch语句处理错误
+将错误作为可选类型处理
+或者断言此错误根本不会发生
+
+
+Swift 中的错误处理并不涉及解除调用栈,这是一个计算代价高昂的过程。就此而言,throw语句的性能特性是可以和return语句相媲美的。
+
+
一个 throwing 函数可以在其内部抛出错误,并将错误传递到函数被调用时的作用域 。
+
+参考: The Swift Programming Language 中文版 SWIFT 的必备 TIP
+
+
+
+
+
+
+
+
+ Last updated: 2018-01-13 20:26:37
+
+
+
+
+
http://codingdoge.cn/2017/08/03/title: 错误处理/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 谢谢大爷~
+
+
+
+
+
+
+
+
+
+
+
+ 微信
+
+ 支付宝
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git "a/2017/08/04/title: \345\211\221\346\214\207Offer \342\200\224\342\200\224 \344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\347\232\204\347\254\254k\344\270\252\347\273\223\347\202\271/index.html" "b/2017/08/04/title: \345\211\221\346\214\207Offer \342\200\224\342\200\224 \344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\347\232\204\347\254\254k\344\270\252\347\273\223\347\202\271/index.html"
new file mode 100644
index 0000000..7e37d6b
--- /dev/null
+++ "b/2017/08/04/title: \345\211\221\346\214\207Offer \342\200\224\342\200\224 \344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\347\232\204\347\254\254k\344\270\252\347\273\223\347\202\271/index.html"
@@ -0,0 +1,522 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 剑指Offer —— 二叉搜索树的第k个结点 | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
剑指Offer —— 二叉搜索树的第k个结点
+
+
2017-08-04
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+题目来源:牛客网
+
+
+
题目描述 给定一颗二叉搜索树,请找出其中的第k大的结点。例如, 5 / \ 3 7 /\ /\ 2 4 6 8 中,按结点数值大小顺序第三个结点的值为4。
+
解题思路
+递归
+迭代way1 中序遍历,找到第k个结点。完全性的模拟左根右,注意返回结果的处理。
+
+
TreeNode* KthNode(TreeNode* pRoot, int k) {
+ if (pRoot) {
+ TreeNode *node = KthNode(pRoot->left, k);
+ if (node) return node;
+ index++;
+ if (index == k) return pRoot;
+ node = KthNode(pRoot->right, k);
+ if (node) return node;
+ }
+ return NULL;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 谢谢大爷~
+
+
+
+
+
+
+
+
+
+
+
+ 微信
+
+ 支付宝
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git "a/2017/08/20/title: Advanced Swift \347\254\224\350\256\260 \342\200\224\342\200\224 Tips/index.html" "b/2017/08/20/title: Advanced Swift \347\254\224\350\256\260 \342\200\224\342\200\224 Tips/index.html"
new file mode 100644
index 0000000..2a2dd3e
--- /dev/null
+++ "b/2017/08/20/title: Advanced Swift \347\254\224\350\256\260 \342\200\224\342\200\224 Tips/index.html"
@@ -0,0 +1,503 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Advanced Swift 笔记 —— Tips | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Advanced Swift 笔记 —— Tips
+
+
2017-08-20
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Curried Function 柯里化函數 一个函数不是接受多个参数,而是只接受部分参数,然后返回一个接受其余参数的函数。
+
Statically Dispatched 靜態派發 定義在類或者協議中的函數就是方法(Method),他們有一個隱式的self。不是方法的函數叫做自由函數(Free Function)。自由函数和那些在结构体上调用的方法是静态派发 (statically dispatched)的。对于这些函数的调用,在编译的时候就已经确定了。对于静态派发的调用,编译器可能能够内联 (inline)这些函数,也就是说,完全不去做函数调用,而是将这部分代码替换为需要执行的函数。静态派发还能够帮助编译器丢弃或者简化那些在编译时就能确定不会被实际执行的代码。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 谢谢大爷~
+
+
+
+
+
+
+
+
+
+
+
+ 微信
+
+ 支付宝
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git "a/2017/08/30/title: Advanced Swift \347\254\224\350\256\260 \342\200\224\342\200\224 \345\205\247\345\273\272\351\233\206\345\220\210\351\241\236\345\236\213/index.html" "b/2017/08/30/title: Advanced Swift \347\254\224\350\256\260 \342\200\224\342\200\224 \345\205\247\345\273\272\351\233\206\345\220\210\351\241\236\345\236\213/index.html"
new file mode 100644
index 0000000..fccb1ab
--- /dev/null
+++ "b/2017/08/30/title: Advanced Swift \347\254\224\350\256\260 \342\200\224\342\200\224 \345\205\247\345\273\272\351\233\206\345\220\210\351\241\236\345\236\213/index.html"
@@ -0,0 +1,523 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Advanced Swift 笔记 —— 內建集合類型 | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Advanced Swift 笔记 —— 內建集合類型
+
+
2017-08-30
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
數組(Array) 盡量不要使用下標索引,如果下標越界會直接導致crash(在並發情況下尤其需要考慮)。
+
+
+
+
1
for x in array .dropFirst()
+
+
1
for x in array .dropLast(5 )
+
+
1
for (index , value) in array .enumerated()
+
+
1
2
3
_ = array .index {
someMatchingLogic($0)
}
+
+
1
2
3
_ = array .map {
someTransformation($0)
}
+
+
1
2
3
_ = array .filter {
someCriteria($0)
}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 谢谢大爷~
+
+
+
+
+
+
+
+
+
+
+
+ 微信
+
+ 支付宝
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/2017/08/30/title: Brew Instruction/index.html b/2017/08/30/title: Brew Instruction/index.html
new file mode 100644
index 0000000..526a501
--- /dev/null
+++ b/2017/08/30/title: Brew Instruction/index.html
@@ -0,0 +1,552 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Brew Instruction | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Brew Instruction
+
+
2017-08-30
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Brew
+
+
+
+brew help
+查看有什麼可用的指令
+
+
+
+
+brew search [someSuit]
+查詢有無someSuit
+
+
+brew install [someSuit]
+安裝someSuit
+
+
+brew info [someSuit]
+查看someSuit的訊息
+
+
+brew uninstall [someSuit]
+移除someSuit
+
+
+brew list
+列出已安裝套件
+
+
+brew outdated
+查詢那些套件已過期
+
+
+brew cleanup -n
+刪除舊的套件版本(-n 是顯示刪除的過程,可以了解有哪些套件被刪除了)
+
+
+brew upgrade [someSuit]
+更新someSuit
+
+
+brew update && brew upgrade && brew doctor
+更新Homebrew上面所有套件
+
+
+brew dips [someSuit]
+列出安裝someSuit需要的依賴套件
+
+
+brew —prefix
+查詢brew安裝的路徑
+
+
+
+
+參考:chiehwen/Homebrew 指令詳解.md Homebrew 指令
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 谢谢大爷~
+
+
+
+
+
+
+
+
+
+
+
+ 微信
+
+ 支付宝
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/404.html b/404.html
new file mode 100644
index 0000000..b2e7c3b
--- /dev/null
+++ b/404.html
@@ -0,0 +1,9 @@
+
+
+
+ CodingDoge's blog | 404
+
+
+
+
+
diff --git a/CNAME b/CNAME
new file mode 100644
index 0000000..99306e0
--- /dev/null
+++ b/CNAME
@@ -0,0 +1 @@
+codingdoge.cn
diff --git a/about/index.html b/about/index.html
new file mode 100644
index 0000000..17d3a2c
--- /dev/null
+++ b/about/index.html
@@ -0,0 +1,425 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ About | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
CodingDoge
+
关于开发、设计,关于生活。
+
目前博客多作为知识点整理,所以有些文章奇奇怪怪(因为还没写完)...各位有缘的看官见笑了😝 恩 就酱...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 谢谢大爷~
+
+
+
+
+
+
+
+
+
+
+
+ 微信
+
+ 支付宝
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/archives/2017/05/index.html b/archives/2017/05/index.html
new file mode 100644
index 0000000..3544b6b
--- /dev/null
+++ b/archives/2017/05/index.html
@@ -0,0 +1,345 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Archives: 2017/5 | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
May, 2017
+
+
+
+
+
+
+
2017-05-13
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/archives/2017/07/index.html b/archives/2017/07/index.html
new file mode 100644
index 0000000..25f1b3b
--- /dev/null
+++ b/archives/2017/07/index.html
@@ -0,0 +1,549 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Archives: 2017/7 | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
July, 2017
+
+
+
+
+
+
+
2017-07-27
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-25
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-25
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/archives/2017/07/page/2/index.html b/archives/2017/07/page/2/index.html
new file mode 100644
index 0000000..0455d6b
--- /dev/null
+++ b/archives/2017/07/page/2/index.html
@@ -0,0 +1,555 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Archives: 2017/7 | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
July, 2017
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/archives/2017/07/page/3/index.html b/archives/2017/07/page/3/index.html
new file mode 100644
index 0000000..d7d8407
--- /dev/null
+++ b/archives/2017/07/page/3/index.html
@@ -0,0 +1,561 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Archives: 2017/7 | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
July, 2017
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-21
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-21
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-17
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-17
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-17
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-17
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/archives/2017/07/page/4/index.html b/archives/2017/07/page/4/index.html
new file mode 100644
index 0000000..26a6b98
--- /dev/null
+++ b/archives/2017/07/page/4/index.html
@@ -0,0 +1,391 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Archives: 2017/7 | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
July, 2017
+
+
+
+
+
+
+
2017-07-17
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-06
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/archives/2017/08/index.html b/archives/2017/08/index.html
new file mode 100644
index 0000000..1582795
--- /dev/null
+++ b/archives/2017/08/index.html
@@ -0,0 +1,561 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Archives: 2017/8 | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
August, 2017
+
+
+
+
+
+
+
2017-08-30
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-08-30
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-08-20
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-08-04
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-08-03
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-08-03
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-08-02
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/archives/2017/08/page/2/index.html b/archives/2017/08/page/2/index.html
new file mode 100644
index 0000000..ed2bdd3
--- /dev/null
+++ b/archives/2017/08/page/2/index.html
@@ -0,0 +1,357 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Archives: 2017/8 | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
August, 2017
+
+
+
+
+
+
+
2017-08-01
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/archives/2017/index.html b/archives/2017/index.html
new file mode 100644
index 0000000..0da7f63
--- /dev/null
+++ b/archives/2017/index.html
@@ -0,0 +1,561 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Archives: 2017 | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
August, 2017
+
+
+
+
+
+
+
2017-08-30
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-08-30
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-08-20
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-08-04
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-08-03
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-08-03
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-08-02
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/archives/2017/page/2/index.html b/archives/2017/page/2/index.html
new file mode 100644
index 0000000..4e31136
--- /dev/null
+++ b/archives/2017/page/2/index.html
@@ -0,0 +1,553 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Archives: 2017 | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
August, 2017
+
+
+
+
+
+
+
2017-08-01
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
July, 2017
+
+
+
+
+
+
+
2017-07-27
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-25
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-25
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/archives/2017/page/3/index.html b/archives/2017/page/3/index.html
new file mode 100644
index 0000000..223870d
--- /dev/null
+++ b/archives/2017/page/3/index.html
@@ -0,0 +1,555 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Archives: 2017 | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
July, 2017
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/archives/2017/page/4/index.html b/archives/2017/page/4/index.html
new file mode 100644
index 0000000..c2f7e22
--- /dev/null
+++ b/archives/2017/page/4/index.html
@@ -0,0 +1,561 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Archives: 2017 | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
July, 2017
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-21
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-21
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-17
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-17
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-17
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/archives/2017/page/5/index.html b/archives/2017/page/5/index.html
new file mode 100644
index 0000000..a171c42
--- /dev/null
+++ b/archives/2017/page/5/index.html
@@ -0,0 +1,457 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Archives: 2017 | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
July, 2017
+
+
+
+
+
+
+
2017-07-17
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-17
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-06
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
May, 2017
+
+
+
+
+
+
+
2017-05-13
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/archives/index.html b/archives/index.html
new file mode 100644
index 0000000..267d439
--- /dev/null
+++ b/archives/index.html
@@ -0,0 +1,561 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Archives | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
August, 2017
+
+
+
+
+
+
+
2017-08-30
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-08-30
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-08-20
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-08-04
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-08-03
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-08-03
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-08-02
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/archives/page/2/index.html b/archives/page/2/index.html
new file mode 100644
index 0000000..14287b3
--- /dev/null
+++ b/archives/page/2/index.html
@@ -0,0 +1,553 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Archives | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
August, 2017
+
+
+
+
+
+
+
2017-08-01
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
July, 2017
+
+
+
+
+
+
+
2017-07-27
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-25
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-25
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/archives/page/3/index.html b/archives/page/3/index.html
new file mode 100644
index 0000000..9de398f
--- /dev/null
+++ b/archives/page/3/index.html
@@ -0,0 +1,555 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Archives | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
July, 2017
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/archives/page/4/index.html b/archives/page/4/index.html
new file mode 100644
index 0000000..2c961ba
--- /dev/null
+++ b/archives/page/4/index.html
@@ -0,0 +1,561 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Archives | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
July, 2017
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-21
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-21
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-17
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-17
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-17
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/archives/page/5/index.html b/archives/page/5/index.html
new file mode 100644
index 0000000..b21c951
--- /dev/null
+++ b/archives/page/5/index.html
@@ -0,0 +1,457 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Archives | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
July, 2017
+
+
+
+
+
+
+
2017-07-17
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-17
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-06
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
May, 2017
+
+
+
+
+
+
+
2017-05-13
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/atom.xml b/atom.xml
new file mode 100644
index 0000000..073a8e6
--- /dev/null
+++ b/atom.xml
@@ -0,0 +1,874 @@
+
+
+ CodingDoge
+ https://www.gravatar.com/avatar/d091400ed78ec5cd8bf36bd5fff7bb8f
+ 关于开发、设计,关于生活。
+
+
+
+ 2018-01-13T12:22:39.908Z
+ http://codingdoge.cn/
+
+
+ CodingDoge
+ codingdoge@outlook.com
+
+
+ Hexo
+
+
+ Advanced Swift 笔记 —— 內建集合類型
+
+ http://codingdoge.cn/2017/08/30/title: Advanced Swift 笔记 —— 內建集合類型/
+ 2017-08-30T07:47:00.000Z
+ 2018-01-13T12:22:39.908Z
+
+ 數組(Array)盡量不要使用下標索引,如果下標越界會直接導致crash(在並發情況下尤其需要考慮)。
1
for x in array .dropFirst()
1
for x in array .dropLast(5 )
1
for (index , value) in array .enumerated()
1
2
3
_ = array .index {
someMatchingLogic($0)
}
1
2
3
_ = array .map {
someTransformation($0)
}
1
2
3
_ = array .filter {
someCriteria($0)
}
]]>
+
+
+
+
+
+ <h2 id="數組-Array"><a href="#數組-Array" class="headerlink" title="數組(Array)"></a>數組(Array)</h2><p>盡量不要使用下標索引,如果下標越界會直接導致crash(在並發情況下尤其需要考慮)。</
+
+
+
+
+
+
+
+
+
+
+
+
+ Brew Instruction
+
+ http://codingdoge.cn/2017/08/30/title: Brew Instruction/
+ 2017-08-30T06:26:00.000Z
+ 2018-01-13T12:36:00.696Z
+
+ Brew
brew help 查看有什麼可用的指令 brew search [someSuit] 查詢有無someSuit brew install [someSuit] 安裝someSuit brew info [someSuit] 查看someSuit的訊息 brew uninstall [someSuit] 移除someSuit brew list 列出已安裝套件 brew outdated 查詢那些套件已過期 brew cleanup -n 刪除舊的套件版本(-n 是顯示刪除的過程,可以了解有哪些套件被刪除了) brew upgrade [someSuit] 更新someSuit brew update && brew upgrade && brew doctor 更新Homebrew上面所有套件 brew dips [someSuit] 列出安裝someSuit需要的依賴套件 brew —prefix 查詢brew安裝的路徑
參考:chiehwen/Homebrew 指令詳解.md Homebrew 指令
]]>
+
+
+
+
+
+ <p><a href="https://brew.sh" target="_blank" rel="noopener">Brew</a></p>
+<table>
+<thead>
+<tr>
+<th>brew help</th>
+<th>查看有什麼可用的指令</th>
+</tr>
+<
+
+
+
+
+
+
+
+
+
+
+
+
+ Advanced Swift 笔记 —— Tips
+
+ http://codingdoge.cn/2017/08/20/title: Advanced Swift 笔记 —— Tips/
+ 2017-08-20T09:35:00.000Z
+ 2018-01-13T12:23:01.178Z
+
+ Curried Function 柯里化函數一个函数不是接受多个参数,而是只接受部分参数,然后返回一个接受其余参数的函数。
Statically Dispatched 靜態派發 定義在類或者協議中的函數就是方法(Method),他們有一個隱式的self。不是方法的函數叫做自由函數(Free Function)。自由函数和那些在结构体上调用的方法是静态派发 (statically dispatched)的。对于这些函数的调用,在编译的时候就已经确定了。对于静态派发的调用,编译器可能能够内联 (inline)这些函数,也就是说,完全不去做函数调用,而是将这部分代码替换为需要执行的函数。静态派发还能够帮助编译器丢弃或者简化那些在编译时就能确定不会被实际执行的代码。
]]>
+
+
+
+
+
+ <h3 id="Curried-Function-柯里化函數"><a href="#Curried-Function-柯里化函數" class="headerlink" title="Curried Function 柯里化函數"></a>Curried Function 柯里化
+
+
+
+
+
+
+
+
+
+
+
+
+ 剑指Offer —— 二叉搜索树的第k个结点
+
+ http://codingdoge.cn/2017/08/04/title: 剑指Offer —— 二叉搜索树的第k个结点/
+ 2017-08-04T01:56:00.000Z
+ 2018-01-14T16:29:06.979Z
+
+ 题目来源:牛客网
题目描述 给定一颗二叉搜索树,请找出其中的第k大的结点。例如, 5 / \ 3 7 /\ /\ 2 4 6 8 中,按结点数值大小顺序第三个结点的值为4。
解题思路 递归 迭代way1 中序遍历,找到第k个结点。完全性的模拟左根右,注意返回结果的处理。 TreeNode* KthNode(TreeNode* pRoot, int k) { if (pRoot) { TreeNode *node = KthNode(pRoot->left, k); if (node) return node; index++; if (index == k) return pRoot; node = KthNode(pRoot->right, k); if (node) return node; } return NULL;} ]]>
+
+
+
+
+
+ <blockquote>
+<p>题目来源:<strong><a href="https://www.nowcoder.com/practice/ef068f602dde4d28aab2b210e859150a?tpId=13&tqId=11215&tPage=4&
+
+
+
+
+
+
+
+
+
+
+
+
+ 错误处理
+
+ http://codingdoge.cn/2017/08/03/title: 错误处理/
+ 2017-08-03T02:47:00.000Z
+ 2018-01-13T12:26:37.175Z
+
+ 错误处理(Error handling)是响应错误以及从错误中恢复的过程。Swift 提供了在运行时对可恢复错误的抛出、捕获、传递和操作的一等公民支持。Swift 中有4种处理错误的方式
把函数抛出的错误传递给调用此函数的代码 用do-catch语句处理错误 将错误作为可选类型处理 或者断言此错误根本不会发生 Swift 中的错误处理并不涉及解除调用栈,这是一个计算代价高昂的过程。就此而言,throw语句的性能特性是可以和return语句相媲美的。
一个 throwing 函数可以在其内部抛出错误,并将错误传递到函数被调用时的作用域 。
参考: The Swift Programming Language 中文版 SWIFT 的必备 TIP
]]>
+
+
+
+
+
+ <p>错误处理(Error handling)是响应错误以及从错误中恢复的过程。Swift 提供了在运行时对可恢复错误的抛出、捕获、传递和操作的一等公民支持。</p>
+<p>Swift 中有4种处理错误的方式</p>
+<ul>
+<li>把函数抛出的错误传递给调用此函数的代码</l
+
+
+
+
+
+
+
+
+
+
+
+
+ Dispatch 在swift中的使用
+
+ http://codingdoge.cn/2017/08/03/title: Dispatch 在swift中的使用/
+ 2017-08-03T01:49:00.000Z
+ 2018-01-13T12:28:30.884Z
+
+ 创建队列当group里所有事件都完成GCD API有两种方式发送通知,第一种是dispatch_group_wait,会阻塞当前进程,等所有任务都完成或等待超时。第二种方法是使用dispatch_group_notify,异步执行闭包,不会阻塞。
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
func downloadPhotosWithCompletion(completion: BatchPhotoDownloadingCompletionClosure?) {
dispatch_async(GlobalUserInitiatedQueue) { // 因为dispatch_group_wait会租塞当前进程,所以要使用dispatch_async将整个方法要放到后台队列才能够保证主线程不被阻塞
var storedError: NSError!
var downloadGroup = dispatch_group_create() // 创建一个dispatch group
for address in [OverlyAttachedGirlfriendURLString,
SuccessKidURLString,
LotsOfFacesURLString]
{
let url = NSURL(string: address)
dispatch_group_enter(downloadGroup) // dispatch_group_enter是通知dispatch group任务开始了,dispatch_group_enter和dispatch_group_leave是成对调用,不然程序就崩溃了。
let photo = DownloadPhoto(url: url!) {
image, error in
if let error = error {
storedError = error
}
dispatch_group_leave(downloadGroup) // 保持和dispatch_group_enter配对。通知任务已经完成
}
PhotoManager.sharedManager.addPhoto(photo)
}
dispatch_group_wait(downloadGroup, DISPATCH_TIME_FOREVER) // dispatch_group_wait等待所有任务都完成直到超时。如果任务完成前就超时了,函数会返回一个非零值,可以通过返回值判断是否超时。也可以用DISPATCH_TIME_FOREVER表示一直等。
dispatch_async(GlobalMainQueue) { // 这里可以保证所有图片任务都完成,然后在main queue里加入完成后要处理的闭包,会在main queue里执行。
if let completion = completion { // 执行闭包内容
completion(error: storedError)
}
}
}
}
Serial Queues 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private let serialQueue = DispatchQueue(label: “serialQueue”)
private var dictionary: [String: Any] = [:]
public func set(_ value: Any, forKey key: String) {
serialQueue.sync {
dictionary[key] = value
}
}
public func object(forKey key: String) -> Any? {
var result: Any?
serialQueue.sync {
result = storage[key]
}
// returns after serialQueue is finished operation
// beacuse serialQueue is run synchronously
return result
}
Concurrent Queues 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private let concurrentQueue = DispatchQueue(label: "concurrentQueue", attributes: .concurrent)
private var dictionary: [String: Any] = [:]
public func set(_ value: Any?, forKey key: String) {
concurrentQueue.async {
self.storage[key] = value
}
}
public func object(forKey key: String) -> Any? {
var result: Any?
concurrentQueue.sync {
result = storage[key]
}
// returns after concurrentQueue is finished operation
// beacuse concurrentQueue is run synchronously
return result
}
Concurrent Queue with Barrier 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private let concurrentQueue = DispatchQueue(label: "concurrentQueue", attributes: .concurrent)
private var dictionary: [String: Any] = [:]
public func set(_ value: Any?, forKey key: String) {
// .barrier flag ensures that within the queue all reading is done
// before the below writing is performed and
// pending readings start after below writing is performed
concurrentQueue.async(flags: .barrier) {
self.storage[key] = value
}
}
public func object(forKey key: String) -> Any? {
var result: Any?
concurrentQueue.sync {
result = storage[key]
}
// returns after concurrentQueue is finished operation
// beacuse concurrentQueue is run synchronously
return result
}
延时 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
typealias Task = (_ cancel: Bool) -> Void
func delay(time: TimeInterval, task: @escaping ()->()) -> Task? {
func dispatch_later(block: @escaping ()->()) {
DispatchQueue .main .asyncAfter (deadline: DispatchTime.now()+time, execute: block)
}
var closure : (()->()) ? = task
var result : Task ?
let delayedClosure : Task = { cancel in
if let internalClosure = closure {
if cancel == false {
DispatchQueue .main .async {
internalClosure ()
}
}
}
closure = nil
result = nil
}
result = delayedClosure
dispatch_later {
if let delayedClosure = result {
delayedClosure (false )
}
}
return result
}
func cancel (task: Task?) {
task ?(true )
}
网络场景异步开启下载、取消下载,开始下载前保证没有遗留下载任务 1
2
3
4
5
6
7
8
9
10
11
12
13
fileprivate let barrierQueue = DispatchQueue (label: "com.meitu.meipu.videoCache.barrierQueue" )
func downloader (url: URL) {
barrierQueue.sync { [weak self ] in
}
}
func cancel () {
barrierQueue.async(group: nil , qos: .default , flags: .barrier) {
}
}
参考:GCD 在 Swift 3 中的玩儿法 GCD 使用指南 Swift 3 中的 GCD 与 Dispatch Queue Swift 3學習指南:重新認識GCD應用 GCD精讲(Swift 3)
]]>
+
+
+
+
+
+ <h2 id="创建队列"><a href="#创建队列" class="headerlink" title="创建队列"></a>创建队列</h2><p>当group里所有事件都完成GCD API有两种方式发送通知,第一种是dispatch_group_wait,会阻塞当前进程
+
+
+
+
+
+
+
+
+
+
+
+
+ Dispatch
+
+ http://codingdoge.cn/2017/08/02/title: Dispatch/
+ 2017-08-02T12:01:00.000Z
+ 2018-01-13T04:59:21.648Z
+
+ Execute code concurrently on multicore hardware by submitting work to dispatch queues managed by the system.
在多核硬件上,通过提交任务到由这个系统管理的派遣队列上,并发的执行代码。
一种把保证回调会在主线程执行的方法:
1
2
3
4
5
6
7
8
#ifndef dispatch_main_async_safe
#define dispatch_main_async_safe(block)\
if (strcmp(dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL), dispatch_queue_get_label(dispatch_get_main_queue())) == 0) {\
block();\
} else {\
dispatch_async(dispatch_get_main_queue(), block);\
}
#endif
1
2
3
4
5
6
7
8
9
10
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
for(id obj in array)
dispatch_group_async(group, queue, ^{
[self doSomethingIntensiveWith:obj];
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_release(group);
[self doSomethingWith:array];
1
2
3
4
5
6
7
8
9
10
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
for(id obj in array)
dispatch_group_async(group, queue, ^{
[self doSomethingIntensiveWith:obj];
});
dispatch_group_notify(group, queue, ^{
[self doSomethingWith:array];
});
dispatch_release(group);
dispatch_barrier_sync和dispatch_barrier_async common different 等待在它前面插入队列的任务先执行完 dispatch_barrier_sync将自己的任务插入到队列的时候,需要等待自己的任务结束之后才会继续插入被写在它后面的任务,然后执行它们 等待他们自己的任务执行完再执行后面的任务 dispatch_barrier_async将自己的任务插入到队列之后,不会等待自己的任务结束,它会继续把后面的任务插入到队列,然后等待自己的任务结束后才执行后面任务
参考:GCD入门(一): 基本概念和Dispatch Queue 深入理解GCD 底层并发 API GCD 中 dispatch_once 的性能与实现 读 Concurrency Programming Guide 笔记(一) iOS并发编程
]]>
+
+
+
+
+
+ <blockquote>
+<p>Execute code concurrently on multicore hardware by submitting work to dispatch queues managed by the system.</p>
+</blockquot
+
+
+
+
+
+
+
+
+
+
+
+
+ 剑指Offer —— 最小的k个数
+
+ http://codingdoge.cn/2017/08/01/title: 剑指Offer —— 最小的k个数/
+ 2017-08-01T02:20:00.000Z
+ 2018-01-14T16:32:50.507Z
+
+ 题目描述 输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。(很典型的题目了)
解题思路 插入或者冒泡排序,优化点在记录已排好的个数(O(n*k)) 最小堆(O(nlogk)) 快排思想(O(n)) way1 不上代码了。
way2 上个建堆的算法,通用、解耦、易测试😂
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
void HeapAdjust(int H[],int s, int length)
{
int tmp = H[s];
int child = 2*s+1; //左孩子结点的位置。(i+1 为当前调整结点的右孩子结点的位置)
while (child < length) {
if(child+1 <length && H[child]<H[child+1]) { // 如果右孩子大于左孩子(找到比当前待调整结点大的孩子结点)
++child ;
}
if(H[s]<H[child]) { // 如果较大的子结点大于父结点
H[s] = H[child]; // 那么把较大的子结点往上移动,替换它的父结点
s = child; // 重新设置s ,即待调整的下一个结点的位置
child = 2*s+1;
} else { // 如果当前待调整结点大于它的左右孩子,则不需要调整,直接退出
break;
}
H[s] = tmp; // 当前待调整的结点放到比其大的孩子结点位置上
}
print(H,length);
}
/**
* 初始堆进行调整
* 将H[0..length-1]建成堆
* 调整完之后第一个元素是序列的最小的元素
*/
void BuildingHeap(int H[], int length)
{
//最后一个有孩子的节点的位置 i= (length -1) / 2
for (int i = (length -1) / 2 ; i >= 0; i--)
{
cout << "i: " << i << endl;
HeapAdjust(H,i,length);
}
}
/**
* 堆排序算法
*/
void HeapSort(int H[],int length)
{
//初始堆
BuildingHeap(H, length);
//从最后一个元素开始对序列进行调整
for (int i = length - 1; i > 0; --i)
{
//交换堆顶元素H[0]和堆中最后一个元素
int temp = H[i]; H[i] = H[0]; H[0] = temp;
//每次交换堆顶元素和堆中最后一个元素之后,都要对堆进行调整
HeapAdjust(H,0,i);
}
}
way3 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
int Partition(vector<int> &input, int begin, int end)
{
trueint first = begin;
trueint last = end;
trueint pivot = input[first];
truewhile (first < last)
true{
truetruewhile (first < last && input[last] >= pivot) last--;
truetrueinput[first] = input[last];
truetruewhile (first < last && input[first] <= pivot) first++;
truetrueinput[last] = input[first];
true}
trueinput[first] = pivot;
truereturn first;
}
vector<int> GetLeastNumbers_Solution(vector<int> &input, int k)
{
trueint len=input.size();
truevector<int> res;
trueif(len==0||k>len||k<=0) return res;
trueif(len==k) return input;
trueint start=0;
trueint end=len-1;
trueint index=Partition(input,start,end);
truewhile(index!=(k-1))
true{
truetrueif(index>k-1)
truetrue{
truetruetrueend=index-1;
truetruetrueindex=Partition(input,start,end);
truetrue}
truetrueelse
truetrue{
truetruetruestart=index+1;
truetruetrueindex=Partition(input,start,end);
truetrue}
true}
truefor (int i = 0; i < k; i++)
truetrueres.push_back(input[i]);
truereturn res;
}
]]>
+
+
+
+
+
+ <hr>
+<h2 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h2><p>输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3
+
+
+
+
+
+
+
+
+
+
+
+
+ 并发(concurrency)和并行(parallelism)
+
+ http://codingdoge.cn/2017/07/27/title: 并发(concurrency)和并行(parallelism)/
+ 2017-07-27T04:29:00.000Z
+ 2018-01-13T14:00:58.116Z
+
+ “并发”指的是程序的结构,“并行”指的是程序运行时的状态
并行(parallelism) 这个概念很好理解。所谓并行,就是同时执行的意思,无需过度解读。判断程序是否处于并行的状态,就看同一时刻是否有超过一个“工作单位”在运行就好了。所以,单线程永远无法达到并行状态。
要达到并行状态,最简单的就是利用多线程和多进程。
并发(concurrency) 要理解“并发”这个概念,必须得清楚,并发指的是程序的“结构”。当我们说这个程序是并发的,实际上,这句话应当表述成“这个程序采用了支持并发的设计”。好,既然并发指的是人为设计的结构,那么怎样的程序结构才叫做支持并发的设计?
正确的并发设计的标准是:使多个操作可以在重叠的时间段内进行(two tasks can start, run, and complete in overlapping time periods)。
这句话的重点有两个。我们先看“(操作)在重叠的时间段内进行”这个概念。它是否就是我们前面说到的并行呢?是,也不是。并行,当然是在重叠的时间段内执行,但是另外一种执行模式,也属于在重叠时间段内进行。这就是协程 。
使用协程时,程序的执行看起来往往是这个样子:
task1, task2 是两段不同的代码,比如两个函数,其中黑色块代表某段代码正在执行。注意,这里从始至终,在任何一个时间点上都只有一段代码在执行,但是,由于 task1 和 task2 在重叠的时间段内执行,所以这是一个支持并发的设计。与并行不同,单核单线程能支持并发。
Different concurrent designs enable different ways to parallelize
]]>
+
+
+
+
+
+ <blockquote>
+<p>“并发”指的是程序的结构,“并行”指的是程序运行时的状态</p>
+</blockquote>
+<h2 id="并行(parallelism)"><a href="#并行(parallelism)" class="headerlink" title=
+
+
+
+
+
+
+
+
+
+
+ iOS 保持界面流畅的技巧
+
+ http://codingdoge.cn/2017/07/25/title: iOS 保持界面流畅的技巧/
+ 2017-07-25T06:21:00.000Z
+ 2018-01-14T16:35:13.663Z
+
+ iOS 保持界面流畅的技巧
CPU 资源消耗原因和解决方案 对象创建 对象的创建会分配内存、调整属性、甚至还有读取文件等操作,比较消耗 CPU 资源。尽量用轻量的对象代替重量的对象,可以对性能有所优化。比如 CALayer 比 UIView 要轻量许多,那么不需要响应触摸事件的控件,用 CALayer 显示会更加合适。如果对象不涉及 UI 操作,则尽量放到后台线程去创建,但可惜的是包含有 CALayer 的控件,都只能在主线程创建和操作。通过 Storyboard 创建视图对象时,其资源消耗会比直接通过代码创建对象要大非常多,在性能敏感的界面里,Storyboard 并不是一个好的技术选择。
尽量推迟对象创建的时间,并把对象的创建分散到多个任务中去。尽管这实现起来比较麻烦,并且带来的优势并不多,但如果有能力做,还是要尽量尝试一下。如果对象可以复用,并且复用的代价比释放、创建新对象要小,那么这类对象应当尽量放到一个缓存池里复用。
对象调整 对象的调整也经常是消耗 CPU 资源的地方。这里特别说一下 CALayer:CALayer 内部并没有属性,当调用属性方法时,它内部是通过运行时 resolveInstanceMethod 为对象临时添加一个方法,并把对应属性值保存到内部的一个 Dictionary 里,同时还会通知 delegate、创建动画等等,非常消耗资源。UIView 的关于显示相关的属性(比如 frame/bounds/transform)等实际上都是 CALayer 属性映射来的,所以对 UIView 的这些属性进行调整时,消耗的资源要远大于一般的属性。对此你在应用中,应该尽量减少不必要的属性修改。
当视图层次调整时,UIView、CALayer 之间会出现很多方法调用与通知,所以在优化性能时,应该尽量避免调整视图层次、添加和移除视图。
对象销毁 对象的销毁虽然消耗资源不多,但累积起来也是不容忽视的。通常当容器类持有大量对象时,其销毁时的资源消耗就非常明显。同样的,如果对象可以放到后台线程去释放,那就挪到后台线程去。这里有个小 Tip:把对象捕获到 block 中,然后扔到后台队列去随便发送个消息以避免编译器警告,就可以让对象在后台线程销毁了。
1
2
3
4
5
NSArray *tmp = self.array;
self.array = nil;
dispatch_async(queue, ^{
[tmp class];
});
布局计算 视图布局的计算是 App 中最为常见的消耗 CPU 资源的地方。如果能在后台线程提前计算好视图布局、并且对视图布局进行缓存,那么这个地方基本就不会产生性能问题了。
不论通过何种技术对视图进行布局,其最终都会落到对 UIView.frame/bounds/center 等属性的调整上。上面也说过,对这些属性的调整非常消耗资源,所以尽量提前计算好布局,在需要时一次性调整好对应属性,而不要多次、频繁的计算和调整这些属性。
Autolayout Autolayout 是苹果本身提倡的技术,在大部分情况下也能很好的提升开发效率,但是 Autolayout 对于复杂视图来说常常会产生严重的性能问题。随着视图数量的增长,Autolayout 带来的 CPU 消耗会呈指数级上升。具体数据可以看这个文章:http://pilky.me/36/。 如果你不想手动调整 frame 等属性,你可以用一些工具方法替代(比如常见的 left/right/top/bottom/width/height 快捷属性),或者使用 ComponentKit、AsyncDisplayKit 等框架。
文本计算 如果一个界面中包含大量文本(比如微博微信朋友圈等),文本的宽高计算会占用很大一部分资源,并且不可避免。如果你对文本显示没有特殊要求,可以参考下 UILabel 内部的实现方式:用 [NSAttributedString boundingRectWithSize:options:context:] 来计算文本宽高,用 -[NSAttributedString drawWithRect:options:context:] 来绘制文本。尽管这两个方法性能不错,但仍旧需要放到后台线程进行以避免阻塞主线程。
如果你用 CoreText 绘制文本,那就可以先生成 CoreText 排版对象,然后自己计算了,并且 CoreText 对象还能保留以供稍后绘制使用。
文本渲染 屏幕上能看到的所有文本内容控件,包括 UIWebView,在底层都是通过 CoreText 排版、绘制为 Bitmap 显示的。常见的文本控件 (UILabel、UITextView 等),其排版和绘制都是在主线程进行的,当显示大量文本时,CPU 的压力会非常大。对此解决方案只有一个,那就是自定义文本控件,用 TextKit 或最底层的 CoreText 对文本异步绘制。尽管这实现起来非常麻烦,但其带来的优势也非常大,CoreText 对象创建好后,能直接获取文本的宽高等信息,避免了多次计算(调整 UILabel 大小时算一遍、UILabel 绘制时内部再算一遍);CoreText 对象占用内存较少,可以缓存下来以备稍后多次渲染。
图片的解码 当你用 UIImage 或 CGImageSource 的那几个方法创建图片时,图片数据并不会立刻解码。图片设置到 UIImageView 或者 CALayer.contents 中去,并且 CALayer 被提交到 GPU 前,CGImage 中的数据才会得到解码。这一步是发生在主线程的,并且不可避免。如果想要绕开这个机制,常见的做法是在后台线程先把图片绘制到 CGBitmapContext 中,然后从 Bitmap 直接创建图片。目前常见的网络图片库都自带这个功能。
图像的绘制 图像的绘制通常是指用那些以 CG 开头的方法把图像绘制到画布中,然后从画布创建图片并显示这样一个过程。这个最常见的地方就是 [UIView drawRect:] 里面了。由于 CoreGraphic 方法通常都是线程安全的,所以图像的绘制可以很容易的放到后台线程进行。一个简单异步绘制的过程大致如下(实际情况会比这个复杂得多,但原理基本一致):
1
2
3
4
5
6
7
8
9
10
11
- (void)display {
dispatch_async(backgroundQueue, ^{
CGContextRef ctx = CGBitmapContextCreate(...);
// draw in context...
CGImageRef img = CGBitmapContextCreateImage(ctx);
CFRelease(ctx);
dispatch_async(mainQueue, ^{
layer.contents = img;
});
});
}
GPU 资源消耗原因和解决方案 相对于 CPU 来说,GPU 能干的事情比较单一:接收提交的纹理(Texture)和顶点描述(三角形),应用变换(transform)、混合并渲染,然后输出到屏幕上。通常你所能看到的内容,主要也就是纹理(图片)和形状(三角模拟的矢量图形)两类。
纹理的渲染 所有的 Bitmap,包括图片、文本、栅格化的内容,最终都要由内存提交到显存,绑定为 GPU Texture。不论是提交到显存的过程,还是 GPU 调整和渲染 Texture 的过程,都要消耗不少 GPU 资源。当在较短时间显示大量图片时(比如 TableView 存在非常多的图片并且快速滑动时),CPU 占用率很低,GPU 占用非常高,界面仍然会掉帧。避免这种情况的方法只能是尽量减少在短时间内大量图片的显示,尽可能将多张图片合成为一张进行显示。
当图片过大,超过 GPU 的最大纹理尺寸时,图片需要先由 CPU 进行预处理,这对 CPU 和 GPU 都会带来额外的资源消耗。目前来说,iPhone 4S 以上机型,纹理尺寸上限都是 4096x4096,更详细的资料可以看这里:iosres.com。所以,尽量不要让图片和视图的大小超过这个值。
视图的混合 (Composing) 当多个视图(或者说 CALayer)重叠在一起显示时,GPU 会首先把他们混合到一起。如果视图结构过于复杂,混合的过程也会消耗很多 GPU 资源。为了减轻这种情况的 GPU 消耗,应用应当尽量减少视图数量和层次,并在不透明的视图里标明 opaque 属性以避免无用的 Alpha 通道合成。当然,这也可以用上面的方法,把多个视图预先渲染为一张图片来显示。
图形的生成 CALayer 的 border、圆角、阴影、遮罩(mask),CASharpLayer 的矢量图形显示,通常会触发离屏渲染(offscreen rendering),而离屏渲染通常发生在 GPU 中。当一个列表视图中出现大量圆角的 CALayer,并且快速滑动时,可以观察到 GPU 资源已经占满,而 CPU 资源消耗很少。这时界面仍然能正常滑动,但平均帧数会降到很低。为了避免这种情况,可以尝试开启 CALayer.shouldRasterize 属性,但这会把原本离屏渲染的操作转嫁到 CPU 上去。对于只需要圆角的某些场合,也可以用一张已经绘制好的圆角图片覆盖到原本视图上面来模拟相同的视觉效果。最彻底的解决办法,就是把需要显示的图形在后台线程绘制为图片,避免使用圆角、阴影、遮罩等属性。
参考:iOS 保持界面流畅的技巧
]]>
+
+
+
+
+
+ <blockquote>
+<p><a href="http://blog.ibireme.com/2015/11/12/smooth_user_interfaces_for_ios/" target="_blank" rel="noopener">iOS 保持界面流畅的技巧</a
+
+
+
+
+
+
+
+
+
+
+
+
+ 剑指Offer —— 滑动窗口的最大值
+
+ http://codingdoge.cn/2017/07/25/title: 剑指Offer —— 滑动窗口的最大值/
+ 2017-07-25T02:43:00.000Z
+ 2018-01-14T16:38:57.866Z
+
+ 题目来源:滑动窗口的最大值
题目描述 给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。
解题思路 用一个双端队列维护当前滑动窗口的状态,队首是当前窗口最大值的下标,当窗口滑动进入一个新值k时,k从队尾依次向前比较,比k小的全部出队,保障了k的权重应有的位置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
vector<int> maxInWindows(const vector<int> &num, unsigned int size) {
truevector<int> ans;
truedeque<int> q;
trueif (num.size() < size || size == 0)
true return ans;
truefor (int i = 0; i < size; i++) {
true while(!q.empty() && num[i] > num[q.back()])
true q.pop_back();
true q.push_back(i);
true}
truefor (int i = size; i < num.size(); i++) {
true ans.push_back(num[q.front()]);
true while (!q.empty() && num[i] >= num[q.back()])
true q.pop_back();
true while (!q.empty() && q.front() <= i-size)
true q.pop_front();
true q.push_back(i);
true}
trueans.push_back(num[q.front()]);
truereturn ans;
}
]]>
+
+
+
+
+
+ <blockquote>
+<p>题目来源:<strong><a href="https://www.nowcoder.com/practice/1624bc35a45c42c0bc17d17fa0cba788?tpId=13&tqId=11217&tPage=4&
+
+
+
+
+
+
+
+
+
+
+
+
+ Search in Rotated Sorted Array
+
+ http://codingdoge.cn/2017/07/23/title: Search in Rotated Sorted Array /
+ 2017-07-23T15:56:00.000Z
+ 2018-01-12T14:25:37.504Z
+
+ 题目来源LeetCode: LeetCode OJ
题目描述: Suppose a sorted array is rotated at some pivot unknown to you beforehand. (i.e., 0 1 2 4 5 6 7 might become 4 5 6 7 0 1 2). You are given a target value to search. If found in the array return its index, otherwise return -1. You may assume no duplicate exists in the array.
题目解释 一个排好序的数组,不知道以哪个点为中心旋转了(部分有序),你的任务是查找给定的数是否存在数组中。在的话返回下标,不在的话返回-1.
分析 查找首先想到O(log(n))的二分查找,但是二分查找的前提是有序数组。题目里一个有序数组旋转后变成了部分有序。通过比较两端大小找到增序部分。
eg: 4 5 6 0 1 2 3 first = 4, mid = 0, last = 3, 通过比较first,mid,last找到增序部分。 在这个例子中为1 2 3, 然后判断target是否在这个增序子序列中, 如果在则直接用二分查找, 不在则在另一部分(例子中为4 5 6 0)中继续分解。
Code int search(const vector<int>& nums, int target){ int first = 0; int last = nums.size(); while(first != last) { const int mid = (first+last)>>1; // 使用位运算加速 if(nums[mid] == target) return mid; if(nums[first] <= nums[mid]) // 找到增序子序列 if(nums[first]<=target && target<nums[mid]) // 找到target在哪个部分 last = mid; else first = mid+1; else if(nums[mid]<target && target<=nums[last-1]) first = mid+1; else last = mid; } return -1;}拓展 当数组为无序时的二分查找
分析 一种是先排序再二分,一种是结合快排思想,每次选择一个关键字,比他大的放右边,比他小的放左边,然后再比较他和需要查找的数的关系,再选择区间进行迭代。如果需要返回查找数的下标,则添加一个纪录下标的数组,这样排好序后也能知道当前数在原始数组中的位置。
初始数组3 1 2 5 4 7 0 6 mid = key = 3 进行一次快排填坑 得到数组0 1 2 3 4 7 5 6 比较mid与target 如果target>mid则迭代mid后半部分 如果target<mid则迭代mid前半部分 直到找到target
Code int BinarySearch(vector<int>& nums, int target){ int num = nums.size(); int index[num]; // index数组纪录下标 以便能找到在数组的初始位置 for(int i = 0; i < num; i++) // 初始化index数组 index[i] = i; int l, r, m, sl, sr, mIndex; l = 0, r = num-1; while(l<=r) // 开始迭代 { mIndex = index[l], m = nums[l]; sl = l, sr = r; while(sl<sr) // 快排思想,左右填坑,并用index记录位置 { while(sl<sr && m<nums[sr]) sr--; nums[sl] = nums[sr]; index[sl] = index[sr]; while(sl<sr && m>nums[sl]) sl++; nums[sr] = nums[sl]; index[sr] = index[sl]; } nums[sl] = m; index[sl] = mIndex; if(m == target) return mIndex; if(target > m) // 判断target在哪个区间 l = sl+1; else r = sl-1; } return -1;}相关题目 Search in Rotated Sorted Array II
相关题目分析 因为允许出现重复数字,但是数组还是部分有序的,所以跳过重复数字即可
Code int search(const vector<int>& nums, int target){ int first = 0; int last = nums.size(); while(first != last) { const int mid = (first+last)>>1; if(nums[mid] == target) return mid; if(nums[first] < nums[mid]) if(nums[first]<=target && target<nums[mid]) last = mid; else first = mid+1; else if(nums[first] > nums[mid]) { if(nums[mid]<target && target<=nums[last-1]) first = mid+1; else last = mid; } else // 特判相等时跳过 first++; } return -1;} ]]>
+
+
+
+
+
+ <blockquote>
+<p>题目来源LeetCode: <strong><a href="https://leetcode.com/problems/search-in-rotated-sorted-array/" target="_blank" rel="noopener"
+
+
+
+
+
+
+
+
+
+
+
+
+ Remove Duplicates from Sorted Array
+
+ http://codingdoge.cn/2017/07/23/title: Remove Duplicates from Sorted Array /
+ 2017-07-23T15:56:00.000Z
+ 2018-01-14T16:41:30.178Z
+
+ 题目来源LeetCode: LeetCode OJ
题目描述 Given a sorted array, remove the duplicates in place such that each element appear only once and return the new length. Do not allocate extra space for another array, you must do this in place with constant memory. For example, Given input array nums = [1,1,2], Your function should return length = 2, with the first two elements of nums being 1 and 2 respectively. It doesn’t matter what you leave beyond the new length.
题目解释 给出一个sorted array(意思是指已经排好序了?),处理后数组里每一个元素只能出现一次,返回处理后的数组长度。 不能使用额外的数组空间,只能用已经给出的确定的内存空间。
分析 因为不太懂sorted array具体指的什么,第一次做的时候以为数组是随机的,相同元素出现的位置是随机的,然后题目也没给出limit time,随手就写了一个O(n^3)
1
2
3
4
5
6
7
8
9
10
for (int i = 0 ; i < num ; i++){
truefor (int j = i+1 ; j < num ; j++){
truetrueif (array [i] == array [j]){
for (int k = j; k < num -1 ; k++)
array [k] = array [k+1 ];
num --;
j--;
}
}
}
自然是T了。然后就把sorted array当做已经排好序的数组,那就容易多了,算法也都是O(1),一看代码就明白,水题,直接上代码。
way1 1
2
3
4
5
6
7
if (nums.empty()) return 0 ;
int index = 0 ;
for (int i = 1 ; i < nums.size(); i++) {
if (nums[index ] != nums[i])
nums[++index ] = nums[i];
}
return index + 1 ;
way2 STL 1
return distance(nums .begin(), unique(nums .begin(), nums.end()))
std::distance template typename iterator_traits::difference_type distance (InputIterator first, InputIterator last); Return distance between iterators Calculates the number of elements between first and last.c++ reference
std::unique equality (1) template ForwardIterator unique (ForwardIterator first, ForwardIterator last); predicate (2) template ForwardIterator unique (ForwardIterator first, ForwardIterator last,
BinaryPredicate pred); Remove consecutive duplicates in range Removes all but the first element from every consecutive group of equivalent elements in the range [first,last).c++ reference
相关题目 RemoveDuplicatesfromSortedArrayII
]]>
+
+
+
+
+
+ <blockquote>
+<p>题目来源LeetCode: <strong><a href="https://leetcode.com/problems/remove-duplicates-from-sorted-array/" target="_blank" rel="noop
+
+
+
+
+
+
+
+
+
+
+
+
+ 常见排序算法总结
+
+ http://codingdoge.cn/2017/07/23/title: 常见排序算法总结 /
+ 2017-07-23T15:56:00.000Z
+ 2018-01-14T16:45:17.006Z
+
+ 排序算法一般分为:内部排序(In-place sort) 不占用额外内存或者占用常数内存,如:插入排序、选择排序、冒泡排序、堆排序、快速排序。 外部排序(Out-place sort) 因为排序数据大,可用内存一次不能容纳所有排序记录,排序过程中需要访问外存,如:归并排序、计数排序、基数排序、桶排序。 也分为:
稳定的排序(stable sort) 插入排序、冒泡排序、归并排序、计数排序、基数排序、桶排序。 不稳定的排序(unstable sort) 选择排序、快速排序、堆排序。算法的稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,ri=rj,且ri在rj之前,而在排序后的序列中,ri仍在rj之前,则称这种排序算法是稳定的;否则称为不稳定的。
不稳定算法的改进:只需要在每个输入元素加一个index,表示初始时的数组索引,当不稳定的算法排好序后,对于相同的元素对index排序即可。
插入排序
最优复杂度:当输入数组就是排好序的时候,复杂度为O(n),而快速排序在这种情况下会产生O(n^2)的复杂度。 最差复杂度:当输入数组为倒序时,复杂度为O(n^2)。
插入排序比较适合用于“少量元素的数组”。插入排序比较适合用于“少量元素的数组”。
1.直接插入排序(Straight Insertion Sort) 工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。具体算法描述如下:
从第一个元素开始,该元素可以认为已经被排序 取出下一个元素,在已经排序的元素序列中从后向前扫描 如果该元素(已排序)大于新元素,将该元素移到下一位置 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置 将新元素插入到该位置后 重复步骤2~5 直接插入排序示例:
如果排序时碰到相等的元素,比较后会把相等的元素放在后面,所以这两个相等的元素之间的前后顺序没有改变,排序是稳定的。
算法的实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void InsertSort (int a[], int n)
{
for (int i = 1 ; i < n; i++)
{
if (a[i] < a[i-1 ])
{
int j = i-1 ;
int temp = a[i];
a[i] = a[i-1 ];
while (x < a[j] && j >= 0 )
{
a[j+1 ] = a[j];
j--;
}
a[j+1 ] = temp;
}
}
}
如果比较操作的代价比交换操作大的话,可以采用二分查找法来减少比较操作的数目。直接插入排序算法的优化算法有:二分插入排序,2-路插入排序。
2.希尔排序(Shell’s Sort) 希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。它是基于插入排序的以下两点性质而提出改进方法的:
插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率 但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位 基本原理是先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。算法具体描述如下:
选择一个步长,其设计者Donald Shell最初建议步长选择为n/2 并且对步长取半直到步长达到1; 以步长为间隔对序列进行排序; 重复步骤2直到步长为1。 希尔排序的示例:
希尔排序是不稳定的。
算法的实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
void ShellInsertSort (int a[], int n)
{
for (int gap=n>>1 ; gap>0 ; gap>>=1 )
{
for (int i=gap; i<n; i++)
{
int temp = a[i];
int j;
for (j=i-gap; j>=0 &&a[j]>temp; j-=gap)
a[j+gap] = a[j];
a[j+gap] = temp;
}
}
}
可能希尔排序最重要的地方在于当用较小步长排序后,以前用的较大步长仍然是有序的。已知的最好步长序列是由Sedgewick提出的(1, 5, 19, 41, 109,…),该序列的项来自两个算式[1] 。这项研究也表明“比较在希尔排序中是最主要的操作,而不是交换。”用这样步长序列的希尔排序比插入排序要快,甚至在小数组中比快速排序和堆排序还快,但是在涉及大量数据时希尔排序还是比快速排序慢。
选择排序
简单选择排序(Simple Selection Sort) 基本原理是在要排序的一组数中,选出最小(或者最大)的一个数与第1个位置的数交换;然后在剩下的数当中再找最小(或者最大)的与第2个位置的数交换,依次类推,直到第n-1个元素(倒数第二个数)和第n个元素(最后一个数)比较为止。算法的具体描述如下:
第一趟,从n 个记录中找出关键码最小的记录与第一个记录交换; 第二趟,从第二个记录开始的n-1 个记录中再选出关键码最小的记录与第二个记录交换;
以此类推…..
第i趟,则从第i 个记录开始的n-i+1 个记录中选出关键码最小的记录与第i 个记录交换,
直到整个序列按关键码有序。
简单选择排序的示例:
算法实现:
1
2
3
4
5
6
7
8
9
10
11
void SelectSort(int a[], int n)
{
trueint min = a[0 ];
truefor (int i = 0 ; i < n; i++)
true{
truetruefor (int j = i; j < n; j++)
truetruetrueif (a[j] < min )
truetruetruetruemin = a[j];
truetruea[i] = min ;
true}
}
选择排序是稳定的。
选择排序的交换操作介于0 和(n-1) 次之间。选择排序的比较操作为n(n-1) 次之间。选择排序的赋值操作介于0 和3(n-1) 次之间。 比较次数O(n^2 ) ,比较次数与关键字的初始状态无关,总的比较次数N=(n-1)+(n-2)+…+1=n(n-1)/2 。交换次数O(n) ,最好情况是,已经有序,交换0 次;最坏情况是,逆序,交换n-1 次。交换次数比冒泡排序较少,由于交换所需CPU时间比比较所需的CPU时间多,n 值较小时,选择排序比冒泡排序快。 原地操作几乎是选择排序的唯一优点,当空间复杂度要求较高时,可以考虑选择排序;实际适用的场合非常罕见。
简单排序的改进——二元选择排序 简单选择排序,每趟循环只能确定一个元素排序后的定位。我们可以考虑改进为每趟循环确定两个元素(当前趟最大和最小记录)的位置,从而减少排序所需的循环次数。改进后对n个数据进行排序,最多只需进行[n/2]趟循环即可。具体实现如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void SelectSort(int r[],int n) {
int i ,j , min ,max , tmp;
for (i=1 ;i <= n/2 ;i++) {
min = i; max = i ;
for (j= i+1 ; j<= n-i; j++) {
if (r[j] > r[max ]) {
max = j ; continue ;
}
if (r[j]< r[min ]) {
min = j ;
}
}
tmp = r[i-1 ]; r[i-1 ] = r[min ]; r[min ] = tmp;
tmp = r[n-i]; r[n-i] = r[max ]; r[max ] = tmp;
}
}
堆排序(Heap Sort)
堆排序是指利用堆这种数据结构所设计的一种排序算法。其基本原理如下: 堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。 若以一维数组存储一个堆,则堆对应一棵完全二叉树,且所有非叶结点的值均不大于(或不小于)其子女的值,根结点(堆顶元素)的值是最小(或最大)的。如:
大顶堆序列:(96, 83, 27, 38, 11, 09) 小顶堆序列:(12, 36, 24, 85, 47, 30, 53, 91)
初始时把n个数的序列看作是一棵顺序存储的二叉树(一维数组存储二叉树),调整它们的存储序,使之成为一个堆,将堆顶元素输出,得到n个元素中最小(或最大)的元素,这时堆的根节点的数最小(或者最大)。然后对前面(n-1)个元素重新调整使之成为堆,输出堆顶元素,得到n个元素中次小(或次大)的元素。依此类推,直到只有两个节点的堆,并对它们作交换,最后得到有n个节点的有序序列。称这个过程为堆排序。 因此,实现堆排序需解决两个问题:
如何将n 个待排序的数建成堆; 输出堆顶元素后,怎样调整剩余n-1 个元素,使其成为一个新堆。 首先讨论第二个问题:输出堆顶元素后,对剩余n-1元素重新建成堆的调整过程。调整小顶堆的方法:
设有m 个元素的堆,输出堆顶元素后,剩下m-1 个元素。将堆底元素送入堆顶((最后一个元素与堆顶进行交换),堆被破坏,其原因仅是根结点不满足堆的性质。 将根结点与左、右子树中较小元素的进行交换。 若与左子树交换:如果左子树堆被破坏,即左子树的根结点不满足堆的性质,则重复方法 2. 若与右子树交换,如果右子树堆被破坏,即右子树的根结点不满足堆的性质。则重复方法 2. 继续对不满足堆性质的子树进行上述交换操作,直到叶子结点,堆被建成。 称这个自根结点到叶子结点的调整过程为筛选。如图:
再讨论对n 个元素初始建堆的过程。 建堆方法:对初始序列建堆的过程,就是一个反复进行筛选的过程。
n个结点的完全二叉树,则最后一个结点是第个结点的子树。 筛选从第个结点为根的子树开始,该子树成为堆。 之后向前依次对各结点为根的子树进行筛选,使之成为堆,直到根结点。 如图建堆初始过程:无序序列:(49,38,65,97,76,13,27,49)
算法的实现:
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
* 已知H[s…m]除了H[s] 外均满足堆的定义
* 调整H[s],使其成为大顶堆.即将对第s个结点为根的子树筛选,
*
* @param H是待调整的堆数组
* @param s是待调整的数组元素的位置
* @param length是数组的长度
*
*/
void HeapAdjust(int H[],int s, int length )
{
int tmp = H[s];
int child = 2 *s+1 ;
while (child < length )
{
if (child+1 <length && H[child]<H[child+1 ])
++child;
if (H[s]<H[child])
{
H[s] = H[child];
s = child;
child = 2 *s+1 ;
}
else
break ;
H[s] = tmp;
}
}
* 初始堆进行调整
* 将H[0..length-1]建成堆
* 调整完之后第一个元素是序列的最小的元素
*/
void BuildingHeap(int H[], int length )
{
for (int i = (length -1 ) / 2 ; i >= 0 ; i--)
HeapAdjust(H,i,length );
}
* 堆排序算法
*/
void HeapSort(int H[],int length )
{
BuildingHeap(H, length );
for (int i = length - 1 ; i > 0 ; --i)
{
int temp = H[i]; H[i] = H[0 ]; H[0 ] = temp;
HeapAdjust(H,0 ,i);
}
}
堆排序是不稳定的。
设树深度为k,
次,交换记录至多k 次。所以,在建好堆后,排序过程中的筛选次数不超过下式:
而建堆时的比较次数不超过4n 次,因此堆排序最坏情况下,时间复杂度也为:O(nlogn )。
交换排序 冒泡排序(Bubble Sort)
冒泡排序重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。 冒泡排序示例:
算法的实现:
1
2
3
4
5
6
7
8
9
10
11
void bubbleSort(int a[], int n)
{
for(int i =0
for(int j = 0
if(a[j] > a[j+1])
{
int tmp = a[j];
a[j] = a[j+1];
a[j+1] = tmp
}
}
冒泡排序是稳定的。
冒泡算法的改进 对冒泡排序常见的改进方法是加入一标志性变量exchange,用于标志某一趟排序过程中是否有数据交换,如果进行某一趟排序时并没有进行数据交换,则说明数据已经按要求排列好,可立即结束排序,避免不必要的比较过程。本文再提供以下两种改进算法:
设置一标志性变量pos,用于记录每趟排序中最后一次进行交换的位置。由于pos位置之后的记录均已交换到位,故在进行下一趟排序时只要扫描到pos位置即可。 改进后算法如下: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void Bubble_1 ( int r[], int n)
{
int i = n -1 ;
while (i > 0 )
{
int pos= 0 ;
for (int j = 0 ; j < i; j++)
if (r[j] > r[j+1 ])
{
pos = j;
int tmp = r[j];
r[j] = r[j+1 ];
r[j+1 ] = tmp;
}
i = pos;
}
}
传统冒泡排序中每一趟排序操作只能找到一个最大值或最小值,我们考虑利用在每趟排序中进行正向和反向两遍冒泡的方法一次可以得到两个最终值(最大者和最小者) , 从而使排序趟数几乎减少了一半。 改进后的算法为: 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
void Bubble_2 (int r[], int n)
{
int low = 0
int high = n-1
int tmp, j;
while (low < high)
{
for (j = low
if (r[j] > r[j+1])
{
tmp = r[j];
r[j]=r[j+1];
r[j+1]=tmp;
}
--high
for (j = high
if (r[j] < r[j-1])
{
tmp = r[j];
r[j]=r[j-1];
r[j-1]=tmp;
}
++low
}
}
快速排序(Quick Sort)
快速排序又称划分交换排序(partition-exchange sort),使用分治法(Divide and conquer)策略来把一个序列(list)分为两个子序列(sub-lists)。其算法描述为:
从数列中挑出一个元素,称为”基准”(pivot)。 重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(相同的数可以到任一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。 递归地(recursively)把小于基准值元素的子数列和大于基准值元素的子数列排序。 快速排序的示例:
一趟排序的过程
排序的全过程
算法的实现(递归):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void quickSort_recursive(int a[], int low, int high)
{
int first = low;
int last = high;
int key = a[first ];
if (low >= high)
return ;
while (first < last )
{
while ((first < last )&&(key <= a[last ]))
last --;
a[first ] = a[last ];
while ((first < last )&&(key >= a[first ]))
first ++;
a[last ] = a[first ];
}
a[first ] = key ;
quickSort_recursive(a, low, first -1 );
quickSort_recursive(a, first +1 , high);
}
快速排序是不稳定的。 快排迭代算法:维护一个栈,存放划分的起点终点
在平均状况下,排序n个项目要Ο(nlogn)次比较。在最坏状况下(正序或逆序)则需要Ο(n^2 )次比较,取决于其递归树的高度。事实上,快速排序通常明显比其他Ο(nlogn)算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来。快速排序是一个不稳定的排序方法。
归并排序(Merge Sort)
归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。
递归法 将序列每相邻两个数字进行归并操作,形成floor(n/2)个序列,排序后每个序列包含两个元素 将上述序列再次归并,形成floor(n/4)个序列,每个序列包含四个元素 重复步骤2,直到所有元素排序完毕 算法实现:
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
void merge_sort_recursive(int arr[], int reg [], int start, int end )
{
if (start >= end )
return ;
int len = end - start, mid = (len >> 1 ) + start;
int start1 = start, end1 = mid;
int start2 = mid + 1 , end2 = end ;
merge_sort_recursive(arr, reg , start1, end1);
merge_sort_recursive(arr, reg , start2, end2);
int k = start;
while (start1 <= end1 && start2 <= end2)
reg [k++] = arr[start1] < arr[start2] ? arr[start1++] : arr[start2++];
while (start1 <= end1)
reg [k++] = arr[start1++];
while (start2 <= end2)
reg [k++] = arr[start2++];
for (k = start; k <= end ; k++)
arr[k] = reg [k];
}
void merge_sort(int arr[], const int len)
{
trueint reg [len];
truemerge_sort_recursive(arr, reg , 0 , len - 1 );
}
两路归并的递归算法 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void MSort (ElemType *r, ElemType *rf,int s, int t)
{
ElemType *rf2;
if (s==t) r[s] = rf[s];
else
{
int m=(s+t)/2 ;
MSort(r, rf2, s, m);
MSort(r, rf2, m+1 , t);
Merge(rf2, rf, s, m+1 ,t);
}
}
void MergeSort_recursive (ElemType *r, ElemType *rf, int n)
{
MSort(r, rf,0 , n-1 );
}
迭代法 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列; 设定两个指针,最初位置分别为两个已经排序序列的起始位置; 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置; 重复步骤3直到某一指针到达序列尾; 将另一序列剩下的所有元素直接复制到合并序列尾; 算法实现:
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
void merge_sort_iteration(int arr[], int len )
{
int * a = arr;
int * b = new int [len ];
for (int seg = 1 ; seg < len ; seg += seg)
{
for (int start = 0 ; start < len ; start += seg + seg)
{
int low = start, mid = min(start + seg, len ), high = min(start + seg + seg, len );
int k = low;
int start1 = low, end1 = mid ;
int start2 = mid , end2 = high;
while (start1 < end1 && start2 < end2)
b[k++] = a[start1] < a[start2] ? a[start1++] : a[start2++];
while (start1 < end1)
b[k++] = a[start1++];
while (start2 < end2)
b[k++] = a[start2++];
}
T* temp = a;
a = b;
b = temp;
}
if (a != arr)
{
for (int i = 0 ; i < len ; i++)
b[i] = a[i];
b = a;
}
delete[] b;
}
归并排序是稳定的。
总结 各种排序的稳定性,时间复杂度和空间复杂度总结:
参考资料有: 真实的归宿——八大排序算法 xiazdong——九大排序算法再总结 wikipedia
]]>
+
+
+
+
+
+ <p>排序算法一般分为:</p>
+<ol>
+<li>内部排序(In-place sort)<br> 不占用额外内存或者占用常数内存,如:插入排序、选择排序、冒泡排序、堆排序、快速排序。</li>
+<li>外部排序(Out-place sort)<br> 因为排序数据大,可用内存一
+
+
+
+
+
+
+
+
+
+
+ self与super的区别
+
+ http://codingdoge.cn/2017/07/23/title: self与super的区别 /
+ 2017-07-23T15:47:00.000Z
+ 2018-01-14T16:47:12.060Z
+
+ 原文CSDN evilotus 有所整理
在ObjC 中的类实现中经常看到这两个关键字”self ”和”super ”,以以前oop 语言的经验,拿c++ 为例,self 相当于this ,super 相当于调用父类的方法,这么看起来是很容易理解的。但是它们真正是如何调用的呢? 你知道吗?
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
@interface Person:NSObject
{
trueNSString* name;
}
(void) setName:(NSString) yourName;
@end//Person
@interface PersonMe:Person
{
trueNSUInteger age;
}
(void) setAge:(NSUInteger) age;
(void) setName:(NSString*) yourName andAge:(NSUInteger) age;
@end// PersonMe
@implementation PersonMe
(void) setName:(NSString*) yourName andAge:(NSUInteger) age
{
true[self setAge:age];
true[super setName:yourName];
}
@end// PersonMe
int main(int argc, char* argv[]) {
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]
PersonMe* me = [[PersonMe alloc] init];
[me setName: @"asdf" andAge: 18];
[me release];
[pool drain];
return 0;
}
上面有简单的两个类,在子类PersonMe 中调用了自己类中的setAge 和父类中的setName ,这些代码看起来很好理解,没什么问题。 然后我在setName:andAge 的方法中加入两行:
1
2
3
4
5
6
7
8
9
10
11
NSLog(@"self ' class is %@", [self class]);
NSLog(@"super' class is %@", [super class]);
```
这样在调用时,会打出来这两个的**class**,先猜下吧,会打印出什么? 按照以前*oop*语言的经验,这里应该会输出:***self ' s class is PersonMe super ' s class is Person***
但是编译运行后,可以发现结果是:
``` //
self 's class is PersonMe
super ' s class is PersonMe
self 的class 和预想的一样,怎么super 的class 也是PersonMe ?
真相 self 是类的隐藏的参数,指向当前当前调用方法的类,另一个隐藏参数是_cmd ,代表当前类方法的selector 。这里只关注这个self 。super 是个啥?super 并不是隐藏的参数,它只是一个“编译器指示符” ,它和self 指向的是相同的消息接收者,拿上面的代码为例,不论是用[self setName] 还是[super setName] ,接收“setName” 这个消息的接收者都是PersonMe* me 这个对象。不同的是,super 告诉编译器,当调用setName 的方法时,要去调用父类的方法,而不是本类里的。
当使用self 调用方法时,会从当前类 的方法列表中开始找,如果没有,就从父类 中再找;而当使用superv时,则从 父类**的方法列表中开始找。然后调用父类的这个方法。
One more step 这种机制到底底层是如何实现的?其实当调用类方法的时候,编译器会将方法调用转成一个C函数方法调用,apple的objcRuntimeRef上说:
Sending Messages When it encounters a method invocation, the compiler might generate a call to any of several functions to perform the actual message dispatch, depending on the receiver, the return value, and the arguments. You can use these functions to dynamically invoke methods from your own plain C code, or to use argument forms not permitted by NSObject’s perform… methods. These functions are declared in /usr/include/objc/objc-runtime.h.
objc_msgSend sends a message with a simple return value to an instance of a class. objc_msgSend_stret sends a message with a data-structure return value to an instance of a class. objc_msgSendSuper sends a message with a simple return value to the superclass of an instance of a class. objc_msgSendSuper_stret sends a message with a data-structure return value to the superclass of an instance of a class. 可以看到会转成调用上面4个方法中的一个,由于_stret系列的和没有_stret的那两个类似,先只关注objc_msgSend和objc_msgSendSuper两个方法。
当使用[self setName]调用时,会使用objc_msgSend的函数,先看下objc_msgSend的函数定义:
1
id objc_msgSend(id theReceiver, SEL theSelector, ...)
第一个参数是消息接收者,第二个参数是调用的具体类方法的selector,后面是selector方法的可变参数。我们先不管这个可变参数,以[self setName:]为例,编译器会替换成调用objc_msgSend的函数调用,其中theReceiver是self,theSelector是 @selector(setName:),这个selector是从当前self的class的方法列表开始找的setName,当找到后把对应的 selector传递过去。
而当使用[super setName]调用时,会使用objc_msgSendSuper函数,看下objc_msgSendSuper的函数定义:
1
id objc_msgSendSuper(struct objc_super *super, SEL op, ...)
第一个参数是个objc_super的结构体,第二个参数还是类似上面的类方法的selector,先看下objc_super这个结构体是什么东西:
struct objc_super { id receiver; Class superClass; };可以看到这个结构体包含了两个成员,一个是receiver,这个类似上面objc_msgSend的第一个参数receiver,第二个成员是记 录写super这个类的父类是什么,拿上面的代码为例,当编译器遇到PersonMe里setName:andAge方法里的[super setName:]时,开始做这几个事:
构建objc_super的结构体,此时这个结构体的第一个成员变量receiver就是PersonMe* me,和self相同。而第二个成员变量superClass就是指类Person,因为PersonMe的超类就是这个Person。
调用objc_msgSendSuper的方法,将这个结构体和setName的sel传递过去。函数里面在做的事情类似这样:从objc_super结构体指向的superClass的方法列表开始找setName的selector,找到后再以 objc_super->receiver去调用这个selector,可能也会使用objc_msgSend这个函数,不过此时的第一个参数 theReceiver就是objc_super->receiver,第二个参数是从objc_super->superClass中找到 的selector
里面的调用机制大体就是这样了,以上面的分析,回过头来看开始的代码,当输出[self class]和[super class]时,是个怎样的过程。
当使用[self class]时,这时的self是PersonMe,在使用objc_msgSend时,第一个参数是receiver也就是self,也是 PersonMe* me这个实例。第二个参数,要先找到class这个方法的selector,先从PersonMe这个类开始找,没有,然后到PersonMe的父类 Person中去找,也没有,再去Person的父类NSObject去找,一层一层向上找之后,在NSObject的类中发现这个class方法,而 NSObject的这个class方法,就是返回receiver的类别,所以这里输出PersonMe。
当使用[super class]时,这时要转换成objc_msgSendSuper的方法。先构造objc_super的结构体吧,第一个成员变量就是self, 第二个成员变量是Person,然后要找class这个selector,先去superClass也就是Person中去找,没有,然后去Person 的父类中去找,结果还是在NSObject中找到了。然后内部使用函数objc_msgSend(objc_super->receiver, @selector(class)) 去调用,此时已经和我们用[self class]调用时相同了,此时的receiver还是PersonMe* me,所以这里返回的也是PersonMe。
Furthor more 在类的方法列表寻找一个方法时,还牵涉到一个概念类对象的isa指针和objc的meta-class概念,这里就不再详细介绍。
]]>
+
+
+
+
+
+ <blockquote>
+<p>原文<a href="http://blog.csdn.net/evilotus/article/details/7284290" target="_blank" rel="noopener"><strong>CSDN evilotus</stro
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Objective-C声明在头文件和实现文件中的区别
+
+ http://codingdoge.cn/2017/07/23/title: Objective-C声明在头文件和实现文件中的区别 /
+ 2017-07-23T15:47:00.000Z
+ 2018-01-14T16:53:10.421Z
+
+ 转自codecloud (有整理)
调试程序的时候,突然想到这个问题,百度一下发现有不少这方面的问答,粗略总结一下: Objective-C中有分类和类扩展的概念,而实现文件中的类声明实际上就是类扩展.
@interface部分为类扩展(extension)
其被设计出来就是为了解决两个问题的
定义类私有方法的地方,也就是下面说到的区别一 实现public readonly,private readwrite的property(意思是在h头文件中定义一个属性对外是readonly的,但在类的内部希望是可读写的,所以可以在m源文件中的@interface部分重新定义此属性为readwrite,此时此属性对外是只读的,对内是读写的). 此外,也可在此部分申明变量和属性,但申明的变量,属性和方法均为私有的,只能够被当前类访问,相当于private。
区别一: 属性在.h文件中和在.m中声明是有区别的。区别就是,在.h文件中声明的属性,外部类可以通过“类实例.属性”来调用,但在.m中声明的则不可以,获取和设置的方法,只能是通过setValue:forKey和valueForKey来实现。
成员变量,有三种权限,就是大家都知道的@private、@protected、@public ,写在.m文件中时,相当于是@private权限,子类无法访问,验证了一下,做权限修改也无效。而写在.h文件中,默认是@protected权限,子类可以访问,可以做权限修改。因为访问权限指针对.h文件。.h文件中成员变量,外部类对其的调用,跟C++一样,用->来调用。
区别二 这样可以提高编译效率,避免重复编译。 因为分开的话只需要编译一次生成对应的.obj文件后,再次应用该类的地方,这个类就不会被再次编译,从而大大提高了效率。这样可以提高编译效率,避免重复编译.
怎么去解释呢…其实这是一个面向对象的思想,所谓”提高”的比较对象,应该是直接将方法写到具体函数里的实现方式. h为编译器提供一个索引、声明,连接obj对象和主程序. 编译器在编译的时候,如果需要,则去查找h,找到了h,再找对应的obj,就可以找到类的方法了. 但是如果直接写入到同一个文件(例如hpp),主程序没有索引,也不清楚具体的类实现了没有,只能一次次重复的编译相同的代码,这样实际上没有把这个类该有的东西抽象出来. 对于函数声明在头文件中,在实现文件中实现,也是避免重复编译,函数可以多次声明,但只能实现一次.
头文件相对于实现文件的作用在于: 头文件可以预先告诉编译器一些必要的声明,让编译器顺利进行下去,在连接实现以前.未必出现实际的定义.
头文件的意义在:* 使得程序简明,清晰.* 避免了重复编写相同的声明代码..c和 .h文件没有必然的联系.关于头文件和实现文件的编译连接的过程 其实要理解C文件与头文件有什么不同之处,首先需要弄明白编译器的工作过程,一般说来编译器会做以下几个过程:
预处理阶段 词法与语法分析阶段 编译阶段,首先编译成纯汇编语句,再将之汇编成跟CPU相关的二进制码,生成各个目标文件 连接阶段,将各个目标文件中的各段代码进行绝对地址定位,生成跟特定平台相关的可执行文件,当然,最后还可以用objcopy生成纯二进制 码,也就是去掉了文件格式信息. 编译器在编译时是以C文件为单位进行的,也就是说如果你的项目中一个C文件都没有,那么你的项目将无法编译,连接器是以目标文件为单位,它将一个或多个目标文件进行函数与变量的重定位,生成最终的可执行文件,在PC上的程序开发,一般都有一个main函数,这是各个编译器的约定,当然,你如果自己写连接器脚本的话,可以不用main函数作为程序入口!!!
有了这些基础知识,再言归正传,为了生成一个最终的可执行文件,就需要一些目标文件,也就是需要C文件,而这些C文件中又需要一个main函数作为可执行程序的入口,那么我们就从一个C文件入手,假定这个C文件内容如下:
1
2
3
4
5
6
7
8
#include \<stdio.h>
#include “mytest.h”
int main (int argc,char **argv)
{
test = 25 ;
printf (“test……………..%d\n”,test);
}
头文件内容如下:int test;
现在以这个例子来讲解编译器的工作: 预处理阶段: 编译器以C文件作为一个单元,首先读这个C文件,发现第一句与第二句是包含一个头文件,就会在所有搜索路径中寻找这两个文件,找到之后,就会将相应头文件中再去处理宏,变量,函数声明,嵌套的头文件包含等,检测依赖关系,进行宏替换,看是否有重复定义与声明的情况发生,最后将那些文件中所有的东东全部扫描进这个当前的C文件中,形成一个中间“C文件”
编译阶段: 在上一步中相当于将那个头文件中的test变量扫描进了一个中间C文件,那么test变量就变成了这个文件中的一个全局变量,此时就将所有这个中间C文件的所有变量,函数分配空间,将各个函数编译成二进制码,按照特定目标文件格式生成目标文件,在这种格式的目标文件中进行各个全局变量,函数的符号描述,将这些二进制码按照一定的标准组织成一个目标文件
连接阶段: 将上一步成生的各个目标文件,根据一些参数,连接生成最终的可执行文件,主要的工作就是重定位各个目标文件的函数,变量等,相当于将个目标文件中的二进制码按一定的规范合到一个文件中
再回到C文件与头文件各写什么内容的话题上: 理论上来说C文件与头文件里的内容,只要是C语言所支持的,无论写什么都可以的,比如你在头文件中写函数体,只要在任何一个C文件包含此头文件就可以将这个函数编译成目标文件的一部分(编译是以C文件为单位的,如果不在任何C文件中包含此头文件的话,这段代码就形同虚设),你可以在C文件中进行函数声明,变量声明,结构体声明,这也不成问题!!! 那为何一定要分成头文件与C文件呢?又为何一般都在头件中进行函数,变量声明,宏声明,结构体声明呢?而在C文件中去进行变量定义,函数实现呢?? 原因如下:
如果在头文件中实现一个函数体,那么如果在多个C文件中引用它,而且又同时编译多个C文件,将其生成的目标文件连接成一个可执行文件,在每个引用此头文件的C文件所生成的目标文件中,都有一份这个函数的代码,如果这段函数又没有定义成局部函数,那么在连接时,就会发现多个相同的函数,就会报错
如果在头文件中定义全局变量,并且将此全局变量赋初值,那么在多个引用此头文件的C文件中同样存在相同变量名的拷贝,关键是此变量被赋了初值,所以编译器就会将此变量放入DATA段,最终在连接阶段,会在DATA段中存在多个相同的变量,它无法将这些变量统一成一个变量,也就是仅为此变量分配一个空间,而不是多份空间,假定这个变量在头文件没有赋初值,编译器就会将之放入BSS段,连接器会对BSS段的多个同名变量仅分配一个存储空间
如果在C文件中声明宏,结构体,函数等,那么我要在另一个C文件中引用相应的宏,结构体,就必须再做一次重复的工作,如果我改了一个C文件中的一个声明,那么又忘了改其它C文件中的声明,这不就出了大问题了,程序的逻辑就变成了你不可想象的了,如果把这些公共的东东放在一个头文件中,想用它的C文件就只需要引用一个就OK了!!!这样岂不方便,要改某个声明的时候,只需要动一下头文件就行了
在头文件中声明结构体,函数等,当你需要将你的代码封装成一个库,让别人来用你的代码,你又不想公布源码,那么人家如何利用你的库呢?也就是如何利用你的库中的各个函数呢??一种方法是公布源码,别人想怎么用就怎么用,另一种是提供头文件,别人从头文件中看你的函数原型,这样人家才知道如何调用你写的函数,就如同你调用printf函数一样,里面的参数是怎样的??你是怎么知道的??还不是看人家的头文件中的相关声明啊!!!当然这些东东都成了C标准,就算不看人家的头文件,你一样可以知道怎么使用.
]]>
+
+
+
+
+
+ <blockquote>
+<p>转自<a href="http://codecloud.net/objective-c-6782.html" target="_blank" rel="noopener">codecloud</a>(有整理)</p>
+</blockquote>
+<
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ CocoaPods 安装及一般使用
+
+ http://codingdoge.cn/2017/07/23/title: CocoaPods 安装及一般使用/
+ 2017-07-23T15:47:00.000Z
+ 2018-01-13T05:06:34.464Z
+
+ WHAT IS COCOAPODS CocoaPods is a dependency manager for Swift and Objective-C Cocoa projects.CocoaPods can help you scale your projects elegantly.
INSTALL CocoaPods是基于ruby建立的,要确保你的电脑里装有Ruby,不过Mac都是自带Ruby的,你可以用rvm 来管理你的ruby.
RVM 实用指南 · Ruby China 然后我们使用Rubygem来安装cocoapods. 由于GFW的原因我们不能访问cocoapods.org,使用淘宝的Ruby镜像来代替:
删除自带的Ruby镜像$ gem sources --remove https://rubygems.org/ 添加淘宝的镜像$ gem sources -a https://gems.ruby-china.org/ (如果这个镜像不能用,就用https://gems.ruby-china.org/ ) 可以用$ gem sources -l来检验。成功即显示以下结果: FF76DDA8-EC18-438C-B921-8603D4688C1D
安装CocoaPods$ sudo gem install cocoapods 配置CocoaPods$ pod setup 如果安装失败的话,根据报错去解决问题,比如gem没更新,ruby版本等。 常见问题:While executing gem ... (Errno::EPERM) 1
Operation not permitted - /usr/bin/xcodeproj
安装Cocoapods, 更新gem出现的问题。 - SegmentFault ios - Cannot install cocoa pods after uninstalling, results in error - Stack Overflow
Using CocoaPods 使用之前你要确保你所想用的库存在CocoaPods中:(拿AFNetworking举例)$ pod search AFNetworking 第一次搜索会需要建立索引,比较慢一些。 搜索完成后会列举出结果和版本:
37122577-4CDC-48C9-9B25-4B943D983810
然后通过创建Podfile 文件来添加依赖关系
先cd进你项目所在的目录(简介里面可以直接复制路径) 利用vim创建Podfile文件$ vim Podfile 然后输入:1
2
3
4
5
platform :ios, '10.0'
target 'TargetName' do
pod 'AFNetworking' , '~> 3.0'
end
文字的意思是,当前AFNetworking支持的iOS最高版本是iOS 10.0,’TargetName’为你项目的名称,要下载的AFNetworking版本是3.0 保存退出。
运行$ pod install 完成后会出现 提示使用’XXX.xcworkspace’文件来代替之前的’XXX.xcodeproj’文件打开项目。 打开项目后会发现 里面有了我们想要加进来的库,可以#import进来了。 增加新的库 如果使用过程中我还想添加其他的库怎么办,只要在Podfile里面接着添加,然后终端再执行pod install就可以了。 更新CocoaPods中的库 第三方库们都有人在维护升级,我们需要隔断时间就要更新下我们工程中第三方库的版本。只需要终端输入命令pod update就可以了。 删除CocoaPods中的某些库 当我们需要去掉某个第三方库时,只需要在Podfile删除该引入该库的语句,然后执行pod update或者pod install就可以了。 升级CocoaPodssudo gem install cocoapods ]]>
+
+
+
+
+
+ <h2 id="WHAT-IS-COCOAPODS"><a href="#WHAT-IS-COCOAPODS" class="headerlink" title="WHAT IS COCOAPODS"></a>WHAT IS <a href="https://cocoapods.
+
+
+
+
+
+
+
+
+
+
+
+
+ 有限状态机在iOS中的应用
+
+ http://codingdoge.cn/2017/07/23/title: 有限状态机在iOS中的应用/
+ 2017-07-23T15:47:00.000Z
+ 2018-01-13T05:06:26.865Z
+
+ 有限状态机(Finite-State Machine, FSM),是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。其实我们平常和很多状态机都打过交道,比如正则表达式、网络协议(如TCP协议状态机)、游戏设计、字符串匹配等等,可能大多数时候我们都没意识到,接下来我们简略了解下状态机。
基本概念 状态(state): 指的是对象在其生命周期中的一种状况,处于某个特定状态中的对象必然会满足某些条件、执行某些动作或者是等待某些事件,而且状态是相对稳定的,如果没有外部条件的触发,一个状态会一直持续下去。 事件(event): 指的是对状态机来讲是有意义的那些事情。事件通常会引起状态的变迁,促使状态机从一种状态切换到另一种状态。 转换(transition): 指的是两个状态之间的一种关系,表明对象将在第一个状态中执行一定的动作,并将在某个事件发生的同时某个特定条件满足时进入第二个状态。 动作(action): 指的是状态机中可以执行的那些操作,当事件被满足或者状态变迁时执行动作,动作不是必需的。 如下图状态表:
关于状态机的详细知识可以参考:UML状态图的实用C/C++设计 ERLANG
在iOS中的使用 背景 假设我们要设计一款网络视频播放器,有基本的播放、暂停功能,当缓冲好时可以进行播放,当URL错误或者视频资源错误时播放失败,我们发现,当我们去描述一个事物以及它的功能时,总是离不开它的状态,如这个播放器,我们可以定义它有播放失败、缓冲中、将要播放…等等状态。
设计 我们可以用状态机去实现这样的功能。
1
2
3
4
5
6
7
8
enum VideoPlayerState {
case failed, // 播放失败
buffering, // 缓冲中
readyToPlay, // 将要播放
playing, // 播放中
paused, // 播放暂停
finished // 播放完毕
}
然后定义当状态发生变换后,针对某个状态我希望它去执行一些逻辑里的动作 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var state: VideoPlayerState = .paused {
didSet {
switch state {
case .failed :
popReminderView()
case .buffering :
case .readyToPlay :
case .playing :
case .paused :
case .finished :
}
}
}
1
2
3
4
5
6
networkRequestCompletion () {
state = .readyToPlay
}
这样,通过state我们能很清晰的知道现在播放器是什么样应该做什么事,在我们的业务逻辑中,当状态变化时通过didSet我们能很方便的去响应对应状态下应该执行的行为。
总结 整篇文章质量或许不是很高,因为还没有大量的代码实践让我去有一个更深更全的体会,但是希望能带给我自己和读者们一点抛砖引玉的效果。我们在编码、设计过程中,多去思考一些,什么地方用什么样的模式更好,比如状态机,来使我们的代码更解耦,易维护,高扩展。
这里有一篇关于Objective-c状态机的实现 ,更抽象,其中用到的枚举值自动转字符串通用方案很有意思,同时可以参考这篇《iOS开发高级:使用宏定义macros 》关于宏定义的使用。 这篇《iOS APP 架构漫谈二 》列举了很具体的运用场景,可以参考。 同时参考的文章有:
关于状态记的开源库推荐:
]]>
+
+
+
+
+
+ <p>有限状态机(Finite-State Machine, FSM),是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。</p>
+<p>其实我们平常和很多状态机都打过交道,比如正则表达式、网络协议(如TCP协议状态机)、游戏设计、字符串匹配等等,可能大多数时候我们
+
+
+
+
+
+
+
+
+
+
+ 视频开发
+
+ http://codingdoge.cn/2017/07/23/title: 视频开发/
+ 2017-07-23T15:47:00.000Z
+ 2018-01-13T05:06:57.320Z
+
+ 下面會介紹視頻的一些基本知識,和在iOS上實現視頻播放和緩存的幾種方案。软解码和硬解码 GPU解码就是所谓的硬解码,CPU解码就是软解码。iOS提供的播放器类使用的是硬解码,所以视频播放对CPU不会有很大的压力,但是支持的播放格式比较单一,一般就是MP4、MOV、M4V这几个。HTTP Live Streaming HTTP Live Streaming(缩写是 HLS)是一个由苹果公司提出的基于HTTP的流媒体网络传输协议。它的工作原理是把整个流分成一个个小的基于HTTP的文件来下载,每次只下载一些。当媒体流正在播放时,客户端可以选择从许多不同的备用源中以不同的速率下载同样的资源,允许流媒体会话适应不同的数据速率。支持的视频流编码为H.264。我们在视频网站上看到的M3U8后缀的播放链接就是使用HLS协议的视频。HLS优点,1、看完一段缓存一段,防止只看一段视频但是把整个视频文件都缓存下来的用户,减少服务器压力和节省流量。2、根据用户网速切换不同的码率,兼顾流程性和清晰度。播放 实现视频播放的两个方案。
一、自己实现对数据编码解码 可以在一些开源播放器上进行二次开发,如Bilibili的ijkplayer ,或者直接对FFmpeg 开发,优点在整个播放过程可控,为后续进行缓存、流量控制、码率切换等开发提供了基础,缺点是复杂,要求高,工程量大。
二、AVFoundation Media Assets, Playback and Editing. 使用Apple自有框架。
AVAsset AVAsset is an abstract, immutable class used to model timed audiovisual media such as videos and sounds. An asset may contain one or more tracks that are intended to be presented or processed together, each of a uniform media type, including but not limited to audio, video, text, closed captions, and subtitles.
Audiovisual media的资源类,通常通过AVURLAsset用URL来实例化,可以用Atom Inspector (一个Apple提供的用来查看视频信息的工具)来观察一个视频的属性,再去AVAsset中对应其属性。
AVAsset属性 视频文件属性 duration: CMTime duration, timescale时长和时间尺度 preferredRate: Float 默认速度 preferredVolume: Float 默认音量 creationDate: AVMetadataItem? 视频创建时间 tracks: [AVAssetTrack] 轨道 trackGroups: [AVAssetTrackGroup] 轨道组 lyrics: String? 当前语言环境合适的歌词 metadata: [AVMetadataItem] 元数据
AVPlayer An AVPlayer is a controller object used to manage the playback and timing of a media asset. It provides the interface to control the player’s transport behavior such as its ability to play, pause, change the playback rate, and seek to various points in time within the media’s timeline. You can use an AVPlayer to play local and remote file-based media, such as QuickTime movies and MP3 audio files, as well as audiovisual media served using HTTP Live Streaming.
AVPlayer是一个控制对象用于管理媒体asset的播放,它提供了相关的接口控制播放器的行为,比如:播放、暂停、改变播放的速率、跳转到媒体时间轴上的某一个点(简单理解就是实现拖动功能显示对应的视频位置内容)。我们能够使用AVPlayer播放本地和远程的媒体文件(使用 HTTP Live Streaming),比如: QuickTime movies 和 MP3 audio files,所以AVPlayer可以满足音视频播放的基本需求。 AVFoundation的层次
AVPlayerItem AVPlayerItem models the timing and presentation state of an asset played by an AVPlayer object. It provides the interface to seek to various times in the media, determine its presentation size, identify its current time, and much more.
AVPlayerItem是一个负责处理AVAsset的资源并通过AVPlayer来播放的载体,提供了seek、确定显示大小、ID、时间等的接口。
AVPlayerLayer AVPlayerLayer is a subclass of CALayer to which an AVPlayer object can direct its visual output. It can be used as the backing layer for a UIView or NSView or can be manually added to the layer hierarchy to present your video content on screen.
负责AVPlayer的视频输出展示。
依赖关系图
简单使用 1
2
3
4
5
6
7
8
9
10
11
class AVPlayerTestView: UIView {
let view: UIView? = nil
func initPlayerView() {
guard let url = URL.init(string: "http://meipu1.video.meipai.com/5e81c08e-2850-4fbd-bfc4-4ded297f9f1c.mp") else { return }
let asset = AVAsset.init(url: url)
let item = AVPlayerItem.init(asset: asset)
let player = AVPlayer.init(playerItem: item)
let playerLayer = AVPlayerLayer.init(layer: player)
view?.layer.addSublayer(playerLayer)
}
}
设置好一个AVPlayer的依赖关系和输出图层后,AVPlayerItem会根据你的URL去请求数据,自己内部做缓冲然后播放。我们需要做的是用KVO监听AVPlayerItem内部几个关键属性的状态,然后做出我们的处理。
AVPlayerItem属性 状态 status: AVPlayerItemStatus .unknown: AVPlayerItemStatus 未知状态 .readyToPlay: AVPlayerItemStatus 准备好去播放 .failed: AVPlayerItemStatus 资源无法被播放 loadedTimeRanges: [NSValue] 加载了的资源的时间范围(一般用来更新缓冲UI) playbackBufferEmpty: Bool 没有缓冲数据 playbackLikelyToKeepUp: Bool 有足够的缓冲大致能播放无卡顿
General State Observations: You can use Key-value observing (KVO) to observe state changes to many of the player’s dynamic properties, such as its currentItem or its playback rate. You should register and unregister for KVO change notifications on the main thread. This avoids the possibility of receiving a partial notification if a change is being made on another thread. AVFoundation invokes observeValue(forKeyPath:of:change:context:) on the main thread, even if the change operation is made on another thread.
基本状态观察者:你能够使用KVO来观察player动态属性的状态改变,比如像: currentItem 或者它的播放速度。我们应该在主线程注册和去除KVO,这能够避免如果在其它线程发送改变而导致接收局部通知,当发生通知,AVFoundation将在主线程调用observeValue(forKeyPath:of:change:context:) 方法,即使是在其他线程发生。
KVO能够很好的观察生成的状态,但是并不能够观察播放时间的改变,所以AVPlayer提供了两个方法来观察时间的改变:
1
2
3
4
5
6
7
8
9
10
11
/*
@param interval
调用block的时间间隔
@param queue
推荐使用串行队列,放在主线程就行了,并行队列会产生不明确的结果
*/
func addPeriodicTimeObserver(forInterval interval: CMTime, queue: DispatchQueue?, using block: @escaping (CMTime) -> Void) -> Any {
// 可以在里面去设置控制状态,刷新进度UI
}
func addBoundaryTimeObserver(forTimes times: [NSValue], queue: DispatchQueue?, using block: @escaping () -> Void) -> Any
Tips:
创建多个AVPlayerLayer只有最近的layer才会显示视频帧 可以创建多个AVPlayerItem来替换AVPlayer的当前item,func replaceCurrentItem(with item: AVPlayerItem?) 监听后要注意控制监听的生命周期 缓存 Apple自有的框架是没有提供缓存功能的,AVPlayer也没有提供直接获取其下载数据的接口,所以想做缓存只能自己来完整的实现。下面有几个方案。
一、自己实现的播放器 这种情况大多是根据下载来的数据解码播放,下载的时候做下缓存就好了
二、自带播放器+LocalServer 在iOS本地开启Local Server服务,然后MPMoviePlayerController请求本地Local Server服务。本地Local Server服务再不停的去对应的视频地址获取视频流。本地Local Server请求的时候,就可以把视频流缓存在本地。Demo来源:Code4App
三、AVPlayer+AVMutableComposition+AVAssetExportSession 原理是直接给AVPlayer传URL,让其内部自己去处理数据下载,然后通过AVMutableComposition和AVAssetExportSession从AVAsset提取视频的数据进行缓存。
AVMutableComposition AVMutableComposition is a mutable subclass of AVComposition you use when you want to create a new composition from existing assets. You can add and remove tracks, and you can add, remove, and scale time ranges.
作用是从现有的AVAsset中创建出一个新的AVComposition,使用者能够从别的asset中提取他们的音频轨道或视频轨道,并且把它们添加到新建的composition中。
AVAssetExportSession An AVAssetExportSession object transcodes the contents of an AVAsset source object to create an output of the form described by a specified export preset.
作用是把AVAsset解码输出到本地文件中。
关键需要把原先的AVAsset(AVURLAsset)实现的数据提取出来后拼接成另一个AVAsset(AVComposition)的数据然后解码输出,由于通过网络url下载下来的视频没有保存视频的原始数据(苹果没有暴露接口给我们获取),下载后播放的avasset不能使用AVAssetExportSession输出到本地文件,要曲线地把下载下来的视频通过重构成另外一个AVAsset实例才能输出。
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
NSString *documentDirectory = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
NSString *myPathDocument = [documentDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.mp4",[_source.videoUrl MD5]]];
NSURL *fileUrl = [NSURL fileURLWithPath:myPathDocument];
if (asset != nil) {
AVMutableComposition *mixComposition = [[AVMutableComposition alloc]init];
AVMutableCompositionTrack *firstTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
[firstTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, asset.duration) ofTrack:[[asset tracksWithMediaType:AVMediaTypeVideo]objectAtIndex:0] atTime:kCMTimeZero error:nil];
AVMutableCompositionTrack *audioTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
[audioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, asset.duration) ofTrack:[[asset tracksWithMediaType:AVMediaTypeAudio]objectAtIndex:0] atTime:kCMTimeZero error:nil];
AVAssetExportSession *exporter = [[AVAssetExportSession alloc]initWithAsset:mixComposition presetName:AVAssetExportPresetHighestQuality];
exporter.outputURL = fileUrl;
if (exporter.supportedFileTypes) {
exporter.outputFileType = [exporter.supportedFileTypes objectAtIndex:0] ;
exporter.shouldOptimizeForNetworkUse = YES;
[exporter exportAsynchronouslyWithCompletionHandler:^{
}];
}
}
四、AVPlayer+AVAssetResourceLoader AVAssetResourceLoadingRequest An AVAssetResourceLoader object mediates resource requests from an AVURLAsset object with a delegate object that you provide. When a request arrives, the resource loader asks your delegate if it is able to handle the request and reports the results back to the asset.AVAssetResourceLoader协调来自AVURLAsset的资源请求,你需要实现它的delegate。当收到一个请求时,ResourceLoader询问你的delegate是否能处理并将结果返回给asset。
AVPlayer和AVAssetResourceLoader的层次结构
AVAssetResourceLoader通过你提供的委托对象去调节AVURLAsset所需要的加载资源。而很重要的一点是,AVAssetResourceLoader仅在AVURLAsset不知道如何去加载这个URL资源时才会被调用,就是说你提供的委托对象在AVURLAsset不知道如何加载资源时才会得到调用。一般我们可以更改URL的scheme用来隐藏真实的URL。如:
参考iOS开发系列–音频播放、录音、视频播放、拍照、视频录制 AVplayer实现播放本地和网络视频(Swift3.0) iOS视频流开发(2)—视频播放 iOS音频播放 (九):边播边缓存 iOS音视频实现边下载边播放 AVFoundation(二):核心AVAsset AVFoundation编程指南2-用AVPlayer播放视频 AV Foundation系列(五)媒体组合
]]>
+
+
+
+
+
+ <p>下面會介紹視頻的一些基本知識,和在iOS上實現視頻播放和緩存的幾種方案。</p>
+<blockquote>
+<ul>
+<li><strong>软解码和硬解码</strong><br>GPU解码就是所谓的硬解码,CPU解码就是软解码。iOS提供的播放器类使用的是硬解码,所以视
+
+
+
+
+
+
+
+
+
+
+
+
+ xcode常用快捷键及其他功能
+
+ http://codingdoge.cn/2017/07/23/title: xcode常用快捷键及其他功能/
+ 2017-07-23T15:47:00.000Z
+ 2018-01-13T05:05:53.756Z
+
+ 快捷键Command+R生成并运行程序 Command+B只生成程序而不运行它 Command+T新建选项卡
标记 // MARK: - \ // TODO: \ // FIXME: \
小功能 Product>Clean删除生成的中间文件
]]>
+
+
+
+
+
+ <h3 id="快捷键"><a href="#快捷键" class="headerlink" title="快捷键"></a>快捷键</h3><hr>
+<p>Command+R生成并运行程序<br>Command+B只生成程序而不运行它<br>Command+T新建选项卡 </
+
+
+
+
+
+
+
+
+
+
+
+
+ OC中ARC forbids explicit message send of '...'错误
+
+ http://codingdoge.cn/2017/07/23/title: OC中ARC forbids explicit message send of '...'错误 /
+ 2017-07-23T15:25:00.000Z
+ 2018-01-14T17:00:06.700Z
+
+ 转自CSDN hahahacff 有所整理
ARC forbids explicit message send of’retainCount’ 同’release’等等
很显然,是ARC 的问题。 错误原因:在创建工程的时候点选了“Use Automatic Reference Counting” 选项,但是又调用了对象的retainCount 方法 ARC是什么? ARC是iOS 5推出的新功能,全称叫ARC(AutomaticReferenceCounting)。 简单地说,就是代码中自动加入了retain/release,原先需要手动添加的用来处理内存管理的引用计数的代码可以自动地由编译器完成了。该机制在iOS 5/ Mac OS X 10.7 开始导入,利用 Xcode4.2可以使用该机制。简单地理解ARC,就是通过指定的语法,让编译器(LLVM3.0)在编译代码时,自动生成实例的引用计数管理部分代码。有一点,ARC并不是GC,它只是一种代码静态分析(StaticAnalyzer)工具。
解决方法 选择要项目,双击中间的工程名称,进入build setting
将中间的Objective-C Automatic Reference Counting改为no ]]>
+
+
+
+
+
+ <blockquote>
+<p>转自<a href="http://blog.csdn.net/hahahacff/article/details/39859901" target="_blank" rel="noopener"><strong>CSDN hahahacff</s
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 解决Arduino CH34x系列在macOS Sierra中找不到串口的问题
+
+ http://codingdoge.cn/2017/07/23/title: 解决Arduino CH34x系列在macOS Sierra中找不到串口的问题/
+ 2017-07-23T15:06:00.000Z
+ 2018-01-12T14:23:28.810Z
+
+ 毕设做Arduino开发,一开始买了淘宝上慧净自己改的Arduino UNO国产板子,回来插在mac上读不出串口,安装了它附带的驱动还是读不出,同学买了原装板说一插上就能读出串口,都没自己装驱动,为了省麻烦,直接跟商家换了原装板,这时候麻烦来了,板子在我电脑上读不出串口,在别人电脑上能读出来,别人的在我电脑上也读不出来…很崩溃,然后从各个角度debug,下面一个个步骤来,基本能解决。
安装Arduino IDE 上面是官网IDE下载的连接,大概是因为GFW的原因下载速度奇慢,大家也可以去搜Arduino中文社区,里面有好人做了*度网盘的下载种子,速度能快点。 IDE一般自带驱动,如果在串口里没发现,可以试试在 D7389412-ED23-427D-82E7-4E1C26114D67
这个系统报告里看看USB下面能不能读出
也可以在bash里输入
如果有类似的即可 2C66575E-D0DD-4DEA-9F34-8F2E1CF4DA90
官方驱动下载 如果都找不到的话可以去沁恒官方下载CH341SER驱动,安装后再查看一遍(安装驱动完会要求重启).
更改SIP设置
Apple在10.11中全面启用了名为 System Integrity Protection (SIP) 的系统完整性保护技术. 受此影响, 未经签名的第三方kext及经过修改的原版kext将无法正常加载, 大部分系统文件即使在root用户下也无法直接进行修改.
前面安装驱动不成功大部分是因为驱动文件冲突导致,所以在此之前先删除之前安装的驱动文件:
1
2
3
sudo rm -rf /System/Library/Extensions/usb.kext
sudo rm -rf /Library/Extensions/usbserial.kext
sudo rm -rf _private_var_db_receipts/com.wch .*
然后参考CH340 CH341 serial adapters fix for El Capitan OS X 可以通过以下步骤修改SIP设置来安装第三方kext:
重启OS X 并且立即按住 Command+ R 来来进入恢复模式 在恢复模式中, 菜单栏上面找到终端(Terminal)并打开 在终端中键入命令csrutil enable —without kext 看到成功的提示信息之后, 输入reboot重启系统注: —without kext 中的 - 有两条 Command 就是 ⌘ 图标
通过以上的操作之后, 采用CH340/1 系列芯片的Arduino开发板又可以被识别出来了. 如果还是无法识别, 请重新安装一次CH341SER驱动.
最后如果还不行(我的就是到这里还不行),请看看你的板子是否接触不良,反复摩擦尝试看能不能读出串口,我之前也试过这个办法,但是没有用,后面偶然也是必然下反应过来,反反复复插USB拔出查看串口状态,终于解决了(无良商家害人) 参考:如何重置 Mac 上的 NVRAM
]]>
+
+
+
+
+
+ <blockquote>
+<p>毕设做Arduino开发,一开始买了淘宝上慧净自己改的Arduino UNO国产板子,回来插在mac上读不出串口,安装了它附带的驱动还是读不出,同学买了原装板说一插上就能读出串口,都没自己装驱动,为了省麻烦,直接跟商家换了原装板,这时候麻烦来了,板
+
+
+
+
+
+
+
+
+
+
+
+
+ 《TCP/IP详解》笔记—第1章 概述
+
+ http://codingdoge.cn/2017/07/23/title: 《TCP-IP详解》笔记—第1章 概述/
+ 2017-07-23T15:04:00.000Z
+ 2018-01-12T14:22:22.232Z
+
+ 《TCP/IP详解》笔记—第1章 概述 一个互连网就是一组通过相同协议族互连在一起的网络。
分层 TCP/IP通常被认为是一个四层协议系统: WX20170320-171317@2x
网络层和运输层之间的区别是最为关键的:网络层(IP)提供点到点的服务,而运输层(TCP和UDP)提供端到端的服务。
在TCP/IP协议族中,网络层IP提供的是一种不可靠的服务。也就是说,它只是尽可能快地把分组从源结点送到目的结点,但是并不提供任何可靠性保证。而另一方面,TCP在不可靠的IP层上提供了一个可靠的运输层。为了提供这种可靠的服务,TCP采用了超时重传、发送和接收端到端的确认分组等机制。
路由器为不同类型的物理网络提供连接。
一个主机也可以有多个接口,但一般不称作路由器,除非它的功能只是单纯地把分组从一个接口传送到另一个接口。
互联网的目的之一是在应用程序中隐藏所有的物理细节。
连接网络的另一个途径是使用网桥。网桥是在链路层上对网络进行互连,而路由器则是在网络层上对网络进行互连。
互联网的地址 47F26D90-176D-4C0A-A315-6A9E199E79F6
这些 32 位的地址通常写成四个十进制的数,其中每个整数对应一个字节。这种表示方法称作“点分十进制表示法(Dotted decimal notation)”。 70807FBE-712D-4A5C-97F2-BBCA12C1F11D
封装 TCP传给IP的数据单元称作TCP报文段或简称为TCP段(TCP segment)。IP传给网络接口层的数据单元称作IP数据报(IP datagram)。通过以太网传输的比特流称作帧(Frame)。
分用 当目的主机收到一个以太网数据帧时,数据就开始从协议栈中由底向上升,同时去掉各层协议加上的报文首部。每层协议盒都要去检查报文首部中的协议标识,以确定接收数据的上层协议。这个过程称作分用(Demultiplexing)。
客户-服务器模型 大部分网络应用程序在编写时都假设一端是客户,另一端是服务器,其目的是为了让服务器为客户提供一些特定的服务。 一般来说,TCP服务器是并发的,而UDP服务器是重复的。
习题 请计算最多有多少个A类、B类和C类网络号。 用匿名FTP(见27.3节)从主机nic.merit.edu上获取文件nsfnet_statistics_history.netcount。该文件包含在NSFNET网络上登记的国内和国外的网络数。画一坐标系,横坐标代表年,纵坐标代表网络总数的对数值。纵坐标的最大值是习题1.1的结果。如果数据显示一个明显的趋势,请估计按照当前的编址体制推算,何时会用完所有的网络地址(3.10节讨论解决该难题的建议)。 获取一份主机需求RFC 拷贝[Braden 1989a],阅读有关应用于TCP/IP协议族每一层的稳健性原则。这个原则的参考对象是什么? 获取一份最新的赋值RFC 拷贝。“quote of the day”协议的有名端口号是什么?哪个RFC 对该协议进行了定义? 如果你有一个接入TCP/IP互联网的主机帐号,它的主IP地址是多少?这台主机是否接入了Internet?它是多接口主机吗? 获取一份RFC 1000的拷贝,了解RFC这个术语从何而来。 与Internet 协会联系,isoc@isoc.org或者+170 3648 9888 ,了解有关加入的情况。 用匿名FTP从主机is.internic.net处获取文件about-internic/information-about-the-internic。 部分习题答案 答案是:27-2(126)+214-2(16 382)+221-2(2 097 150)=2 113 658。每一部分都减去2是因为全0或全1网络ID是非法的。 图D-1显示了直到1993年8月的有关数据。如果网络数继续呈指数增长的话,虚线估计了2000年可能达到的最大的网络数。 “自由地接收,保守地发送。” 资料来源于 即时通讯网 ,仅做学习参考
]]>
+
+
+
+
+
+ <h1 id="《TCP-IP详解》笔记—第1章-概述"><a href="#《TCP-IP详解》笔记—第1章-概述" class="headerlink" title="《TCP/IP详解》笔记—第1章 概述"></a><a href="http://docs.52im.net
+
+
+
+
+
+
+
+
+
+
+
+
+ STL常见用法 — map
+
+ http://codingdoge.cn/2017/07/21/title: STL常见用法 — map/
+ 2017-07-21T05:10:00.000Z
+ 2018-01-14T17:04:04.187Z
+
+ STL常见用法 —代码敲得少,STL总是用的不熟,便自己整理一些用法,有眼缘的顺手用着吧。
简介 maps是一个关联容器,用来存储key-value式的元素,提供一对一hash。内部的实现,自建一颗红黑树,这棵树具有对数据自动排序的功能。比如一个班级中,每个学生的学号和他的姓名就存在一对一映射的关系。
第一个值是关键字(key),每个关键字只能在map中出现一次 第二个值为关键字的值(value)
用法 头文件 1
2
3
4
5
6
7
8
#include <map>
template<
class Key,
class T,
class Compare = std::less<Key>,
class Allocator = std::allocator<std::pair<const Key, T> >
> class map;
构造函数 1
2
3
4
5
namespace pmr {
template <class Key, class T, class Compare = std::less<Key>>
using map = std::map<Key, T, Compare,
std::pmr::polymorphic_allocator<std::pair<const Key,T>>>
}
成员函数 成员函数 实现操作 begin 返回一个起始的迭代器 end 返回一个末尾的迭代器 empty 检查容器是否为空 size 返回容器内元素个数 clear 清除容器内容 insert 插入元素或节点 erase 删除元素 swap 交换内容 count 返回指定key的元素个数 find 通过key查找元素
用法演示 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <map>
void mapTest()
{
std::map<int, int> m; // 构造函数
m.insert(pair<int, int>(1, 10)); // 插入元素
m.insert(pair<int, int>(2, 30));
m.insert(pair<int, int>(4, 50));
map<int, int>::iterator it; // 迭代器
it = m.find(3); // 通过key查找元素
if (it == m.end()) // 如果返回值为尾部迭代器则无此元素
{
cout << "Not find" << endl;
m.insert(pair<int, int>(3, 20));
}
for (it = m.begin(); it != m.end(); it++) // 通过迭代器遍历map容器
{
cout << it->first << " " << it->second << endl; // 通过迭代器访问元素的key-value
}
}
参考:C/C++ - Map (STL) 用法與心得完全攻略
]]>
+
+
+
+
+
+ <h1 id="STL常见用法-—"><a href="#STL常见用法-—" class="headerlink" title="STL常见用法 —"></a>STL常见用法 —</h1><blockquote>
+<p>代码敲得少,STL总是用的不熟,便自己整理一些用法,有眼缘
+
+
+
+
+
+
+
+
+
+
+
+
+ STL常见用法 — vector
+
+ http://codingdoge.cn/2017/07/21/title: STL常见用法 — vector/
+ 2017-07-21T05:06:00.000Z
+ 2018-01-14T17:04:41.751Z
+
+ 代码敲得少,STL总是用的不熟,便自己整理一些用法,有眼缘的顺手用着吧。
简介 vector是线性容器,它的元素严格的按照线性序列排序,和动态数组很相似,和数组一样,它的元素存储在一块连续的存储空间中,这也意味着我们不仅可以使用迭代器(iterator)访问元素,还可以使用指针的偏移方式访问,和常规数组不一样的是,vector能够自动存储元素,可以自动增长或缩小存储空间。
用法 头文件 构造函数 1
2
3
4
5
6
7
8
template <class T , class Allocator = std : :allocator<T> > class vector ;
namespace pmr {
template <class T >
using vector = std ::vector <T, std ::pmr::polymorphic_allocator<T>>;
}
std::vector is a sequence container that encapsulates dynamic size arrays. std::pmr::vector is an alias template that uses a polymorphic allocator 成员函数 成员函数 实现操作 c.assign(beg,end) 将[beg; end)区间中的数据赋值给c c.assign(n,elem) 将n个elem的拷贝赋值给c c.at(idx) 传回索引idx所指的数据,如果idx越界,抛出out_of_range c.back() 传回最后一个数据,不检查这个数据是否存在 c.begin() 传回迭代器重的可一个数据 c.capacity() 返回容器中数据个数 c.clear() 移除容器中所有数据 c.empty() 判断容器是否为空 c.end() 指向迭代器中的最后一个数据地址 c.erase(pos) 删除pos位置的数据,传回下一个数据的位置 c.erase(beg,end) 删除[beg,end)区间的数据,传回下一个数据的位置 c.front() 传回第一个数据 get_allocator 使用构造函数返回一个拷贝 c.insert(pos,elem) 在pos位置插入一个elem拷贝,传回新数据位置 c.insert(pos,n,elem) 在pos位置插入n个elem数据。无返回值 c.insert(pos,beg,end) 在pos位置插入在[beg,end)区间的数据。无返回值 c.max_size() 返回容器中最大数据的数量 c.pop_back() 删除最后一个数据 c.push_back(elem) 在尾部加入一个数据 c.rbegin() 传回一个逆向队列的第一个数据 c.rend() 传回一个逆向队列的最后一个数据的下一个位置 c.resize(num) 重新指定队列的长度 c.reserve() 保留适当的容量 c.size() 返回容器中实际数据的个数 c1.swap(c2) 将c1和c2元素互换 swap(c1,c2) 同上操作 vector c 创建一个空的vector vector c1(c2) 复制一个vector vector c(n) 创建一个vector,含有n个数据,数据均已缺省构造产生 vector c(n, elem) 创建一个含有n个elem拷贝的vector。 vector c(beg,end) 创建一个以[beg;end)区间的vector c.~ vector () 销毁所有数据,释放内存
用法演示 使用reverse将元素翻转: 需要头文件#include<algorithm> 1
reverse(vec .begin(),vec.end())
将元素翻转(在vector中,如果一个函数中需要两个迭代器,一般后一个都不包含)
使用sort排序: 需要头文件#include, sort(vec.begin(),vec.end());(默认是按升序排列,即从小到大). 可以通过重写排序比较函数按照降序比较,如下: 定义排序比较函数:
1
2
3
4
bool Comp (const int &a,const int &b)
{
return a>b;
}
调用时:sort(vec.begin(),vec.end(),Comp),这样就降序排序。
]]>
+
+
+
+
+
+ <blockquote>
+<p>代码敲得少,STL总是用的不熟,便自己整理一些用法,有眼缘的顺手用着吧。 </p>
+</blockquote>
+<hr>
+<h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介<
+
+
+
+
+
+
+
+
+
+
+
+
+ 常用宏定义
+
+ http://codingdoge.cn/2017/07/17/title: 常用宏定义/
+ 2017-07-17T06:08:00.000Z
+ 2018-01-13T05:07:28.710Z
+
+ iOS开发高级:使用宏定义macros 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
#ifdef DEBUG
# define DLog(...) NSLog(__VA_ARGS__)
#else
# define DLog(...)
#endif
#define kScreenWidth ([UIScreen mainScreen].bounds.size.width)
#define kScreenHeight ([UIScreen mainScreen].bounds.size.height)
#define RGB(r, g, b, a) [UIColor colorWithRed:r/255.0f green:g/255.0f blue:b/255.0f alpha:a]
#define HEXCOLOR(c) [UIColor colorWithRed:((c>>16)&0xFF)/255.0f green:((c>>8)&0xFF)/255.0f blue:(c&0xFF)/255.0f alpha:1.0f]
#define BACKGROUND_COLOR [UIColor colorWithRed:242.0/255.0 green:236.0/255.0 blue:231.0/255.0 alpha:1.0]
#define CLEARCOLOR [UIColor clearColor]
#define LOADIMAGE(file,type) [UIImage imageWithContentsOfFile:[[NSBundle mainBundle]pathForResource:file ofType:type]]
#define NavigationBar_HEIGHT 44
#define IOS_VERSION [[[UIDevice currentDevice] systemVersion] floatValue]
#define CurrentSystemVersion [[UIDevice currentDevice] systemVersion]
#if TARGET_OS_IPHONE
#endif
#if TARGET_IPHONE_SIMULATOR
#endif
#define VIEWWITHTAG(_OBJECT, _TAG) [_OBJECT viewWithTag : _TAG]
#define BACK(block) dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block)
#define MAIN(block) dispatch_async(dispatch_get_main_queue(),block)
#define USER_DEFAULT [NSUserDefaults standardUserDefaults]
]]>
+
+
+
+
+
+ <p><a href="http://blog.csdn.net/songrotek/article/details/8929963" target="_blank" rel="noopener">iOS开发高级:使用宏定义macros</a></p>
+<figure class
+
+
+
+
+
+
+
+
+
+
+
+
+ STL常见用法 — queue
+
+ http://codingdoge.cn/2017/07/17/title: STL常见用法 — queue/
+ 2017-07-17T03:19:00.000Z
+ 2018-01-14T17:04:46.168Z
+
+ 代码敲得少,STL总是用的不熟,便自己整理一些用法,有眼缘的顺手用着吧。
简介 队列(queue)是一种特殊的线性表,是一种先进先出(First In First Out)的数据结构,允许在队列末尾插入元素,队列头取出元素,在STL中是用list或者deque实现,封闭头部即可。
用法 头文件 构造函数 1
template <class T , class Container = deque <T> > class queue ;
队列适配器默认用deque容器实现,也可以指定使用list容器来实现
1
2
3
queue <Elem> q;
queue <Elem, list <Elem> > q;
queue <Elem> q1(q2);
成员函数 成员函数 实现操作 Elem& back() 返回队列最后一个元素 bool empty()const 如果队列为空,返回true,否则返回false Elem& front() 返回队列第一个元素 void pop() 移除队列中的第一个元素 void push(const Elem& e) 在队列末尾插入元素e size_type size()const 返回队列中的元素数目
用法演示 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#include <queue>
using namespace std ;
int main ()
{
int n, m, size;
queue <int > q;
q.push(1 );
q.push(2 );
while (!q.empty())
{
n = q.front();
m = q.back();
size = q.size();
q.pop();
printf ("%d %d %d\n" , n, m, size);
}
return 0 ;
}
]]>
+
+
+
+
+
+ <blockquote>
+<p>代码敲得少,STL总是用的不熟,便自己整理一些用法,有眼缘的顺手用着吧。 </p>
+</blockquote>
+<hr>
+<h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介<
+
+
+
+
+
+
+
+
+
+
+
+
+ 插件管理——Vundle
+
+ http://codingdoge.cn/2017/07/17/title: 插件管理——Vundle/
+ 2017-07-17T03:18:00.000Z
+ 2018-01-14T17:19:40.848Z
+
+ 都说Vim是程序员写给自己的编辑器,其中的情结可想而知。
vim因为其庞大而强劲的插件受到无比的推崇,而插件的查找和管理便成了一个问题。
Vundle 便是一个Github 上为了解决这个问题的项目(致敬贡献者们),使用步骤如下:
安装Vundle,在终端输入以下代码即可 git clone http://github.com/gmarik/vundle.git ~/.vim/bundle/vundle
~/.vim 来自哪? 在mac 中Vim 配置文件.vimrc 在/usr/share/vim/下,一般是没有权限更改的,但是这个.vimrc是全局配置文件,我们只要更改用户配置文件即可 怎么查看/usr/ ?显示隐藏文件 ,自行百度,终端中敲入代码即可。Finder 下或者桌面 上的Go,文件夹输入/usr/。mac 下的Vim用户配置文件默认是没有的,需要我们自行创建 终端下输入1
2
3
> mkdir ~/.vim
> touch ~/.vimrc
>
~/即为用户根目录。
在.vimrc文件中加一句Bundle plugin_name.vimrc示例:1
2
3
4
5
6
7
8
9
10
11
set nocompatible
filetype off
set rtp+=~/.vim /bundle/vundle/
call vundle#rc()
Bundle 'gmarik/vundle'
Bundle 'vim-plugin-foo'
Bundle 'vim-plugin-bar'
filetype plugin indent on
执行Vundle安装命令 Vundle的其它命令 :BundleList列举出列表中(.vimrc中)配置的所有插件 :BundleInstall安装列表中全部插件 :BundleInstall!更新列表中全部插件 :BundleSearch foo查找foo插件 :BundleSearch! foo刷新foo插件缓存 :BundleClean清除列表中没有的插件 :BundleClean!清除列表中没有的插件
参考Git时代的VIM不完全使用教程 使用Vundle来管理vim的插件 zhongcq 的VIM配置
]]>
+
+
+
+
+
+ <blockquote>
+<p>都说Vim是程序员写给自己的编辑器,其中的情结可想而知。</p>
+</blockquote>
+<p>vim因为其庞大而强劲的插件受到无比的推崇,而插件的查找和管理便成了一个问题。</p>
+<p><a href="https://github.com
+
+
+
+
+
+
+
+
+
+
+
+
+ 漫谈iOS系列之:内存管理
+
+ http://codingdoge.cn/2017/07/17/title: 漫谈iOS系列之:内存管理/
+ 2017-07-17T03:18:00.000Z
+ 2018-01-14T14:05:07.599Z
+
+ 引用计数推荐一篇来自@杨萧玉的引用计数原理Blog
简介 iOS中对内存管理的机制(堆内存),每一个对象都有一个与之关联的引用计数(Reference Counting)。当一个对象“被拥有”的时候引用计数+1,当一个对象引用计数为零时该对象被释放。 比拟 比如上班,最早进入办公室的人需要开灯,之后进入办公室的人需要照明, 下班离开办公室的人不需要照明,最后离开办公室的人需要关灯。 这样对应的引用计数就是:第一个人进入办公室开灯,引用计数是1。之后进入办公室需要照明引用计数是2。下班一个人离开办公室引用计数变成了1,最后一个离开了办公室,引用计数变成了0 。 引用计数如何储存 TaggedPointer一篇极好的文章 总体来说,我的理解是如果一个对象使用了Tagged Pointer 技术(比如NSString ,NSNumber 等),指针里面会直接存数据内容,不会再作为“指针”指向其它地址,从Runtime来理解就是不会使用isa指针,也就不会继承苹果的内存管理方式(Reference Counting)。 判断当前对象是否在使用 TaggedPointer 是看标志位是否为1: 1
2
3
4
5
6
7
8
9
10
11
12
13
#if SUPPORT_MSB_TAGGED_POINTERS
# define TAG_MASK (1ULL<<63)
#else
# define TAG_MASK 1
inline bool
objc_object::isTaggedPointer()
{
#if SUPPORT_TAGGED_POINTERS
return ((uintptr_t )this & TAG_MASK);
#else
return false ;
#endif
}
isa 指针 指针的内存空间很大,有时候可以优化指针,在指针中存储一部分内容。下面列出不同架构下的64位环境中isa指针结构: 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
union isa_t
{
isa_t () { }
isa_t (uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
#if SUPPORT_NONPOINTER_ISA
# if __arm64__
# define ISA_MASK 0x00000001fffffff8ULL
# define ISA_MAGIC_MASK 0x000003fe00000001ULL
# define ISA_MAGIC_VALUE 0x000001a400000001ULL
struct {
uintptr_t indexed : 1 ;
uintptr_t has_assoc : 1 ;
uintptr_t has_cxx_dtor : 1 ;
uintptr_t shiftcls : 30 ;
uintptr_t magic : 9 ;
uintptr_t weakly_referenced : 1 ;
uintptr_t deallocating : 1 ;
uintptr_t has_sidetable_rc : 1 ;
uintptr_t extra_rc : 19 ;
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
};
# elif __x86_64__
# define ISA_MASK 0x00007ffffffffff8ULL
# define ISA_MAGIC_MASK 0x0000000000000001ULL
# define ISA_MAGIC_VALUE 0x0000000000000001ULL
struct {
uintptr_t indexed : 1 ;
uintptr_t has_assoc : 1 ;
uintptr_t has_cxx_dtor : 1 ;
uintptr_t shiftcls : 44 ;
uintptr_t weakly_referenced : 1 ;
uintptr_t deallocating : 1 ;
uintptr_t has_sidetable_rc : 1 ;
uintptr_t extra_rc : 14 ;
# define RC_ONE (1ULL<<50)
# define RC_HALF (1ULL<<13)
};
# else
# error unknown architecture
# endif
#endif
};
只有arm64 架构的设备支持优化,下面列出了isa指针中变量对应的含义:
变量名 含义 indexed 0 表示普通的isa指针,1 表示使用优化,存储引用计数 has_assoc 表示该对象是否包含 associated object,如果没有,则析构时会更快 has_cxx_dtor 表示该对象是否有 C++ 或 ARC 的析构函数,如果没有,则析构时更快 shiftcls 类的指针 magic 固定值为 0xd2,用于在调试时分辨对象是否未完成初始化 weakly_referenced 表示该对象是否有过weak对象,如果没有,则析构时更快 deallocating 表示该对象是否正在析构 has_sidetable_rc 表示该对象的引用计数值是否过大无法存储在isa指针 extra_rc 存储引用计数值减一后的结果
散列表 散列表来存储引用计数具体是用DenseMap类来实现,实现中有锁保证其安全性。 获取引用计数 在MRC环境下可以使用retainCount方法获取某个对象的引用计数。 在ARC环境下可以使用Core Foundation 库的CFGetRetainCount((__bridge CFTypeRef)(obj))方法和Runtime的_objc_rootRetainCount()方法来获取引用计数,也可以使用KVC技术来获取valueForKey:@"retainCount"。注意以上方法不是线程安全的。 注意 NSString 定义的对象是保存在字符串常量区,没有用引用计数管理内存,如果输出其retainCount,为-1。 retainCount
注意其中的Do not use this method 。
MRC(Manual Reference Counting) MRC从字面上理解就是手动管理引用计数,也就是手动管理内存。相关的内存管理方法有retain,release,autorelease,其中retain方法是对引用计数+1,相应的release是对引用计数-1,autorelease是将对象加入自动释放池,下文会讲到。
示例代码
1
2
3
4
5
6
7
// 以预定Person 类为例
Person * person = ; // 申请对象,此时引用计数=1
; //此时引用记数+1,现为2
; //引用计数-1,此时引用计数=1
; //引用计数-1,此时引用计数=0,内存被释放
; // 将对象加入自动释放池
Person *person = ; // 也可以在创建对象时将其加入自动释放池
按道理来说创建一个对象,然后release后该对象引用计数为零,但是实际情况中并不会出现这种现象,release后再输出其引用计数还是为1,在我的理解中有两种可能:
该对象在引用计数为1的时候进行release后,对象已经被释放,此时再调用retainCount毫无意义,因为该对象已经不存在了,为了防止某些错误保护这个retainCount方法所以编译器不会报错,但是输出值为释放前的值; 编译器为我们做了各种优化,也许是记录retainCount为零消耗过大或者没有意义。 重写了`dealloc`方便查看对象是否被释放
输出其`retainCount`然后释放
可以看到并不会出现引用计数为零的情况,但是该对象确实被释放了
小知识: 指针错误 :访问了一块坏的内存(已经被回收的,不可用的内存)。僵尸对象 :所占内存已经被回收的对象,僵尸对象不能再被使用。(打开僵尸对象检测)空指针 :没有指向任何东西的指针(存储的东西是0, null, nil),给空指针发送消息不会报错。注意 :不能使用[p retaion]让僵尸对象起死复生。
在MRC管理时代有一个黄金法则:
谁创建谁负责。如果你通过alloc,new,copy来创建了一个对象,那么你就必须调用release或者autorelease方法; 谁retain,谁release。只要你调用了retain,无论这个对象时如何生成的,你都要调用release; ARC 原理 前段编译器会为“拥有的”每一个对象加入相应的release语句,如果对象的所有权修饰符是__strong,那么它就是被拥有的。如果再某个方法内创建了一个对象,前端编译器会在方法末尾自动插入release语句已销毁它。而类拥有的对象(实例变量/属性)会在dealloc方法内被释放。 编译器所为
编译器为我们做的,我们可以手动完成达到优化 比如:__autoreleasing在ARC中主要用在参数传递返回值(out-parameters)和引用传递参数(pass-by-reference)的情况下,有这种指针(NSError **)的函数参数如果不加修饰符,编译器会默认将他们认定为__autoreleasing类型。 比如常用的NSError的使用:
1
2
3
4
5
NSError *__autoreleasing error ;
if (![data writeToFile:filename options:NSDataWritingAtomic error :&error ])
{
NSLog(@"Error: %@" , error );
}
如果你把error定义为了strong型,编译器会隐式地做如下事情,保证最终传入函数的参数依然是个__autoreleasing类型的引用。
1
2
3
4
5
6
7
NSError *error ;
NSError *__autoreleasing tempError = error ;
if (![data writeToFile:filename options:NSDataWritingAtomic error :&tempError])
{
error = tempError;
NSLog(@"Error :%@" , error );
}
所以为了提高效率,避免这种情况,我们一般在定义error的时候将其老老实实地声明为__autoreleasing类型。
循环引用 平常我们容易造成循环引用的三种情况:
NSTimer 先看NSTimer使用的代码:1
_timer = [NSTimer scheduledTimerWithTimeInterval: 1.0 target: self selector: @selector (runTimer) userInfo: nil repeats: YES];
其中_timer是实例变量被self保留,_timer的target是self,self被_timer保留,引发循环引用。 循环引用
解除方法就是使target中的对象不是viewController从而断开引用,iOS10之前我们可以写个类别重新封装target来实现,iOS10之后系统给了新方法:
1
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval )interval repeats:(BOOL )repeats block:(void (^)(NSTimer *timer))block;
不再需要target,而是传入一个block,在block里面进行循环调用方法 关于block怎么解决循环引用请看下面 block 简介 block和其他语言的闭包或lambda表达式是一回事,block的使用很像函数指针,不过与函数最大的不同是:block可以访问函数以外、词法作用域以内的外部变量的值。换句话说,block不仅实现函数的功能,还能携带函数的执行环境。
block基本语法
1
2
3
4
5
6
7
8
9
long (^sum ) (int , int ) = nil;
sum = ^ long (int a, int b) {
return a + b;
};
long s = sum (1 , 2 );
定义一个实例函数,该函数返回block:
1
2
3
4
5
6
7
8
- (long (^)(int , int )) sumBlock {
int base = 100 ;
return [[ ^ long (int a, int b) {
return base + a + b;
} copy] autorelease];
}
[self sumBlock ](1 ,2 );
根据在内存中的位置将block分为三种类型:
* `NSGlobalBlock`: 类似函数,位于text段;* `NSStackBlock`: 位于栈内存,函数返回后block将无效;* `NSMallocBlock`: 位于堆内存。block其实包含两个部分内容:
block执行的代码,这是在编译的时候已经生成好的; 一个包含block执行时需要的所有外部变量值的数据结构。 block将使用到的、作用域附近到的变量的值建立一份快照拷贝到栈上。 block的数据结构
对于 block 外的变量引用,block 默认是将其复制到其数据结构中来实现访问的: 传入外部变量
对于用 __block 修饰的外部变量引用,block 是复制其引用地址来实现访问的: 用__block修饰
初步了解了block后看看它怎么构成循环引用并怎么解决的吧
1
2
3
4
5
6
7
8
9
10
11
12
13
typedef void (^block)();
@property (copy , nonatomic ) block myBlock;
@property (copy , nonatomic ) NSString *blockString;
- (void )testBlock {
self .myBlock = ^() {
NSString *localString = self .blockString;
};
}
看了前面关于block的一些介绍应该容易看出来,当我们往block中传入数据时是保存在了block的堆中,如上述代码中引用了self相当于对self进行了一次retain,而self本身持有block于是造成了循环引用,同时在block中release``self没有用,因为在block中操作作用范围仅仅来block的函数栈,影响不到堆中的self,解决方法如下:
1
2
3
4
5
__weak typeof (self ) weakSelf = self ;
self .myBlock = ^(){
__strong typeof (weakSelf) = strongSelf;
NSString *localString = strongSelf;
}
其中传入一个若引用就不会造成循环引用,然后在block的函数栈中用一个强指针来接受传进来的弱指针,防止弱指针被提前释放产生野指针。
参考文章: Cooper – 正确使用Block避免Cycle Retain和Crash 唐巧 – 谈Objective-C block的实现 Dev Talking – Objective-C中的Block
delegate 我们对代理的写法一般都是:1
@property (nonatomic , weak ) id <TestDelegate> delegate;
如果使用strong的话很明显会造成循环引用(delegate调用self的一些东西),今天被面试官问道如果使用delegate出现了循环引用怎么解决,我说用weak,他说换一个,然后就懵住了,只回答了思路,找到互相引用的对象(可以用Instruments)然后断开引用。
Autorelease 简介 很好理解,字面意思上看就是自动释放,我们可以通过使用autorelease让编译器帮我们在某个时刻自动释放内存。在MRC时我们使用NSAutorelease类来使用自动释放机制,代码如下: 1
2
3
NSAutoreleasePool pool = [[NSAutoreleasePool alloc] init];
[pool release];
也可以直接使用[obj autorelease]。 现在基本上都是ARC环境,这个时候我们使用的是autoreleasepool(自动释放池),比如常见的:
1
2
3
4
5
6
7
8
9
10
11
12
13
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain (argc, argv, nil , NSStringFromClass ([AppDelegate class ]));
}
}
int main(int argc, const char *argv[]) {
@autoreleasepool {
}
return 0 ;
}
它的作用是把我们在{}中申请的对象在事件处理完时自动释放掉,其中的原理推荐阅读Qi Tang 的iOS 中的 Autorelease Pool 。 前面说到的事件处理完时其实就是一次runloop结束时。 runloop和autorelease
程序运行 -> 开启事件循环 -> 发生触摸事件 -> 创建自动释放池 -> 处理触摸事件 -> 事件对象加入自动释放池 -> 一次事件循环结束, 销毁自动释放池
1
2
3
4
5
6
7
8
9
10
NSArray *urls = <# An array of file URLs #>;
for (NSURL *url in urls) {
@autoreleasepool {
NSError *error;
NSString *fileContents = [NSString stringWithContentsOfURL:url
encoding:NSUTF8StringEncoding error:&error];
}
}
参考文章: sunnyxx —— 黑幕背后的Autorelease Jerry4me —— iOS中autorelease的那些事儿 tutuge —— @autoreleasepool-内存的分配与释放
修饰词 ]]>
+
+
+
+
+
+ <h2 id="引用计数"><a href="#引用计数" class="headerlink" title="引用计数"></a>引用计数</h2><p><a href="http://yulingtianxia.com/blog/2015/12/06/The-Principl
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 漫谈iOS系列之:多线程
+
+ http://codingdoge.cn/2017/07/17/title: 漫谈iOS系列之:多线程/
+ 2017-07-17T03:17:00.000Z
+ 2018-01-13T05:08:06.362Z
+
+ 线程基本概念 线程(thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。 同一进程中的多条线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等等。但同一进程中的多个线程有各自的调用栈(call stack),自己的寄存器环境(register context),自己的线程本地存储(thread-local storage)。线程的状态 :
新生态(New Thread) 可运行态(Runnable) 阻塞/非运行态(Not Runnable) 死亡态(Dead)
死锁 : 是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。
死锁条件 :
互斥条件:所谓互斥就是进程在某一时间内独占资源。 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。 不剥夺条件:进程已获得资源,在末使用完之前,不能强行剥夺。 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。 创建线程的开销 多线程的代价及上下文切换
pThread POSIX线程(POSIX Threads,常被缩写为Pthreads)是POSIX的线程标准,跨平台,适用于多种操作系统(类Unix操作系统中,都使用Pthreads作为操作系统的线程,Windows操作系统也有其移植版pthreads-win32),可移植性强,是一套纯C语言的通用API,且线程的生命周期需要程序员自己管理,使用难度较大,所以在实际开发中通常不使用。 Pthreads API中大致共有100个函数调用,全都以”pthread_”开头,并可以分为四类:
线程管理,例如创建线程,等待(join)线程,查询线程状态等。 互斥锁(Mutex):创建、摧毁、锁定、解锁、设置属性等操作。 条件变量(Condition Variable):创建、摧毁、等待、通知、设置与查询属性等操作。 使用了互斥锁的线程间的同步管理。 pThread在实际开发中基本不使用,所以大概了解下就好了。
NSThread GCD dispatch_barrier_async&dispatch_barrier_sync 在队列中,barrier块必须单独执行,不能与其他block并行。这只对并发队列有意义,并发队列如果发现接下来要执行的block是个barrier block,那么就一直要等到当前所有并发的block都执行完毕,才会单独执行这个barrier block代码块,等到这个barrier block执行完毕,再继续正常处理其他并发block。
async, sync两者区别在于async将自己的任务插入队列后, 不用等待自己的任务结束, 继续把后面的任务插入队列, 然后等待自己的任务运行结束才执行后面的任务, sync将自己的任务插入队列后,需要等待自己的任务运行结束才能将后面的任务插入队列。
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
#import <Foundation/Foundation.h>
@interface Person : NSObject
@property (nonatomic , copy ) NSString *name;
@end
#import "Person.h"
@interface Person ()
@end
static NSString *_name;
static dispatch_queue_t _concurrentQueue;
@implementation Person
- (instancetype )init
{
if (self = [super init]) {
_concurrentQueue = dispatch_queue_create("com.person.syncQueue" , DISPATCH_QUEUE_CONCURRENT);
}
return self ;
}
- (void )setName:(NSString *)name
{
dispatch_barrier_async(_concurrentQueue, ^{
_name = [name copy ];
});
}
- (NSString *)name
{
__block NSString *tempName;
dispatch_sync (_concurrentQueue, ^{
tempName = _name;
});
return tempName;
}
@end
NSOperation NSOperation默认是非并发的,当你调用-[NSOperation start]方法时,该方法会等任务结束才会返回; 并发的NSOperation是指,当你调用-[NSOperation start]后,NSOperation会在非当前线程(建立一个NSThread,或是dispatch async等)执行任务,并在任务结束之前就返回;
需要注意的是,并发行为都需要你自己实现,若要实现并发,你需要做很多额外的工作:
你需要创建一个subclass; 除了重载main方法,实现并发你还需要至少重载;start,isConcurrent,isExecuting,isFinished四个方法; 在start里,创建Thread或者调用一个异步函数; 更新isExecuting,并且发送相应KVO消息; 任务结束后,你还得更新isExecuting和isFinished,发送相应KVO消息。 实现一个并发的NSOperation比较少见,具体如何实现,可以读读文档: NSOperation Class Reference 大多数情况下NSOperation都设计成非并发,这样实现起来会简单很多; 并且,一般会配合NSOperationQueue使用,由NSOperationQueue来负责执行NSOperation,而非直接调用-[NSOperation start]。
若有复杂任务需要并发执行,一般也是拆成多个NSOperation,由NSOperationQueue来并发的执行多个NSOperation。
参考:关于iOS多线程,你看我就够了 dispatch_barrier_sync和dispatch_barrier_async iOS开发:深入理解GCD 第二篇(dispatch_group、dispatch_barrier、基于线程安全的多读单写) NSOperation的并发和非并发有什么区别呀?
]]>
+
+
+
+
+
+ <hr>
+<h1 id="线程基本概念"><a href="#线程基本概念" class="headerlink" title="线程基本概念"></a>线程基本概念</h1><p>线程(thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单
+
+
+
+
+
+
+
+
+
+
+
+
+ 剑指Offer —— 复杂链表的复制
+
+ http://codingdoge.cn/2017/07/06/title: 剑指Offer —— 复杂链表的复制/
+ 2017-07-06T02:44:00.000Z
+ 2018-01-14T17:25:14.000Z
+
+ 题目来源牛客网:复杂链表的复制
题目描述 输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
数据结构 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct RandomListNode {
int label;
struct RandomListNode *next, *random;
RandomListNode(int x) :
label(x), next(NULL), random(NULL) {
}
};
*/
class Solution {
public :
RandomListNode* Clone (RandomListNode* pHead) {
}
};
解题思路 一、递归思想:把大问题转化若干子问题 此题转化为一个头结点和除去头结点剩余部分,剩余部分操作和原问题一致
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
RandomListNode* Clone (RandomListNode* pHead)
{
if (pHead==NULL )
return NULL ;
RandomListNode* pClonedHead=new RandomListNode(pHead->label);
pClonedHead->next = pHead->next;
pClonedHead->random = pHead->random;
pClonedHead->next=Clone (pHead->next);
return pClonedHead;
}
二、 复制每个节点,如:复制节点A得到A1,将A1插入节点A后面 遍历链表,A1->random = A->random->next; 将链表拆分成原链表和复制后的链表 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
RandomListNode* Clone(RandomListNode* pHead)
{
if (!pHead) return NULL;
RandomListNode *currNode = pHead;
while (currNode){
RandomListNode *node = new RandomListNode(currNode-> label);
node ->next = currNode-> next;
currNode -> next = node;
currNode = node-> next;
}
currNode = pHead;
while (currNode){
RandomListNode *node = currNode-> next;
if (currNode-> random){
node ->random = currNode->random -> next;
}
currNode = node-> next;
}
RandomListNode *pCloneHead = pHead-> next;
RandomListNode *tmp;
currNode = pHead;
while (currNode-> next){
tmp = currNode-> next;
currNode ->next =tmp-> next;
currNode = tmp;
}
return pCloneHead;
}
三、哈希表法 时间空间复杂度都是O(n)
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
RandomListNode* Clone (RandomListNode* pHead)
{
if (pHead==NULL )
return NULL ;
unordered_multimap<RandomListNode*,RandomListNode*> table;
RandomListNode* pClonedHead=new RandomListNode(pHead->label);
pClonedHead->next=NULL ;
pClonedHead->random=NULL ;
table.insert(make_pair(pHead,pClonedHead));
RandomListNode* pNode=pHead->next;
RandomListNode* pClonedNode=pClonedHead;
while (pNode!=NULL )
{
RandomListNode* pClonedTail=new RandomListNode(pNode->label);
pClonedTail->next=NULL ;
pClonedTail->random=NULL ;
pClonedNode->next=pClonedTail;
pClonedNode=pClonedTail;
table.insert(make_pair(pNode,pClonedTail));
pNode=pNode->next;
}
pNode=pHead;
pClonedNode=pClonedHead;
while (pNode!=NULL )
{
if (pNode->random!=NULL )
{
pClonedNode->random=table.find(pNode->random)->second;
}
pNode=pNode->next;
pClonedNode=pClonedNode->next;
}
return pClonedHead;
}
]]>
+
+
+
+
+
+ <blockquote>
+<p>题目来源牛客网:<strong><a href="https://www.nowcoder.com/practice/f836b2c43afc4b35ad6adc41ec941dba?tpId=13&tqId=11178&tPage
+
+
+
+
+
+
+
+
+
+
+
+
+ 动态规划(Dynamic Programming)
+
+ http://codingdoge.cn/2017/05/13/title: 动态规划(Dynamic Programming)/
+ 2017-05-13T11:39:00.000Z
+ 2018-01-12T14:27:10.181Z
+
+ (以下简称DP)基本思想 将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。适合用DP求解的问题,经分解得到的子问题一般不是相互独立的,如果使用分治法求解,有些子问题会被重复计算多次,可以用一个表来记录所有已解决的子问题的答案,不管该子问题以后是否被用到,只要它被计算过,就将其结果填入表中。
DP算法适用于解最优化问题。通常可以按以下步骤设计:
找出最优解的性质,并刻画其结构特征; 递归地定义最优值; 以自底向上的方式计算出最优值; 根据计算最优值时得到的信息,构造最优解 基本要素 最优子结构 问题的最优解包含了其子问题的最优解。
重叠子问题 在用递归算法自顶向下求解问题时,每次产生的子问题并不总是新问题,有些子问题被计算多次。DP算法正式利用了这种子问题的重叠性质,对每一个子问题只解一次,而后将其解保存在一个表格中,当再次需要解此问题时,只是简单地用常数时间查看一下结果。通常不同的子问题个数随问题的大小呈多项式增长,因此用DP算法通常只需要多项式时间,从而获得较高的解题效率。
最长公共子序列 问题描述 一个给定序列的子序列是在该序列中删去若干元素后得到的序列。确切的说,若给定序列X = {x1, x2, ..., xm},则另一序列Z = {z1, z2, ..., zk},X的子序列是指存在一个严格递增下标序列{i1, i2, ..., ik}使得对于所有j = 1, 2, ..., k有zj = xij。例如,序列Z = {B, C, D, B}是序列X = {A, B, C, B, D, A, B}的子序列,相应的递增下标序列为{2, 3, 5, 7}。 给定两个序列X和Y,当另一序列Z既是X的子序列又是Y的子序列时,称Z是序列X和Y的公共子序列。 例如,若X = {A, B, C, B, D, A, B},Y = {B, D, C, A, B, A},序列{B, C, A}是X和Y的一个公共子序列,但它不是X和Y的最长公共子序列。序列{B, C, B, A}也是X和Y的一个公共子序列,它的长度为4,而且它是X和Y的最长公共子序列,因为X和Y没有长度大于4的公共子序列。
最长公共子序列问题 给定两个序列X = {x1, x2, ..., xm}和Y = {y1, y2, ..., ym},找出X和Y的最长公共子序列。
按照DP算法设计的各个步骤求解 最长公共子序列结构 设序列X = {x1, x2, ..., xm}和Y = {y1, y2, ..., yn}的最长公共子序列为Z = {z1, z2, ..., zk},则
若xm = yn,则zk = xm = yn,且Z 子问题的递归结构
计算最优值 构造最长公共子序列 算法的改进 ]]>
+
+
+
+
+
+ <p>(以下简称DP)</p>
+<hr>
+<h2 id="基本思想"><a href="#基本思想" class="headerlink" title="基本思想"></a>基本思想</h2><p>将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。
+
+
+
+
+
+
+
+
+
+
diff --git a/baidu_verify_pl2cC4IAHr.html b/baidu_verify_pl2cC4IAHr.html
new file mode 100644
index 0000000..4f75cfb
--- /dev/null
+++ b/baidu_verify_pl2cC4IAHr.html
@@ -0,0 +1,425 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ pl2cC4IAHr
+
+
+
+
+
+
+
CodingDoge
+
关于开发、设计,关于生活。
+
目前博客多作为知识点整理,所以有些文章奇奇怪怪(因为还没写完)...各位有缘的看官见笑了😝 恩 就酱...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 谢谢大爷~
+
+
+
+
+
+
+
+
+
+
+
+ 微信
+
+ 支付宝
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/categories/Algorithm/index.html b/categories/Algorithm/index.html
new file mode 100644
index 0000000..44baf4a
--- /dev/null
+++ b/categories/Algorithm/index.html
@@ -0,0 +1,588 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Categories: Algorithm | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-08-04
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-08-01
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-25
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-06
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/categories/Algorithm/page/2/index.html b/categories/Algorithm/page/2/index.html
new file mode 100644
index 0000000..d1d118e
--- /dev/null
+++ b/categories/Algorithm/page/2/index.html
@@ -0,0 +1,384 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Categories: Algorithm | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-05-13
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/categories/CPP/index.html b/categories/CPP/index.html
new file mode 100644
index 0000000..e731ef4
--- /dev/null
+++ b/categories/CPP/index.html
@@ -0,0 +1,452 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Categories: CPP | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-21
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-21
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-17
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/categories/Network/index.html b/categories/Network/index.html
new file mode 100644
index 0000000..753bc4c
--- /dev/null
+++ b/categories/Network/index.html
@@ -0,0 +1,384 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Categories: Network | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/categories/OS/index.html b/categories/OS/index.html
new file mode 100644
index 0000000..dd91cad
--- /dev/null
+++ b/categories/OS/index.html
@@ -0,0 +1,412 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Categories: OS | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-27
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/categories/Shell/index.html b/categories/Shell/index.html
new file mode 100644
index 0000000..0d9f300
--- /dev/null
+++ b/categories/Shell/index.html
@@ -0,0 +1,418 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Categories: Shell | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-08-30
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-17
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/categories/iOS/index.html b/categories/iOS/index.html
new file mode 100644
index 0000000..fe464be
--- /dev/null
+++ b/categories/iOS/index.html
@@ -0,0 +1,594 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Categories: iOS | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-08-30
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-08-20
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-08-03
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-08-03
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-08-02
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-25
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/categories/iOS/page/2/index.html b/categories/iOS/page/2/index.html
new file mode 100644
index 0000000..8174718
--- /dev/null
+++ b/categories/iOS/page/2/index.html
@@ -0,0 +1,588 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Categories: iOS | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-17
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/categories/iOS/page/3/index.html b/categories/iOS/page/3/index.html
new file mode 100644
index 0000000..19020dd
--- /dev/null
+++ b/categories/iOS/page/3/index.html
@@ -0,0 +1,424 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Categories: iOS | CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-17
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-17
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/categories/index.html b/categories/index.html
new file mode 100644
index 0000000..d40892d
--- /dev/null
+++ b/categories/index.html
@@ -0,0 +1,1426 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ CodingDoge | 关于开发、设计,关于生活。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
iOS
+
+
+
+
+
+
+
+
2017-08-20
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-08-30
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-08-02
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-08-03
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-25
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-17
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-17
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-17
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-08-03
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Shell
+
+
+
+
+
+
+
+
2017-08-30
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-17
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Algorithm
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-08-04
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-06
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-08-01
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-25
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-05-13
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
CPP
+
+
+
+
+
+
+
+
2017-07-21
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-17
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-21
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Network
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
OS
+
+
+
+
+
+
+
+
2017-07-27
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2017-07-23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
扫一扫,分享到微信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/content.json b/content.json
new file mode 100644
index 0000000..0fd4240
--- /dev/null
+++ b/content.json
@@ -0,0 +1 @@
+{"meta":{"title":"CodingDoge","subtitle":"关于开发、设计,关于生活。","description":"Keep it simple, stuip.","author":"CodingDoge","url":"http://codingdoge.cn"},"pages":[{"title":"","date":"2017-12-07T23:57:56.000Z","updated":"2017-12-07T23:03:58.000Z","comments":true,"path":"404.html","permalink":"http://codingdoge.cn/404.html","excerpt":"","text":"CodingDoge's blog | 404"},{"title":"","date":"2017-12-07T23:03:58.000Z","updated":"2017-12-07T23:03:58.000Z","comments":true,"path":"baidu_verify_pl2cC4IAHr.html","permalink":"http://codingdoge.cn/baidu_verify_pl2cC4IAHr.html","excerpt":"","text":"pl2cC4IAHr"},{"title":"About","date":"2017-07-20T04:10:23.000Z","updated":"2018-01-14T17:34:54.960Z","comments":true,"path":"about/index.html","permalink":"http://codingdoge.cn/about/index.html","excerpt":"","text":""},{"title":"","date":"2017-12-20T17:58:19.000Z","updated":"2017-12-20T17:58:19.000Z","comments":false,"path":"categories/index.html","permalink":"http://codingdoge.cn/categories/index.html","excerpt":"","text":""},{"title":"","date":"2017-12-07T23:03:58.000Z","updated":"2017-12-07T23:03:58.000Z","comments":true,"path":"search/index.html","permalink":"http://codingdoge.cn/search/index.html","excerpt":"","text":"layout: search title: search"},{"title":"","date":"2018-01-14T17:27:15.013Z","updated":"2017-12-20T17:56:35.000Z","comments":false,"path":"tags/index.html","permalink":"http://codingdoge.cn/tags/index.html","excerpt":"","text":""}],"posts":[{"title":"Advanced Swift 笔记 —— 內建集合類型","slug":"title: Advanced Swift 笔记 —— 內建集合類型","date":"2017-08-30T07:47:00.000Z","updated":"2018-01-13T12:22:39.908Z","comments":true,"path":"2017/08/30/title: Advanced Swift 笔记 —— 內建集合類型/","link":"","permalink":"http://codingdoge.cn/2017/08/30/title: Advanced Swift 笔记 —— 內建集合類型/","excerpt":"","text":"數組(Array)盡量不要使用下標索引,如果下標越界會直接導致crash(在並發情況下尤其需要考慮)。 迭代全部 1for x in array 迭代除了第一個元素以外的其餘部分 1for x in array.dropFirst() 迭代除了最後5個元素以外的數組 1for x in array.dropLast(5) 列舉數組中的元素和對應下標 1for (index, value) in array.enumerated() 尋找一個指定元素的位置 123_ = array.index { someMatchingLogic($0)} 對數組所有元素變形 123_ = array.map { someTransformation($0)} 篩選符合某個標準的元素 123_ = array.filter { someCriteria($0)}","categories":[{"name":"iOS","slug":"iOS","permalink":"http://codingdoge.cn/categories/iOS/"}],"tags":[{"name":"Reading Notes","slug":"Reading-Notes","permalink":"http://codingdoge.cn/tags/Reading-Notes/"}]},{"title":"Brew Instruction","slug":"title: Brew Instruction","date":"2017-08-30T06:26:00.000Z","updated":"2018-01-13T12:36:00.696Z","comments":true,"path":"2017/08/30/title: Brew Instruction/","link":"","permalink":"http://codingdoge.cn/2017/08/30/title: Brew Instruction/","excerpt":"","text":"Brew brew help 查看有什麼可用的指令 brew search [someSuit] 查詢有無someSuit brew install [someSuit] 安裝someSuit brew info [someSuit] 查看someSuit的訊息 brew uninstall [someSuit] 移除someSuit brew list 列出已安裝套件 brew outdated 查詢那些套件已過期 brew cleanup -n 刪除舊的套件版本(-n 是顯示刪除的過程,可以了解有哪些套件被刪除了) brew upgrade [someSuit] 更新someSuit brew update && brew upgrade && brew doctor 更新Homebrew上面所有套件 brew dips [someSuit] 列出安裝someSuit需要的依賴套件 brew —prefix 查詢brew安裝的路徑 參考:chiehwen/Homebrew 指令詳解.mdHomebrew 指令","categories":[{"name":"Shell","slug":"Shell","permalink":"http://codingdoge.cn/categories/Shell/"}],"tags":[{"name":"Tool","slug":"Tool","permalink":"http://codingdoge.cn/tags/Tool/"}]},{"title":"Advanced Swift 笔记 —— Tips","slug":"title: Advanced Swift 笔记 —— Tips","date":"2017-08-20T09:35:00.000Z","updated":"2018-01-13T12:23:01.178Z","comments":true,"path":"2017/08/20/title: Advanced Swift 笔记 —— Tips/","link":"","permalink":"http://codingdoge.cn/2017/08/20/title: Advanced Swift 笔记 —— Tips/","excerpt":"","text":"Curried Function 柯里化函數一个函数不是接受多个参数,而是只接受部分参数,然后返回一个接受其余参数的函数。 Statically Dispatched 靜態派發定義在類或者協議中的函數就是方法(Method),他們有一個隱式的self。不是方法的函數叫做自由函數(Free Function)。自由函数和那些在结构体上调用的方法是静态派发 (statically dispatched)的。对于这些函数的调用,在编译的时候就已经确定了。对于静态派发的调用,编译器可能能够内联 (inline)这些函数,也就是说,完全不去做函数调用,而是将这部分代码替换为需要执行的函数。静态派发还能够帮助编译器丢弃或者简化那些在编译时就能确定不会被实际执行的代码。","categories":[{"name":"iOS","slug":"iOS","permalink":"http://codingdoge.cn/categories/iOS/"}],"tags":[{"name":"Reading Notes","slug":"Reading-Notes","permalink":"http://codingdoge.cn/tags/Reading-Notes/"}]},{"title":"剑指Offer —— 二叉搜索树的第k个结点","slug":"title: 剑指Offer —— 二叉搜索树的第k个结点","date":"2017-08-04T01:56:00.000Z","updated":"2018-01-14T16:29:06.979Z","comments":true,"path":"2017/08/04/title: 剑指Offer —— 二叉搜索树的第k个结点/","link":"","permalink":"http://codingdoge.cn/2017/08/04/title: 剑指Offer —— 二叉搜索树的第k个结点/","excerpt":"","text":"题目来源:牛客网 题目描述给定一颗二叉搜索树,请找出其中的第k大的结点。例如, 5 / \\ 3 7 /\\ /\\ 2 4 6 8 中,按结点数值大小顺序第三个结点的值为4。 解题思路 递归 迭代way1中序遍历,找到第k个结点。完全性的模拟左根右,注意返回结果的处理。 TreeNode* KthNode(TreeNode* pRoot, int k) { if (pRoot) { TreeNode *node = KthNode(pRoot->left, k); if (node) return node; index++; if (index == k) return pRoot; node = KthNode(pRoot->right, k); if (node) return node; } return NULL; }","categories":[{"name":"Algorithm","slug":"Algorithm","permalink":"http://codingdoge.cn/categories/Algorithm/"}],"tags":[{"name":"剑指Offer","slug":"剑指Offer","permalink":"http://codingdoge.cn/tags/剑指Offer/"}]},{"title":"错误处理","slug":"title: 错误处理","date":"2017-08-03T02:47:00.000Z","updated":"2018-01-13T12:26:37.175Z","comments":true,"path":"2017/08/03/title: 错误处理/","link":"","permalink":"http://codingdoge.cn/2017/08/03/title: 错误处理/","excerpt":"","text":"错误处理(Error handling)是响应错误以及从错误中恢复的过程。Swift 提供了在运行时对可恢复错误的抛出、捕获、传递和操作的一等公民支持。 Swift 中有4种处理错误的方式 把函数抛出的错误传递给调用此函数的代码 用do-catch语句处理错误 将错误作为可选类型处理 或者断言此错误根本不会发生 Swift 中的错误处理并不涉及解除调用栈,这是一个计算代价高昂的过程。就此而言,throw语句的性能特性是可以和return语句相媲美的。 一个 throwing 函数可以在其内部抛出错误,并将错误传递到函数被调用时的作用域。 参考: The Swift Programming Language 中文版SWIFT 的必备 TIP","categories":[{"name":"iOS","slug":"iOS","permalink":"http://codingdoge.cn/categories/iOS/"}],"tags":[{"name":"Swift","slug":"Swift","permalink":"http://codingdoge.cn/tags/Swift/"}]},{"title":"Dispatch 在swift中的使用","slug":"title: Dispatch 在swift中的使用","date":"2017-08-03T01:49:00.000Z","updated":"2018-01-13T12:28:30.884Z","comments":true,"path":"2017/08/03/title: Dispatch 在swift中的使用/","link":"","permalink":"http://codingdoge.cn/2017/08/03/title: Dispatch 在swift中的使用/","excerpt":"","text":"创建队列当group里所有事件都完成GCD API有两种方式发送通知,第一种是dispatch_group_wait,会阻塞当前进程,等所有任务都完成或等待超时。第二种方法是使用dispatch_group_notify,异步执行闭包,不会阻塞。 1234567891011121314151617181920212223242526272829func downloadPhotosWithCompletion(completion: BatchPhotoDownloadingCompletionClosure?) { dispatch_async(GlobalUserInitiatedQueue) { // 因为dispatch_group_wait会租塞当前进程,所以要使用dispatch_async将整个方法要放到后台队列才能够保证主线程不被阻塞 var storedError: NSError! var downloadGroup = dispatch_group_create() // 创建一个dispatch group for address in [OverlyAttachedGirlfriendURLString, SuccessKidURLString, LotsOfFacesURLString] { let url = NSURL(string: address) dispatch_group_enter(downloadGroup) // dispatch_group_enter是通知dispatch group任务开始了,dispatch_group_enter和dispatch_group_leave是成对调用,不然程序就崩溃了。 let photo = DownloadPhoto(url: url!) { image, error in if let error = error { storedError = error } dispatch_group_leave(downloadGroup) // 保持和dispatch_group_enter配对。通知任务已经完成 } PhotoManager.sharedManager.addPhoto(photo) } dispatch_group_wait(downloadGroup, DISPATCH_TIME_FOREVER) // dispatch_group_wait等待所有任务都完成直到超时。如果任务完成前就超时了,函数会返回一个非零值,可以通过返回值判断是否超时。也可以用DISPATCH_TIME_FOREVER表示一直等。 dispatch_async(GlobalMainQueue) { // 这里可以保证所有图片任务都完成,然后在main queue里加入完成后要处理的闭包,会在main queue里执行。 if let completion = completion { // 执行闭包内容 completion(error: storedError) } } }} Serial Queues12345678910111213141516171819private let serialQueue = DispatchQueue(label: “serialQueue”)private var dictionary: [String: Any] = [:]public func set(_ value: Any, forKey key: String) { serialQueue.sync { dictionary[key] = value }}public func object(forKey key: String) -> Any? { var result: Any? serialQueue.sync { result = storage[key] } // returns after serialQueue is finished operation // beacuse serialQueue is run synchronously return result} Concurrent Queues1234567891011121314151617181920private let concurrentQueue = DispatchQueue(label: "concurrentQueue", attributes: .concurrent)private var dictionary: [String: Any] = [:] public func set(_ value: Any?, forKey key: String) { concurrentQueue.async { self.storage[key] = value }}public func object(forKey key: String) -> Any? { var result: Any? concurrentQueue.sync { result = storage[key] } // returns after concurrentQueue is finished operation // beacuse concurrentQueue is run synchronously return result} Concurrent Queue with Barrier1234567891011121314151617181920212223private let concurrentQueue = DispatchQueue(label: "concurrentQueue", attributes: .concurrent)private var dictionary: [String: Any] = [:] public func set(_ value: Any?, forKey key: String) { // .barrier flag ensures that within the queue all reading is done // before the below writing is performed and // pending readings start after below writing is performed concurrentQueue.async(flags: .barrier) { self.storage[key] = value }}public func object(forKey key: String) -> Any? { var result: Any? concurrentQueue.sync { result = storage[key] } // returns after concurrentQueue is finished operation // beacuse concurrentQueue is run synchronously return result} 延时1234567891011121314151617181920212223242526272829303132333435typealias Task = (_ cancel: Bool) -> Void func delay(time: TimeInterval, task: @escaping ()->()) -> Task? { func dispatch_later(block: @escaping ()->()) { DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()+time, execute: block) } var closure: (()->())? = task var result: Task? let delayedClosure: Task = { cancel in if let internalClosure = closure { if cancel == false { DispatchQueue.main.async { internalClosure() } } } closure = nil result = nil } result = delayedClosure dispatch_later { if let delayedClosure = result { delayedClosure(false) } } return result } func cancel(task: Task?) { task?(true) } 网络场景异步开启下载、取消下载,开始下载前保证没有遗留下载任务12345678910111213fileprivate let barrierQueue = DispatchQueue(label: \"com.meitu.meipu.videoCache.barrierQueue\")func downloader(url: URL) { barrierQueue.sync { [weak self] in // add downloader task }}func cancel() { barrierQueue.async(group: nil, qos: .default, flags: .barrier) { // cacle task }} 参考:GCD 在 Swift 3 中的玩儿法GCD 使用指南Swift 3 中的 GCD 与 Dispatch QueueSwift 3學習指南:重新認識GCD應用GCD精讲(Swift 3)","categories":[{"name":"iOS","slug":"iOS","permalink":"http://codingdoge.cn/categories/iOS/"}],"tags":[{"name":"Swift","slug":"Swift","permalink":"http://codingdoge.cn/tags/Swift/"}]},{"title":"Dispatch","slug":"title: Dispatch","date":"2017-08-02T12:01:00.000Z","updated":"2018-01-13T04:59:21.648Z","comments":true,"path":"2017/08/02/title: Dispatch/","link":"","permalink":"http://codingdoge.cn/2017/08/02/title: Dispatch/","excerpt":"","text":"Execute code concurrently on multicore hardware by submitting work to dispatch queues managed by the system. 在多核硬件上,通过提交任务到由这个系统管理的派遣队列上,并发的执行代码。 一种把保证回调会在主线程执行的方法: 12345678#ifndef dispatch_main_async_safe#define dispatch_main_async_safe(block)\\ if (strcmp(dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL), dispatch_queue_get_label(dispatch_get_main_queue())) == 0) {\\ block();\\ } else {\\ dispatch_async(dispatch_get_main_queue(), block);\\ }#endif 12345678910dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);dispatch_group_t group = dispatch_group_create();for(id obj in array) dispatch_group_async(group, queue, ^{ [self doSomethingIntensiveWith:obj]; });dispatch_group_wait(group, DISPATCH_TIME_FOREVER);dispatch_release(group); [self doSomethingWith:array]; 12345678910dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);dispatch_group_t group = dispatch_group_create();for(id obj in array) dispatch_group_async(group, queue, ^{ [self doSomethingIntensiveWith:obj]; });dispatch_group_notify(group, queue, ^{ [self doSomethingWith:array];});dispatch_release(group); dispatch_barrier_sync和dispatch_barrier_async common different 等待在它前面插入队列的任务先执行完 dispatch_barrier_sync将自己的任务插入到队列的时候,需要等待自己的任务结束之后才会继续插入被写在它后面的任务,然后执行它们 等待他们自己的任务执行完再执行后面的任务 dispatch_barrier_async将自己的任务插入到队列之后,不会等待自己的任务结束,它会继续把后面的任务插入到队列,然后等待自己的任务结束后才执行后面任务 参考:GCD入门(一): 基本概念和Dispatch Queue深入理解GCD底层并发 APIGCD 中 dispatch_once 的性能与实现读 Concurrency Programming Guide 笔记(一)iOS并发编程","categories":[{"name":"iOS","slug":"iOS","permalink":"http://codingdoge.cn/categories/iOS/"}],"tags":[{"name":"多线程","slug":"多线程","permalink":"http://codingdoge.cn/tags/多线程/"}]},{"title":"剑指Offer —— 最小的k个数","slug":"title: 剑指Offer —— 最小的k个数","date":"2017-08-01T02:20:00.000Z","updated":"2018-01-14T16:32:50.507Z","comments":true,"path":"2017/08/01/title: 剑指Offer —— 最小的k个数/","link":"","permalink":"http://codingdoge.cn/2017/08/01/title: 剑指Offer —— 最小的k个数/","excerpt":"","text":"题目描述输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。(很典型的题目了) 解题思路 插入或者冒泡排序,优化点在记录已排好的个数(O(n*k)) 最小堆(O(nlogk)) 快排思想(O(n)) way1不上代码了。 way2上个建堆的算法,通用、解耦、易测试😂 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950void HeapAdjust(int H[],int s, int length) { int tmp = H[s]; int child = 2*s+1; //左孩子结点的位置。(i+1 为当前调整结点的右孩子结点的位置) while (child < length) { if(child+1 <length && H[child]<H[child+1]) { // 如果右孩子大于左孩子(找到比当前待调整结点大的孩子结点) ++child ; } if(H[s]<H[child]) { // 如果较大的子结点大于父结点 H[s] = H[child]; // 那么把较大的子结点往上移动,替换它的父结点 s = child; // 重新设置s ,即待调整的下一个结点的位置 child = 2*s+1; } else { // 如果当前待调整结点大于它的左右孩子,则不需要调整,直接退出 break; } H[s] = tmp; // 当前待调整的结点放到比其大的孩子结点位置上 } print(H,length); } /** * 初始堆进行调整 * 将H[0..length-1]建成堆 * 调整完之后第一个元素是序列的最小的元素 */ void BuildingHeap(int H[], int length) { //最后一个有孩子的节点的位置 i= (length -1) / 2 for (int i = (length -1) / 2 ; i >= 0; i--) { cout << "i: " << i << endl; HeapAdjust(H,i,length); }} /** * 堆排序算法 */ void HeapSort(int H[],int length) { //初始堆 BuildingHeap(H, length); //从最后一个元素开始对序列进行调整 for (int i = length - 1; i > 0; --i) { //交换堆顶元素H[0]和堆中最后一个元素 int temp = H[i]; H[i] = H[0]; H[0] = temp; //每次交换堆顶元素和堆中最后一个元素之后,都要对堆进行调整 HeapAdjust(H,0,i); } } way31234567891011121314151617181920212223242526272829303132333435363738394041int Partition(vector<int> &input, int begin, int end){trueint first = begin;trueint last = end;trueint pivot = input[first];truewhile (first < last)true{truetruewhile (first < last && input[last] >= pivot) last--;truetrueinput[first] = input[last];truetruewhile (first < last && input[first] <= pivot) first++;truetrueinput[last] = input[first];true}trueinput[first] = pivot;truereturn first;}vector<int> GetLeastNumbers_Solution(vector<int> &input, int k){trueint len=input.size();truevector<int> res;trueif(len==0||k>len||k<=0) return res;trueif(len==k) return input;trueint start=0;trueint end=len-1;trueint index=Partition(input,start,end);truewhile(index!=(k-1))true{truetrueif(index>k-1)truetrue{truetruetrueend=index-1;truetruetrueindex=Partition(input,start,end);truetrue}truetrueelsetruetrue{truetruetruestart=index+1;truetruetrueindex=Partition(input,start,end);truetrue}true}truefor (int i = 0; i < k; i++)truetrueres.push_back(input[i]);truereturn res;}","categories":[{"name":"Algorithm","slug":"Algorithm","permalink":"http://codingdoge.cn/categories/Algorithm/"}],"tags":[{"name":"剑指Offer","slug":"剑指Offer","permalink":"http://codingdoge.cn/tags/剑指Offer/"}]},{"title":"并发(concurrency)和并行(parallelism)","slug":"title: 并发(concurrency)和并行(parallelism)","date":"2017-07-27T04:29:00.000Z","updated":"2018-01-13T14:00:58.116Z","comments":true,"path":"2017/07/27/title: 并发(concurrency)和并行(parallelism)/","link":"","permalink":"http://codingdoge.cn/2017/07/27/title: 并发(concurrency)和并行(parallelism)/","excerpt":"","text":"“并发”指的是程序的结构,“并行”指的是程序运行时的状态 并行(parallelism)这个概念很好理解。所谓并行,就是同时执行的意思,无需过度解读。判断程序是否处于并行的状态,就看同一时刻是否有超过一个“工作单位”在运行就好了。所以,单线程永远无法达到并行状态。 要达到并行状态,最简单的就是利用多线程和多进程。 并发(concurrency)要理解“并发”这个概念,必须得清楚,并发指的是程序的“结构”。当我们说这个程序是并发的,实际上,这句话应当表述成“这个程序采用了支持并发的设计”。好,既然并发指的是人为设计的结构,那么怎样的程序结构才叫做支持并发的设计? 正确的并发设计的标准是:使多个操作可以在重叠的时间段内进行(two tasks can start, run, and complete in overlapping time periods)。 这句话的重点有两个。我们先看“(操作)在重叠的时间段内进行”这个概念。它是否就是我们前面说到的并行呢?是,也不是。并行,当然是在重叠的时间段内执行,但是另外一种执行模式,也属于在重叠时间段内进行。这就是协程。 使用协程时,程序的执行看起来往往是这个样子: task1, task2 是两段不同的代码,比如两个函数,其中黑色块代表某段代码正在执行。注意,这里从始至终,在任何一个时间点上都只有一段代码在执行,但是,由于 task1 和 task2 在重叠的时间段内执行,所以这是一个支持并发的设计。与并行不同,单核单线程能支持并发。 Different concurrent designs enable different ways to parallelize","categories":[{"name":"OS","slug":"OS","permalink":"http://codingdoge.cn/categories/OS/"}],"tags":[]},{"title":"iOS 保持界面流畅的技巧","slug":"title: iOS 保持界面流畅的技巧","date":"2017-07-25T06:21:00.000Z","updated":"2018-01-14T16:35:13.663Z","comments":true,"path":"2017/07/25/title: iOS 保持界面流畅的技巧/","link":"","permalink":"http://codingdoge.cn/2017/07/25/title: iOS 保持界面流畅的技巧/","excerpt":"","text":"iOS 保持界面流畅的技巧 CPU 资源消耗原因和解决方案对象创建对象的创建会分配内存、调整属性、甚至还有读取文件等操作,比较消耗 CPU 资源。尽量用轻量的对象代替重量的对象,可以对性能有所优化。比如 CALayer 比 UIView 要轻量许多,那么不需要响应触摸事件的控件,用 CALayer 显示会更加合适。如果对象不涉及 UI 操作,则尽量放到后台线程去创建,但可惜的是包含有 CALayer 的控件,都只能在主线程创建和操作。通过 Storyboard 创建视图对象时,其资源消耗会比直接通过代码创建对象要大非常多,在性能敏感的界面里,Storyboard 并不是一个好的技术选择。 尽量推迟对象创建的时间,并把对象的创建分散到多个任务中去。尽管这实现起来比较麻烦,并且带来的优势并不多,但如果有能力做,还是要尽量尝试一下。如果对象可以复用,并且复用的代价比释放、创建新对象要小,那么这类对象应当尽量放到一个缓存池里复用。 对象调整对象的调整也经常是消耗 CPU 资源的地方。这里特别说一下 CALayer:CALayer 内部并没有属性,当调用属性方法时,它内部是通过运行时 resolveInstanceMethod 为对象临时添加一个方法,并把对应属性值保存到内部的一个 Dictionary 里,同时还会通知 delegate、创建动画等等,非常消耗资源。UIView 的关于显示相关的属性(比如 frame/bounds/transform)等实际上都是 CALayer 属性映射来的,所以对 UIView 的这些属性进行调整时,消耗的资源要远大于一般的属性。对此你在应用中,应该尽量减少不必要的属性修改。 当视图层次调整时,UIView、CALayer 之间会出现很多方法调用与通知,所以在优化性能时,应该尽量避免调整视图层次、添加和移除视图。 对象销毁对象的销毁虽然消耗资源不多,但累积起来也是不容忽视的。通常当容器类持有大量对象时,其销毁时的资源消耗就非常明显。同样的,如果对象可以放到后台线程去释放,那就挪到后台线程去。这里有个小 Tip:把对象捕获到 block 中,然后扔到后台队列去随便发送个消息以避免编译器警告,就可以让对象在后台线程销毁了。 12345NSArray *tmp = self.array;self.array = nil;dispatch_async(queue, ^{ [tmp class];}); 布局计算视图布局的计算是 App 中最为常见的消耗 CPU 资源的地方。如果能在后台线程提前计算好视图布局、并且对视图布局进行缓存,那么这个地方基本就不会产生性能问题了。 不论通过何种技术对视图进行布局,其最终都会落到对 UIView.frame/bounds/center 等属性的调整上。上面也说过,对这些属性的调整非常消耗资源,所以尽量提前计算好布局,在需要时一次性调整好对应属性,而不要多次、频繁的计算和调整这些属性。 AutolayoutAutolayout 是苹果本身提倡的技术,在大部分情况下也能很好的提升开发效率,但是 Autolayout 对于复杂视图来说常常会产生严重的性能问题。随着视图数量的增长,Autolayout 带来的 CPU 消耗会呈指数级上升。具体数据可以看这个文章:http://pilky.me/36/。 如果你不想手动调整 frame 等属性,你可以用一些工具方法替代(比如常见的 left/right/top/bottom/width/height 快捷属性),或者使用 ComponentKit、AsyncDisplayKit 等框架。 文本计算如果一个界面中包含大量文本(比如微博微信朋友圈等),文本的宽高计算会占用很大一部分资源,并且不可避免。如果你对文本显示没有特殊要求,可以参考下 UILabel 内部的实现方式:用 [NSAttributedString boundingRectWithSize:options:context:] 来计算文本宽高,用 -[NSAttributedString drawWithRect:options:context:] 来绘制文本。尽管这两个方法性能不错,但仍旧需要放到后台线程进行以避免阻塞主线程。 如果你用 CoreText 绘制文本,那就可以先生成 CoreText 排版对象,然后自己计算了,并且 CoreText 对象还能保留以供稍后绘制使用。 文本渲染屏幕上能看到的所有文本内容控件,包括 UIWebView,在底层都是通过 CoreText 排版、绘制为 Bitmap 显示的。常见的文本控件 (UILabel、UITextView 等),其排版和绘制都是在主线程进行的,当显示大量文本时,CPU 的压力会非常大。对此解决方案只有一个,那就是自定义文本控件,用 TextKit 或最底层的 CoreText 对文本异步绘制。尽管这实现起来非常麻烦,但其带来的优势也非常大,CoreText 对象创建好后,能直接获取文本的宽高等信息,避免了多次计算(调整 UILabel 大小时算一遍、UILabel 绘制时内部再算一遍);CoreText 对象占用内存较少,可以缓存下来以备稍后多次渲染。 图片的解码当你用 UIImage 或 CGImageSource 的那几个方法创建图片时,图片数据并不会立刻解码。图片设置到 UIImageView 或者 CALayer.contents 中去,并且 CALayer 被提交到 GPU 前,CGImage 中的数据才会得到解码。这一步是发生在主线程的,并且不可避免。如果想要绕开这个机制,常见的做法是在后台线程先把图片绘制到 CGBitmapContext 中,然后从 Bitmap 直接创建图片。目前常见的网络图片库都自带这个功能。 图像的绘制图像的绘制通常是指用那些以 CG 开头的方法把图像绘制到画布中,然后从画布创建图片并显示这样一个过程。这个最常见的地方就是 [UIView drawRect:] 里面了。由于 CoreGraphic 方法通常都是线程安全的,所以图像的绘制可以很容易的放到后台线程进行。一个简单异步绘制的过程大致如下(实际情况会比这个复杂得多,但原理基本一致): 1234567891011- (void)display { dispatch_async(backgroundQueue, ^{ CGContextRef ctx = CGBitmapContextCreate(...); // draw in context... CGImageRef img = CGBitmapContextCreateImage(ctx); CFRelease(ctx); dispatch_async(mainQueue, ^{ layer.contents = img; }); });} GPU 资源消耗原因和解决方案相对于 CPU 来说,GPU 能干的事情比较单一:接收提交的纹理(Texture)和顶点描述(三角形),应用变换(transform)、混合并渲染,然后输出到屏幕上。通常你所能看到的内容,主要也就是纹理(图片)和形状(三角模拟的矢量图形)两类。 纹理的渲染所有的 Bitmap,包括图片、文本、栅格化的内容,最终都要由内存提交到显存,绑定为 GPU Texture。不论是提交到显存的过程,还是 GPU 调整和渲染 Texture 的过程,都要消耗不少 GPU 资源。当在较短时间显示大量图片时(比如 TableView 存在非常多的图片并且快速滑动时),CPU 占用率很低,GPU 占用非常高,界面仍然会掉帧。避免这种情况的方法只能是尽量减少在短时间内大量图片的显示,尽可能将多张图片合成为一张进行显示。 当图片过大,超过 GPU 的最大纹理尺寸时,图片需要先由 CPU 进行预处理,这对 CPU 和 GPU 都会带来额外的资源消耗。目前来说,iPhone 4S 以上机型,纹理尺寸上限都是 4096x4096,更详细的资料可以看这里:iosres.com。所以,尽量不要让图片和视图的大小超过这个值。 视图的混合 (Composing)当多个视图(或者说 CALayer)重叠在一起显示时,GPU 会首先把他们混合到一起。如果视图结构过于复杂,混合的过程也会消耗很多 GPU 资源。为了减轻这种情况的 GPU 消耗,应用应当尽量减少视图数量和层次,并在不透明的视图里标明 opaque 属性以避免无用的 Alpha 通道合成。当然,这也可以用上面的方法,把多个视图预先渲染为一张图片来显示。 图形的生成CALayer 的 border、圆角、阴影、遮罩(mask),CASharpLayer 的矢量图形显示,通常会触发离屏渲染(offscreen rendering),而离屏渲染通常发生在 GPU 中。当一个列表视图中出现大量圆角的 CALayer,并且快速滑动时,可以观察到 GPU 资源已经占满,而 CPU 资源消耗很少。这时界面仍然能正常滑动,但平均帧数会降到很低。为了避免这种情况,可以尝试开启 CALayer.shouldRasterize 属性,但这会把原本离屏渲染的操作转嫁到 CPU 上去。对于只需要圆角的某些场合,也可以用一张已经绘制好的圆角图片覆盖到原本视图上面来模拟相同的视觉效果。最彻底的解决办法,就是把需要显示的图形在后台线程绘制为图片,避免使用圆角、阴影、遮罩等属性。 参考:iOS 保持界面流畅的技巧","categories":[{"name":"iOS","slug":"iOS","permalink":"http://codingdoge.cn/categories/iOS/"}],"tags":[{"name":"UI","slug":"UI","permalink":"http://codingdoge.cn/tags/UI/"}]},{"title":"剑指Offer —— 滑动窗口的最大值","slug":"title: 剑指Offer —— 滑动窗口的最大值","date":"2017-07-25T02:43:00.000Z","updated":"2018-01-14T16:38:57.866Z","comments":true,"path":"2017/07/25/title: 剑指Offer —— 滑动窗口的最大值/","link":"","permalink":"http://codingdoge.cn/2017/07/25/title: 剑指Offer —— 滑动窗口的最大值/","excerpt":"","text":"题目来源:滑动窗口的最大值 题目描述给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。 解题思路用一个双端队列维护当前滑动窗口的状态,队首是当前窗口最大值的下标,当窗口滑动进入一个新值k时,k从队尾依次向前比较,比k小的全部出队,保障了k的权重应有的位置 123456789101112131415161718192021vector<int> maxInWindows(const vector<int> &num, unsigned int size) {truevector<int> ans;truedeque<int> q;trueif (num.size() < size || size == 0) true return ans;truefor (int i = 0; i < size; i++) {true while(!q.empty() && num[i] > num[q.back()])true q.pop_back();true q.push_back(i);true}truefor (int i = size; i < num.size(); i++) {true ans.push_back(num[q.front()]);true while (!q.empty() && num[i] >= num[q.back()])true q.pop_back();true while (!q.empty() && q.front() <= i-size)true q.pop_front();true q.push_back(i);true}trueans.push_back(num[q.front()]);truereturn ans;}","categories":[{"name":"Algorithm","slug":"Algorithm","permalink":"http://codingdoge.cn/categories/Algorithm/"}],"tags":[{"name":"剑指Offer","slug":"剑指Offer","permalink":"http://codingdoge.cn/tags/剑指Offer/"}]},{"title":"Search in Rotated Sorted Array","slug":"title: Search in Rotated Sorted Array ","date":"2017-07-23T15:56:00.000Z","updated":"2018-01-12T14:25:37.504Z","comments":true,"path":"2017/07/23/title: Search in Rotated Sorted Array /","link":"","permalink":"http://codingdoge.cn/2017/07/23/title: Search in Rotated Sorted Array /","excerpt":"","text":"题目来源LeetCode: LeetCode OJ 题目描述: Suppose a sorted array is rotated at some pivot unknown to you beforehand.(i.e., 0 1 2 4 5 6 7 might become 4 5 6 7 0 1 2).You are given a target value to search. If found in the array return its index, otherwise return -1.You may assume no duplicate exists in the array. 题目解释一个排好序的数组,不知道以哪个点为中心旋转了(部分有序),你的任务是查找给定的数是否存在数组中。在的话返回下标,不在的话返回-1. 分析查找首先想到O(log(n))的二分查找,但是二分查找的前提是有序数组。题目里一个有序数组旋转后变成了部分有序。通过比较两端大小找到增序部分。 eg: 4 5 6 0 1 2 3first = 4, mid = 0, last = 3,通过比较first,mid,last找到增序部分。在这个例子中为1 2 3,然后判断target是否在这个增序子序列中,如果在则直接用二分查找,不在则在另一部分(例子中为4 5 6 0)中继续分解。 Codeint search(const vector<int>& nums, int target) { int first = 0; int last = nums.size(); while(first != last) { const int mid = (first+last)>>1; // 使用位运算加速 if(nums[mid] == target) return mid; if(nums[first] <= nums[mid]) // 找到增序子序列 if(nums[first]<=target && target<nums[mid]) // 找到target在哪个部分 last = mid; else first = mid+1; else if(nums[mid]<target && target<=nums[last-1]) first = mid+1; else last = mid; } return -1; } 拓展当数组为无序时的二分查找 分析一种是先排序再二分,一种是结合快排思想,每次选择一个关键字,比他大的放右边,比他小的放左边,然后再比较他和需要查找的数的关系,再选择区间进行迭代。如果需要返回查找数的下标,则添加一个纪录下标的数组,这样排好序后也能知道当前数在原始数组中的位置。 初始数组3 1 2 5 4 7 0 6mid = key = 3 进行一次快排填坑得到数组0 1 2 3 4 7 5 6比较mid与target如果target>mid则迭代mid后半部分如果target<mid则迭代mid前半部分直到找到target Codeint BinarySearch(vector<int>& nums, int target) { int num = nums.size(); int index[num]; // index数组纪录下标 以便能找到在数组的初始位置 for(int i = 0; i < num; i++) // 初始化index数组 index[i] = i; int l, r, m, sl, sr, mIndex; l = 0, r = num-1; while(l<=r) // 开始迭代 { mIndex = index[l], m = nums[l]; sl = l, sr = r; while(sl<sr) // 快排思想,左右填坑,并用index记录位置 { while(sl<sr && m<nums[sr]) sr--; nums[sl] = nums[sr]; index[sl] = index[sr]; while(sl<sr && m>nums[sl]) sl++; nums[sr] = nums[sl]; index[sr] = index[sl]; } nums[sl] = m; index[sl] = mIndex; if(m == target) return mIndex; if(target > m) // 判断target在哪个区间 l = sl+1; else r = sl-1; } return -1; } 相关题目Search in Rotated Sorted Array II 相关题目分析因为允许出现重复数字,但是数组还是部分有序的,所以跳过重复数字即可 Codeint search(const vector<int>& nums, int target) { int first = 0; int last = nums.size(); while(first != last) { const int mid = (first+last)>>1; if(nums[mid] == target) return mid; if(nums[first] < nums[mid]) if(nums[first]<=target && target<nums[mid]) last = mid; else first = mid+1; else if(nums[first] > nums[mid]) { if(nums[mid]<target && target<=nums[last-1]) first = mid+1; else last = mid; } else // 特判相等时跳过 first++; } return -1; }","categories":[{"name":"Algorithm","slug":"Algorithm","permalink":"http://codingdoge.cn/categories/Algorithm/"}],"tags":[{"name":"LeetCode","slug":"LeetCode","permalink":"http://codingdoge.cn/tags/LeetCode/"}]},{"title":"Remove Duplicates from Sorted Array","slug":"title: Remove Duplicates from Sorted Array ","date":"2017-07-23T15:56:00.000Z","updated":"2018-01-14T16:41:30.178Z","comments":true,"path":"2017/07/23/title: Remove Duplicates from Sorted Array /","link":"","permalink":"http://codingdoge.cn/2017/07/23/title: Remove Duplicates from Sorted Array /","excerpt":"","text":"题目来源LeetCode: LeetCode OJ 题目描述Given a sorted array, remove the duplicates in place such that each element appear only once and return the new length.Do not allocate extra space for another array, you must do this in place with constant memory.For example,Given input array nums = [1,1,2],Your function should return length = 2, with the first two elements of nums being 1 and 2 respectively. It doesn’t matter what you leave beyond the new length. 题目解释给出一个sorted array(意思是指已经排好序了?),处理后数组里每一个元素只能出现一次,返回处理后的数组长度。不能使用额外的数组空间,只能用已经给出的确定的内存空间。 分析因为不太懂sorted array具体指的什么,第一次做的时候以为数组是随机的,相同元素出现的位置是随机的,然后题目也没给出limit time,随手就写了一个O(n^3) 12345678910for(int i = 0; i < num; i++){ truefor(int j = i+1; j < num; j++){ truetrueif(array[i] == array[j]){ for(int k = j; k < num-1; k++) array[k] = array[k+1]; num--; j--; } } } 自然是T了。然后就把sorted array当做已经排好序的数组,那就容易多了,算法也都是O(1),一看代码就明白,水题,直接上代码。 way11234567if (nums.empty()) return 0; int index = 0; for (int i = 1; i < nums.size(); i++) { if (nums[index] != nums[i]) nums[++index] = nums[i]; } return index + 1; way2 STL1return distance(nums.begin(), unique(nums.begin(), nums.end())); std::distancetemplatetypename iterator_traits::difference_type distance (InputIterator first, InputIterator last);Return distance between iteratorsCalculates the number of elements between first and last.c++ reference std::uniqueequality (1)template ForwardIterator unique (ForwardIterator first, ForwardIterator last);predicate (2)template ForwardIterator unique (ForwardIterator first, ForwardIterator last, BinaryPredicate pred); Remove consecutive duplicates in rangeRemoves all but the first element from every consecutive group of equivalent elements in the range [first,last).c++ reference 相关题目RemoveDuplicatesfromSortedArrayII","categories":[{"name":"Algorithm","slug":"Algorithm","permalink":"http://codingdoge.cn/categories/Algorithm/"}],"tags":[{"name":"LeetCode","slug":"LeetCode","permalink":"http://codingdoge.cn/tags/LeetCode/"}]},{"title":"常见排序算法总结","slug":"title: 常见排序算法总结 ","date":"2017-07-23T15:56:00.000Z","updated":"2018-01-14T16:45:17.006Z","comments":true,"path":"2017/07/23/title: 常见排序算法总结 /","link":"","permalink":"http://codingdoge.cn/2017/07/23/title: 常见排序算法总结 /","excerpt":"","text":"排序算法一般分为: 内部排序(In-place sort) 不占用额外内存或者占用常数内存,如:插入排序、选择排序、冒泡排序、堆排序、快速排序。 外部排序(Out-place sort) 因为排序数据大,可用内存一次不能容纳所有排序记录,排序过程中需要访问外存,如:归并排序、计数排序、基数排序、桶排序。 也分为: 稳定的排序(stable sort) 插入排序、冒泡排序、归并排序、计数排序、基数排序、桶排序。 不稳定的排序(unstable sort) 选择排序、快速排序、堆排序。 算法的稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,ri=rj,且ri在rj之前,而在排序后的序列中,ri仍在rj之前,则称这种排序算法是稳定的;否则称为不稳定的。 不稳定算法的改进:只需要在每个输入元素加一个index,表示初始时的数组索引,当不稳定的算法排好序后,对于相同的元素对index排序即可。 插入排序 最优复杂度:当输入数组就是排好序的时候,复杂度为O(n),而快速排序在这种情况下会产生O(n^2)的复杂度。最差复杂度:当输入数组为倒序时,复杂度为O(n^2)。 插入排序比较适合用于“少量元素的数组”。插入排序比较适合用于“少量元素的数组”。 1.直接插入排序(Straight Insertion Sort) 工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。具体算法描述如下: 从第一个元素开始,该元素可以认为已经被排序 取出下一个元素,在已经排序的元素序列中从后向前扫描 如果该元素(已排序)大于新元素,将该元素移到下一位置 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置 将新元素插入到该位置后 重复步骤2~5 直接插入排序示例: 如果排序时碰到相等的元素,比较后会把相等的元素放在后面,所以这两个相等的元素之间的前后顺序没有改变,排序是稳定的。 算法的实现: 123456789101112131415161718void InsertSort(int a[], int n){ for(int i = 1; i < n; i++) { if(a[i] < a[i-1]) //若第i个元素大于i-1元素,直接插入。小于的话,移动有序表后插入 { int j = i-1; int temp = a[i]; //存储待排序元素 a[i] = a[i-1]; //先后移一个元素 while(x < a[j] && j >= 0) //查找在有序表中的插入位置,并注意边界判断 { a[j+1] = a[j]; j--; //元素后移 } a[j+1] = temp; //插入到正确位置 } }} 如果比较操作的代价比交换操作大的话,可以采用二分查找法来减少比较操作的数目。直接插入排序算法的优化算法有:二分插入排序,2-路插入排序。 2.希尔排序(Shell’s Sort) 希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。它是基于插入排序的以下两点性质而提出改进方法的: 插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率 但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位 基本原理是先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。算法具体描述如下: 选择一个步长,其设计者Donald Shell最初建议步长选择为n/2并且对步长取半直到步长达到1; 以步长为间隔对序列进行排序; 重复步骤2直到步长为1。 希尔排序的示例: 希尔排序是不稳定的。 算法的实现: 1234567891011121314void ShellInsertSort(int a[], int n){ for (int gap=n>>1; gap>0; gap>>=1) // 确定步长,直到步长为1 { for (int i=gap; i<n; i++) // 以步长为间隔进行排序 { int temp = a[i]; int j; for (j=i-gap; j>=0&&a[j]>temp; j-=gap) // 排序细节 a[j+gap] = a[j]; a[j+gap] = temp; } }} 可能希尔排序最重要的地方在于当用较小步长排序后,以前用的较大步长仍然是有序的。已知的最好步长序列是由Sedgewick提出的(1, 5, 19, 41, 109,…),该序列的项来自两个算式[1]。这项研究也表明“比较在希尔排序中是最主要的操作,而不是交换。”用这样步长序列的希尔排序比插入排序要快,甚至在小数组中比快速排序和堆排序还快,但是在涉及大量数据时希尔排序还是比快速排序慢。 选择排序 简单选择排序(Simple Selection Sort) 基本原理是在要排序的一组数中,选出最小(或者最大)的一个数与第1个位置的数交换;然后在剩下的数当中再找最小(或者最大)的与第2个位置的数交换,依次类推,直到第n-1个元素(倒数第二个数)和第n个元素(最后一个数)比较为止。算法的具体描述如下: 第一趟,从n 个记录中找出关键码最小的记录与第一个记录交换; 第二趟,从第二个记录开始的n-1 个记录中再选出关键码最小的记录与第二个记录交换; 以此类推….. 第i趟,则从第i 个记录开始的n-i+1 个记录中选出关键码最小的记录与第i 个记录交换, 直到整个序列按关键码有序。 简单选择排序的示例: 算法实现: 1234567891011void SelectSort(int a[], int n){trueint min = a[0];truefor (int i = 0; i < n; i++)true{truetruefor (int j = i; j < n; j++) // 每次选出序列中最小的数truetruetrueif (a[j] < min)truetruetruetruemin = a[j];truetruea[i] = min; // 将每次选好的数放在正确的位置true}} 选择排序是稳定的。 选择排序的交换操作介于0和(n-1)次之间。选择排序的比较操作为n(n-1)次之间。选择排序的赋值操作介于0和3(n-1)次之间。比较次数O(n^2 ),比较次数与关键字的初始状态无关,总的比较次数N=(n-1)+(n-2)+…+1=n(n-1)/2。交换次数O(n),最好情况是,已经有序,交换0次;最坏情况是,逆序,交换n-1次。交换次数比冒泡排序较少,由于交换所需CPU时间比比较所需的CPU时间多,n值较小时,选择排序比冒泡排序快。原地操作几乎是选择排序的唯一优点,当空间复杂度要求较高时,可以考虑选择排序;实际适用的场合非常罕见。 简单排序的改进——二元选择排序简单选择排序,每趟循环只能确定一个元素排序后的定位。我们可以考虑改进为每趟循环确定两个元素(当前趟最大和最小记录)的位置,从而减少排序所需的循环次数。改进后对n个数据进行排序,最多只需进行[n/2]趟循环即可。具体实现如下: 123456789101112131415161718void SelectSort(int r[],int n) { int i ,j , min ,max, tmp; for (i=1 ;i <= n/2;i++) { // 做不超过n/2趟选择排序 min = i; max = i ; //分别记录最大和最小关键字记录位置 for (j= i+1; j<= n-i; j++) { if (r[j] > r[max]) { max = j ; continue ; } if (r[j]< r[min]) { min = j ; } } //该交换操作还可分情况讨论以提高效率 tmp = r[i-1]; r[i-1] = r[min]; r[min] = tmp; tmp = r[n-i]; r[n-i] = r[max]; r[max] = tmp; } } 堆排序(Heap Sort) 堆排序是指利用堆这种数据结构所设计的一种排序算法。其基本原理如下:堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。若以一维数组存储一个堆,则堆对应一棵完全二叉树,且所有非叶结点的值均不大于(或不小于)其子女的值,根结点(堆顶元素)的值是最小(或最大)的。如: 大顶堆序列:(96, 83, 27, 38, 11, 09) 小顶堆序列:(12, 36, 24, 85, 47, 30, 53, 91) 初始时把n个数的序列看作是一棵顺序存储的二叉树(一维数组存储二叉树),调整它们的存储序,使之成为一个堆,将堆顶元素输出,得到n个元素中最小(或最大)的元素,这时堆的根节点的数最小(或者最大)。然后对前面(n-1)个元素重新调整使之成为堆,输出堆顶元素,得到n个元素中次小(或次大)的元素。依此类推,直到只有两个节点的堆,并对它们作交换,最后得到有n个节点的有序序列。称这个过程为堆排序。因此,实现堆排序需解决两个问题: 如何将n 个待排序的数建成堆; 输出堆顶元素后,怎样调整剩余n-1 个元素,使其成为一个新堆。 首先讨论第二个问题:输出堆顶元素后,对剩余n-1元素重新建成堆的调整过程。调整小顶堆的方法: 设有m 个元素的堆,输出堆顶元素后,剩下m-1 个元素。将堆底元素送入堆顶((最后一个元素与堆顶进行交换),堆被破坏,其原因仅是根结点不满足堆的性质。 将根结点与左、右子树中较小元素的进行交换。 若与左子树交换:如果左子树堆被破坏,即左子树的根结点不满足堆的性质,则重复方法 2. 若与右子树交换,如果右子树堆被破坏,即右子树的根结点不满足堆的性质。则重复方法 2. 继续对不满足堆性质的子树进行上述交换操作,直到叶子结点,堆被建成。 称这个自根结点到叶子结点的调整过程为筛选。如图: 再讨论对n 个元素初始建堆的过程。建堆方法:对初始序列建堆的过程,就是一个反复进行筛选的过程。 n个结点的完全二叉树,则最后一个结点是第个结点的子树。 筛选从第个结点为根的子树开始,该子树成为堆。 之后向前依次对各结点为根的子树进行筛选,使之成为堆,直到根结点。 如图建堆初始过程:无序序列:(49,38,65,97,76,13,27,49) 算法的实现: 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556/** * 已知H[s…m]除了H[s] 外均满足堆的定义 * 调整H[s],使其成为大顶堆.即将对第s个结点为根的子树筛选, * * @param H是待调整的堆数组 * @param s是待调整的数组元素的位置 * @param length是数组的长度 * */ void HeapAdjust(int H[],int s, int length) { int tmp = H[s]; int child = 2*s+1; //左孩子结点的位置。(i+1 为当前调整结点的右孩子结点的位置) while (child < length) { if(child+1 <length && H[child]<H[child+1]) // 如果右孩子大于左孩子(找到比当前待调整结点大的孩子结点) ++child; if(H[s]<H[child]) { // 如果较大的子结点大于父结点 H[s] = H[child]; // 那么把较大的子结点往上移动,替换它的父结点 s = child; // 重新设置s ,即待调整的下一个结点的位置 child = 2*s+1; } else // 如果当前待调整结点大于它的左右孩子,则不需要调整,直接退出 break; H[s] = tmp; // 当前待调整的结点放到比其大的孩子结点位置上 } } /** * 初始堆进行调整 * 将H[0..length-1]建成堆 * 调整完之后第一个元素是序列的最小的元素 */ void BuildingHeap(int H[], int length) { //最后一个有孩子的节点的位置 i= (length -1) / 2 for (int i = (length -1) / 2 ; i >= 0; i--) HeapAdjust(H,i,length);} /** * 堆排序算法 */ void HeapSort(int H[],int length) { //初始堆 BuildingHeap(H, length); //从最后一个元素开始对序列进行调整 for (int i = length - 1; i > 0; --i) { //交换堆顶元素H[0]和堆中最后一个元素 int temp = H[i]; H[i] = H[0]; H[0] = temp; //每次交换堆顶元素和堆中最后一个元素之后,都要对堆进行调整 HeapAdjust(H,0,i); } } 堆排序是不稳定的。 设树深度为k, 次,交换记录至多k 次。所以,在建好堆后,排序过程中的筛选次数不超过下式: 而建堆时的比较次数不超过4n 次,因此堆排序最坏情况下,时间复杂度也为:O(nlogn )。 交换排序冒泡排序(Bubble Sort) 冒泡排序重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。冒泡排序示例: 算法的实现: 1234567891011void bubbleSort(int a[], int n){ for(int i =0 ; i< n-1; ++i) for(int j = 0; j < n-i-1; ++j) if(a[j] > a[j+1]) { int tmp = a[j]; a[j] = a[j+1]; a[j+1] = tmp; } } 冒泡排序是稳定的。 冒泡算法的改进对冒泡排序常见的改进方法是加入一标志性变量exchange,用于标志某一趟排序过程中是否有数据交换,如果进行某一趟排序时并没有进行数据交换,则说明数据已经按要求排列好,可立即结束排序,避免不必要的比较过程。本文再提供以下两种改进算法: 设置一标志性变量pos,用于记录每趟排序中最后一次进行交换的位置。由于pos位置之后的记录均已交换到位,故在进行下一趟排序时只要扫描到pos位置即可。改进后算法如下: 1234567891011121314151617void Bubble_1 ( int r[], int n){ int i = n -1; //初始时,最后位置保持不变 while (i > 0) { int pos= 0; //每趟开始时,无记录交换 for (int j = 0; j < i; j++) if (r[j] > r[j+1]) { pos = j; //记录交换的位置 int tmp = r[j]; r[j] = r[j+1]; r[j+1] = tmp; } i = pos; //为下一趟排序作准备 } } 传统冒泡排序中每一趟排序操作只能找到一个最大值或最小值,我们考虑利用在每趟排序中进行正向和反向两遍冒泡的方法一次可以得到两个最终值(最大者和最小者) , 从而使排序趟数几乎减少了一半。改进后的算法为: 12345678910111213141516171819202122232425void Bubble_2 (int r[], int n){ int low = 0; int high = n-1; //设置变量的初始值 int tmp, j; while (low < high) { for (j = low; j < high; ++j) //正向冒泡,找到最大者 if (r[j] > r[j+1]) { tmp = r[j]; r[j]=r[j+1]; r[j+1]=tmp; } --high; //修改high值, 前移一位 for (j = high; j > low; --j) //反向冒泡,找到最小者 if (r[j] < r[j-1]) { tmp = r[j]; r[j]=r[j-1]; r[j-1]=tmp; } ++low; //修改low值,后移一位 } } 快速排序(Quick Sort) 快速排序又称划分交换排序(partition-exchange sort),使用分治法(Divide and conquer)策略来把一个序列(list)分为两个子序列(sub-lists)。其算法描述为: 从数列中挑出一个元素,称为”基准”(pivot)。 重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(相同的数可以到任一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。 递归地(recursively)把小于基准值元素的子数列和大于基准值元素的子数列排序。 快速排序的示例: 一趟排序的过程 排序的全过程 算法的实现(递归): 1234567891011121314151617181920void quickSort_recursive(int a[], int low, int high){ int first = low; int last = high; int key = a[first]; if(low >= high) return ; while(first < last) { while((first < last)&&(key <= a[last])) last--; a[first] = a[last]; while((first < last)&&(key >= a[first])) first++; a[last] = a[first]; } a[first] = key; quickSort_recursive(a, low, first-1); quickSort_recursive(a, first+1, high);} 快速排序是不稳定的。快排迭代算法:维护一个栈,存放划分的起点终点 在平均状况下,排序n个项目要Ο(nlogn)次比较。在最坏状况下(正序或逆序)则需要Ο(n^2 )次比较,取决于其递归树的高度。事实上,快速排序通常明显比其他Ο(nlogn)算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来。快速排序是一个不稳定的排序方法。 归并排序(Merge Sort) 归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。 递归法 将序列每相邻两个数字进行归并操作,形成floor(n/2)个序列,排序后每个序列包含两个元素 将上述序列再次归并,形成floor(n/4)个序列,每个序列包含四个元素 重复步骤2,直到所有元素排序完毕 算法实现: 12345678910111213141516171819202122232425void merge_sort_recursive(int arr[], int reg[], int start, int end){ if (start >= end) return; int len = end - start, mid = (len >> 1) + start; int start1 = start, end1 = mid; int start2 = mid + 1, end2 = end; merge_sort_recursive(arr, reg, start1, end1); merge_sort_recursive(arr, reg, start2, end2); int k = start; while (start1 <= end1 && start2 <= end2) reg[k++] = arr[start1] < arr[start2] ? arr[start1++] : arr[start2++]; while (start1 <= end1) reg[k++] = arr[start1++]; while (start2 <= end2) reg[k++] = arr[start2++]; for (k = start; k <= end; k++) arr[k] = reg[k];}void merge_sort(int arr[], const int len){trueint reg[len];truemerge_sort_recursive(arr, reg, 0, len - 1);} 两路归并的递归算法12345678910111213141516void MSort(ElemType *r, ElemType *rf,int s, int t) { ElemType *rf2; if(s==t) r[s] = rf[s]; else { int m=(s+t)/2; /*平分*p 表*/ MSort(r, rf2, s, m); /*递归地将p[s…m]归并为有序的p2[s…m]*/ MSort(r, rf2, m+1, t); /*递归地将p[m+1…t]归并为有序的p2[m+1…t]*/ Merge(rf2, rf, s, m+1,t); /*将p2[s…m]和p2[m+1…t]归并到p1[s…t]*/ } } void MergeSort_recursive(ElemType *r, ElemType *rf, int n) { /*对顺序表*p 作归并排序*/ MSort(r, rf,0, n-1); } 迭代法 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列; 设定两个指针,最初位置分别为两个已经排序序列的起始位置; 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置; 重复步骤3直到某一指针到达序列尾; 将另一序列剩下的所有元素直接复制到合并序列尾; 算法实现: 12345678910111213141516171819202122232425262728293031void merge_sort_iteration(int arr[], int len){ int* a = arr; int* b = new int[len]; for (int seg = 1; seg < len; seg += seg) { for (int start = 0; start < len; start += seg + seg) { int low = start, mid = min(start + seg, len), high = min(start + seg + seg, len); int k = low; int start1 = low, end1 = mid; int start2 = mid, end2 = high; while (start1 < end1 && start2 < end2) b[k++] = a[start1] < a[start2] ? a[start1++] : a[start2++]; while (start1 < end1) b[k++] = a[start1++]; while (start2 < end2) b[k++] = a[start2++]; } T* temp = a; a = b; b = temp; } if (a != arr) { for (int i = 0; i < len; i++) b[i] = a[i]; b = a; } delete[] b;} 归并排序是稳定的。 总结各种排序的稳定性,时间复杂度和空间复杂度总结: 参考资料有:真实的归宿——八大排序算法xiazdong——九大排序算法再总结wikipedia","categories":[{"name":"Algorithm","slug":"Algorithm","permalink":"http://codingdoge.cn/categories/Algorithm/"}],"tags":[]},{"title":"self与super的区别","slug":"title: self与super的区别 ","date":"2017-07-23T15:47:00.000Z","updated":"2018-01-14T16:47:12.060Z","comments":true,"path":"2017/07/23/title: self与super的区别 /","link":"","permalink":"http://codingdoge.cn/2017/07/23/title: self与super的区别 /","excerpt":"","text":"原文CSDN evilotus有所整理 在ObjC中的类实现中经常看到这两个关键字”self”和”super”,以以前oop语言的经验,拿c++为例,self相当于this,super相当于调用父类的方法,这么看起来是很容易理解的。但是它们真正是如何调用的呢? 你知道吗? 以下面的代码为例: 12345678910111213141516171819202122232425262728@interface Person:NSObject{trueNSString* name;} (void) setName:(NSString) yourName;@end //Person @interface PersonMe:Person{trueNSUInteger age;} (void) setAge:(NSUInteger) age; (void) setName:(NSString*) yourName andAge:(NSUInteger) age;@end // PersonMe @implementation PersonMe(void) setName:(NSString*) yourName andAge:(NSUInteger) age{true[self setAge:age];true[super setName:yourName];}@end // PersonMe int main(int argc, char* argv[]) {NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]PersonMe* me = [[PersonMe alloc] init];[me setName: @"asdf" andAge: 18];[me release];[pool drain];return 0;} 上面有简单的两个类,在子类PersonMe中调用了自己类中的setAge和父类中的setName,这些代码看起来很好理解,没什么问题。 然后我在setName:andAge的方法中加入两行: 1234567891011NSLog(@"self ' class is %@", [self class]);NSLog(@"super' class is %@", [super class]);``` 这样在调用时,会打出来这两个的**class**,先猜下吧,会打印出什么? 按照以前*oop*语言的经验,这里应该会输出:***self ' s class is PersonMe super ' s class is Person***但是编译运行后,可以发现结果是:``` // self 's class is PersonMesuper ' s class is PersonMe self的class和预想的一样,怎么super的class也是PersonMe? 真相self是类的隐藏的参数,指向当前当前调用方法的类,另一个隐藏参数是_cmd,代表当前类方法的selector。这里只关注这个self。super是个啥?super并不是隐藏的参数,它只是一个“编译器指示符”,它和self指向的是相同的消息接收者,拿上面的代码为例,不论是用[self setName]还是[super setName],接收“setName”这个消息的接收者都是PersonMe* me这个对象。不同的是,super告诉编译器,当调用setName的方法时,要去调用父类的方法,而不是本类里的。 当使用self调用方法时,会从当前类的方法列表中开始找,如果没有,就从父类中再找;而当使用superv时,则从父类**的方法列表中开始找。然后调用父类的这个方法。 One more step这种机制到底底层是如何实现的?其实当调用类方法的时候,编译器会将方法调用转成一个C函数方法调用,apple的objcRuntimeRef上说: Sending MessagesWhen it encounters a method invocation, the compiler might generate a call to any of several functions to perform the actual message dispatch, depending on the receiver, the return value, and the arguments. You can use these functions to dynamically invoke methods from your own plain C code, or to use argument forms not permitted by NSObject’s perform… methods. These functions are declared in /usr/include/objc/objc-runtime.h. objc_msgSend sends a message with a simple return value to an instance of a class. objc_msgSend_stret sends a message with a data-structure return value to an instance ofa class. objc_msgSendSuper sends a message with a simple return value to the superclass of an instance of a class. objc_msgSendSuper_stret sends a message with a data-structure return value to the superclass of an instance of a class. 可以看到会转成调用上面4个方法中的一个,由于_stret系列的和没有_stret的那两个类似,先只关注objc_msgSend和objc_msgSendSuper两个方法。 当使用[self setName]调用时,会使用objc_msgSend的函数,先看下objc_msgSend的函数定义: 1id objc_msgSend(id theReceiver, SEL theSelector, ...) 第一个参数是消息接收者,第二个参数是调用的具体类方法的selector,后面是selector方法的可变参数。我们先不管这个可变参数,以[self setName:]为例,编译器会替换成调用objc_msgSend的函数调用,其中theReceiver是self,theSelector是 @selector(setName:),这个selector是从当前self的class的方法列表开始找的setName,当找到后把对应的 selector传递过去。 而当使用[super setName]调用时,会使用objc_msgSendSuper函数,看下objc_msgSendSuper的函数定义: 1id objc_msgSendSuper(struct objc_super *super, SEL op, ...) 第一个参数是个objc_super的结构体,第二个参数还是类似上面的类方法的selector,先看下objc_super这个结构体是什么东西: struct objc_super { id receiver; Class superClass; }; 可以看到这个结构体包含了两个成员,一个是receiver,这个类似上面objc_msgSend的第一个参数receiver,第二个成员是记 录写super这个类的父类是什么,拿上面的代码为例,当编译器遇到PersonMe里setName:andAge方法里的[super setName:]时,开始做这几个事: 构建objc_super的结构体,此时这个结构体的第一个成员变量receiver就是PersonMe* me,和self相同。而第二个成员变量superClass就是指类Person,因为PersonMe的超类就是这个Person。 调用objc_msgSendSuper的方法,将这个结构体和setName的sel传递过去。函数里面在做的事情类似这样:从objc_super结构体指向的superClass的方法列表开始找setName的selector,找到后再以 objc_super->receiver去调用这个selector,可能也会使用objc_msgSend这个函数,不过此时的第一个参数 theReceiver就是objc_super->receiver,第二个参数是从objc_super->superClass中找到 的selector 里面的调用机制大体就是这样了,以上面的分析,回过头来看开始的代码,当输出[self class]和[super class]时,是个怎样的过程。 当使用[self class]时,这时的self是PersonMe,在使用objc_msgSend时,第一个参数是receiver也就是self,也是 PersonMe* me这个实例。第二个参数,要先找到class这个方法的selector,先从PersonMe这个类开始找,没有,然后到PersonMe的父类 Person中去找,也没有,再去Person的父类NSObject去找,一层一层向上找之后,在NSObject的类中发现这个class方法,而 NSObject的这个class方法,就是返回receiver的类别,所以这里输出PersonMe。 当使用[super class]时,这时要转换成objc_msgSendSuper的方法。先构造objc_super的结构体吧,第一个成员变量就是self, 第二个成员变量是Person,然后要找class这个selector,先去superClass也就是Person中去找,没有,然后去Person 的父类中去找,结果还是在NSObject中找到了。然后内部使用函数objc_msgSend(objc_super->receiver, @selector(class)) 去调用,此时已经和我们用[self class]调用时相同了,此时的receiver还是PersonMe* me,所以这里返回的也是PersonMe。 Furthor more 在类的方法列表寻找一个方法时,还牵涉到一个概念类对象的isa指针和objc的meta-class概念,这里就不再详细介绍。","categories":[{"name":"iOS","slug":"iOS","permalink":"http://codingdoge.cn/categories/iOS/"}],"tags":[{"name":"OC","slug":"OC","permalink":"http://codingdoge.cn/tags/OC/"},{"name":"转载","slug":"转载","permalink":"http://codingdoge.cn/tags/转载/"}]},{"title":"Objective-C声明在头文件和实现文件中的区别","slug":"title: Objective-C声明在头文件和实现文件中的区别 ","date":"2017-07-23T15:47:00.000Z","updated":"2018-01-14T16:53:10.421Z","comments":true,"path":"2017/07/23/title: Objective-C声明在头文件和实现文件中的区别 /","link":"","permalink":"http://codingdoge.cn/2017/07/23/title: Objective-C声明在头文件和实现文件中的区别 /","excerpt":"","text":"转自codecloud(有整理) 调试程序的时候,突然想到这个问题,百度一下发现有不少这方面的问答,粗略总结一下: 属性写在.h文件中和在.m文件中有什么区别? Objective-C中有分类和类扩展的概念,而实现文件中的类声明实际上就是类扩展. @interface部分为类扩展(extension) 其被设计出来就是为了解决两个问题的 定义类私有方法的地方,也就是下面说到的区别一 实现public readonly,private readwrite的property(意思是在h头文件中定义一个属性对外是readonly的,但在类的内部希望是可读写的,所以可以在m源文件中的@interface部分重新定义此属性为readwrite,此时此属性对外是只读的,对内是读写的). 此外,也可在此部分申明变量和属性,但申明的变量,属性和方法均为私有的,只能够被当前类访问,相当于private。 区别一:属性在.h文件中和在.m中声明是有区别的。区别就是,在.h文件中声明的属性,外部类可以通过“类实例.属性”来调用,但在.m中声明的则不可以,获取和设置的方法,只能是通过setValue:forKey和valueForKey来实现。 成员变量,有三种权限,就是大家都知道的@private、@protected、@public ,写在.m文件中时,相当于是@private权限,子类无法访问,验证了一下,做权限修改也无效。而写在.h文件中,默认是@protected权限,子类可以访问,可以做权限修改。因为访问权限指针对.h文件。.h文件中成员变量,外部类对其的调用,跟C++一样,用->来调用。 区别二这样可以提高编译效率,避免重复编译。因为分开的话只需要编译一次生成对应的.obj文件后,再次应用该类的地方,这个类就不会被再次编译,从而大大提高了效率。这样可以提高编译效率,避免重复编译. 怎么去解释呢…其实这是一个面向对象的思想,所谓”提高”的比较对象,应该是直接将方法写到具体函数里的实现方式.h为编译器提供一个索引、声明,连接obj对象和主程序.编译器在编译的时候,如果需要,则去查找h,找到了h,再找对应的obj,就可以找到类的方法了.但是如果直接写入到同一个文件(例如hpp),主程序没有索引,也不清楚具体的类实现了没有,只能一次次重复的编译相同的代码,这样实际上没有把这个类该有的东西抽象出来.对于函数声明在头文件中,在实现文件中实现,也是避免重复编译,函数可以多次声明,但只能实现一次. 头文件相对于实现文件的作用在于: 头文件可以预先告诉编译器一些必要的声明,让编译器顺利进行下去,在连接实现以前.未必出现实际的定义. 头文件的意义在: * 使得程序简明,清晰. * 避免了重复编写相同的声明代码. .c和.h文件没有必然的联系. 关于头文件和实现文件的编译连接的过程其实要理解C文件与头文件有什么不同之处,首先需要弄明白编译器的工作过程,一般说来编译器会做以下几个过程: 预处理阶段 词法与语法分析阶段 编译阶段,首先编译成纯汇编语句,再将之汇编成跟CPU相关的二进制码,生成各个目标文件 连接阶段,将各个目标文件中的各段代码进行绝对地址定位,生成跟特定平台相关的可执行文件,当然,最后还可以用objcopy生成纯二进制码,也就是去掉了文件格式信息. 编译器在编译时是以C文件为单位进行的,也就是说如果你的项目中一个C文件都没有,那么你的项目将无法编译,连接器是以目标文件为单位,它将一个或多个目标文件进行函数与变量的重定位,生成最终的可执行文件,在PC上的程序开发,一般都有一个main函数,这是各个编译器的约定,当然,你如果自己写连接器脚本的话,可以不用main函数作为程序入口!!! 有了这些基础知识,再言归正传,为了生成一个最终的可执行文件,就需要一些目标文件,也就是需要C文件,而这些C文件中又需要一个main函数作为可执行程序的入口,那么我们就从一个C文件入手,假定这个C文件内容如下: 12345678#include \\<stdio.h>#include “mytest.h”int main(int argc,char **argv){test = 25;printf(“test……………..%d\\n”,test);} 头文件内容如下:int test; 现在以这个例子来讲解编译器的工作: 预处理阶段:编译器以C文件作为一个单元,首先读这个C文件,发现第一句与第二句是包含一个头文件,就会在所有搜索路径中寻找这两个文件,找到之后,就会将相应头文件中再去处理宏,变量,函数声明,嵌套的头文件包含等,检测依赖关系,进行宏替换,看是否有重复定义与声明的情况发生,最后将那些文件中所有的东东全部扫描进这个当前的C文件中,形成一个中间“C文件” 编译阶段:在上一步中相当于将那个头文件中的test变量扫描进了一个中间C文件,那么test变量就变成了这个文件中的一个全局变量,此时就将所有这个中间C文件的所有变量,函数分配空间,将各个函数编译成二进制码,按照特定目标文件格式生成目标文件,在这种格式的目标文件中进行各个全局变量,函数的符号描述,将这些二进制码按照一定的标准组织成一个目标文件 连接阶段:将上一步成生的各个目标文件,根据一些参数,连接生成最终的可执行文件,主要的工作就是重定位各个目标文件的函数,变量等,相当于将个目标文件中的二进制码按一定的规范合到一个文件中 再回到C文件与头文件各写什么内容的话题上:理论上来说C文件与头文件里的内容,只要是C语言所支持的,无论写什么都可以的,比如你在头文件中写函数体,只要在任何一个C文件包含此头文件就可以将这个函数编译成目标文件的一部分(编译是以C文件为单位的,如果不在任何C文件中包含此头文件的话,这段代码就形同虚设),你可以在C文件中进行函数声明,变量声明,结构体声明,这也不成问题!!!那为何一定要分成头文件与C文件呢?又为何一般都在头件中进行函数,变量声明,宏声明,结构体声明呢?而在C文件中去进行变量定义,函数实现呢??原因如下: 如果在头文件中实现一个函数体,那么如果在多个C文件中引用它,而且又同时编译多个C文件,将其生成的目标文件连接成一个可执行文件,在每个引用此头文件的C文件所生成的目标文件中,都有一份这个函数的代码,如果这段函数又没有定义成局部函数,那么在连接时,就会发现多个相同的函数,就会报错 如果在头文件中定义全局变量,并且将此全局变量赋初值,那么在多个引用此头文件的C文件中同样存在相同变量名的拷贝,关键是此变量被赋了初值,所以编译器就会将此变量放入DATA段,最终在连接阶段,会在DATA段中存在多个相同的变量,它无法将这些变量统一成一个变量,也就是仅为此变量分配一个空间,而不是多份空间,假定这个变量在头文件没有赋初值,编译器就会将之放入BSS段,连接器会对BSS段的多个同名变量仅分配一个存储空间 如果在C文件中声明宏,结构体,函数等,那么我要在另一个C文件中引用相应的宏,结构体,就必须再做一次重复的工作,如果我改了一个C文件中的一个声明,那么又忘了改其它C文件中的声明,这不就出了大问题了,程序的逻辑就变成了你不可想象的了,如果把这些公共的东东放在一个头文件中,想用它的C文件就只需要引用一个就OK了!!!这样岂不方便,要改某个声明的时候,只需要动一下头文件就行了 在头文件中声明结构体,函数等,当你需要将你的代码封装成一个库,让别人来用你的代码,你又不想公布源码,那么人家如何利用你的库呢?也就是如何利用你的库中的各个函数呢??一种方法是公布源码,别人想怎么用就怎么用,另一种是提供头文件,别人从头文件中看你的函数原型,这样人家才知道如何调用你写的函数,就如同你调用printf函数一样,里面的参数是怎样的??你是怎么知道的??还不是看人家的头文件中的相关声明啊!!!当然这些东东都成了C标准,就算不看人家的头文件,你一样可以知道怎么使用.","categories":[{"name":"iOS","slug":"iOS","permalink":"http://codingdoge.cn/categories/iOS/"}],"tags":[{"name":"OC","slug":"OC","permalink":"http://codingdoge.cn/tags/OC/"},{"name":"转载","slug":"转载","permalink":"http://codingdoge.cn/tags/转载/"}]},{"title":"CocoaPods 安装及一般使用","slug":"title: CocoaPods 安装及一般使用","date":"2017-07-23T15:47:00.000Z","updated":"2018-01-13T05:06:34.464Z","comments":true,"path":"2017/07/23/title: CocoaPods 安装及一般使用/","link":"","permalink":"http://codingdoge.cn/2017/07/23/title: CocoaPods 安装及一般使用/","excerpt":"","text":"WHAT IS COCOAPODSCocoaPods is a dependency manager for Swift and Objective-C Cocoa projects.CocoaPods can help you scale your projects elegantly. INSTALLCocoaPods是基于ruby建立的,要确保你的电脑里装有Ruby,不过Mac都是自带Ruby的,你可以用rvm来管理你的ruby. RVM 实用指南 · Ruby China然后我们使用Rubygem来安装cocoapods.由于GFW的原因我们不能访问cocoapods.org,使用淘宝的Ruby镜像来代替: 删除自带的Ruby镜像$ gem sources --remove https://rubygems.org/ 添加淘宝的镜像$ gem sources -a https://gems.ruby-china.org/(如果这个镜像不能用,就用https://gems.ruby-china.org/) 可以用$ gem sources -l来检验。成功即显示以下结果: FF76DDA8-EC18-438C-B921-8603D4688C1D 安装CocoaPods$ sudo gem install cocoapods 配置CocoaPods$ pod setup 如果安装失败的话,根据报错去解决问题,比如gem没更新,ruby版本等。常见问题:While executing gem ... (Errno::EPERM)1Operation not permitted - /usr/bin/xcodeproj 安装Cocoapods, 更新gem出现的问题。 - SegmentFaultios - Cannot install cocoa pods after uninstalling, results in error - Stack Overflow Using CocoaPods 使用之前你要确保你所想用的库存在CocoaPods中:(拿AFNetworking举例)$ pod search AFNetworking第一次搜索会需要建立索引,比较慢一些。 搜索完成后会列举出结果和版本: 37122577-4CDC-48C9-9B25-4B943D983810 然后通过创建Podfile文件来添加依赖关系 先cd进你项目所在的目录(简介里面可以直接复制路径) 利用vim创建Podfile文件$ vim Podfile 然后输入:12345platform :ios, '10.0'target 'TargetName' dopod 'AFNetworking', '~> 3.0'end 文字的意思是,当前AFNetworking支持的iOS最高版本是iOS 10.0,’TargetName’为你项目的名称,要下载的AFNetworking版本是3.0保存退出。 运行$ pod install完成后会出现提示使用’XXX.xcworkspace’文件来代替之前的’XXX.xcodeproj’文件打开项目。打开项目后会发现里面有了我们想要加进来的库,可以#import进来了。 增加新的库如果使用过程中我还想添加其他的库怎么办,只要在Podfile里面接着添加,然后终端再执行pod install就可以了。 更新CocoaPods中的库第三方库们都有人在维护升级,我们需要隔断时间就要更新下我们工程中第三方库的版本。只需要终端输入命令pod update就可以了。 删除CocoaPods中的某些库当我们需要去掉某个第三方库时,只需要在Podfile删除该引入该库的语句,然后执行pod update或者pod install就可以了。 升级CocoaPodssudo gem install cocoapods","categories":[{"name":"iOS","slug":"iOS","permalink":"http://codingdoge.cn/categories/iOS/"}],"tags":[{"name":"Tool","slug":"Tool","permalink":"http://codingdoge.cn/tags/Tool/"}]},{"title":"有限状态机在iOS中的应用","slug":"title: 有限状态机在iOS中的应用","date":"2017-07-23T15:47:00.000Z","updated":"2018-01-13T05:06:26.865Z","comments":true,"path":"2017/07/23/title: 有限状态机在iOS中的应用/","link":"","permalink":"http://codingdoge.cn/2017/07/23/title: 有限状态机在iOS中的应用/","excerpt":"","text":"有限状态机(Finite-State Machine, FSM),是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。 其实我们平常和很多状态机都打过交道,比如正则表达式、网络协议(如TCP协议状态机)、游戏设计、字符串匹配等等,可能大多数时候我们都没意识到,接下来我们简略了解下状态机。 基本概念 状态(state): 指的是对象在其生命周期中的一种状况,处于某个特定状态中的对象必然会满足某些条件、执行某些动作或者是等待某些事件,而且状态是相对稳定的,如果没有外部条件的触发,一个状态会一直持续下去。 事件(event): 指的是对状态机来讲是有意义的那些事情。事件通常会引起状态的变迁,促使状态机从一种状态切换到另一种状态。 转换(transition): 指的是两个状态之间的一种关系,表明对象将在第一个状态中执行一定的动作,并将在某个事件发生的同时某个特定条件满足时进入第二个状态。 动作(action): 指的是状态机中可以执行的那些操作,当事件被满足或者状态变迁时执行动作,动作不是必需的。 如下图状态表: 关于状态机的详细知识可以参考:UML状态图的实用C/C++设计ERLANG 在iOS中的使用背景假设我们要设计一款网络视频播放器,有基本的播放、暂停功能,当缓冲好时可以进行播放,当URL错误或者视频资源错误时播放失败,我们发现,当我们去描述一个事物以及它的功能时,总是离不开它的状态,如这个播放器,我们可以定义它有播放失败、缓冲中、将要播放…等等状态。 设计我们可以用状态机去实现这样的功能。 先设定播放器可能出现的状态: 12345678enum VideoPlayerState { case failed, // 播放失败 buffering, // 缓冲中 readyToPlay, // 将要播放 playing, // 播放中 paused, // 播放暂停 finished // 播放完毕} 然后定义当状态发生变换后,针对某个状态我希望它去执行一些逻辑里的动作 1234567891011121314151617181920var state: VideoPlayerState = .paused { didSet { switch state { case .failed: // 当视频加载失败时,我希望去执行的一些事件,比如弹出提示框 popReminderView() case .buffering: case .readyToPlay: case .playing: case .paused: case .finished: } }} 在写我们的业务逻辑时,相应的去更改播放器的状态 123456// 比如我们重写网络层的一些方法,当缓冲好时准备播放networkRequestCompletion() { state = .readyToPlay} 这样,通过state我们能很清晰的知道现在播放器是什么样应该做什么事,在我们的业务逻辑中,当状态变化时通过didSet我们能很方便的去响应对应状态下应该执行的行为。 总结整篇文章质量或许不是很高,因为还没有大量的代码实践让我去有一个更深更全的体会,但是希望能带给我自己和读者们一点抛砖引玉的效果。我们在编码、设计过程中,多去思考一些,什么地方用什么样的模式更好,比如状态机,来使我们的代码更解耦,易维护,高扩展。 这里有一篇关于Objective-c状态机的实现,更抽象,其中用到的枚举值自动转字符串通用方案很有意思,同时可以参考这篇《iOS开发高级:使用宏定义macros》关于宏定义的使用。这篇《iOS APP 架构漫谈二》列举了很具体的运用场景,可以参考。同时参考的文章有: 有限状态机 有限状态机(FSM)的设计与实现 关于状态记的开源库推荐: Objective-C: TransitionKit Swift:SwiftTask","categories":[{"name":"iOS","slug":"iOS","permalink":"http://codingdoge.cn/categories/iOS/"}],"tags":[]},{"title":"视频开发","slug":"title: 视频开发","date":"2017-07-23T15:47:00.000Z","updated":"2018-01-13T05:06:57.320Z","comments":true,"path":"2017/07/23/title: 视频开发/","link":"","permalink":"http://codingdoge.cn/2017/07/23/title: 视频开发/","excerpt":"","text":"下面會介紹視頻的一些基本知識,和在iOS上實現視頻播放和緩存的幾種方案。 软解码和硬解码GPU解码就是所谓的硬解码,CPU解码就是软解码。iOS提供的播放器类使用的是硬解码,所以视频播放对CPU不会有很大的压力,但是支持的播放格式比较单一,一般就是MP4、MOV、M4V这几个。 HTTP Live StreamingHTTP Live Streaming(缩写是 HLS)是一个由苹果公司提出的基于HTTP的流媒体网络传输协议。它的工作原理是把整个流分成一个个小的基于HTTP的文件来下载,每次只下载一些。当媒体流正在播放时,客户端可以选择从许多不同的备用源中以不同的速率下载同样的资源,允许流媒体会话适应不同的数据速率。支持的视频流编码为H.264。我们在视频网站上看到的M3U8后缀的播放链接就是使用HLS协议的视频。HLS优点,1、看完一段缓存一段,防止只看一段视频但是把整个视频文件都缓存下来的用户,减少服务器压力和节省流量。2、根据用户网速切换不同的码率,兼顾流程性和清晰度。 播放实现视频播放的两个方案。 一、自己实现对数据编码解码可以在一些开源播放器上进行二次开发,如Bilibili的ijkplayer,或者直接对FFmpeg开发,优点在整个播放过程可控,为后续进行缓存、流量控制、码率切换等开发提供了基础,缺点是复杂,要求高,工程量大。 二、AVFoundationMedia Assets, Playback and Editing. 使用Apple自有框架。 AVAsset AVAsset is an abstract, immutable class used to model timed audiovisual media such as videos and sounds. An asset may contain one or more tracks that are intended to be presented or processed together, each of a uniform media type, including but not limited to audio, video, text, closed captions, and subtitles. Audiovisual media的资源类,通常通过AVURLAsset用URL来实例化,可以用Atom Inspector(一个Apple提供的用来查看视频信息的工具)来观察一个视频的属性,再去AVAsset中对应其属性。 AVAsset属性 视频文件属性 duration: CMTime duration, timescale时长和时间尺度 preferredRate: Float 默认速度 preferredVolume: Float 默认音量 creationDate: AVMetadataItem? 视频创建时间 tracks: [AVAssetTrack] 轨道 trackGroups: [AVAssetTrackGroup] 轨道组 lyrics: String? 当前语言环境合适的歌词 metadata: [AVMetadataItem] 元数据 AVPlayer An AVPlayer is a controller object used to manage the playback and timing of a media asset. It provides the interface to control the player’s transport behavior such as its ability to play, pause, change the playback rate, and seek to various points in time within the media’s timeline. You can use an AVPlayer to play local and remote file-based media, such as QuickTime movies and MP3 audio files, as well as audiovisual media served using HTTP Live Streaming. AVPlayer是一个控制对象用于管理媒体asset的播放,它提供了相关的接口控制播放器的行为,比如:播放、暂停、改变播放的速率、跳转到媒体时间轴上的某一个点(简单理解就是实现拖动功能显示对应的视频位置内容)。我们能够使用AVPlayer播放本地和远程的媒体文件(使用 HTTP Live Streaming),比如: QuickTime movies 和 MP3 audio files,所以AVPlayer可以满足音视频播放的基本需求。 AVFoundation的层次 AVPlayerItem AVPlayerItem models the timing and presentation state of an asset played by an AVPlayer object. It provides the interface to seek to various times in the media, determine its presentation size, identify its current time, and much more. AVPlayerItem是一个负责处理AVAsset的资源并通过AVPlayer来播放的载体,提供了seek、确定显示大小、ID、时间等的接口。 AVPlayerLayer AVPlayerLayer is a subclass of CALayer to which an AVPlayer object can direct its visual output. It can be used as the backing layer for a UIView or NSView or can be manually added to the layer hierarchy to present your video content on screen. 负责AVPlayer的视频输出展示。 依赖关系图 简单使用1234567891011class AVPlayerTestView: UIView { let view: UIView? = nil func initPlayerView() { guard let url = URL.init(string: "http://meipu1.video.meipai.com/5e81c08e-2850-4fbd-bfc4-4ded297f9f1c.mp") else { return } let asset = AVAsset.init(url: url) let item = AVPlayerItem.init(asset: asset) let player = AVPlayer.init(playerItem: item) let playerLayer = AVPlayerLayer.init(layer: player) view?.layer.addSublayer(playerLayer) }} 设置好一个AVPlayer的依赖关系和输出图层后,AVPlayerItem会根据你的URL去请求数据,自己内部做缓冲然后播放。我们需要做的是用KVO监听AVPlayerItem内部几个关键属性的状态,然后做出我们的处理。 AVPlayerItem属性 状态 status: AVPlayerItemStatus .unknown: AVPlayerItemStatus 未知状态 .readyToPlay: AVPlayerItemStatus 准备好去播放 .failed: AVPlayerItemStatus 资源无法被播放 loadedTimeRanges: [NSValue] 加载了的资源的时间范围(一般用来更新缓冲UI) playbackBufferEmpty: Bool 没有缓冲数据 playbackLikelyToKeepUp: Bool 有足够的缓冲大致能播放无卡顿 General State Observations: You can use Key-value observing (KVO) to observe state changes to many of the player’s dynamic properties, such as its currentItem or its playback rate. You should register and unregister for KVO change notifications on the main thread. This avoids the possibility of receiving a partial notification if a change is being made on another thread. AVFoundation invokes observeValue(forKeyPath:of:change:context:) on the main thread, even if the change operation is made on another thread. 基本状态观察者:你能够使用KVO来观察player动态属性的状态改变,比如像: currentItem 或者它的播放速度。我们应该在主线程注册和去除KVO,这能够避免如果在其它线程发送改变而导致接收局部通知,当发生通知,AVFoundation将在主线程调用observeValue(forKeyPath:of:change:context:) 方法,即使是在其他线程发生。 KVO能够很好的观察生成的状态,但是并不能够观察播放时间的改变,所以AVPlayer提供了两个方法来观察时间的改变: 1234567891011/* @param interval 调用block的时间间隔 @param queue 推荐使用串行队列,放在主线程就行了,并行队列会产生不明确的结果*/func addPeriodicTimeObserver(forInterval interval: CMTime, queue: DispatchQueue?, using block: @escaping (CMTime) -> Void) -> Any { // 可以在里面去设置控制状态,刷新进度UI}func addBoundaryTimeObserver(forTimes times: [NSValue], queue: DispatchQueue?, using block: @escaping () -> Void) -> Any Tips: 创建多个AVPlayerLayer只有最近的layer才会显示视频帧 可以创建多个AVPlayerItem来替换AVPlayer的当前item,func replaceCurrentItem(with item: AVPlayerItem?) 监听后要注意控制监听的生命周期 缓存Apple自有的框架是没有提供缓存功能的,AVPlayer也没有提供直接获取其下载数据的接口,所以想做缓存只能自己来完整的实现。下面有几个方案。 一、自己实现的播放器这种情况大多是根据下载来的数据解码播放,下载的时候做下缓存就好了 二、自带播放器+LocalServer在iOS本地开启Local Server服务,然后MPMoviePlayerController请求本地Local Server服务。本地Local Server服务再不停的去对应的视频地址获取视频流。本地Local Server请求的时候,就可以把视频流缓存在本地。Demo来源:Code4App 三、AVPlayer+AVMutableComposition+AVAssetExportSession原理是直接给AVPlayer传URL,让其内部自己去处理数据下载,然后通过AVMutableComposition和AVAssetExportSession从AVAsset提取视频的数据进行缓存。 AVMutableComposition AVMutableComposition is a mutable subclass of AVComposition you use when you want to create a new composition from existing assets. You can add and remove tracks, and you can add, remove, and scale time ranges. 作用是从现有的AVAsset中创建出一个新的AVComposition,使用者能够从别的asset中提取他们的音频轨道或视频轨道,并且把它们添加到新建的composition中。 AVAssetExportSession An AVAssetExportSession object transcodes the contents of an AVAsset source object to create an output of the form described by a specified export preset. 作用是把AVAsset解码输出到本地文件中。 关键需要把原先的AVAsset(AVURLAsset)实现的数据提取出来后拼接成另一个AVAsset(AVComposition)的数据然后解码输出,由于通过网络url下载下来的视频没有保存视频的原始数据(苹果没有暴露接口给我们获取),下载后播放的avasset不能使用AVAssetExportSession输出到本地文件,要曲线地把下载下来的视频通过重构成另外一个AVAsset实例才能输出。 12345678910111213141516171819202122232425NSString *documentDirectory = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];NSString *myPathDocument = [documentDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.mp4",[_source.videoUrl MD5]]];NSURL *fileUrl = [NSURL fileURLWithPath:myPathDocument];if (asset != nil) {AVMutableComposition *mixComposition = [[AVMutableComposition alloc]init];AVMutableCompositionTrack *firstTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];[firstTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, asset.duration) ofTrack:[[asset tracksWithMediaType:AVMediaTypeVideo]objectAtIndex:0] atTime:kCMTimeZero error:nil];AVMutableCompositionTrack *audioTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];[audioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, asset.duration) ofTrack:[[asset tracksWithMediaType:AVMediaTypeAudio]objectAtIndex:0] atTime:kCMTimeZero error:nil];AVAssetExportSession *exporter = [[AVAssetExportSession alloc]initWithAsset:mixComposition presetName:AVAssetExportPresetHighestQuality];exporter.outputURL = fileUrl;if (exporter.supportedFileTypes) {exporter.outputFileType = [exporter.supportedFileTypes objectAtIndex:0] ;exporter.shouldOptimizeForNetworkUse = YES;[exporter exportAsynchronouslyWithCompletionHandler:^{}];}} 四、AVPlayer+AVAssetResourceLoaderAVAssetResourceLoadingRequest An AVAssetResourceLoader object mediates resource requests from an AVURLAsset object with a delegate object that you provide. When a request arrives, the resource loader asks your delegate if it is able to handle the request and reports the results back to the asset.AVAssetResourceLoader协调来自AVURLAsset的资源请求,你需要实现它的delegate。当收到一个请求时,ResourceLoader询问你的delegate是否能处理并将结果返回给asset。 AVPlayer和AVAssetResourceLoader的层次结构 AVAssetResourceLoader通过你提供的委托对象去调节AVURLAsset所需要的加载资源。而很重要的一点是,AVAssetResourceLoader仅在AVURLAsset不知道如何去加载这个URL资源时才会被调用,就是说你提供的委托对象在AVURLAsset不知道如何加载资源时才会得到调用。一般我们可以更改URL的scheme用来隐藏真实的URL。如: 参考iOS开发系列–音频播放、录音、视频播放、拍照、视频录制AVplayer实现播放本地和网络视频(Swift3.0)iOS视频流开发(2)—视频播放iOS音频播放 (九):边播边缓存iOS音视频实现边下载边播放AVFoundation(二):核心AVAssetAVFoundation编程指南2-用AVPlayer播放视频AV Foundation系列(五)媒体组合","categories":[{"name":"iOS","slug":"iOS","permalink":"http://codingdoge.cn/categories/iOS/"}],"tags":[{"name":"Swift","slug":"Swift","permalink":"http://codingdoge.cn/tags/Swift/"}]},{"title":"xcode常用快捷键及其他功能","slug":"title: xcode常用快捷键及其他功能","date":"2017-07-23T15:47:00.000Z","updated":"2018-01-13T05:05:53.756Z","comments":true,"path":"2017/07/23/title: xcode常用快捷键及其他功能/","link":"","permalink":"http://codingdoge.cn/2017/07/23/title: xcode常用快捷键及其他功能/","excerpt":"","text":"快捷键 Command+R生成并运行程序Command+B只生成程序而不运行它Command+T新建选项卡 标记 // MARK: - \\// TODO: \\// FIXME: \\ 小功能 Product>Clean删除生成的中间文件","categories":[{"name":"iOS","slug":"iOS","permalink":"http://codingdoge.cn/categories/iOS/"}],"tags":[{"name":"Tool","slug":"Tool","permalink":"http://codingdoge.cn/tags/Tool/"}]},{"title":"OC中ARC forbids explicit message send of '...'错误","slug":"title: OC中ARC forbids explicit message send of '...'错误 ","date":"2017-07-23T15:25:00.000Z","updated":"2018-01-14T17:00:06.700Z","comments":true,"path":"2017/07/23/title: OC中ARC forbids explicit message send of '...'错误 /","link":"","permalink":"http://codingdoge.cn/2017/07/23/title: OC中ARC forbids explicit message send of '...'错误 /","excerpt":"","text":"转自CSDN hahahacff有所整理 ARC forbids explicit message send of’retainCount’ 同’release’等等 很显然,是ARC的问题。错误原因:在创建工程的时候点选了“Use Automatic Reference Counting”选项,但是又调用了对象的retainCount方法 ARC是什么?ARC是iOS 5推出的新功能,全称叫ARC(AutomaticReferenceCounting)。简单地说,就是代码中自动加入了retain/release,原先需要手动添加的用来处理内存管理的引用计数的代码可以自动地由编译器完成了。该机制在iOS 5/ Mac OS X 10.7 开始导入,利用 Xcode4.2可以使用该机制。简单地理解ARC,就是通过指定的语法,让编译器(LLVM3.0)在编译代码时,自动生成实例的引用计数管理部分代码。有一点,ARC并不是GC,它只是一种代码静态分析(StaticAnalyzer)工具。 解决方法 选择要项目,双击中间的工程名称,进入build setting 将中间的Objective-C Automatic Reference Counting改为no","categories":[{"name":"iOS","slug":"iOS","permalink":"http://codingdoge.cn/categories/iOS/"}],"tags":[{"name":"Bug","slug":"Bug","permalink":"http://codingdoge.cn/tags/Bug/"},{"name":"OC","slug":"OC","permalink":"http://codingdoge.cn/tags/OC/"},{"name":"转载","slug":"转载","permalink":"http://codingdoge.cn/tags/转载/"}]},{"title":"解决Arduino CH34x系列在macOS Sierra中找不到串口的问题","slug":"title: 解决Arduino CH34x系列在macOS Sierra中找不到串口的问题","date":"2017-07-23T15:06:00.000Z","updated":"2018-01-12T14:23:28.810Z","comments":true,"path":"2017/07/23/title: 解决Arduino CH34x系列在macOS Sierra中找不到串口的问题/","link":"","permalink":"http://codingdoge.cn/2017/07/23/title: 解决Arduino CH34x系列在macOS Sierra中找不到串口的问题/","excerpt":"","text":"毕设做Arduino开发,一开始买了淘宝上慧净自己改的Arduino UNO国产板子,回来插在mac上读不出串口,安装了它附带的驱动还是读不出,同学买了原装板说一插上就能读出串口,都没自己装驱动,为了省麻烦,直接跟商家换了原装板,这时候麻烦来了,板子在我电脑上读不出串口,在别人电脑上能读出来,别人的在我电脑上也读不出来…很崩溃,然后从各个角度debug,下面一个个步骤来,基本能解决。 安装Arduino IDE上面是官网IDE下载的连接,大概是因为GFW的原因下载速度奇慢,大家也可以去搜Arduino中文社区,里面有好人做了*度网盘的下载种子,速度能快点。IDE一般自带驱动,如果在串口里没发现,可以试试在 D7389412-ED23-427D-82E7-4E1C26114D67 这个系统报告里看看USB下面能不能读出 也可以在bash里输入 1ls /dev/tty* 如果有类似的即可 2C66575E-D0DD-4DEA-9F34-8F2E1CF4DA90 官方驱动下载如果都找不到的话可以去沁恒官方下载CH341SER驱动,安装后再查看一遍(安装驱动完会要求重启). 更改SIP设置 Apple在10.11中全面启用了名为 System Integrity Protection (SIP) 的系统完整性保护技术. 受此影响, 未经签名的第三方kext及经过修改的原版kext将无法正常加载, 大部分系统文件即使在root用户下也无法直接进行修改. 前面安装驱动不成功大部分是因为驱动文件冲突导致,所以在此之前先删除之前安装的驱动文件: 123sudo rm -rf /System/Library/Extensions/usb.kextsudo rm -rf /Library/Extensions/usbserial.kextsudo rm -rf _private_var_db_receipts/com.wch.* 然后参考CH340 CH341 serial adapters fix for El Capitan OS X可以通过以下步骤修改SIP设置来安装第三方kext: 重启OS X 并且立即按住 Command+ R 来来进入恢复模式 在恢复模式中, 菜单栏上面找到终端(Terminal)并打开 在终端中键入命令csrutil enable —without kext 看到成功的提示信息之后, 输入reboot重启系统 注: —without kext 中的 - 有两条Command 就是 ⌘ 图标 通过以上的操作之后, 采用CH340/1 系列芯片的Arduino开发板又可以被识别出来了. 如果还是无法识别, 请重新安装一次CH341SER驱动. 最后如果还不行(我的就是到这里还不行),请看看你的板子是否接触不良,反复摩擦尝试看能不能读出串口,我之前也试过这个办法,但是没有用,后面偶然也是必然下反应过来,反反复复插USB拔出查看串口状态,终于解决了(无良商家害人) 参考:如何重置 Mac 上的 NVRAM","categories":[{"name":"OS","slug":"OS","permalink":"http://codingdoge.cn/categories/OS/"}],"tags":[{"name":"Bug","slug":"Bug","permalink":"http://codingdoge.cn/tags/Bug/"}]},{"title":"《TCP/IP详解》笔记—第1章 概述","slug":"title: 《TCP-IP详解》笔记—第1章 概述","date":"2017-07-23T15:04:00.000Z","updated":"2018-01-12T14:22:22.232Z","comments":true,"path":"2017/07/23/title: 《TCP-IP详解》笔记—第1章 概述/","link":"","permalink":"http://codingdoge.cn/2017/07/23/title: 《TCP-IP详解》笔记—第1章 概述/","excerpt":"","text":"《TCP/IP详解》笔记—第1章 概述 一个互连网就是一组通过相同协议族互连在一起的网络。 分层TCP/IP通常被认为是一个四层协议系统: WX20170320-171317@2x 网络层和运输层之间的区别是最为关键的:网络层(IP)提供点到点的服务,而运输层(TCP和UDP)提供端到端的服务。 在TCP/IP协议族中,网络层IP提供的是一种不可靠的服务。也就是说,它只是尽可能快地把分组从源结点送到目的结点,但是并不提供任何可靠性保证。而另一方面,TCP在不可靠的IP层上提供了一个可靠的运输层。为了提供这种可靠的服务,TCP采用了超时重传、发送和接收端到端的确认分组等机制。 路由器为不同类型的物理网络提供连接。 一个主机也可以有多个接口,但一般不称作路由器,除非它的功能只是单纯地把分组从一个接口传送到另一个接口。 互联网的目的之一是在应用程序中隐藏所有的物理细节。 连接网络的另一个途径是使用网桥。网桥是在链路层上对网络进行互连,而路由器则是在网络层上对网络进行互连。 互联网的地址 47F26D90-176D-4C0A-A315-6A9E199E79F6 这些 32 位的地址通常写成四个十进制的数,其中每个整数对应一个字节。这种表示方法称作“点分十进制表示法(Dotted decimal notation)”。 70807FBE-712D-4A5C-97F2-BBCA12C1F11D 封装TCP传给IP的数据单元称作TCP报文段或简称为TCP段(TCP segment)。IP传给网络接口层的数据单元称作IP数据报(IP datagram)。通过以太网传输的比特流称作帧(Frame)。 分用当目的主机收到一个以太网数据帧时,数据就开始从协议栈中由底向上升,同时去掉各层协议加上的报文首部。每层协议盒都要去检查报文首部中的协议标识,以确定接收数据的上层协议。这个过程称作分用(Demultiplexing)。 客户-服务器模型大部分网络应用程序在编写时都假设一端是客户,另一端是服务器,其目的是为了让服务器为客户提供一些特定的服务。一般来说,TCP服务器是并发的,而UDP服务器是重复的。 习题 请计算最多有多少个A类、B类和C类网络号。 用匿名FTP(见27.3节)从主机nic.merit.edu上获取文件nsfnet_statistics_history.netcount。该文件包含在NSFNET网络上登记的国内和国外的网络数。画一坐标系,横坐标代表年,纵坐标代表网络总数的对数值。纵坐标的最大值是习题1.1的结果。如果数据显示一个明显的趋势,请估计按照当前的编址体制推算,何时会用完所有的网络地址(3.10节讨论解决该难题的建议)。 获取一份主机需求RFC拷贝[Braden 1989a],阅读有关应用于TCP/IP协议族每一层的稳健性原则。这个原则的参考对象是什么? 获取一份最新的赋值RFC拷贝。“quote of the day”协议的有名端口号是什么?哪个RFC对该协议进行了定义? 如果你有一个接入TCP/IP互联网的主机帐号,它的主IP地址是多少?这台主机是否接入了Internet?它是多接口主机吗? 获取一份RFC 1000的拷贝,了解RFC这个术语从何而来。 与Internet协会联系,isoc@isoc.org或者+170 3648 9888,了解有关加入的情况。 用匿名FTP从主机is.internic.net处获取文件about-internic/information-about-the-internic。 部分习题答案 答案是:27-2(126)+214-2(16 382)+221-2(2 097 150)=2 113 658。每一部分都减去2是因为全0或全1网络ID是非法的。 图D-1显示了直到1993年8月的有关数据。如果网络数继续呈指数增长的话,虚线估计了2000年可能达到的最大的网络数。 “自由地接收,保守地发送。” 资料来源于 即时通讯网,仅做学习参考","categories":[{"name":"Network","slug":"Network","permalink":"http://codingdoge.cn/categories/Network/"}],"tags":[{"name":"Reading Notes","slug":"Reading-Notes","permalink":"http://codingdoge.cn/tags/Reading-Notes/"}]},{"title":"STL常见用法 — map","slug":"title: STL常见用法 — map","date":"2017-07-21T05:10:00.000Z","updated":"2018-01-14T17:04:04.187Z","comments":true,"path":"2017/07/21/title: STL常见用法 — map/","link":"","permalink":"http://codingdoge.cn/2017/07/21/title: STL常见用法 — map/","excerpt":"","text":"STL常见用法 — 代码敲得少,STL总是用的不熟,便自己整理一些用法,有眼缘的顺手用着吧。 简介maps是一个关联容器,用来存储key-value式的元素,提供一对一hash。内部的实现,自建一颗红黑树,这棵树具有对数据自动排序的功能。比如一个班级中,每个学生的学号和他的姓名就存在一对一映射的关系。 第一个值是关键字(key),每个关键字只能在map中出现一次 第二个值为关键字的值(value) 用法头文件12345678#include <map>template< class Key, class T, class Compare = std::less<Key>, class Allocator = std::allocator<std::pair<const Key, T> >> class map; 构造函数12345namespace pmr { template <class Key, class T, class Compare = std::less<Key>> using map = std::map<Key, T, Compare, std::pmr::polymorphic_allocator<std::pair<const Key,T>>>} 成员函数 成员函数 实现操作 begin 返回一个起始的迭代器 end 返回一个末尾的迭代器 empty 检查容器是否为空 size 返回容器内元素个数 clear 清除容器内容 insert 插入元素或节点 erase 删除元素 swap 交换内容 count 返回指定key的元素个数 find 通过key查找元素 用法演示1234567891011121314151617181920#include <map>void mapTest(){ std::map<int, int> m; // 构造函数 m.insert(pair<int, int>(1, 10)); // 插入元素 m.insert(pair<int, int>(2, 30)); m.insert(pair<int, int>(4, 50)); map<int, int>::iterator it; // 迭代器 it = m.find(3); // 通过key查找元素 if (it == m.end()) // 如果返回值为尾部迭代器则无此元素 { cout << "Not find" << endl; m.insert(pair<int, int>(3, 20)); } for (it = m.begin(); it != m.end(); it++) // 通过迭代器遍历map容器 { cout << it->first << " " << it->second << endl; // 通过迭代器访问元素的key-value }} 参考:C/C++ - Map (STL) 用法與心得完全攻略","categories":[{"name":"CPP","slug":"CPP","permalink":"http://codingdoge.cn/categories/CPP/"}],"tags":[{"name":"STL","slug":"STL","permalink":"http://codingdoge.cn/tags/STL/"}]},{"title":"STL常见用法 — vector","slug":"title: STL常见用法 — vector","date":"2017-07-21T05:06:00.000Z","updated":"2018-01-14T17:04:41.751Z","comments":true,"path":"2017/07/21/title: STL常见用法 — vector/","link":"","permalink":"http://codingdoge.cn/2017/07/21/title: STL常见用法 — vector/","excerpt":"","text":"代码敲得少,STL总是用的不熟,便自己整理一些用法,有眼缘的顺手用着吧。 简介vector是线性容器,它的元素严格的按照线性序列排序,和动态数组很相似,和数组一样,它的元素存储在一块连续的存储空间中,这也意味着我们不仅可以使用迭代器(iterator)访问元素,还可以使用指针的偏移方式访问,和常规数组不一样的是,vector能够自动存储元素,可以自动增长或缩小存储空间。 用法头文件1#include <vector> 构造函数12345678// 1template<class T, class Allocator = std::allocator<T> > class vector;// 2namespace pmr { template <class T> using vector = std::vector<T, std::pmr::polymorphic_allocator<T>>;} std::vector is a sequence container that encapsulates dynamic size arrays. std::pmr::vector is an alias template that uses a polymorphic allocator 成员函数 成员函数 实现操作 c.assign(beg,end) 将[beg; end)区间中的数据赋值给c c.assign(n,elem) 将n个elem的拷贝赋值给c c.at(idx) 传回索引idx所指的数据,如果idx越界,抛出out_of_range c.back() 传回最后一个数据,不检查这个数据是否存在 c.begin() 传回迭代器重的可一个数据 c.capacity() 返回容器中数据个数 c.clear() 移除容器中所有数据 c.empty() 判断容器是否为空 c.end() 指向迭代器中的最后一个数据地址 c.erase(pos) 删除pos位置的数据,传回下一个数据的位置 c.erase(beg,end) 删除[beg,end)区间的数据,传回下一个数据的位置 c.front() 传回第一个数据 get_allocator 使用构造函数返回一个拷贝 c.insert(pos,elem) 在pos位置插入一个elem拷贝,传回新数据位置 c.insert(pos,n,elem) 在pos位置插入n个elem数据。无返回值 c.insert(pos,beg,end) 在pos位置插入在[beg,end)区间的数据。无返回值 c.max_size() 返回容器中最大数据的数量 c.pop_back() 删除最后一个数据 c.push_back(elem) 在尾部加入一个数据 c.rbegin() 传回一个逆向队列的第一个数据 c.rend() 传回一个逆向队列的最后一个数据的下一个位置 c.resize(num) 重新指定队列的长度 c.reserve() 保留适当的容量 c.size() 返回容器中实际数据的个数 c1.swap(c2) 将c1和c2元素互换 swap(c1,c2) 同上操作 vector c 创建一个空的vector vector c1(c2) 复制一个vector vector c(n) 创建一个vector,含有n个数据,数据均已缺省构造产生 vector c(n, elem) 创建一个含有n个elem拷贝的vector。 vector c(beg,end) 创建一个以[beg;end)区间的vector c.~ vector () 销毁所有数据,释放内存 用法演示 使用reverse将元素翻转:需要头文件#include<algorithm> 1reverse(vec.begin(),vec.end()); 将元素翻转(在vector中,如果一个函数中需要两个迭代器,一般后一个都不包含) 使用sort排序:需要头文件#include,sort(vec.begin(),vec.end());(默认是按升序排列,即从小到大).可以通过重写排序比较函数按照降序比较,如下: 定义排序比较函数: 1234bool Comp(const int &a,const int &b){ return a>b;} 调用时:sort(vec.begin(),vec.end(),Comp),这样就降序排序。","categories":[{"name":"CPP","slug":"CPP","permalink":"http://codingdoge.cn/categories/CPP/"}],"tags":[{"name":"STL","slug":"STL","permalink":"http://codingdoge.cn/tags/STL/"}]},{"title":"常用宏定义","slug":"title: 常用宏定义","date":"2017-07-17T06:08:00.000Z","updated":"2018-01-13T05:07:28.710Z","comments":true,"path":"2017/07/17/title: 常用宏定义/","link":"","permalink":"http://codingdoge.cn/2017/07/17/title: 常用宏定义/","excerpt":"","text":"iOS开发高级:使用宏定义macros 123456789101112131415161718192021222324252627282930313233343536373839404142434445//1. 打印日志#ifdef DEBUG# define DLog(...) NSLog(__VA_ARGS__)#else# define DLog(...)#endif//2. 获取屏幕 宽度、高度#define kScreenWidth ([UIScreen mainScreen].bounds.size.width)#define kScreenHeight ([UIScreen mainScreen].bounds.size.height)//3. 颜色#define RGB(r, g, b, a) [UIColor colorWithRed:r/255.0f green:g/255.0f blue:b/255.0f alpha:a]#define HEXCOLOR(c) [UIColor colorWithRed:((c>>16)&0xFF)/255.0f green:((c>>8)&0xFF)/255.0f blue:(c&0xFF)/255.0f alpha:1.0f]//背景色 #define BACKGROUND_COLOR [UIColor colorWithRed:242.0/255.0 green:236.0/255.0 blue:231.0/255.0 alpha:1.0] //清除背景色 #define CLEARCOLOR [UIColor clearColor] //4.加载图片宏:#define LOADIMAGE(file,type) [UIImage imageWithContentsOfFile:[[NSBundle mainBundle]pathForResource:file ofType:type]]//5. NavBar高度#define NavigationBar_HEIGHT 44//6. 获取系统版本#define IOS_VERSION [[[UIDevice currentDevice] systemVersion] floatValue]#define CurrentSystemVersion [[UIDevice currentDevice] systemVersion]//7. 判断是真机还是模拟器#if TARGET_OS_IPHONE //iPhone Device#endif#if TARGET_IPHONE_SIMULATOR //iPhone Simulator#endif//8. 设置View的tag属性#define VIEWWITHTAG(_OBJECT, _TAG) [_OBJECT viewWithTag : _TAG]//9. GCD#define BACK(block) dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block)#define MAIN(block) dispatch_async(dispatch_get_main_queue(),block)//10. NSUserDefaults 实例化#define USER_DEFAULT [NSUserDefaults standardUserDefaults]","categories":[{"name":"iOS","slug":"iOS","permalink":"http://codingdoge.cn/categories/iOS/"}],"tags":[{"name":"OC","slug":"OC","permalink":"http://codingdoge.cn/tags/OC/"}]},{"title":"STL常见用法 — queue","slug":"title: STL常见用法 — queue","date":"2017-07-17T03:19:00.000Z","updated":"2018-01-14T17:04:46.168Z","comments":true,"path":"2017/07/17/title: STL常见用法 — queue/","link":"","permalink":"http://codingdoge.cn/2017/07/17/title: STL常见用法 — queue/","excerpt":"","text":"代码敲得少,STL总是用的不熟,便自己整理一些用法,有眼缘的顺手用着吧。 简介 队列(queue)是一种特殊的线性表,是一种先进先出(First In First Out)的数据结构,允许在队列末尾插入元素,队列头取出元素,在STL中是用list或者deque实现,封闭头部即可。 用法头文件1#include <queue> 构造函数1template <class T, class Container = deque<T> > class queue; 队列适配器默认用deque容器实现,也可以指定使用list容器来实现 123queue <Elem> q; // 创建一个空的queue,默认使用deque容器queue <Elem, list<Elem> > q; // 使用list容器queue <Elem> q1(q2); // 复制q2 成员函数 成员函数 实现操作 Elem& back() 返回队列最后一个元素 bool empty()const 如果队列为空,返回true,否则返回false Elem& front() 返回队列第一个元素 void pop() 移除队列中的第一个元素 void push(const Elem& e) 在队列末尾插入元素e size_type size()const 返回队列中的元素数目 用法演示1234567891011121314151617181920212223#include <stdio.h> #include <queue> using namespace std; int main() { int n, m, size; queue <int> q; //定义队列q q.push(1); q.push(2); //将1 、2压入队列 while (!q.empty()) //判断队列是否为空 { n = q.front(); //返回队列头部数据 m = q.back(); //返回队列尾部数据 size = q.size(); //返回队列里的数据个数 q.pop(); //队列头部数据出队 printf(\"%d %d %d\\n\", n, m, size); } return 0; }","categories":[{"name":"CPP","slug":"CPP","permalink":"http://codingdoge.cn/categories/CPP/"}],"tags":[{"name":"STL","slug":"STL","permalink":"http://codingdoge.cn/tags/STL/"}]},{"title":"插件管理——Vundle","slug":"title: 插件管理——Vundle","date":"2017-07-17T03:18:00.000Z","updated":"2018-01-14T17:19:40.848Z","comments":true,"path":"2017/07/17/title: 插件管理——Vundle/","link":"","permalink":"http://codingdoge.cn/2017/07/17/title: 插件管理——Vundle/","excerpt":"","text":"都说Vim是程序员写给自己的编辑器,其中的情结可想而知。 vim因为其庞大而强劲的插件受到无比的推崇,而插件的查找和管理便成了一个问题。 Vundle便是一个Github上为了解决这个问题的项目(致敬贡献者们),使用步骤如下: 安装Vundle,在终端输入以下代码即可git clone http://github.com/gmarik/vundle.git ~/.vim/bundle/vundle ~/.vim 来自哪?在mac中Vim配置文件.vimrc在/usr/share/vim/下,一般是没有权限更改的,但是这个.vimrc是全局配置文件,我们只要更改用户配置文件即可 怎么查看/usr/? 显示隐藏文件,自行百度,终端中敲入代码即可。 Finder下或者桌面上的Go,文件夹输入/usr/。mac下的Vim用户配置文件默认是没有的,需要我们自行创建终端下输入123> mkdir ~/.vim> touch ~/.vimrc> ~/即为用户根目录。 在Vim Script选好插件在.vimrc文件中加一句Bundle plugin_name.vimrc示例:1234567891011set nocompatible \" be iMprovedfiletype off \" required!set rtp+=~/.vim/bundle/vundle/call vundle#rc()\" let Vundle manage Vundle\" required!Bundle 'gmarik/vundle'\" vim-scripts reposBundle 'vim-plugin-foo'Bundle 'vim-plugin-bar'filetype plugin indent on \" required! 执行Vundle安装命令1:BundleInstall Vundle的其它命令 :BundleList 列举出列表中(.vimrc中)配置的所有插件 :BundleInstall 安装列表中全部插件 :BundleInstall! 更新列表中全部插件 :BundleSearch foo 查找foo插件 :BundleSearch! foo 刷新foo插件缓存 :BundleClean 清除列表中没有的插件 :BundleClean! 清除列表中没有的插件 参考Git时代的VIM不完全使用教程使用Vundle来管理vim的插件zhongcq 的VIM配置","categories":[{"name":"Shell","slug":"Shell","permalink":"http://codingdoge.cn/categories/Shell/"}],"tags":[{"name":"Tool","slug":"Tool","permalink":"http://codingdoge.cn/tags/Tool/"}]},{"title":"漫谈iOS系列之:内存管理","slug":"title: 漫谈iOS系列之:内存管理","date":"2017-07-17T03:18:00.000Z","updated":"2018-01-14T14:05:07.599Z","comments":true,"path":"2017/07/17/title: 漫谈iOS系列之:内存管理/","link":"","permalink":"http://codingdoge.cn/2017/07/17/title: 漫谈iOS系列之:内存管理/","excerpt":"","text":"引用计数推荐一篇来自@杨萧玉的引用计数原理Blog 简介iOS中对内存管理的机制(堆内存),每一个对象都有一个与之关联的引用计数(Reference Counting)。当一个对象“被拥有”的时候引用计数+1,当一个对象引用计数为零时该对象被释放。 比拟比如上班,最早进入办公室的人需要开灯,之后进入办公室的人需要照明, 下班离开办公室的人不需要照明,最后离开办公室的人需要关灯。这样对应的引用计数就是:第一个人进入办公室开灯,引用计数是1。之后进入办公室需要照明引用计数是2。下班一个人离开办公室引用计数变成了1,最后一个离开了办公室,引用计数变成了0 。 引用计数如何储存 TaggedPointer一篇极好的文章总体来说,我的理解是如果一个对象使用了Tagged Pointer技术(比如NSString,NSNumber等),指针里面会直接存数据内容,不会再作为“指针”指向其它地址,从Runtime来理解就是不会使用isa指针,也就不会继承苹果的内存管理方式(Reference Counting)。判断当前对象是否在使用 TaggedPointer 是看标志位是否为1: 12345678910111213#if SUPPORT_MSB_TAGGED_POINTERS# define TAG_MASK (1ULL<<63)#else# define TAG_MASK 1inline bool objc_object::isTaggedPointer() {#if SUPPORT_TAGGED_POINTERS return ((uintptr_t)this & TAG_MASK);#else return false;#endif} isa 指针指针的内存空间很大,有时候可以优化指针,在指针中存储一部分内容。下面列出不同架构下的64位环境中isa指针结构: 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253union isa_t { isa_t() { } isa_t(uintptr_t value) : bits(value) { } Class cls; uintptr_t bits;#if SUPPORT_NONPOINTER_ISA# if __arm64__# define ISA_MASK 0x00000001fffffff8ULL# define ISA_MAGIC_MASK 0x000003fe00000001ULL# define ISA_MAGIC_VALUE 0x000001a400000001ULL struct { uintptr_t indexed : 1; uintptr_t has_assoc : 1; uintptr_t has_cxx_dtor : 1; uintptr_t shiftcls : 30; // MACH_VM_MAX_ADDRESS 0x1a0000000 uintptr_t magic : 9; uintptr_t weakly_referenced : 1; uintptr_t deallocating : 1; uintptr_t has_sidetable_rc : 1; uintptr_t extra_rc : 19;# define RC_ONE (1ULL<<45)# define RC_HALF (1ULL<<18) };# elif __x86_64__# define ISA_MASK 0x00007ffffffffff8ULL# define ISA_MAGIC_MASK 0x0000000000000001ULL# define ISA_MAGIC_VALUE 0x0000000000000001ULL struct { uintptr_t indexed : 1; uintptr_t has_assoc : 1; uintptr_t has_cxx_dtor : 1; uintptr_t shiftcls : 44; // MACH_VM_MAX_ADDRESS 0x7fffffe00000 uintptr_t weakly_referenced : 1; uintptr_t deallocating : 1; uintptr_t has_sidetable_rc : 1; uintptr_t extra_rc : 14;# define RC_ONE (1ULL<<50)# define RC_HALF (1ULL<<13) };# else // Available bits in isa field are architecture-specific.# error unknown architecture# endif// SUPPORT_NONPOINTER_ISA#endif}; 只有arm64架构的设备支持优化,下面列出了isa指针中变量对应的含义: 变量名 含义 indexed 0 表示普通的isa指针,1 表示使用优化,存储引用计数 has_assoc 表示该对象是否包含 associated object,如果没有,则析构时会更快 has_cxx_dtor 表示该对象是否有 C++ 或 ARC 的析构函数,如果没有,则析构时更快 shiftcls 类的指针 magic 固定值为 0xd2,用于在调试时分辨对象是否未完成初始化 weakly_referenced 表示该对象是否有过weak对象,如果没有,则析构时更快 deallocating 表示该对象是否正在析构 has_sidetable_rc 表示该对象的引用计数值是否过大无法存储在isa指针 extra_rc 存储引用计数值减一后的结果 散列表散列表来存储引用计数具体是用DenseMap类来实现,实现中有锁保证其安全性。 获取引用计数在MRC环境下可以使用retainCount方法获取某个对象的引用计数。在ARC环境下可以使用Core Foundation 库的CFGetRetainCount((__bridge CFTypeRef)(obj))方法和Runtime的_objc_rootRetainCount()方法来获取引用计数,也可以使用KVC技术来获取valueForKey:@"retainCount"。注意以上方法不是线程安全的。 注意NSString 定义的对象是保存在字符串常量区,没有用引用计数管理内存,如果输出其retainCount,为-1。 retainCount 注意其中的Do not use this method。 MRC(Manual Reference Counting) MRC从字面上理解就是手动管理引用计数,也就是手动管理内存。相关的内存管理方法有retain,release,autorelease,其中retain方法是对引用计数+1,相应的release是对引用计数-1,autorelease是将对象加入自动释放池,下文会讲到。 示例代码 1234567// 以预定Person类为例Person* person = [[Person alloc] init]; // 申请对象,此时引用计数=1[person retain]; //此时引用记数+1,现为2[person release]; //引用计数-1,此时引用计数=1[person release]; //引用计数-1,此时引用计数=0,内存被释放[person autorelease]; // 将对象加入自动释放池Person *person = [[[Person alloc] init] autorelease]; // 也可以在创建对象时将其加入自动释放池 按道理来说创建一个对象,然后release后该对象引用计数为零,但是实际情况中并不会出现这种现象,release后再输出其引用计数还是为1,在我的理解中有两种可能: 该对象在引用计数为1的时候进行release后,对象已经被释放,此时再调用retainCount毫无意义,因为该对象已经不存在了,为了防止某些错误保护这个retainCount方法所以编译器不会报错,但是输出值为释放前的值; 编译器为我们做了各种优化,也许是记录retainCount为零消耗过大或者没有意义。 重写了`dealloc`方便查看对象是否被释放 输出其`retainCount`然后释放 可以看到并不会出现引用计数为零的情况,但是该对象确实被释放了 小知识:指针错误:访问了一块坏的内存(已经被回收的,不可用的内存)。僵尸对象:所占内存已经被回收的对象,僵尸对象不能再被使用。(打开僵尸对象检测)空指针:没有指向任何东西的指针(存储的东西是0, null, nil),给空指针发送消息不会报错。注意:不能使用[p retaion]让僵尸对象起死复生。 在MRC管理时代有一个黄金法则: 谁创建谁负责。如果你通过alloc,new,copy来创建了一个对象,那么你就必须调用release或者autorelease方法; 谁retain,谁release。只要你调用了retain,无论这个对象时如何生成的,你都要调用release; ARC 原理前段编译器会为“拥有的”每一个对象加入相应的release语句,如果对象的所有权修饰符是__strong,那么它就是被拥有的。如果再某个方法内创建了一个对象,前端编译器会在方法末尾自动插入release语句已销毁它。而类拥有的对象(实例变量/属性)会在dealloc方法内被释放。 编译器所为 编译器为我们做的,我们可以手动完成达到优化比如:__autoreleasing在ARC中主要用在参数传递返回值(out-parameters)和引用传递参数(pass-by-reference)的情况下,有这种指针(NSError **)的函数参数如果不加修饰符,编译器会默认将他们认定为__autoreleasing类型。比如常用的NSError的使用: 12345NSError *__autoreleasing error;if (![data writeToFile:filename options:NSDataWritingAtomic error:&error]){ NSLog(@\"Error: %@\", error);} 如果你把error定义为了strong型,编译器会隐式地做如下事情,保证最终传入函数的参数依然是个__autoreleasing类型的引用。 1234567NSError *error;NSError *__autoreleasing tempError = error; // 编译器添加if (![data writeToFile:filename options:NSDataWritingAtomic error:&tempError]){ error = tempError; // 编译器添加 NSLog(@\"Error :%@\", error);} 所以为了提高效率,避免这种情况,我们一般在定义error的时候将其老老实实地声明为__autoreleasing类型。 循环引用 平常我们容易造成循环引用的三种情况: NSTimer 先看NSTimer使用的代码: 1_timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(runTimer) userInfo:nil repeats:YES]; 其中_timer是实例变量被self保留,_timer的target是self,self被_timer保留,引发循环引用。 循环引用 解除方法就是使target中的对象不是viewController从而断开引用,iOS10之前我们可以写个类别重新封装target来实现,iOS10之后系统给了新方法: 1+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block; 不再需要target,而是传入一个block,在block里面进行循环调用方法 关于block怎么解决循环引用请看下面 block 简介block和其他语言的闭包或lambda表达式是一回事,block的使用很像函数指针,不过与函数最大的不同是:block可以访问函数以外、词法作用域以内的外部变量的值。换句话说,block不仅实现函数的功能,还能携带函数的执行环境。 block基本语法 123456789// 声明一个block变量long (^sum) (int, int) = nil;// sum是个block变量,该block类型有两个int型参数,返回类型是long。// 定义block并赋给变量sumsum = ^ long (int a, int b) { return a + b;};// 调用block:long s = sum(1, 2); 定义一个实例函数,该函数返回block: 12345678- (long (^)(int, int)) sumBlock { int base = 100; return [[ ^ long (int a, int b) { return base + a + b; } copy] autorelease]; }// 调用block[self sumBlock](1,2); 根据在内存中的位置将block分为三种类型: * `NSGlobalBlock`: 类似函数,位于text段; * `NSStackBlock`: 位于栈内存,函数返回后block将无效; * `NSMallocBlock`: 位于堆内存。 block其实包含两个部分内容: block执行的代码,这是在编译的时候已经生成好的; 一个包含block执行时需要的所有外部变量值的数据结构。 block将使用到的、作用域附近到的变量的值建立一份快照拷贝到栈上。 block的数据结构 对于 block 外的变量引用,block 默认是将其复制到其数据结构中来实现访问的: 传入外部变量 对于用 __block 修饰的外部变量引用,block 是复制其引用地址来实现访问的: 用__block修饰 初步了解了block后看看它怎么构成循环引用并怎么解决的吧 12345678910111213typedef void(^block)();@property (copy, nonatomic) block myBlock;@property (copy, nonatomic) NSString *blockString;- (void)testBlock { self.myBlock = ^() { //其实注释中的代码,同样会造成循环引用 NSString *localString = self.blockString; //NSString *localString = _blockString; //[self doSomething]; };} 看了前面关于block的一些介绍应该容易看出来,当我们往block中传入数据时是保存在了block的堆中,如上述代码中引用了self相当于对self进行了一次retain,而self本身持有block于是造成了循环引用,同时在block中release``self没有用,因为在block中操作作用范围仅仅来block的函数栈,影响不到堆中的self,解决方法如下: 12345__weak typeof(self) weakSelf = self;self.myBlock = ^(){ __strong typeof(weakSelf) = strongSelf; NSString *localString = strongSelf;} 其中传入一个若引用就不会造成循环引用,然后在block的函数栈中用一个强指针来接受传进来的弱指针,防止弱指针被提前释放产生野指针。 参考文章:Cooper – 正确使用Block避免Cycle Retain和Crash唐巧 – 谈Objective-C block的实现Dev Talking – Objective-C中的Block delegate我们对代理的写法一般都是:1@property (nonatomic, weak) id<TestDelegate> delegate; 如果使用strong的话很明显会造成循环引用(delegate调用self的一些东西),今天被面试官问道如果使用delegate出现了循环引用怎么解决,我说用weak,他说换一个,然后就懵住了,只回答了思路,找到互相引用的对象(可以用Instruments)然后断开引用。 Autorelease 简介很好理解,字面意思上看就是自动释放,我们可以通过使用autorelease让编译器帮我们在某个时刻自动释放内存。在MRC时我们使用NSAutorelease类来使用自动释放机制,代码如下: 123NSAutoreleasePool pool = [[NSAutoreleasePool alloc] init];// Code benefitting from a local autorelease pool.[pool release]; 也可以直接使用[obj autorelease]。现在基本上都是ARC环境,这个时候我们使用的是autoreleasepool(自动释放池),比如常见的: 12345678910111213//iOS programint main(int argc, char * argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); }}//Command line programint main(int argc, const char *argv[]) { @autoreleasepool { //... } return 0;} 它的作用是把我们在{}中申请的对象在事件处理完时自动释放掉,其中的原理推荐阅读Qi Tang的iOS 中的 Autorelease Pool。前面说到的事件处理完时其实就是一次runloop结束时。 runloop和autorelease 程序运行 -> 开启事件循环 -> 发生触摸事件 -> 创建自动释放池 -> 处理触摸事件 -> 事件对象加入自动释放池 -> 一次事件循环结束, 销毁自动释放池 什么时候用@autoreleasepool 写基于命令行的的程序时,就是没有UI框架,如AppKit等Cocoa框架时。 写循环,循环里面包含了大量临时创建的对象。(本文的例子) 创建了新的线程。(非Cocoa程序创建线程时才需要) 长时间在后台运行的任务。 利用@autoreleasepool优化循环当我们一个循环内创建了很多临时对象时,可以通过使用@autoreleasepool在每次循环结束时释放内存: 12345678910//来自Apple文档,见参考NSArray *urls = <# An array of file URLs #>;for (NSURL *url in urls) { @autoreleasepool { NSError *error; NSString *fileContents = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error]; /* Process the string, creating and autoreleasing more objects. */ }} 参考文章:sunnyxx —— 黑幕背后的AutoreleaseJerry4me —— iOS中autorelease的那些事儿tutuge —— @autoreleasepool-内存的分配与释放 修饰词","categories":[{"name":"iOS","slug":"iOS","permalink":"http://codingdoge.cn/categories/iOS/"}],"tags":[{"name":"Swift","slug":"Swift","permalink":"http://codingdoge.cn/tags/Swift/"},{"name":"OC","slug":"OC","permalink":"http://codingdoge.cn/tags/OC/"}]},{"title":"漫谈iOS系列之:多线程","slug":"title: 漫谈iOS系列之:多线程","date":"2017-07-17T03:17:00.000Z","updated":"2018-01-13T05:08:06.362Z","comments":true,"path":"2017/07/17/title: 漫谈iOS系列之:多线程/","link":"","permalink":"http://codingdoge.cn/2017/07/17/title: 漫谈iOS系列之:多线程/","excerpt":"","text":"线程基本概念线程(thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。同一进程中的多条线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等等。但同一进程中的多个线程有各自的调用栈(call stack),自己的寄存器环境(register context),自己的线程本地存储(thread-local storage)。线程的状态: 新生态(New Thread) 可运行态(Runnable) 阻塞/非运行态(Not Runnable) 死亡态(Dead) 死锁:是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。 死锁条件: 互斥条件:所谓互斥就是进程在某一时间内独占资源。 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。 不剥夺条件:进程已获得资源,在末使用完之前,不能强行剥夺。 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。 创建线程的开销多线程的代价及上下文切换 pThreadPOSIX线程(POSIX Threads,常被缩写为Pthreads)是POSIX的线程标准,跨平台,适用于多种操作系统(类Unix操作系统中,都使用Pthreads作为操作系统的线程,Windows操作系统也有其移植版pthreads-win32),可移植性强,是一套纯C语言的通用API,且线程的生命周期需要程序员自己管理,使用难度较大,所以在实际开发中通常不使用。Pthreads API中大致共有100个函数调用,全都以”pthread_”开头,并可以分为四类: 线程管理,例如创建线程,等待(join)线程,查询线程状态等。 互斥锁(Mutex):创建、摧毁、锁定、解锁、设置属性等操作。 条件变量(Condition Variable):创建、摧毁、等待、通知、设置与查询属性等操作。 使用了互斥锁的线程间的同步管理。 pThread在实际开发中基本不使用,所以大概了解下就好了。 NSThreadGCDdispatch_barrier_async&dispatch_barrier_sync在队列中,barrier块必须单独执行,不能与其他block并行。这只对并发队列有意义,并发队列如果发现接下来要执行的block是个barrier block,那么就一直要等到当前所有并发的block都执行完毕,才会单独执行这个barrier block代码块,等到这个barrier block执行完毕,再继续正常处理其他并发block。 async, sync两者区别在于async将自己的任务插入队列后, 不用等待自己的任务结束, 继续把后面的任务插入队列, 然后等待自己的任务运行结束才执行后面的任务, sync将自己的任务插入队列后,需要等待自己的任务运行结束才能将后面的任务插入队列。 12345678910111213141516171819202122232425262728293031323334353637#import <Foundation/Foundation.h> @interface Person : NSObject@property (nonatomic, copy) NSString *name;@end #import \"Person.h\" @interface Person ()@end static NSString *_name;static dispatch_queue_t _concurrentQueue;@implementation Person- (instancetype)init{ if (self = [super init]) { _concurrentQueue = dispatch_queue_create(\"com.person.syncQueue\", DISPATCH_QUEUE_CONCURRENT); } return self;}- (void)setName:(NSString *)name{ dispatch_barrier_async(_concurrentQueue, ^{ _name = [name copy]; });}- (NSString *)name{ __block NSString *tempName; dispatch_sync(_concurrentQueue, ^{ tempName = _name; }); return tempName;}@end NSOperationNSOperation默认是非并发的,当你调用-[NSOperation start]方法时,该方法会等任务结束才会返回;并发的NSOperation是指,当你调用-[NSOperation start]后,NSOperation会在非当前线程(建立一个NSThread,或是dispatch async等)执行任务,并在任务结束之前就返回; 需要注意的是,并发行为都需要你自己实现,若要实现并发,你需要做很多额外的工作: 你需要创建一个subclass; 除了重载main方法,实现并发你还需要至少重载;start,isConcurrent,isExecuting,isFinished四个方法; 在start里,创建Thread或者调用一个异步函数; 更新isExecuting,并且发送相应KVO消息; 任务结束后,你还得更新isExecuting和isFinished,发送相应KVO消息。实现一个并发的NSOperation比较少见,具体如何实现,可以读读文档: NSOperation Class Reference 大多数情况下NSOperation都设计成非并发,这样实现起来会简单很多;并且,一般会配合NSOperationQueue使用,由NSOperationQueue来负责执行NSOperation,而非直接调用-[NSOperation start]。 若有复杂任务需要并发执行,一般也是拆成多个NSOperation,由NSOperationQueue来并发的执行多个NSOperation。 参考:关于iOS多线程,你看我就够了 dispatch_barrier_sync和dispatch_barrier_asynciOS开发:深入理解GCD 第二篇(dispatch_group、dispatch_barrier、基于线程安全的多读单写)NSOperation的并发和非并发有什么区别呀?","categories":[{"name":"iOS","slug":"iOS","permalink":"http://codingdoge.cn/categories/iOS/"}],"tags":[{"name":"OC","slug":"OC","permalink":"http://codingdoge.cn/tags/OC/"}]},{"title":"剑指Offer —— 复杂链表的复制","slug":"title: 剑指Offer —— 复杂链表的复制","date":"2017-07-06T02:44:00.000Z","updated":"2018-01-14T17:25:14.000Z","comments":true,"path":"2017/07/06/title: 剑指Offer —— 复杂链表的复制/","link":"","permalink":"http://codingdoge.cn/2017/07/06/title: 剑指Offer —— 复杂链表的复制/","excerpt":"","text":"题目来源牛客网:复杂链表的复制 题目描述输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空) 数据结构123456789101112131415/*struct RandomListNode { int label; struct RandomListNode *next, *random; RandomListNode(int x) : label(x), next(NULL), random(NULL) { }};*/class Solution {public: RandomListNode* Clone(RandomListNode* pHead) { }}; 解题思路一、递归思想:把大问题转化若干子问题此题转化为一个头结点和除去头结点剩余部分,剩余部分操作和原问题一致 123456789101112131415RandomListNode* Clone(RandomListNode* pHead) { if(pHead==NULL) return NULL; //开辟一个新节点 RandomListNode* pClonedHead=new RandomListNode(pHead->label); pClonedHead->next = pHead->next; pClonedHead->random = pHead->random; //递归其他节点 pClonedHead->next=Clone(pHead->next); return pClonedHead; } 二、 复制每个节点,如:复制节点A得到A1,将A1插入节点A后面 遍历链表,A1->random = A->random->next; 将链表拆分成原链表和复制后的链表 1234567891011121314151617181920212223242526272829RandomListNode* Clone(RandomListNode* pHead){ if(!pHead) return NULL; RandomListNode *currNode = pHead; while(currNode){ RandomListNode *node = new RandomListNode(currNode->label); node->next = currNode->next; currNode->next = node; currNode = node->next; } currNode = pHead; while(currNode){ RandomListNode *node = currNode->next; if(currNode->random){ node->random = currNode->random->next; } currNode = node->next; } //拆分 RandomListNode *pCloneHead = pHead->next; RandomListNode *tmp; currNode = pHead; while(currNode->next){ tmp = currNode->next; currNode->next =tmp->next; currNode = tmp; } return pCloneHead; } 三、哈希表法时间空间复杂度都是O(n) 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061RandomListNode* Clone(RandomListNode* pHead){ if(pHead==NULL) return NULL; //定义一个哈希表 unordered_multimap<RandomListNode*,RandomListNode*> table; // 开辟一个头结点 RandomListNode* pClonedHead=new RandomListNode(pHead->label); pClonedHead->next=NULL; pClonedHead->random=NULL; // 将头结点放入map中 table.insert(make_pair(pHead,pClonedHead)); //设置操作指针 RandomListNode* pNode=pHead->next; RandomListNode* pClonedNode=pClonedHead; // 第一遍先将简单链表复制一下 while(pNode!=NULL) { // 不断开辟pNode的拷贝结点 RandomListNode* pClonedTail=new RandomListNode(pNode->label); pClonedTail->next=NULL; pClonedTail->random=NULL; //连接新节点,更新当前节点 pClonedNode->next=pClonedTail; pClonedNode=pClonedTail; //将对应关系 插入到哈希表中 table.insert(make_pair(pNode,pClonedTail)); //向后移动操作节点 pNode=pNode->next; } //需从头开始设置random节点,设置操作指针 pNode=pHead; pClonedNode=pClonedHead; // 根据map中保存的数据,找到对应的节点 while(pNode!=NULL) { if(pNode->random!=NULL) { //找到对应节点,更新复制链表 pClonedNode->random=table.find(pNode->random)->second; } //向后移动操作节点 pNode=pNode->next; pClonedNode=pClonedNode->next; } return pClonedHead; }","categories":[{"name":"Algorithm","slug":"Algorithm","permalink":"http://codingdoge.cn/categories/Algorithm/"}],"tags":[{"name":"剑指Offer","slug":"剑指Offer","permalink":"http://codingdoge.cn/tags/剑指Offer/"}]},{"title":"动态规划(Dynamic Programming)","slug":"title: 动态规划(Dynamic Programming)","date":"2017-05-13T11:39:00.000Z","updated":"2018-01-12T14:27:10.181Z","comments":true,"path":"2017/05/13/title: 动态规划(Dynamic Programming)/","link":"","permalink":"http://codingdoge.cn/2017/05/13/title: 动态规划(Dynamic Programming)/","excerpt":"","text":"(以下简称DP) 基本思想将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。适合用DP求解的问题,经分解得到的子问题一般不是相互独立的,如果使用分治法求解,有些子问题会被重复计算多次,可以用一个表来记录所有已解决的子问题的答案,不管该子问题以后是否被用到,只要它被计算过,就将其结果填入表中。 DP算法适用于解最优化问题。通常可以按以下步骤设计: 找出最优解的性质,并刻画其结构特征; 递归地定义最优值; 以自底向上的方式计算出最优值; 根据计算最优值时得到的信息,构造最优解 基本要素 最优子结构问题的最优解包含了其子问题的最优解。 重叠子问题在用递归算法自顶向下求解问题时,每次产生的子问题并不总是新问题,有些子问题被计算多次。DP算法正式利用了这种子问题的重叠性质,对每一个子问题只解一次,而后将其解保存在一个表格中,当再次需要解此问题时,只是简单地用常数时间查看一下结果。通常不同的子问题个数随问题的大小呈多项式增长,因此用DP算法通常只需要多项式时间,从而获得较高的解题效率。 最长公共子序列问题描述一个给定序列的子序列是在该序列中删去若干元素后得到的序列。确切的说,若给定序列X = {x1, x2, ..., xm},则另一序列Z = {z1, z2, ..., zk},X的子序列是指存在一个严格递增下标序列{i1, i2, ..., ik}使得对于所有j = 1, 2, ..., k有zj = xij。例如,序列Z = {B, C, D, B}是序列X = {A, B, C, B, D, A, B}的子序列,相应的递增下标序列为{2, 3, 5, 7}。给定两个序列X和Y,当另一序列Z既是X的子序列又是Y的子序列时,称Z是序列X和Y的公共子序列。例如,若X = {A, B, C, B, D, A, B},Y = {B, D, C, A, B, A},序列{B, C, A}是X和Y的一个公共子序列,但它不是X和Y的最长公共子序列。序列{B, C, B, A}也是X和Y的一个公共子序列,它的长度为4,而且它是X和Y的最长公共子序列,因为X和Y没有长度大于4的公共子序列。 最长公共子序列问题给定两个序列X = {x1, x2, ..., xm}和Y = {y1, y2, ..., ym},找出X和Y的最长公共子序列。 按照DP算法设计的各个步骤求解 最长公共子序列结构设序列X = {x1, x2, ..., xm}和Y = {y1, y2, ..., yn}的最长公共子序列为Z = {z1, z2, ..., zk},则 若xm = yn,则zk = xm = yn,且Z 子问题的递归结构 计算最优值 构造最长公共子序列 算法的改进","categories":[{"name":"Algorithm","slug":"Algorithm","permalink":"http://codingdoge.cn/categories/Algorithm/"}],"tags":[]}]}
\ No newline at end of file
diff --git a/css/fonts/fontawesome/FontAwesome.otf b/css/fonts/fontawesome/FontAwesome.otf
new file mode 100644
index 0000000000000000000000000000000000000000..401ec0f36e4f73b8efa40bd6f604fe80d286db70
GIT binary patch
literal 134808
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