From 961057485f6350cba7f7ff305fb033bd4fe75cf7 Mon Sep 17 00:00:00 2001 From: YuHongpeng <480814177@qq.com> Date: Mon, 3 Jun 2019 22:35:54 +0800 Subject: [PATCH 01/18] 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 9973b4735625cd5b542ac71e4cd295521badfb58 Mon Sep 17 00:00:00 2001 From: YuHongpeng <480814177@qq.com> Date: Mon, 3 Jun 2019 22:35:55 +0800 Subject: [PATCH 02/18] Site updated: 2019-06-03 22:35:54 --- .../index.html" | 331 +++ .../index.html" | 230 ++ .../index.html" | 248 ++ .../index.html" | 306 +++ .../index.html" | 293 +++ .../index.html" | 290 +++ .../index.html" | 241 ++ .../index.html" | 278 +++ .../index.html" | 278 +++ .../index.html" | 278 +++ .../index.html" | 278 +++ .../index.html" | 355 +++ .../index.html" | 317 +++ .../index.html" | 274 +++ .../index.html" | 239 ++ .../index.html" | 275 +++ .../index.html" | 266 +++ .../index.html" | 300 +++ .../index.html" | 238 ++ .../index.html" | 296 +++ .../index.html" | 352 +++ .../index.html" | 248 ++ .../index.html" | 234 ++ .../index.html" | 245 ++ .../index.html" | 269 +++ .../index.html" | 240 ++ .../index.html" | 250 ++ .../index.html" | 450 ++++ .../index.html" | 262 +++ .../index.html" | 366 +++ .../index.html" | 298 +++ .../index.html" | 264 +++ .../index.html" | 244 ++ .../index.html" | 228 ++ .../index.html" | 313 +++ .../ES6\350\257\255\346\263\225/index.html" | 314 +++ .../index.html" | 256 +++ .../index.html" | 264 +++ .../index.html" | 369 +++ .../03/RN\346\212\245\351\224\231/index.html" | 278 +++ .../index.html" | 259 +++ .../index.html" | 238 ++ .../index.html" | 263 +++ .../index.html" | 310 +++ .../index.html" | 216 ++ .../index.html" | 266 +++ .../index.html" | 350 +++ .../index.html" | 244 ++ .../Swift\346\263\233\345\236\213/index.html" | 240 ++ .../index.html" | 221 ++ .../index.html" | 243 ++ .../index.html" | 223 ++ .../index.html" | 230 ++ .../index.html" | 264 +++ .../index.html" | 258 +++ .../index.html" | 330 +++ .../index.html" | 225 ++ "2019/06/03/npm\345\222\214cnpm/index.html" | 244 ++ .../index.html" | 299 +++ .../index.html" | 290 +++ .../index.html" | 402 ++++ .../index.html" | 246 ++ .../index.html" | 296 +++ .../index.html" | 289 +++ .../index.html" | 218 ++ .../index.html" | 303 +++ .../index.html" | 215 ++ .../index.html" | 298 +++ .../index.html" | 242 ++ .../index.html" | 215 ++ .../index.html" | 293 +++ .../index.html" | 242 ++ .../index.html" | 360 +++ .../index.html" | 330 +++ archives/2016/10/index.html | 238 ++ archives/2016/11/index.html | 333 +++ archives/2016/12/index.html | 257 +++ archives/2016/index.html | 376 +++ archives/2016/page/2/index.html | 281 +++ archives/2017/01/index.html | 200 ++ archives/2017/02/index.html | 238 ++ archives/2017/03/index.html | 257 +++ archives/2017/04/index.html | 200 ++ archives/2017/11/index.html | 200 ++ archives/2017/12/index.html | 219 ++ archives/2017/index.html | 376 +++ archives/2017/page/2/index.html | 224 ++ archives/2018/01/index.html | 295 +++ archives/2018/02/index.html | 200 ++ archives/2018/index.html | 314 +++ archives/2019/06/index.html | 316 +++ archives/2019/06/page/2/index.html | 316 +++ archives/2019/06/page/3/index.html | 316 +++ archives/2019/06/page/4/index.html | 316 +++ archives/2019/index.html | 316 +++ archives/2019/page/2/index.html | 316 +++ archives/2019/page/3/index.html | 316 +++ archives/2019/page/4/index.html | 316 +++ archives/index.html | 316 +++ archives/page/2/index.html | 316 +++ archives/page/3/index.html | 316 +++ archives/page/4/index.html | 316 +++ archives/page/5/index.html | 386 ++++ archives/page/6/index.html | 386 ++++ archives/page/7/index.html | 376 +++ archives/page/8/index.html | 262 +++ css/fonts/FontAwesome.otf | Bin 0 -> 62856 bytes css/fonts/fontawesome-webfont.eot | Bin 0 -> 38205 bytes css/fonts/fontawesome-webfont.svg | 414 ++++ css/fonts/fontawesome-webfont.ttf | Bin 0 -> 80652 bytes css/fonts/fontawesome-webfont.woff | Bin 0 -> 44432 bytes css/images/banner.jpg | Bin 0 -> 224710 bytes css/style.css | 1374 +++++++++++ fancybox/blank.gif | Bin 0 -> 43 bytes fancybox/fancybox_loading.gif | Bin 0 -> 6567 bytes fancybox/fancybox_loading@2x.gif | Bin 0 -> 13984 bytes fancybox/fancybox_overlay.png | Bin 0 -> 1003 bytes fancybox/fancybox_sprite.png | Bin 0 -> 1362 bytes fancybox/fancybox_sprite@2x.png | Bin 0 -> 6553 bytes fancybox/helpers/fancybox_buttons.png | Bin 0 -> 1080 bytes fancybox/helpers/jquery.fancybox-buttons.css | 97 + fancybox/helpers/jquery.fancybox-buttons.js | 122 + fancybox/helpers/jquery.fancybox-media.js | 199 ++ fancybox/helpers/jquery.fancybox-thumbs.css | 55 + fancybox/helpers/jquery.fancybox-thumbs.js | 165 ++ fancybox/jquery.fancybox.css | 273 +++ fancybox/jquery.fancybox.js | 2017 +++++++++++++++++ fancybox/jquery.fancybox.pack.js | 46 + index.html | 1030 +++++++++ js/script.js | 137 ++ page/2/index.html | 1099 +++++++++ page/3/index.html | 783 +++++++ page/4/index.html | 1134 +++++++++ page/5/index.html | 1162 ++++++++++ page/6/index.html | 978 ++++++++ page/7/index.html | 1166 ++++++++++ page/8/index.html | 523 +++++ placeholder | 0 search.xml | 1113 +++++++++ tags/AES-Base64-MD5/index.html | 200 ++ tags/Alamofire/index.html | 200 ++ tags/AlertView/index.html | 200 ++ tags/GCD/index.html | 200 ++ tags/Hexo/index.html | 200 ++ tags/OC-JS/index.html | 200 ++ tags/Quartz-2D/index.html | 200 ++ tags/Quartz2D/index.html | 219 ++ tags/RunTime/index.html | 200 ++ tags/SDWebImage/index.html | 200 ++ tags/SQL/index.html | 200 ++ tags/Swift/index.html | 219 ++ tags/TableView/index.html | 200 ++ tags/WKWebView/index.html | 286 +++ tags/git/index.html | 200 ++ "tags/\345\274\202\346\255\245/index.html" | 200 ++ .../index.html" | 200 ++ 156 files changed, 47338 insertions(+) create mode 100644 "2016/10/13/SQlite\344\275\277\347\224\250\345\255\246\344\271\240/index.html" create mode 100644 "2016/10/20/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" create mode 100644 "2016/10/27/iOS\345\274\271\346\241\206\345\274\217\345\233\276/index.html" create mode 100644 "2016/11/05/Hexo\344\270\252\344\272\272\345\215\232\345\256\242\346\220\255\345\273\272/index.html" create mode 100644 "2016/11/05/TableView\344\275\277\347\224\250\346\200\273\347\273\223/index.html" create mode 100644 "2016/11/20/git\345\255\246\344\271\240\346\200\273\347\273\223/index.html" create mode 100644 "2016/11/22/OC RunTime\345\255\246\344\271\240/index.html" create mode 100644 "2016/11/22/Quartz2D\347\273\230\345\210\266\347\272\277\346\235\241/index.html" create mode 100644 "2016/11/22/Swift3.0\345\255\246\344\271\240(\344\272\224)/index.html" create mode 100644 "2016/11/22/Swift3.0\345\255\246\344\271\240(\345\233\233)/index.html" create mode 100644 "2016/11/22/WKWebiView\344\275\277\347\224\250/index.html" create mode 100644 "2016/12/04/Quartz2D\345\210\235\350\257\206/index.html" create mode 100644 "2016/12/04/Quartz2D\347\273\230\345\210\266\346\233\262\347\272\277/index.html" create mode 100644 "2016/12/05/GCD\345\256\236\347\216\260\346\214\211\351\222\256\345\256\232\346\227\266\344\272\256\350\265\267/index.html" create mode 100644 "2016/12/10/Quartz2D\347\273\230\345\210\266\345\275\242\347\212\266/index.html" create mode 100644 "2017/01/18/\345\274\202\346\255\245\344\270\213\350\275\275\345\233\276\347\211\207/index.html" create mode 100644 "2017/02/02/iOS\351\235\231\346\200\201\345\272\223\345\212\250\346\200\201\345\272\223/index.html" create mode 100644 "2017/02/08/SDWebImage\345\255\246\344\271\240/index.html" create mode 100644 "2017/02/20/OC\344\270\216js\344\272\244\344\272\222\345\255\246\344\271\240/index.html" create mode 100644 "2017/03/01/Swift3.0\345\255\246\344\271\240(\344\270\200)/index.html" create mode 100644 "2017/03/05/Swift3.0\345\255\246\344\271\240(\344\272\214)/index.html" create mode 100644 "2017/03/08/Swift3.0\345\255\246\344\271\240(\344\270\211)/index.html" create mode 100644 "2017/03/25/\347\237\245\350\257\206\347\202\271\346\200\273\347\273\223/index.html" create mode 100644 "2017/04/10/Alamofire\346\272\220\347\240\201\345\255\246\344\271\240/index.html" create mode 100644 "2017/11/29/CocoaPods\345\210\266\344\275\234\347\247\201\346\234\211pod/index.html" create mode 100644 "2017/12/02/\346\234\254\345\234\260\345\267\262\346\234\211\351\241\271\347\233\256\346\267\273\345\212\240Pod/index.html" create mode 100644 "2017/12/14/Mac-\345\256\211\350\243\205MySQL/index.html" create mode 100644 "2018/01/08/Swift\345\212\240\345\257\206\347\233\270\345\205\263/index.html" create mode 100644 "2018/01/10/\347\275\221\347\273\234\344\270\203\345\261\202\345\215\217\350\256\256/index.html" create mode 100644 "2018/01/12/iOS\345\215\263\346\227\266\351\200\232\350\256\257\345\256\236\347\216\260\344\272\214/index.html" create mode 100644 "2018/01/17/\351\235\242\350\257\225\344\270\255\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223/index.html" create mode 100644 "2018/01/18/\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223-\344\272\214/index.html" create mode 100644 "2018/01/19/Block\345\255\246\344\271\240\346\216\242\347\251\266/index.html" create mode 100644 "2018/02/03/Mac\345\256\211\350\243\205VirtualBox\350\277\236\346\216\245U\347\233\230/index.html" create mode 100644 "2019/06/03/Charles \346\212\223\345\214\205/index.html" create mode 100644 "2019/06/03/ES6\350\257\255\346\263\225/index.html" create mode 100644 "2019/06/03/GCD \344\277\241\345\217\267\351\207\217/index.html" create mode 100644 "2019/06/03/JavaScript\345\237\272\347\241\200/index.html" create mode 100644 "2019/06/03/MasonrySnapkit\344\275\277\347\224\250\346\200\273\347\273\223/index.html" create mode 100644 "2019/06/03/RN\346\212\245\351\224\231/index.html" create mode 100644 "2019/06/03/React-Native\345\274\200\345\217\221iOS\346\211\223\345\214\205/index.html" create mode 100644 "2019/06/03/ReactNative\344\270\255ref\344\275\277\347\224\250/index.html" create mode 100644 "2019/06/03/Swift \345\215\217\350\256\256\344\275\277\347\224\250/index.html" create mode 100644 "2019/06/03/Swift \345\237\272\347\241\200\347\237\245\350\257\206/index.html" create mode 100644 "2019/06/03/SwiftLint\344\275\277\347\224\250/index.html" create mode 100644 "2019/06/03/Swift\344\270\213\346\240\207\345\222\214\350\277\220\347\256\227\347\254\246\351\207\215\350\275\275/index.html" create mode 100644 "2019/06/03/Swift\345\215\217\350\256\256\345\210\253\345\220\215/index.html" create mode 100644 "2019/06/03/Swift\345\215\217\350\256\256\346\211\251\345\261\225/index.html" create mode 100644 "2019/06/03/Swift\346\263\233\345\236\213/index.html" create mode 100644 "2019/06/03/Swift\351\224\231\350\257\257\345\244\204\347\220\206/index.html" create mode 100644 "2019/06/03/Swift\351\241\271\347\233\256\344\270\255\351\233\206\346\210\220RN/index.html" create mode 100644 "2019/06/03/Xcode\344\270\255\344\275\277\347\224\250Debug Memory Graph\346\243\200\346\237\245\345\206\205\345\255\230\346\263\204\346\274\217/index.html" create mode 100644 "2019/06/03/iOS \345\212\250\346\200\201\350\256\276\347\275\256TableHeaderView\351\253\230\345\272\246/index.html" create mode 100644 "2019/06/03/iOS\345\274\200\345\217\221_\344\272\216\351\270\277\351\271\217/index.html" create mode 100644 "2019/06/03/iOS\351\200\232\350\277\207URL\350\216\267\345\217\226HTML\344\270\255\346\240\207\351\242\230/index.html" create mode 100644 "2019/06/03/iOS\351\235\242\350\257\225OC\350\257\255\350\250\200\347\211\271\346\200\247/index.html" create mode 100644 "2019/06/03/is \345\205\263\351\224\256\345\255\227\347\261\273\345\236\213\346\243\200\346\237\245/index.html" create mode 100644 "2019/06/03/npm\345\222\214cnpm/index.html" create mode 100644 "2019/06/03/swift\344\270\255\345\274\272\345\274\225\347\224\250\345\276\252\347\216\257\345\222\214weak\345\205\263\351\224\256\345\255\227/index.html" create mode 100644 "2019/06/03/typescripe \345\256\211\350\243\205\345\217\212\350\257\255\346\263\225/index.html" create mode 100644 "2019/06/03/\343\200\21252\344\270\252\346\226\271\346\263\225\347\254\254\344\270\200\347\253\240\343\200\213/index.html" create mode 100644 "2019/06/03/\344\270\200\350\207\252\345\212\250\345\274\225\347\224\250\350\256\241\346\225\260/index.html" create mode 100644 "2019/06/03/\347\254\254\344\270\203\347\253\240Blocks\351\235\242\350\257\225/index.html" create mode 100644 "2019/06/03/\347\254\254\344\270\211\347\253\240UI\350\247\206\345\233\276\345\223\215\345\272\224\351\223\276\347\246\273\345\261\217\346\270\262\346\237\223\351\235\242\350\257\225/index.html" create mode 100644 "2019/06/03/\347\254\254\344\271\235\347\253\240Runloop\351\235\242\350\257\225/index.html" create mode 100644 "2019/06/03/\347\254\254\344\272\224\347\253\240Runtime\347\233\270\345\205\263\351\235\242\350\257\225/index.html" create mode 100644 "2019/06/03/\347\254\254\345\205\253\347\253\240\345\244\232\347\272\277\347\250\213\351\235\242\350\257\225/index.html" create mode 100644 "2019/06/03/\347\254\254\345\205\255\347\253\240\345\206\205\345\255\230\347\256\241\347\220\206\351\235\242\350\257\225/index.html" create mode 100644 "2019/06/03/\347\254\254\345\215\201\344\270\200\347\253\240\350\256\276\350\256\241\346\250\241\345\274\217\351\235\242\350\257\225/index.html" create mode 100644 "2019/06/03/\347\254\254\345\215\201\344\270\211\347\253\240\347\256\227\346\263\225\351\235\242\350\257\225/index.html" create mode 100644 "2019/06/03/\347\254\254\345\215\201\344\272\214\347\253\240\346\236\266\346\236\204\345\222\214\346\241\206\346\236\266\351\235\242\350\257\225/index.html" create mode 100644 "2019/06/03/\347\254\254\345\215\201\345\233\233\347\253\240\347\254\254\344\270\211\346\226\271\345\272\223\351\235\242\350\257\225/index.html" create mode 100644 "2019/06/03/\347\254\254\345\215\201\347\253\240\347\275\221\347\273\234\347\233\270\345\205\263/index.html" create mode 100644 "2019/06/03/\347\254\254\345\233\233\347\253\240OC\350\257\255\350\250\200\347\211\271\346\200\247\351\235\242\350\257\225/index.html" create mode 100644 archives/2016/10/index.html create mode 100644 archives/2016/11/index.html create mode 100644 archives/2016/12/index.html create mode 100644 archives/2016/index.html create mode 100644 archives/2016/page/2/index.html create mode 100644 archives/2017/01/index.html create mode 100644 archives/2017/02/index.html create mode 100644 archives/2017/03/index.html create mode 100644 archives/2017/04/index.html create mode 100644 archives/2017/11/index.html create mode 100644 archives/2017/12/index.html create mode 100644 archives/2017/index.html create mode 100644 archives/2017/page/2/index.html create mode 100644 archives/2018/01/index.html create mode 100644 archives/2018/02/index.html create mode 100644 archives/2018/index.html create mode 100644 archives/2019/06/index.html create mode 100644 archives/2019/06/page/2/index.html create mode 100644 archives/2019/06/page/3/index.html create mode 100644 archives/2019/06/page/4/index.html create mode 100644 archives/2019/index.html create mode 100644 archives/2019/page/2/index.html create mode 100644 archives/2019/page/3/index.html create mode 100644 archives/2019/page/4/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 archives/page/6/index.html create mode 100644 archives/page/7/index.html create mode 100644 archives/page/8/index.html create mode 100644 css/fonts/FontAwesome.otf create mode 100644 css/fonts/fontawesome-webfont.eot create mode 100644 css/fonts/fontawesome-webfont.svg create mode 100644 css/fonts/fontawesome-webfont.ttf create mode 100644 css/fonts/fontawesome-webfont.woff create mode 100644 css/images/banner.jpg create mode 100644 css/style.css create mode 100644 fancybox/blank.gif create mode 100644 fancybox/fancybox_loading.gif create mode 100644 fancybox/fancybox_loading@2x.gif create mode 100644 fancybox/fancybox_overlay.png create mode 100644 fancybox/fancybox_sprite.png create mode 100644 fancybox/fancybox_sprite@2x.png create mode 100644 fancybox/helpers/fancybox_buttons.png create mode 100644 fancybox/helpers/jquery.fancybox-buttons.css create mode 100644 fancybox/helpers/jquery.fancybox-buttons.js create mode 100644 fancybox/helpers/jquery.fancybox-media.js create mode 100644 fancybox/helpers/jquery.fancybox-thumbs.css create mode 100644 fancybox/helpers/jquery.fancybox-thumbs.js create mode 100644 fancybox/jquery.fancybox.css create mode 100644 fancybox/jquery.fancybox.js create mode 100644 fancybox/jquery.fancybox.pack.js create mode 100644 index.html create mode 100644 js/script.js create mode 100644 page/2/index.html create mode 100644 page/3/index.html create mode 100644 page/4/index.html create mode 100644 page/5/index.html create mode 100644 page/6/index.html create mode 100644 page/7/index.html create mode 100644 page/8/index.html delete mode 100644 placeholder create mode 100644 search.xml create mode 100644 tags/AES-Base64-MD5/index.html create mode 100644 tags/Alamofire/index.html create mode 100644 tags/AlertView/index.html create mode 100644 tags/GCD/index.html create mode 100644 tags/Hexo/index.html create mode 100644 tags/OC-JS/index.html create mode 100644 tags/Quartz-2D/index.html create mode 100644 tags/Quartz2D/index.html create mode 100644 tags/RunTime/index.html create mode 100644 tags/SDWebImage/index.html create mode 100644 tags/SQL/index.html create mode 100644 tags/Swift/index.html create mode 100644 tags/TableView/index.html create mode 100644 tags/WKWebView/index.html create mode 100644 tags/git/index.html create mode 100644 "tags/\345\274\202\346\255\245/index.html" create mode 100644 "tags/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" diff --git "a/2016/10/13/SQlite\344\275\277\347\224\250\345\255\246\344\271\240/index.html" "b/2016/10/13/SQlite\344\275\277\347\224\250\345\255\246\344\271\240/index.html" new file mode 100644 index 0000000..8bde06f --- /dev/null +++ "b/2016/10/13/SQlite\344\275\277\347\224\250\345\255\246\344\271\240/index.html" @@ -0,0 +1,331 @@ + + + + + + + + SQL 使用 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ SQL 使用 +

+ + +
+ +
+ +

常用SQL 语句

创建一张表

+
create table if not exists t_product(productId integer, productName text, productPrice real);
+

删除一张表

+
drop table if exists t_product;
+

插入一条记录

+

如果不写某个字段和对应值就会为NUll

+
insert into t_product(productId, productName, productPrice) values (0001, 'iPhone7', 6100.8);
+

修改记录

+
update t_product set productId = 20002 where productName = 'iPhone5s';
+

删除

+
delete from t_product where productName = 'iphone5s';
+

注意:条件语句不能单独存在,只能在修改/删除/查询之后

+

简单查询

准备数据生成sql语句文件

+
NSMutableString *mutableString = [NSMutableString string];
+int productIdValue = arc4random_uniform(1000);
+NSString *productNameValue = [NSString stringWithFormat:@"iPhone_%d",1 + arc4random_uniform(8)];
+CGFloat productPriceValue = 3000 + arc4random_uniform(1000);
+
+for (NSInteger i = 0; i < 1000; i++) {
+    [mutableString appendFormat:@"insert into t_product(productId, productName, productPrice) values (%d, %@, %f); \r\n", productIdValue, productNameValue, productPriceValue];
+}
+
+// 写入文件
+[mutableString writeToFile:@"/Users/Apeng/work/Demo/Sqlite/products.sql" atomically:YES encoding:NSUTF8StringEncoding error:NULL];
+

生成的sql语句文件

+

+

SQL 查询语句

+
select *(表示字段)from t_product(表名)where(条件语句) productId > 100 (字段满足的条件) ;
+
+select * from t_product where productPrice < 5000; 
+


or: 或

+

+

and: 且

+

+

分页查询

+

limit 0,5 ( limit 索引,每页取得数据条数 )

+

第一页 limit 0,5;

+

第二页 limit 5,5;

+

第三页 limit 10,5;

+

第四页 limit 15,5;

+

第N页 limit (N - 1)* 5

+
select * from t_product where productName = 'iPhone_8' and productPrice < 3300 order by productPrice limit 5,5;
+

+

排序查询

+

order by 默认是从小到大,一般是在某一个结果之后

+

默认升序

+
// 默认升序
+select * from t_product where productName = 'iPhone_8' and productPrice < 3300 order by productPrice;
+
+// 降序
+select * from t_product where productName = 'iPhone_8' and productPrice < 3300 order by productPrice desc;
+

+

模糊查询

+

关键字搜索 ,% 是指通配符

+

SQL 顺序

+
select * from 表名 条件语句(模糊匹配)排序语句 分页语句
+
+select * from t_product where productName like '%_4' order by productPrice limit 0,5;
+

+

主键

+

数据库的约定俗称,自增长,建议创建表时增加主键,也可以之后再设计表

+
CREATE TABLE IF NOT EXISTS t_class (id integer PRIMARY KEY, className text, classNO integer); 
+

外键约束

+

当两张表有关联时,需要使用外键约束,一张表中的字段名所对应的值只能来自于另一张表中

+

+

添加外键约束之后的变化

+

+

多表查询

+

t_department(部门表)和 t_employee(员工表)两张表

+

需求:在 t_employee 表中查出部门研发部的人员

+

1 嵌套查询:

+

先查根据部门名称查出部ID,

+

+

然后再用部门ID查出员工表中的员工

+

以上两步的代码可合并

+

根据部门名称查出部门ID

+
SELECT id from t_department WHERE departmentName = '研发部';
+

根据部门id查出这个部门的人员

+
SELECT * from t_employee WHERE departmentID = 1;
+

合并后为

+
select * from t_employee where departmentID = (SELECT id from t_department WHERE departmentName = '研发部'); 
+

结果为:

+

+
    +
  1. 链接查询
  2. +
+

给表起别名,给字段起别名

+
select employee.*, depart.departmentName deptN from t_department depart, t_employee employee;
+

/*多表查询之链接查询/

+
select t_employee.*, t_department.departmentName FROM t_department, t_employee;
+

+

消除笛卡尔积

+
select employee.*, depart.departmentName deptN from t_department depart, t_employee employee where employee.departmentID = depart.id and employee.departmentID = 1;
+

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2016/10/20/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" "b/2016/10/20/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" new file mode 100644 index 0000000..0cb7eab --- /dev/null +++ "b/2016/10/20/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" @@ -0,0 +1,230 @@ + + + + + + + + 本地缓存 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ 本地缓存 +

+ + +
+ +
+ +

保存图片到沙盒

1
2
3
4
5
6
- (void)saveImage:(UIImage *)image InSanBoxWithIndexPath:(NSInteger)indexPath {

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *filePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:[NSString stringWithFormat:@"pic_%ld.png", indexPath]]; // 保存文件的名称
[UIImagePNGRepresentation(image)writeToFile:filePath atomically:YES];
}
+

获取沙盒中的图片

1
2
3
4
5
6
7
- (UIImage *)fetchImageFromSanBoxWithIndexPath:(NSInteger)indexPath
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *filePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:[NSString stringWithFormat:@"pic_%ld.png", indexPath]]; // 保存文件的名称
UIImage *img = [UIImage imageWithContentsOfFile:filePath];
return img;
}
+

删除沙盒中的图片

1
2
3
4
5
6
7
8
9
10
11
12
- (BOOL)deleteImageAtIndexPath:(NSInteger)index;
{

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *filePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:[NSString stringWithFormat:@"pic_%ld.png", index]];

if([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
return [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
}

return NO;
}
+

修改图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
- (UIImage *)fixOrientation:(UIImage *)aImage {

// No-op if the orientation is already correct
if (aImage.imageOrientation == UIImageOrientationUp)
return aImage;

// We need to calculate the proper transformation to make the image upright.
// We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored.
CGAffineTransform transform = CGAffineTransformIdentity;

switch (aImage.imageOrientation) {
case UIImageOrientationDown:
case UIImageOrientationDownMirrored:
transform = CGAffineTransformTranslate(transform, aImage.size.width, aImage.size.height);
transform = CGAffineTransformRotate(transform, M_PI);
break;

case UIImageOrientationLeft:
case UIImageOrientationLeftMirrored:
transform = CGAffineTransformTranslate(transform, aImage.size.width, 0);
transform = CGAffineTransformRotate(transform, M_PI_2);
break;

case UIImageOrientationRight:
case UIImageOrientationRightMirrored:
transform = CGAffineTransformTranslate(transform, 0, aImage.size.height);
transform = CGAffineTransformRotate(transform, -M_PI_2);
break;
default:
break;
}

switch (aImage.imageOrientation) {
case UIImageOrientationUpMirrored:
case UIImageOrientationDownMirrored:
transform = CGAffineTransformTranslate(transform, aImage.size.width, 0);
transform = CGAffineTransformScale(transform, -1, 1);
break;

case UIImageOrientationLeftMirrored:
case UIImageOrientationRightMirrored:
transform = CGAffineTransformTranslate(transform, aImage.size.height, 0);
transform = CGAffineTransformScale(transform, -1, 1);
break;
default:
break;
}

// Now we draw the underlying CGImage into a new context, applying the transform
// calculated above.
CGContextRef ctx = CGBitmapContextCreate(NULL, aImage.size.width, aImage.size.height,
CGImageGetBitsPerComponent(aImage.CGImage), 0,
CGImageGetColorSpace(aImage.CGImage),
CGImageGetBitmapInfo(aImage.CGImage));
CGContextConcatCTM(ctx, transform);
switch (aImage.imageOrientation) {
case UIImageOrientationLeft:
case UIImageOrientationLeftMirrored:
case UIImageOrientationRight:
case UIImageOrientationRightMirrored:
// Grr...
CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.height,aImage.size.width), aImage.CGImage);
break;

default:
CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.width,aImage.size.height), aImage.CGImage);
break;
}
CGImageRef cgimg = CGBitmapContextCreateImage(ctx);
UIImage *img = [UIImage imageWithCGImage:cgimg];
CGContextRelease(ctx);
CGImageRelease(cgimg);
return img;
}
+ +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2016/10/27/iOS\345\274\271\346\241\206\345\274\217\345\233\276/index.html" "b/2016/10/27/iOS\345\274\271\346\241\206\345\274\217\345\233\276/index.html" new file mode 100644 index 0000000..f5c0430 --- /dev/null +++ "b/2016/10/27/iOS\345\274\271\346\241\206\345\274\217\345\233\276/index.html" @@ -0,0 +1,248 @@ + + + + + + + + iOS 弹框视图 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ iOS 弹框视图 +

+ + +
+ +
+ +

UIActionSheet UIAlertView UIAlertViewController 的区别

+

UIActionSheet

    +
  • iOS 8.3 之后过期
  • +
  • 模态弹出,显示在试图的底部 类似菜单提供选择
  • +
+
1
2
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"title" delegate:self cancelButtonTitle:@"取消" destructiveButtonTitle:nil otherButtonTitles:@"按钮1", @"按钮2", nil];
[actionSheet showInView:self.view];
+

UIAlertView

    +
  • iOS 9.0 之后过期
  • +
  • 模态弹出,显示在试图的中间位置 类似一个对话框提供选择
  • +
+
1
2
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"考勤方式" message:nil delegate:self cancelButtonTitle:@"取消" otherButtonTitles:faceBtn, fingerPrintBtn, nil];
[alertView show];
+

UIAlertViewController

    +
  • iOS 8.0 起可以使用
  • +
  • style : 默认样式, 类似 UIActionSheet 列表形式
  • +
  • style 为:UIAlertControllerStyleAlert 对话框形式
  • +
  • 设置按钮的样式
  • +
+

如果修改 UIAlertAction 中stye 为:

+

UIAlertActionStyleCancel
取消按钮会和其他按钮分开

+

如果修改 UIAlertAction 中stye 为:

+

UIAlertActionStyleDestructive
取消按钮会以红色警告的方式显示, 但是按钮之间不会分开

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"考勤方式" message:nil preferredStyle:UIAlertControllerStyleActionSheet];
UIAlertAction *faceBtn = [UIAlertAction actionWithTitle:@"人脸识别" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { }];

UIAlertAction *fingerPrintBtn = [UIAlertAction actionWithTitle:@"指纹认证" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {

}];

UIAlertAction *cancelBtn = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {

}];

[alert addAction:faceBtn];
[alert addAction:fingerPrintBtn];
[alert addAction:cancelBtn];
[self presentViewController:alert animated:YES completion:nil];
+ +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2016/11/05/Hexo\344\270\252\344\272\272\345\215\232\345\256\242\346\220\255\345\273\272/index.html" "b/2016/11/05/Hexo\344\270\252\344\272\272\345\215\232\345\256\242\346\220\255\345\273\272/index.html" new file mode 100644 index 0000000..b6565fe --- /dev/null +++ "b/2016/11/05/Hexo\344\270\252\344\272\272\345\215\232\345\256\242\346\220\255\345\273\272/index.html" @@ -0,0 +1,306 @@ + + + + + + + + Hexo个人博客搭建 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ Hexo个人博客搭建 +

+ + +
+ +
+ +

安装Homebrew

+
1
/usr/bin/ruby -e " (curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
+

安装git

1
sudo brew install git
+

安装node.js

+

安装hexo

    +
  • 需要输入密码: sudo 管理员权限 -g 全局安装
  • +
+
1
sudo npm install -g hexo
+

初始化 执行 hexo init 命令

    +
  • 指定安装文件 blog
  • +
+
1
hexo init blog
+
    +
  • cd 到 文件夹 blog 下,安装 npm
  • +
+
1
npm install
+
    +
  • 开启服务
  • +
+
1
hexo s
+ +

关联 github

    +
  • 添加 ssh key 到github

    +
  • +
  • 设置 Mac 显示隐藏文件

    +
      +
    • 在终端执行一下命令后,重启Finder

      +
      1
      defaults write com.apple.finder AppleShowAllFiles -bool true
      +
    • +
    +
  • +
  • 检查 SSH keys 是否存在 github

    +
      +
    • 打开 Finder 前往(command + shit + G)

      +
      1
      ~/.ssh/id_rsa.pub
      +
    • +
    +
  • +
  • 如果有文件说明存在 SSH key, 打开 id_rsa.pub 文件,复制里面的信息

    +
  • +
  • 进入 Github –> Settings –> SSH keys –> add SSH key
    任意添加一个title 粘贴内容到key 里,点击Add key 绿色按钮即可。
  • +
+

创建名字为 用户名.github.io 的仓库

自行百度。。。。

+

配置 hexo

    +
  • vim 打开 _config.yml修改 deploy 模块
  • +
+
1
vim _config.yml
+
1
2
3
4
deploy:
type: git
repository: https://github.com/用户名/用户.github.io.git
branch: master
+

冒号后需要加空格

+

生成 hexo 的静态页面

1
hexo generate 后者 hexo g
+
    +
  • 报以下错
  • +
+
1
2
ERROR Local hexo not found in ~/blog
ERROR Try runing: 'npm install hexo --save'
+
    +
  • 执行下面代码
  • +
+
1
npm install hexo --save
+

部署 hexo

1
hexo deploy 或者 hexo d
+
    +
  • 报以下错误
  • +
+
1
ERROR Deployer not found: git
+

解决方法:

+
1
2
npm install hexo-deployer-git --save
hexo d
+
    +
  • 配置 Deloyment 前,需要配置自己的身份信息
  • +
+
1
2
git config --global user.name "yourname"
git config --global user.email "youremail"
+

如果执行完以上命令还是不行,删除blog文件下的 .deploy_git 文件重新 hexo deploy

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2016/11/05/TableView\344\275\277\347\224\250\346\200\273\347\273\223/index.html" "b/2016/11/05/TableView\344\275\277\347\224\250\346\200\273\347\273\223/index.html" new file mode 100644 index 0000000..436b048 --- /dev/null +++ "b/2016/11/05/TableView\344\275\277\347\224\250\346\200\273\347\273\223/index.html" @@ -0,0 +1,293 @@ + + + + + + + + TableView 使用 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ TableView 使用 +

+ + +
+ +
+ +

tableView 使用总结

    +
  • 字典的 key enumerator 的使用

    +
  • +
  • tableViewCell 的 selected 方法可以监听cell 的选中, 使用selected 参数

    +
  • +
  • 网络慢时点击左边cell右边数据不能及时刷新, 用户看到的还是上一个cell显示的数据

    +
      +
    • 解决办法: 在网络加载之前就reload数据,让显示空白
    • +
    +
  • +
  • 点击左边的cell时 上拉加载更多控件的显示和隐藏逻辑

    +
      +
    • 解决办法: 在每次刷新右边数据时,控制footer的显示
    • +
    +
  • +
+
    +
  • 在numberOfRowsInSection方法中控制

    +
  • +
  • 每次点击cell之前要结束上一个没有刷新完的,防止奔溃

    +
  • +
  • 加载更多时

    +

    在加载第一页时设置currentPage为1,在加载更多的逻辑中 ++currentPage
    点击cell加载时上一个加载更多的网络数据还没有回来,在当前请求之前结束掉所有的网络请求
    停止之后的刷新控件状态会有错误,没有更多还是还可以加载状态不确定
    在点击左边cell后刷新右边的之前需要检查右边tableView 的footer的状态
    下拉刷新

    +
  • +
  • 第一次下拉刷新之后,第二次下拉刷新,将数据添加到数组中会导致两次数据重复

    +
      +
    • 解决办法:清除以前的所有数据
    • +
    +
  • +
  • 下拉刷新完毕后结束刷新后需要检查footer的状态

    +
  • +
  • 网络慢时连续点击cell,连发多个请求成功回调时数据显示错乱,或者是数据已经回来了但是还是显示上一个请求正在请求。。。

    +
      +
    • 解决办法:点击左边某个类别时,结束所有的刷新(上拉和下拉)
    • +
    +
  • +
  • 连续点击发送请求

    +
      +
    • 解决办法:保存请求参数, 比较上一次参数和当前参数, 如果 != 直接return,不让进入成功回调解析数据, 失败的时候也是同样 return,不提示用户
    • +
    +
  • +
  • 控制器正在请求数据时控制器销毁, 导致奔溃

    +
      +
    • 解决办法:在dealloc 中停止所有的操作
    • +
    +
  • +
+

cell 的frame 设置

    +
  • cell 之间的间隙以及cell的边距 重写frame 在super 之前设置

    +
      +
    • 设置frame 的x ,width, height 等属性
    • +
    +
  • +
  • 分页的2种做法

    +
      +
    • 下拉刷新

      +

      page 加载最新的 加载第一页 浪费用户的流量
      加载比当前更新的数据, 需要服务器支持,将当前最新的数据id发送给服务器,服务器加载比当前id还大的数据插入到最前面

      +
    • +
    • 上拉加载

      +

      将页码发给服务器

      +

      将最后面的数据的id发给服务器,

      +

      加载比当前id更小的数据

      +
    • +
    +
  • +
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2016/11/20/git\345\255\246\344\271\240\346\200\273\347\273\223/index.html" "b/2016/11/20/git\345\255\246\344\271\240\346\200\273\347\273\223/index.html" new file mode 100644 index 0000000..cfdb3d5 --- /dev/null +++ "b/2016/11/20/git\345\255\246\344\271\240\346\200\273\347\273\223/index.html" @@ -0,0 +1,290 @@ + + + + + + + + git 使用 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ git 使用 +

+ + +
+ +
+ +

使用 git 提交代码

    +
  • 初始化工作目录

    +
    1
    git init
    +
  • +
  • 配置用户名和邮箱(当前仓库)

    +
    1
    2
    git config user.name "aTreey"
    git config user.email "480814177@qq.com"
    +
  • +
+
    +
  • 配置全局的用户名和密码

    +
    1
    2
    git config --global user.name "aTreey"
    git config --global user.email "480814177@qq.com"
    +
  • +
  • 创建文件

    +
    1
    touch main.m
    +
  • +
  • 添加文件到暂缓区

    +
    1
    git add main.m
    +
  • +
  • 查看文件状态

    +
    1
    git status
    +
  • +
  • 新添加的文件或者新修改的文件在工作区中,没有添加到暂缓区中

    +
  • +
+
1
Untracked files: (红色)
+
    +
  • 需要添加到暂缓区中
  • +
+
1
git add main.m
+
    +
  • 出现一下提示方可提交
  • +
+
1
Changes to be committed: (绿色)
+
    +
  • 给git起别名

    +
    1
    2
    git config alias.st "status"
    git config alias.ci "commit -m"
    +
  • +
  • 查看所有版本库日志(只能查看当前版本的以前日志)

    +
    1
    git log
    +
  • +
  • 查看指定文件的版本库日志

    +
    1
    git log 文件名
    +
  • +
  • 查看所有的操作的日志

    +
    1
    git reflog
    +
  • +
+

版本会退

    +
  • 强制回退到当前版本

    +
    1
    git reset --hard HEAD
    +
  • +
  • 强制回退到当前版本的上一个版本

    +
    1
    git reset --hard HEAD^
    +
  • +
  • 强制回退到当前版本的上上个版本

    +
    1
    git reset --hard HEAD^^
    +
  • +
  • 强制回退到当前版本的前100个版本

    +
    1
    git reset --hard HEAD~100
    +
  • +
  • 强制回退指定版本

    +
    1
    git reset --hard HEAD 版本号前七位
  • +
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2016/11/22/OC RunTime\345\255\246\344\271\240/index.html" "b/2016/11/22/OC RunTime\345\255\246\344\271\240/index.html" new file mode 100644 index 0000000..aaa26ad --- /dev/null +++ "b/2016/11/22/OC RunTime\345\255\246\344\271\240/index.html" @@ -0,0 +1,241 @@ + + + + + + + + OC中运行时 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ OC中运行时 +

+ + +
+ +
+ +

RunTime
对于C语言,函数的调用在编译的时候会决定调用哪个函数,而对于面向对象的语言对于[调用某个对象的方法或函数]就叫「消息传递」。所以在 Java,C++ 这些语言里[调用某个对象的方法或函数]和 Objective-C 里[向某个对象发送消息]在概念上就是一样的事情,

+

RunTime 简介

    +
  • RunTime 简称运行时,OC中通常叫运行时机制,主要的是消息机制
  • +
  • 消息机制:在OC中,属于动态调用,在编译的时候并不能决定真正调用哪个函数,只有在真正运行的时候才会根据函数的名称找到对应的函数来调用
  • +
+

RunTime 作用

    +
  • 发送消息:让对象发送消息
  • +
  • 交换方法
  • +
  • 动态添加方法
  • +
  • 动态获取属性和方法
  • +
  • 字典转模型
  • +
  • 给分类添加属性
  • +
+

原理:给分类添加一个属性,重写了setter、getter方法,但是没有生成 _成员变量,需要通过运行时关联到分类中, 其本质上就是给这个类添加关联,存值和取值
code

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
self.oldurlPath = urlPath;

- (void)setOldurlPath:(NSString *)oldurlPath {

// RunTime 关联
// 第一个参数:给哪个对象添加关联
// 第二个参数:关联的key,通过这个key获取
// 第三个参数:关联的value
// 第四个参数:关联的策略
objc_setAssociatedObject(self, key, oldurlPath, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

- (NSString *)oldurlPath {
return objc_getAssociatedObject(self, key);
}
+ +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2016/11/22/Quartz2D\347\273\230\345\210\266\347\272\277\346\235\241/index.html" "b/2016/11/22/Quartz2D\347\273\230\345\210\266\347\272\277\346\235\241/index.html" new file mode 100644 index 0000000..2be06a3 --- /dev/null +++ "b/2016/11/22/Quartz2D\347\273\230\345\210\266\347\272\277\346\235\241/index.html" @@ -0,0 +1,278 @@ + + + + + + + + WKWebiView 使用 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ WKWebiView 使用 +

+ + +
+ +
+ +

WKWebiView iOS8.0 出现,性能要远远好于 iOS 2.0时出现的 UIWebView,主要记录WKWebView 的创建和代理方法

创建WKWebView

WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds];
+self.webView = webView;
+[self.view addSubview:webView];
+webView.UIDelegate = self;
+webView.navigationDelegate = self;
+[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.baidu.com"]]];
+

遵守协议 WKNavgationDelegate WKUIDelegate

+

代理方法

    +
  • WKNavgationDelegate 方法

    +
      +
    • 发送请求之前决定是否跳转

      +
      1
      2
      3
      4
      5
      6
      7
      - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {

      decisionHandler(WKNavigationActionPolicyAllow);
      // 不允许跳转
      decisionHandler(WKNavigationActionPolicyCancel);
      NSLog(@"decidePolicyForNavigationAction");
      }
      +
    • +
    • 收到相应之后决定是否跳转

      +
      1
      2
      3
      4
      5
      6
      7
      8
      9
      - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {

      decisionHandler(WKNavigationResponsePolicyAllow);

      decisionHandler(WKNavigationResponsePolicyCancel);

      NSLog(@"decidePolicyForNavigationResponse");

      }
      +
    • +
    • 开始加载时调用

      +
      1
      2
      3
      4
      - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation {

      NSLog(@"didStartProvisionalNavigation");
      }
      +
    • +
    • 接受到服务器的跳转请求时调用

      +
      1
      2
      3
      - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation {
      NSLog(@"didReceiveServerRedirectForProvisionalNavigation");
      }
      +
    • +
    +
  • +
+
- 页面加载失败时调用 **存在缓存问题**
+
+
1
2
3
4
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {

NSLog(@"error= %@", error);
}
+ + +- 内容开始返回时调用 + +
1
2
3
4
- (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation {

NSLog(@"didCommitNavigation");
}
+
    +
  • 内容返回成功后调用

    +
    1
    2
    3
    4
    - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation {

    NSLog(@"didFinishNavigation");
    }
    +
  • +
  • 开始返回错误时调用

    +
    1
    2
    3
    4
    - (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {

    NSLog(@"didFailNavigation");
    }
    +
  • +
+
    +
  • 目前不知道什么时候调用

    +
    1
    2
    3
    - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler {
    NSLog(@"didReceiveAuthenticationChallenge");
    }
    +
      +
    • 目前不知道什么时候调用

      +
      1
      2
      3
      4
      - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView API_AVAILABLE(macosx(10.11), ios(9.0)) {

      NSLog(@"webViewWebContentProcessDidTerminate");
      }
      +
    • +
    +
  • +
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2016/11/22/Swift3.0\345\255\246\344\271\240(\344\272\224)/index.html" "b/2016/11/22/Swift3.0\345\255\246\344\271\240(\344\272\224)/index.html" new file mode 100644 index 0000000..34e229e --- /dev/null +++ "b/2016/11/22/Swift3.0\345\255\246\344\271\240(\344\272\224)/index.html" @@ -0,0 +1,278 @@ + + + + + + + + WKWebiView 使用 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ WKWebiView 使用 +

+ + +
+ +
+ +

WKWebiView iOS8.0 出现,性能要远远好于 iOS 2.0时出现的 UIWebView,主要记录WKWebView 的创建和代理方法

创建WKWebView

WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds];
+self.webView = webView;
+[self.view addSubview:webView];
+webView.UIDelegate = self;
+webView.navigationDelegate = self;
+[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.baidu.com"]]];
+

遵守协议 WKNavgationDelegate WKUIDelegate

+

代理方法

    +
  • WKNavgationDelegate 方法

    +
      +
    • 发送请求之前决定是否跳转

      +
      1
      2
      3
      4
      5
      6
      7
      - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {

      decisionHandler(WKNavigationActionPolicyAllow);
      // 不允许跳转
      decisionHandler(WKNavigationActionPolicyCancel);
      NSLog(@"decidePolicyForNavigationAction");
      }
      +
    • +
    • 收到相应之后决定是否跳转

      +
      1
      2
      3
      4
      5
      6
      7
      8
      9
      - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {

      decisionHandler(WKNavigationResponsePolicyAllow);

      decisionHandler(WKNavigationResponsePolicyCancel);

      NSLog(@"decidePolicyForNavigationResponse");

      }
      +
    • +
    • 开始加载时调用

      +
      1
      2
      3
      4
      - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation {

      NSLog(@"didStartProvisionalNavigation");
      }
      +
    • +
    • 接受到服务器的跳转请求时调用

      +
      1
      2
      3
      - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation {
      NSLog(@"didReceiveServerRedirectForProvisionalNavigation");
      }
      +
    • +
    +
  • +
+
- 页面加载失败时调用 **存在缓存问题**
+
+
1
2
3
4
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {

NSLog(@"error= %@", error);
}
+ + +- 内容开始返回时调用 + +
1
2
3
4
- (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation {

NSLog(@"didCommitNavigation");
}
+
    +
  • 内容返回成功后调用

    +
    1
    2
    3
    4
    - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation {

    NSLog(@"didFinishNavigation");
    }
    +
  • +
  • 开始返回错误时调用

    +
    1
    2
    3
    4
    - (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {

    NSLog(@"didFailNavigation");
    }
    +
  • +
+
    +
  • 目前不知道什么时候调用

    +
    1
    2
    3
    - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler {
    NSLog(@"didReceiveAuthenticationChallenge");
    }
    +
      +
    • 目前不知道什么时候调用

      +
      1
      2
      3
      4
      - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView API_AVAILABLE(macosx(10.11), ios(9.0)) {

      NSLog(@"webViewWebContentProcessDidTerminate");
      }
      +
    • +
    +
  • +
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2016/11/22/Swift3.0\345\255\246\344\271\240(\345\233\233)/index.html" "b/2016/11/22/Swift3.0\345\255\246\344\271\240(\345\233\233)/index.html" new file mode 100644 index 0000000..3a32118 --- /dev/null +++ "b/2016/11/22/Swift3.0\345\255\246\344\271\240(\345\233\233)/index.html" @@ -0,0 +1,278 @@ + + + + + + + + WKWebiView 使用 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ WKWebiView 使用 +

+ + +
+ +
+ +

WKWebiView iOS8.0 出现,性能要远远好于 iOS 2.0时出现的 UIWebView,主要记录WKWebView 的创建和代理方法

创建WKWebView

WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds];
+self.webView = webView;
+[self.view addSubview:webView];
+webView.UIDelegate = self;
+webView.navigationDelegate = self;
+[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.baidu.com"]]];
+

遵守协议 WKNavgationDelegate WKUIDelegate

+

代理方法

    +
  • WKNavgationDelegate 方法

    +
      +
    • 发送请求之前决定是否跳转

      +
      1
      2
      3
      4
      5
      6
      7
      - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {

      decisionHandler(WKNavigationActionPolicyAllow);
      // 不允许跳转
      decisionHandler(WKNavigationActionPolicyCancel);
      NSLog(@"decidePolicyForNavigationAction");
      }
      +
    • +
    • 收到相应之后决定是否跳转

      +
      1
      2
      3
      4
      5
      6
      7
      8
      9
      - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {

      decisionHandler(WKNavigationResponsePolicyAllow);

      decisionHandler(WKNavigationResponsePolicyCancel);

      NSLog(@"decidePolicyForNavigationResponse");

      }
      +
    • +
    • 开始加载时调用

      +
      1
      2
      3
      4
      - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation {

      NSLog(@"didStartProvisionalNavigation");
      }
      +
    • +
    • 接受到服务器的跳转请求时调用

      +
      1
      2
      3
      - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation {
      NSLog(@"didReceiveServerRedirectForProvisionalNavigation");
      }
      +
    • +
    +
  • +
+
- 页面加载失败时调用 **存在缓存问题**
+
+
1
2
3
4
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {

NSLog(@"error= %@", error);
}
+ + +- 内容开始返回时调用 + +
1
2
3
4
- (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation {

NSLog(@"didCommitNavigation");
}
+
    +
  • 内容返回成功后调用

    +
    1
    2
    3
    4
    - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation {

    NSLog(@"didFinishNavigation");
    }
    +
  • +
  • 开始返回错误时调用

    +
    1
    2
    3
    4
    - (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {

    NSLog(@"didFailNavigation");
    }
    +
  • +
+
    +
  • 目前不知道什么时候调用

    +
    1
    2
    3
    - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler {
    NSLog(@"didReceiveAuthenticationChallenge");
    }
    +
      +
    • 目前不知道什么时候调用

      +
      1
      2
      3
      4
      - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView API_AVAILABLE(macosx(10.11), ios(9.0)) {

      NSLog(@"webViewWebContentProcessDidTerminate");
      }
      +
    • +
    +
  • +
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2016/11/22/WKWebiView\344\275\277\347\224\250/index.html" "b/2016/11/22/WKWebiView\344\275\277\347\224\250/index.html" new file mode 100644 index 0000000..51ae439 --- /dev/null +++ "b/2016/11/22/WKWebiView\344\275\277\347\224\250/index.html" @@ -0,0 +1,278 @@ + + + + + + + + WKWebiView 使用 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ WKWebiView 使用 +

+ + +
+ +
+ +

WKWebiView iOS8.0 出现,性能要远远好于 iOS 2.0时出现的 UIWebView,主要记录WKWebView 的创建和代理方法

创建WKWebView

WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds];
+self.webView = webView;
+[self.view addSubview:webView];
+webView.UIDelegate = self;
+webView.navigationDelegate = self;
+[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.baidu.com"]]];
+

遵守协议 WKNavgationDelegate WKUIDelegate

+

代理方法

    +
  • WKNavgationDelegate 方法

    +
      +
    • 发送请求之前决定是否跳转

      +
      1
      2
      3
      4
      5
      6
      7
      - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {

      decisionHandler(WKNavigationActionPolicyAllow);
      // 不允许跳转
      decisionHandler(WKNavigationActionPolicyCancel);
      NSLog(@"decidePolicyForNavigationAction");
      }
      +
    • +
    • 收到相应之后决定是否跳转

      +
      1
      2
      3
      4
      5
      6
      7
      8
      9
      - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {

      decisionHandler(WKNavigationResponsePolicyAllow);

      decisionHandler(WKNavigationResponsePolicyCancel);

      NSLog(@"decidePolicyForNavigationResponse");

      }
      +
    • +
    • 开始加载时调用

      +
      1
      2
      3
      4
      - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation {

      NSLog(@"didStartProvisionalNavigation");
      }
      +
    • +
    • 接受到服务器的跳转请求时调用

      +
      1
      2
      3
      - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation {
      NSLog(@"didReceiveServerRedirectForProvisionalNavigation");
      }
      +
    • +
    +
  • +
+
- 页面加载失败时调用 **存在缓存问题**
+
+
1
2
3
4
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {

NSLog(@"error= %@", error);
}
+ + +- 内容开始返回时调用 + +
1
2
3
4
- (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation {

NSLog(@"didCommitNavigation");
}
+
    +
  • 内容返回成功后调用

    +
    1
    2
    3
    4
    - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation {

    NSLog(@"didFinishNavigation");
    }
    +
  • +
  • 开始返回错误时调用

    +
    1
    2
    3
    4
    - (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {

    NSLog(@"didFailNavigation");
    }
    +
  • +
+
    +
  • 目前不知道什么时候调用

    +
    1
    2
    3
    - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler {
    NSLog(@"didReceiveAuthenticationChallenge");
    }
    +
      +
    • 目前不知道什么时候调用

      +
      1
      2
      3
      4
      - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView API_AVAILABLE(macosx(10.11), ios(9.0)) {

      NSLog(@"webViewWebContentProcessDidTerminate");
      }
      +
    • +
    +
  • +
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2016/12/04/Quartz2D\345\210\235\350\257\206/index.html" "b/2016/12/04/Quartz2D\345\210\235\350\257\206/index.html" new file mode 100644 index 0000000..b8035ba --- /dev/null +++ "b/2016/12/04/Quartz2D\345\210\235\350\257\206/index.html" @@ -0,0 +1,355 @@ + + + + + + + + Quartz2D 初识 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ Quartz2D 初识 +

+ + +
+ +
+ +

Quartz2D 二维绘图引擎,支持iOS和Mac系统么,可以实现的功能有

+
    +
  • 绘制图形: 线条,三角形,矩形,圆,弧等
  • +
  • 绘制文字
  • +
  • 绘制图像或者图片
  • +
  • 读取生成PDF
  • +
  • 截图/裁剪图片
  • +
  • 自定义UI控件
  • +
  • 手势解锁
  • +
  • 图形上下文
  • +
+

保存绘图信息和状态

确定输出目标(PDF,Bitmap或者显示器)

+
Bitmap Graphics Context
+PDF Granhics Context
+Window Graphics Context
+Layer Graphics Context
+

绘制线段

+
- (void)drawLine {
+    // 获取图形上下文
+    // 所用的是UIGraphics上下文
+    CGContextRef ctx = UIGraphicsGetCurrentContext();
+
+    // 创建描述路径
+    CGMutablePathRef path = CGPathCreateMutable();
+    // 设置起点
+    // path : 表示给那个路径设置起点
+    CGPathMoveToPoint(path, NULL, 10, 10);
+    CGPathAddLineToPoint(path, NULL, 80, 80);
+
+    // 添加路径到上下文
+    CGContextAddPath(ctx, path);
+
+    // 渲染
+    CGContextStrokePath(ctx);
+}
+

系统底层自动将路径添加到图形上下文

+
- (void)drawLine2 {
+
+    CGContextRef ctx = UIGraphicsGetCurrentContext();
+
+    // 描述路径
+    // 此方法底层会自动将路径添加到图形上下文
+    CGContextMoveToPoint(ctx, 50, 50);
+    CGContextAddLineToPoint(ctx, 100, 100);
+
+    CGContextSetLineWidth(ctx, 5);
+
+    [[UIColor redColor] set];
+    // 渲染
+    CGContextStrokePath(ctx);
+}
+

使用C语言函数的封装

+
- (void)drawLine3 {
+    // 使用UIKit 已经封装的功能,面向对象
+    // 贝塞尔路径
+
+    UIBezierPath *path = [UIBezierPath bezierPath];
+    [path moveToPoint:CGPointMake(10, 10)];
+    [path addLineToPoint:CGPointMake(50, 50)];
+
+    // 绘制路径
+    [path stroke];
+}
+

绘制多条线段

+
    +
  • 绘制多条线,状态不好管理
  • +
  • 默认下一条线的起点是上一条线的终点
  • +
  • 绘制多天不连接的线时需要重新设置起点
  • +
  • 为了方便管理,应该让每一条线对应一条路径
  • +
  • 使用UIBeizerPath
  • +
+

code:

+
- (void)drawMoreLine {
+    CGContextRef ctx = UIGraphicsGetCurrentContext();
+
+    // 描述路径
+    CGContextMoveToPoint(ctx, 20, 20);
+    CGContextAddLineToPoint(ctx, 100, 100);
+
+    // 设置第二条线的起点
+    CGContextMoveToPoint(ctx, 20, 30);
+    CGContextAddLineToPoint(ctx, 150, 100);
+
+    // 设置绘图状态要在渲染之前,都是给上下文设置
+    // 描边颜色
+    [[UIColor greenColor] setStroke];
+    // 线宽
+    CGContextSetLineWidth(ctx, 10);
+    // 连接样式
+    // kCGLineJoinMiter,    斜接
+    // kCGLineJoinRound,    圆角
+    // kCGLineJoinBevel     平切
+    CGContextSetLineJoin(ctx, kCGLineJoinBevel);
+    // 顶角样式,线的顶端效果
+    CGContextSetLineCap(ctx, kCGLineCapRound);
+
+    // 渲染
+    CGContextStrokePath(ctx);
+}
+

使用UIBezierPath 绘制多条直线并设置不同状态

+
- (void)drawUIBezierPahtState {
+
+    // 第一条线
+    UIBezierPath *path = [UIBezierPath bezierPath];
+    [path moveToPoint:CGPointMake(10, 10)];
+    [path addLineToPoint:CGPointMake(80, 80)];
+
+    path.lineWidth = 20;
+    path.lineJoinStyle = kCGLineJoinRound;
+    path.lineCapStyle = kCGLineCapRound;
+    [[UIColor redColor] setStroke];
+
+    [path stroke];
+
+    // 第二条线
+    {
+        UIBezierPath *path = [UIBezierPath bezierPath];
+        [path moveToPoint:CGPointMake(40, 70)];
+        [path addLineToPoint:CGPointMake(150, 40)];
+
+        path.lineWidth = 20;
+        path.lineJoinStyle = kCGLineJoinRound;
+        path.lineCapStyle = kCGLineCapRound;
+        [[UIColor greenColor] setStroke];
+
+        [path stroke];
+    }
+}
+
+ +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2016/12/04/Quartz2D\347\273\230\345\210\266\346\233\262\347\272\277/index.html" "b/2016/12/04/Quartz2D\347\273\230\345\210\266\346\233\262\347\272\277/index.html" new file mode 100644 index 0000000..9863e3e --- /dev/null +++ "b/2016/12/04/Quartz2D\347\273\230\345\210\266\346\233\262\347\272\277/index.html" @@ -0,0 +1,317 @@ + + + + + + + + Quartz2D绘制曲线 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ Quartz2D绘制曲线 +

+ + +
+ +
+ +

使用函数绘制曲线

+

Quartz 2D提供了CGContextAddCurveToPoint() 函数和CGContextAddQuadCurveToPoint()两个函数来向当前上下文添加曲线,前者用于添加贝塞尔曲线,后者用于添加二次曲线。
确定一条贝塞尔曲线需要4个点:开始点、第一个控制点、第二个控制点和结束点。

+

确定一条二次曲线需要三个点:开始点、控制点和结束点。

+

添加贝塞尔曲线

+
CGContextAddCurveToPoint()
+
+CGContextRef ctx = UIGraphicsGetCurrentContext();
+
+// 添加曲线路径
+// 设置起点
+CGContextMoveToPoint(ctx, 0, 0);
+
    +
  • 参数1: 上下文对象
  • +
  • 参数2: 控制点1X坐标
  • +
  • 参数3: 控制点1Y坐标
  • +
  • 参数4: 控制点2X坐标
  • +
  • 参数5: 控制点2Y坐标
  • +
  • 参数6: 终点x坐标
  • +
  • 参数7: 终点Y坐标
  • +
+

code:

+
CGContextAddCurveToPoint(ctx, 30, 200, 300, 20, 300, 300);
+
+// 设置颜色
+[[UIColor magentaColor] setStroke];
+
+// 渲染
+CGContextStrokePath(ctx);
+
+// 关闭图形上下文
+CGContextClosePath(ctx);
+添加二次曲线
+CGContextAddQuadCurveToPoint()
+
+// 获取图形上下文
+CGContextRef ctx = UIGraphicsGetCurrentContext();
+
+// 起始点
+CGContextMoveToPoint(ctx, 20, 10);
+
    +
  • 参数: 图形上下文
  • +
  • 参数: 控制点坐标
  • +
  • 参数: 结束点坐标
  • +
+

添加二次曲线

+
CGContextAddQuadCurveToPoint(ctx, 30, 200, 200, 40);
+
+[[UIColor cyanColor] setStroke];
+CGContextStrokePath(ctx);
+// 关闭图形上下文
+CGContextClosePath(ctx);
+

绘制形状

+

使用绘制二次曲线的函数绘制花瓣

+

程序中CGContextAddFlower代码分别添加5瓣花朵路径、6瓣花朵路径、7瓣花朵路径,然后使用不同的颜色来填充这些路径。注意到上面的程序并未在每次添加花朵路径后立即关闭,这也是允许的,而且每次填充路径时并不会再次填充前一次已经填充过的路径。这是因为只用程序绘制了CGContextRef当前所包含的路径,系统会自动清除已经绘制的路径。

+

注意:每次绘制完成后,CGContextRef会自动清除已经绘制完成的路径

+

该方法负责绘制花朵。
n:该参数控制花朵的花瓣数;dx、dy:控制花朵的位置;size:控制花朵的大小;
length:控制花瓣的长度

+
void CGContextAddFlower(CGContextRef c , NSInteger n
+                        , CGFloat dx , CGFloat dy , CGFloat size , CGFloat length)
+{
+    CGContextMoveToPoint(c , dx , dy + size);  // 移动到指定点
+    CGFloat dig = 2 * M_PI / n;
+    // 采用循环添加n段二次曲线路径
+    for(int i = 1; i < n + 1 ; i++)
+    {
+        // 计算控制点坐标
+        CGFloat ctrlX = sin((i - 0.5) * dig) * length + dx;
+        CGFloat ctrlY= cos((i - 0.5 ) * dig) * length + dy;
+        // 计算结束点的坐标
+        CGFloat x = sin(i * dig) * size + dx;
+        CGFloat y =cos(i * dig) * size + dy;
+        // 添加二次曲线路径
+        CGContextAddQuadCurveToPoint(c, ctrlX , ctrlY , x , y);
+    }
+}
+

绘制花瓣

+
- (void)drawStar
+{
+    CGContextRef ctx = UIGraphicsGetCurrentContext();  // 获取绘图的CGContextRef
+    CGContextBeginPath(ctx);  // 开始添加路径
+    CGContextAddFlower(ctx , 5 , 50 , 100 , 30 , 80);  // 添加5瓣花朵的路径
+    CGContextSetRGBFillColor(ctx, 1, 0, 0, 1);  // 设置填充颜色
+    CGContextFillPath(ctx);
+    CGContextAddFlower(ctx , 6 , 160 , 100 , 30 , 80);  // 添加6瓣花朵的路径
+    CGContextSetRGBFillColor(ctx, 1, 1, 0, 1);  // 设置填充颜色
+    CGContextFillPath(ctx);
+    CGContextAddFlower(ctx , 7 , 270 , 100 , 30 , 80);  // 添加7瓣花朵的路径
+    CGContextSetRGBFillColor(ctx, 1, 0, 1, 1);  // 设置填充颜色
+    CGContextFillPath(ctx);
+    CGContextClosePath(ctx);  // 关闭路径
+}
+
+ +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2016/12/05/GCD\345\256\236\347\216\260\346\214\211\351\222\256\345\256\232\346\227\266\344\272\256\350\265\267/index.html" "b/2016/12/05/GCD\345\256\236\347\216\260\346\214\211\351\222\256\345\256\232\346\227\266\344\272\256\350\265\267/index.html" new file mode 100644 index 0000000..aece2f2 --- /dev/null +++ "b/2016/12/05/GCD\345\256\236\347\216\260\346\214\211\351\222\256\345\256\232\346\227\266\344\272\256\350\265\267/index.html" @@ -0,0 +1,274 @@ + + + + + + + + GCD实现按钮定时亮起 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ GCD实现按钮定时亮起 +

+ + +
+ +
+ +

某些时候需要按钮不能连续点击,需要在一定的时间之后才可以允许交互,或者是实现类似发送验证码的按钮效果,具体做法是采用定时器

+

使用GCD定时器

创建定时器对象

__block NSInteger time = 30; // 需要强应用
+

创建队列

dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
+

创建dispatch源(定时器)

(dispatach_source…timer..)
+
    +
  • 01参数:要创建的source 是什么类型的,

    +

    (DISPATCH_SOURCE_TYPE_TIMER)定时器

    +
  • +
  • 04参数:队列 —-线程 决定block 在哪个线程中调用

    +
  • +
+

代码

+
dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
+

设置定时器

    +
  • 01参数:定时器对象

    +
  • +
  • 02参数:开始时间 (DISPATCH_TIME_NOW) 什么时候开始执行第一次任务

    +
  • +
  • 03参数:间隔时间 GCD时间单位:纳秒

    +
  • +
  • 04参数:leewayInSeconds精准度:允许的误差: 0 表示绝对精准

    +
  • +
+

code:

+
dispatch_source_set_timer(_timer, dispatch_walltime(NULL, 0), 1.0 * NSEC_PER_SEC, 0);
+

定时器每隔一段时间就要执行任务(block回调)

dispatch_source_set_event_handler(_timer, ^{
+    if (time <= 0) {
+        dispatch_source_cancel(_timer);
+        dispatch_async(dispatch_get_main_queue(), ^{
+
+            // 设置按钮的样式
+            [self.button setTitle:@"重新获取验证码" forState:UIControlStateNormal];
+            [self.button setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
+            [self.button setUserInteractionEnabled:YES];
+        });
+    } else {
+
+        NSInteger seconds = time;
+        dispatch_async(dispatch_get_main_queue(), ^{
+            [self.button setTitle:[NSString stringWithFormat:@"重新发送(%.2ld)", seconds] forState:UIControlStateNormal];
+            [self.button setTitleColor:[UIColor lightGrayColor] forState:UIControlStateNormal];
+            [self.button setUserInteractionEnabled:NO];
+        });
+        time--;
+    }
+});
+

启动定时器(默认是停止的)

dispatch_resume(timer);
+
+ +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2016/12/10/Quartz2D\347\273\230\345\210\266\345\275\242\347\212\266/index.html" "b/2016/12/10/Quartz2D\347\273\230\345\210\266\345\275\242\347\212\266/index.html" new file mode 100644 index 0000000..c48ba49 --- /dev/null +++ "b/2016/12/10/Quartz2D\347\273\230\345\210\266\345\275\242\347\212\266/index.html" @@ -0,0 +1,239 @@ + + + + + + + + Quartz 2D绘制形状(二) | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ Quartz 2D绘制形状(二) +

+ + +
+ +
+ +

使用Quartz 2D绘制圆弧,扇形,下载进度以及绘制的图形的填充和描边属性的设置.
将贝塞尔路径添加到图形上下文中
图形上下文中的状态保存和恢复
对图形上下文进行矩阵操作(平移,缩放,选转),实现一些特殊效果

+

使用贝塞尔曲线绘制矩形

1
2
3
4
5
6
7
{
UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, 20, 30)];
[[UIColor magentaColor] setStroke];
[[UIColor brownColor] setFill];
[path fill]; // 填充
[path stroke]; // 描边
}
+

圆角矩形

1
2
3
4
5
6
7
8
// 绘制圆角矩形
{
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(35, 10, 50, 70) cornerRadius:5.0];
[[UIColor magentaColor] setStroke];
[[UIColor cyanColor] setFill];
[path fill];
[path stroke];
}
+

绘制圆

1
2
3
4
5
6
7
8
// 画圆
{
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(90, 5, 40, 40) cornerRadius:20.0];
[[UIColor purpleColor] setStroke];
[[UIColor magentaColor] setFill];
[path fill];
[path stroke];
}
+

绘制扇形

1
2
3
4
5
6
CGPoint center = CGPointMake(200, 50);
UIBezierPath *sectorPath = [UIBezierPath bezierPathWithArcCenter:center radius:100 startAngle:0 endAngle:M_PI_4 clockwise:YES];
[sectorPath addLineToPoint:center];
[[UIColor brownColor] setStroke];
[sectorPath fill];
[sectorPath stroke];
+

绘制圆弧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 绘制圆弧
* 参数1: 圆心坐标
* 参数2: 半径
* 参数3: 开始角度
* 参数4: 结束角度 使用弧度制表示
* 参数5: clockwis顺时针方法的
*/

UIBezierPath *circuLarArcPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(100, 60) radius:50.0 startAngle:0 endAngle:M_PI_2 clockwise:YES];

[circuLarArcPath moveToPoint:CGPointMake(100, 60)];

[[UIColor magentaColor] setStroke];
[[UIColor brownColor] setFill];
[circuLarArcPath stroke];
[circuLarArcPath fill];
+

绘制文字

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
/**
* 绘制文字
* drawAtPoint 不能换行,当文字过多过长时不会有效果
* 使用 drawInRect 可以实现换行
*/


NSString *text = @"我是绘制文字价格就看过几个框架刚开始大家高考的感觉是快乐的关键时刻大家赶快来时的结果看了风水格局来看就哭了感觉树大根深来对抗肌肤抵抗力";

NSMutableDictionary *attributeDict = [NSMutableDictionary dictionary];
attributeDict[NSFontAttributeName] = [UIFont systemFontOfSize:18];

// 字体颜色
attributeDict[NSForegroundColorAttributeName] = [UIColor greenColor];
// 描边颜色
attributeDict[NSStrokeColorAttributeName] = [UIColor yellowColor];
// 描边宽度
attributeDict[NSStrokeWidthAttributeName] = @1;
// 阴影
NSShadow *shadow = [[NSShadow alloc] init];
shadow.shadowColor = [UIColor redColor];
shadow.shadowOffset = CGSizeMake(1.0, 1.0);

attributeDict[NSShadowAttributeName] = shadow;

// 可以换行
[text drawInRect:self.bounds withAttributes:attributeDict];

// 不换行
[text drawAtPoint:CGPointZero withAttributes:attributeDict];
+

绘制图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 绘制图片
* drawAtPoint 默认绘制的内容尺寸和图片的尺寸一样大
*
*/

- (void)drawImage {

UIImage *image = [UIImage imageNamed:@"wc_dk"];
UIImage *image1 = [UIImage imageNamed:@"wc_hq"];

[image drawAtPoint:CGPointMake(10, 90)];
[image1 drawAtPoint:CGPointMake(70, 80)];

// 绘制在控件内部,和控件大小一致
[image1 drawInRect:self.bounds];

// 以平铺的方式绘制在控件的内部
[image1 drawAsPatternInRect:self.bounds];
}
+

圆形下载进度的绘制

drawRect 方法不能手动调用,因为图形上下文不能手动创建
需要调用setNeedsDisplay方法来重绘,系统会创建图形上下文
绘制时用到的定时器,一般不使用NSTimer定时器,因为调度优先级比较低,不会被准时带,界面会出现卡顿的现象
CADisplayLink 每次刷新屏幕的时候就会调用,1s刷新 60 次
setNeedsDisplay调用此方法时并不是立即调用drawRect方法,只是给给当前控件添加刷新标记,直到下一次屏幕刷新的时候才调用drawRect 方法,正好与CADisplayLink 方法一致,所以界面流畅不会卡顿
图形上下文状态栈

+

UIBezierPath图形上下文和CGContextRef的图形上下文不是同一个图形上下文

+

可以将贝塞尔路径添加到图形上下文中

+

也可以保存当前的图形上下文,CGContextSaveGState(ctx);
在下一次使用时可以恢复,CGContextRestoreGState(ctx);
画两种不同状态的线条

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
/**
* 图形上下文栈的理解
* 画两条状态不同的线段
*/

// 获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();

// 第一条
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(10, 60)];
[path addLineToPoint:CGPointMake(240, 60)];

// 把路径添加到上下文
CGContextAddPath(ctx, path.CGPath);

// 保存上下的状态
CGContextSaveGState(ctx);

// 设置当前状态
[[UIColor greenColor] setStroke];
//此时设置path 的状态无效,需要设置当前上下文的状态
path.lineWidth = 5.0;
CGContextSetLineWidth(ctx, 5.0);

// 渲染上下文
CGContextStrokePath(ctx);


// 第二根

// 可使用第一根的路径,因为每次绘制完成之后都会清除
path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(60, 10)];
[path addLineToPoint:CGPointMake(60, 240)];

// 添加路径到上下文 需要转换为CGPath
CGContextAddPath(ctx, path.CGPath);

// 还原之前保存的上下文状态
CGContextRestoreGState(ctx);
CGContextStrokePath(ctx);
图形上下文矩阵

// 使用矩阵

// 获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();

// 使用贝塞尔路径
UIBezierPath *ovalPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(-150, -100, 300, 200)];
[[UIColor cyanColor] setFill];



// 以上绘制的图形有一部分看不到,通过对上下文矩阵操作来实现,平移上下文
// 操作矩阵必须在添加路径之前

// 平移
CGContextTranslateCTM(ctx, 150, 100);

// 缩放
CGContextScaleCTM(ctx, 0.5, 0.5);

// 旋转
// 可以实现点无法计算时的效果
CGContextRotateCTM(ctx, M_PI_4);



// 添加路径到上下文
CGContextAddPath(ctx, ovalPath.CGPath);

// 操作矩阵放在添加路径之后无效果
// CGContextTranslateCTM(ctx, 150, 100);

CGContextFillPath(ctx);

// 正常的绘制椭圆
// UIBezierPath *ovalPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(50, 20, 200, 100)];
// ovalPath.lineWidth = 4.0;
// [[UIColor magentaColor] setFill];
// [[UIColor yellowColor] setStroke];
// [ovalPath stroke];
// [ovalPath fill];
+ +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2017/01/18/\345\274\202\346\255\245\344\270\213\350\275\275\345\233\276\347\211\207/index.html" "b/2017/01/18/\345\274\202\346\255\245\344\270\213\350\275\275\345\233\276\347\211\207/index.html" new file mode 100644 index 0000000..5587e60 --- /dev/null +++ "b/2017/01/18/\345\274\202\346\255\245\344\270\213\350\275\275\345\233\276\347\211\207/index.html" @@ -0,0 +1,275 @@ + + + + + + + + 异步下载图片 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ 异步下载图片 +

+ + +
+ +
+ +

异步下载

同步发送网络请求会卡UI线程,采用异步来下载图片。

+

异步下载存在的问题

+
    +
  • 错行问题

    +
      +
    • 解决:使用MVC模式,使数据一对一可解决,
    • +
    • 在MVC中增加image属性后,会导致单个模式的体积增大
    • +
    • 内存占用
    • +
    • 当接收到内存警告时清空图片时需要遍历模型中将image置为nil
    • +
    • 将图片用字典缓存起来达到一对一, 清空内存中的图片时只需要remove字典就ok
    • +
    • 创建下载任务的字典,在开始下载时将正在下载的URL写入到字典中下载完成后将字典中对应的值移除,写入防止来回滑动时重复发送网络请求,下载完后移除是为了防止如果接受到内存警告后下载的图片都已清除,但是下载任务的字典的值为移除就不会再一次去下载图片
    • +
    • 缓存下载的图片到沙盒中

      +
    • +
    • 获取图片下载路径最后一个分隔符后面的字符串作为图片名保存

      +
    • +
    +
  • +
+

stringByAppendingPathComponentstringByAppendingString 两个方法有区别

+

stringByAppendingString 方法是获取caches文件名,在文件夹名后面拼接字符串,在caches上一级目录下创建文件,和caches文件夹属于同一个级别

+
// 获取caches文件路径
+NSString *cachesPath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
+
+NSString *URL = @"http://wwww.baidu.com/image/123456.png";
+
+NSString *imageName = [URL lastPathComponent];
+
+// 拼接路径
+// 此方法生成的路径在caches文件下目录下
+NSString *filePath = [cachesPath stringByAppendingPathComponent];
+
+
+// 读取图片
+if (内存中没有) {
+    // 沙盒读取
+    UIImage *sandBoxImage = [UIImage imageWithContetsOfFile:filePath];
+
+    **加入到内存中,方便下次加载**
+} 
+
+if (sandBoxImage == nil) {
+    // 下载图片
+}
+
+// 写入到沙盒
+if (imageData != nil) {
+    [imageData writeToFile: filePath atomically:NO];
+}
+
+ +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2017/02/02/iOS\351\235\231\346\200\201\345\272\223\345\212\250\346\200\201\345\272\223/index.html" "b/2017/02/02/iOS\351\235\231\346\200\201\345\272\223\345\212\250\346\200\201\345\272\223/index.html" new file mode 100644 index 0000000..ffdeafc --- /dev/null +++ "b/2017/02/02/iOS\351\235\231\346\200\201\345\272\223\345\212\250\346\200\201\345\272\223/index.html" @@ -0,0 +1,266 @@ + + + + + + + + WKWebiView 使用 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ WKWebiView 使用 +

+ + +
+ +
+ +

iOS中静态库动态库介绍及编译

    +
  • 静态库
  • +
+

iOS中根据源码的公开情况可分为2种

+
    +
  • 开源库
    源代码公开,能够看到具体的代码实现
  • +
+

比如 SDWebImage AFNetworking。。。

+
    +
  • 闭源库
    不公开源代码, 是经过编译后的二进制文件, 看不到具体实现
    主要分为:静态库、动态库

    +
  • +
  • 存在形式

    +
  • +
+

静态库: .a 和 .framework
动态库:.tbd 和 .framework (iOS 9 之前是 .dylib, iOS 9 之后取消了 .dylib 而使用 .tbd 来代替 .dylib )

+

.framework 的库可以是静态库也可以是静态库, 凡是系统的 .framework 都是动态库,第三方的基本上全是静态中

+
    +
  • 两者区别
  • +
+

静态库:链接时会被完整的复制到可执行文件中,每次被使用都会被复制
动态库:链接时不复制,程序运行时由系统动态加载到内存中,供程序调用,系统只加载一次,多个程序可以公用,节省内存

+

静态库编译

编译时如果使用的是模拟器,生成的 .a 只能模拟器用,真机不可用,生成的 .a 文件在 Debug-iphonesimulator 文件夹下

+

编译时如果使用 generic iOS Device,生成的 .a 只能真机而模拟器不可用,生成的 .a 文件在 Debug-iphoneos 文件夹下

+

编译静态库时,自己手动创建的类的头文件不会被导出,只是默认导出创建工程时类的头文件

+

导出手动添加类的头文件,点击工程名称 –> build Phases –> Copy Files 添加头文件,然后编译

+
    +
  • 架构种类
  • +
+

模拟器:i386 32位架构 (4s~5) / x86_64 64位架构 (5s以后机型)
真机:armv7 32位架构 (4~4s) / armv7s 特殊架构 (5~5C) / arm64 64位架构 (5s 以后机型)

+
    +
  • 架构查看
    lipo -info xxxx.a
    默认生成模拟器只生成一种架构 i386 x86_64;真机默认导出两种架构 armv7 / arm64

    +
  • +
  • 合并多个架构

    +
  • +
+

将模拟器的多个架构合并,项目名称–>Build Settings –> Achitectures –> Build Active Architecture Only –> Debug设置为NO
合并真机多个架构 按照以上方法运行真机即可
合并真机和模拟器架构 cd products lipo -create Debug-iphoneos/xxx.a Debug-iphonessimulator/xxx.a -output xx.a
合成 5 个架构 在Build Setting –> Architectures 中手动添加三种架构,再编译合并

+
    +
  • 静态库报错
  • +
+

Undefined symbols for architecture (arm64 / armv7 / armv7s / i386 / x86_64),架构包导入出错,静态库有真机与模拟器

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2017/02/08/SDWebImage\345\255\246\344\271\240/index.html" "b/2017/02/08/SDWebImage\345\255\246\344\271\240/index.html" new file mode 100644 index 0000000..ec6b7cc --- /dev/null +++ "b/2017/02/08/SDWebImage\345\255\246\344\271\240/index.html" @@ -0,0 +1,300 @@ + + + + + + + + SDWebImage的用法及原理 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ SDWebImage的用法及原理 +

+ + +
+ +
+ +

SDWebImage 介绍

SDWebImage 是用于网络中下载且缓存图片,并设置图片到对应的控件或上

+

Demo: https://github.com/aTreey/DownImage.git

+
    +
  • 提供了UIImageView的category用来加载网络图片并且下载的图片的缓存进行管理
  • +
  • 采用异步方式来下载,确保不会阻塞主线程,memory+disk来缓存网络图片,自动管理缓存
  • +
  • 支持GIF动画,[self.imageView setImageWithURL:[[NSBundle mainBundle] URLForResource:@”xx.gif” withExtension:nil];
  • +
  • 支持WebP格式
  • +
  • 同一个URL的网络图片不会被重复下载
  • +
  • 失效的URL不会被无限重试
  • +
+

SDWebImage 的使用

克隆

+

git clone https://github.com/rs/SDWebImage.git
使用以上命令克隆会报错,框架中Vendors文件夹中的文件未能全部下载导致报错

+

解决办法:https://github.com/rs/SDWebImage/blob/master/Docs/ManualInstallation.md

+

git clone –recursive https://github.com/rs/SDWebImage.git

+

文件全部下载

+

自定义operation 加入NSOperationQueue中

+
    +
  • 自定义NSOperation,
    重写main方法
  • +
+
1
2
3
4
5
6
7
8
9
10
11
- (void)main {
@autoreleasepool {
NSURL *url = [NSURL URLWithString:_urlStr];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];

if (_finishBlock) {
_finishBlock(image);
}
}
}
+
    +
  • 一个NSOperation对象可以通过调用start方法来执行任务,默认是同步执行的。也可以将NSOperation添加到一个NSOperationQueue(操作队列)中去执行,而且是异步执行的
    创建队列
  • +
+
1
2
3
4
- (NSOperationQueue *)downLoadQueue {
if (!_downLoadQueue) _downLoadQueue = [[NSOperationQueue alloc] init];
return _downLoadQueue;
}
+
    +
  • 添加一个任务到队列
  • +
+
1
2
3
4
5
6
7
8
9
[_downLoadQueue addOperation:operation];
添加一组operation, 是否阻塞当前线程

[_downLoadQueue addOperations:@[operation] waitUntilFinished:NO];
添加一个block 形式的operation

[_downLoadQueue addOperationWithBlock:^{
NSLog(@"执行一个新的线程");
}];
+

注意:

+
    +
  1. NSOperation 添加到 queue之后,通常短时间内就会执行,但是如果存在依赖,或者整个queue被暂停等原因,也可能会需要等待

    +
  2. +
  3. NSOperation添加到queue之后,绝不要修改NSOperation对象的状态,因为NSOperation对象可能会在任何时候运行,因此改变NSOperation对象的依赖或者数据会产生不利的影响,只能查看NSOperation 对象的状态,比如是否正在运行、等待运行、已经完成等

    +
  4. +
+
    +
  • NSOperation 添加依赖,
    依赖关系不局限于相同的queue 中的NSOperation对象,可以夸队列进行依赖,但是不能循环依赖,
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// NSOperation 添加依赖
{
NSOperationQueue *testQueue = [[NSOperationQueue alloc] init];
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"operation1 NSThread = %@", [NSThread currentThread]);
}];

NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"operation2 NSThead = %@", [NSThread currentThread]);
}];

// 添加依赖,1 依赖于 2,只有2 执行完之后才执行1
[operation1 addDependency:operation2];
// 加入到队列
[testQueue addOperation:operation1];
[testQueue addOperation:operation2];
}
+
    +
  • 修改Operation的执行顺序
    对于添加到queue中的operation执行顺序有一下2点决定
    operation 是否已经准备好,是否添加了依赖
    根据多有operations的相对优先性来决定,优先等级是operation对象本身的一个属性,默认都是“普通”优先级,可以通过setQueuePriority方法提高和降低优先级,优先级只能用于相同 queue 中的 operation,多个queue中operation的优先级相互独立,因此不同queue中的低优先级的operation可能比高优先级的operation更早执行
    注意优先级和依赖不能相互替代,优先级只是对已经准备好的operation确定执行顺序,先满足依赖,然后再看已经准备好的operation中的优先级

    +
  • +
  • 设置 queue 的最大并发数,队列中最多同时运行几条线程
    虽然NSOperationQueue类设计用于并发执行Operations,你也可以强制单个queue一次只能执行一个Operation。setMaxConcurrentOperationCount:方法可以配置queue的最大并发操作数量。设为1就表示queue每次只能执行一个操作。不过operation执行的顺序仍然依赖于其它因素,比如operation是否准备好和operation的优先级等。因此串行化的operation queue并不等同于GCD中的串行dispatch queue

    +
  • +
+
1
2
3
4
// 每次只能执行一个操作  
queue.maxConcurrentOperationCount = 1;
// 或者这样写
[queue setMaxConcurrentOperationCount:1];
+
    +
  • 取消 operations
    一旦添加到operation queue,queue就拥有了这个Operation对象并且不能被删除,只能取消。调用Operation对象的cancel方法取消单个操作,也可以调用operation queue的cancelAllOperations方法取消当前queue中的所有操作, 使用cancel属性来判断是否已经取消了
  • +
+

[operation cacel] 只是打了一个死亡标记, 并没有正真意义上的取消,称为 “自杀“,需要在被取消的任务中时时判断是否取消(在程序的关键处), 如果取消,结束任务, 具体是指在自定义的 operation 的main方法中结束

+
1
2
3
4
5
6
[queue cancelAllOperations] 由队列来取消,称为 “他杀”

// 取消单个操作
[operation cancel];
// 取消queue中所有的操作
[queue cancelAllOperations];
+
    +
  • 等待operation 完成
  • +
+

为了最佳的性能,你应该设计你的应用尽可能地异步操作,让应用在Operation正在执行时可以去处理其它事情。如果需要在当前线程中处理operation完成后的结果,可以使用NSOperation的waitUntilFinished方法阻塞当前线程,等待operation完成。通常我们应该避免编写这样的代码,阻塞当前线程可能是一种简便的解决方案,但是它引入了更多的串行代码,限制了整个应用的并发性,同时也降低了用户体验。绝对不要在应用主线程中等待一个Operation,只能在第二或次要线程中等待。阻塞主线程将导致应用无法响应用户事件,应用也将表现为无响应。

1
2
// 会阻塞当前线程,等到某个operation执行完毕  
[operation waitUntilFinished];

+

除了等待单个Operation完成,你也可以同时等待一个queue中的所有操作,使用NSOperationQueue的waitUntilAllOperationsAreFinished方法。注意:在等待一个 queue时,应用的其它线程仍然可以往queue中添加Operation,因此可能会加长线程的等待时间。

+

// 阻塞当前线程,等待queue的所有操作执行完毕
[queue waitUntilAllOperationsAreFinished];
暂停和继续operation
如果你想临时暂停Operations的执行,可以使用queue的setSuspended:方法暂停queue。不过暂停一个queue不会导致正在执行的operation在任务中途暂停,只是简单地阻止调度新Operation执行。你可以在响应用户请求时,暂停一个queue来暂停等待中的任务。稍后根据用户的请求,可以再次调用setSuspended:方法继续queue中operation的执行

1
2
3
4
5
// 暂停queue  
[queue setSuspended:YES];

// 继续queue
[queue setSuspended:NO];

+

增加Manager管理类,

作用:

+
    +
  • 下载队列
  • +
  • 图像缓存
  • +
  • 操作缓存(内存缓存)
  • +
  • 沙盒缓存
  • +
  • 防止错乱,取消老的未开始下载的操作
  • +
  • 图片显示逻辑:内存缓存 – 沙盒缓存 – 网络下载
  • +
  • 图片缓存逻辑:下载完成 – 缓存沙盒 – 加载到内存
  • +
+

注意:缓存到本地时只能保存property列表里的对象(NSData, NSDate, NSNumber, NSString, NSArray, NSDictory), 图片需要转化为二进制数据

+

SDWebImage

    +
  • SDWebImageDownloader 下完图片后都需要手动设置给UIImageView
  • +
  • SDWebImageManager 下完图片后都需要手动设置给UIImageView
  • +
  • 下载后要显示可以使用 sd_setImageWithURL 方法
  • +
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2017/02/20/OC\344\270\216js\344\272\244\344\272\222\345\255\246\344\271\240/index.html" "b/2017/02/20/OC\344\270\216js\344\272\244\344\272\222\345\255\246\344\271\240/index.html" new file mode 100644 index 0000000..c9ea2d1 --- /dev/null +++ "b/2017/02/20/OC\344\270\216js\344\272\244\344\272\222\345\255\246\344\271\240/index.html" @@ -0,0 +1,238 @@ + + + + + + + + OC和JS交互 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ OC和JS交互 +

+ + +
+ +
+ +

WebView相关属性

autoResizingMask 是UIView的属性,作用是实现子控件相对于父控件的自动布局

+
1
2
3
4
5
6
7
8
9
autoResizingMask 是UIViewAutoresizing 默认是 UIViewAutoresizingNone

UIViewAutoresizingNone = 0,
UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
UIViewAutoresizingFlexibleWidth = 1 << 1,
UIViewAutoresizingFlexibleRightMargin = 1 << 2,
UIViewAutoresizingFlexibleTopMargin = 1 << 3,
UIViewAutoresizingFlexibleHeight = 1 << 4,
UIViewAutoresizingFlexibleBottomMargin = 1 << 5
+

各属性解释:

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
UIViewAutoresizingNone
不会随父视图的改变而改变

UIViewAutoresizingFlexibleLeftMargin

自动调整view与父视图左边距,以保证右边距不变

UIViewAutoresizingFlexibleWidth
自动调整view的宽度,保证左边距和右边距不变

UIViewAutoresizingFlexibleRightMargin
自动调整view与父视图右边距,以保证左边距不变

UIViewAutoresizingFlexibleTopMargin
自动调整view与父视图上边距,以保证下边距不变

UIViewAutoresizingFlexibleHeight
自动调整view的高度,以保证上边距和下边距不变

UIViewAutoresizingFlexibleBottomMargin
自动调整view与父视图的下边距,以保证上边距不变
+

OC 调用js

webViewDidFinishLoad:(UIWebView *)webView方法中

+
    +
  • 删除增加结点
  • +
  • 添加点击事件
  • +
+

js调用OC

webView:(UIWebview )webView shouldStartLoadWithRequest:(NSURLRequest )request方法中

+

调用js页面点击按钮后保存图片到相册,必须调用系统提供的方法,不能自己自定义方法

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2017/03/01/Swift3.0\345\255\246\344\271\240(\344\270\200)/index.html" "b/2017/03/01/Swift3.0\345\255\246\344\271\240(\344\270\200)/index.html" new file mode 100644 index 0000000..56fd234 --- /dev/null +++ "b/2017/03/01/Swift3.0\345\255\246\344\271\240(\344\270\200)/index.html" @@ -0,0 +1,296 @@ + + + + + + + + Swift 3.0 学习 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ Swift 3.0 学习 +

+ + +
+ +
+ +

和OC区别

    +
  • 没有了.h 和 .m 文件,没有main.m 文件
  • +
  • 程序入口发生变化 在appdelegate中的 @UIApplicationMain
  • +
  • 每行代码可以不写 ‘;’
  • +
  • self可以不写,建议不写self,因为闭包中必须要写
  • +
  • 真假表示只有 true/ false,没有非0即真的概念
  • +
  • 类型自动推到
  • +
+

类型自动推到

提前指定类型

1
2
let float :Double = 9.6 
print(float)
+

常量和变量

    +
  • let 声明常量,有且只有一次赋值机会
  • +
  • var 声明变量
  • +
  • 尽量选择使用let如需修改值再使用var 系统会报警告
  • +
+

可选项

    +
  • Optional: 表示一个常量或者是变量可能有值可能为nil,在打印的时候会带有Optional字样
  • +
  • ? 表示是一个可选项
  • +
  • 可选项不能参与运算
  • +
  • ! 表示可选项中一定要有值,可以强制解包,解包时如果没有值会boom

    +
  • +
  • ?? 合并空选项 快速判断可选项是否为nil, 如果为nil取后面的默认值,在基本数据类型和字符串使用较多

    +
  • +
+

条件分支

    +
  • if let
  • +
+

快速赋值,判断赋值对象是否为nil,如果不为nil,就赋值进入分支
生成request是url是一个可选项,需要强制解包或者合并空选项

+
1
2
3
4
let urlString = "http://www.baidu.com"
let url = NSURL(string: urlString)
let request = NSURLRequest(URL: url!)
print(request)
+
    +
  • 以上代码使用if let实现
  • +
+
1
2
3
4
if let u = NSURL(string: urlString) {
let request = NSURLRequest(URL: u)
print(request)
}
+
    +
  • where条件语句配合多重判断
  • +
+
1
2
3
4
5
6
if url != nil  {
if url?.host == "www.baidu.com" {
let request = NSURLRequest(URL: url!)
print(request)
}
}
+
    +
  • 使用if let
  • +
+
1
2
3
4
if let u = url where u.host == "www.baidu.com" {
let request = NSURLRequest(URL: u)
print(request)
}
+
    +
  • 多重判断
  • +
+
1
2
3
4
if let u = url, s = string {
let request = NSURLRequest(URL: u)
print(request)
}
+
    +
  • guard let 和 if let 相反如果为nil,可以直接返回
  • +
+
1
2
3
4
5
6
7
let urlString  = "http://www.baidi.com"
let url = NSURL(string: urlString)
guard let u = url else { return }

//程序走到这个地方就表示 u 一定有值
let request = NSURLRequest(URL: u)
print(request)
+
    +
  • swith语句

    +
      +
    • swift语句中可以case任意类型,
    • +
    • 在OC中只能case整数
    • +
    • 不需要写break,会自动跳出,
    • +
    • 如果需要实现case穿透,需要加入fallthrough语句
    • +
    • case语句中临时变量不需要写‘{ }’
    • +
    • 可同时case多个值
    • +
    • 每句case语句中至少要有一行可执行的代码
    • +
    +
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let string = "string1"
switch string {
case "string", "string1":
let str = "stringOne"
print(str)

case "string2":
let str = "stringTWO"
print(str)

case "string3":
print(string)

case "string4":
print(string)
default:
print("empty")
}
+
    +
  • switch 中同样能够赋值和使用 where 子句
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
let point = CGPoint(x: 10, y: 10)
switch point {
case let p where p.x == 0 && p.y == 0:
print("中心点")
case let p where p.x == 0:
print("Y轴")
case let p where p.y == 0:
print("X轴")
case let p where abs(p.x) == abs(p.y):
print("对角线")
default:
print("其他")
}
+
    +
  • 循环语句
  • +
+
1
2
3
4
5
6
7
for i in 0 ..< 10 { // 不包含10  循环范围中两边的值格式必须一直(空格问题)
print(i)
}

for _ in 0...5 {// 包含5 _ 表示忽略,不关心,只占位
print("循环语句")
}
+ +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2017/03/05/Swift3.0\345\255\246\344\271\240(\344\272\214)/index.html" "b/2017/03/05/Swift3.0\345\255\246\344\271\240(\344\272\214)/index.html" new file mode 100644 index 0000000..7f7fd10 --- /dev/null +++ "b/2017/03/05/Swift3.0\345\255\246\344\271\240(\344\272\214)/index.html" @@ -0,0 +1,352 @@ + + + + + + + + Swift3.0学习(二) | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ Swift3.0学习(二) +

+ + +
+ +
+ +

字符串

+

字符串遍历
字符串字节长度
字符串长度

+

let string = “我是switf,语言”

+

// 字符串遍历
for s in string.characters {
print(s)
}

+

// 字符串的长度
let length = string.characters.count;
print(length)

+

// 字符串字节长度 汉字对应3个字节,英文是1个字节
let characterLength = string.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)
print(characterLength)

+

字符串拼接

+
let str1 = "你好"
+   let str2 = "不好"
+   let a = 20
+
+   // 第一种拼接
+   print("\(str1)\(str2)\(a)")
+
+   // 第二种拼接
+   let str = str1 + str2
+   let strA = str1 + String(a)
+   print(str)
+   print(strA)
+

截取子串 as 的使用

+
let subString = str1.substringToIndex("小程生生世世".endIndex);
+   print(subString)
+
+   let subStr = str1.substringFromIndex("听说".startIndex)
+   print(subStr)
+
+   let range = str1.rangeOfString("小程序")
+   let subStr1 = str1.substringWithRange(range!)
+
+   print(subStr1)
+
+   // 截取到某一个位置
+   let subStr2 = str1.substringFromIndex(str1.endIndex.advancedBy(-2))
+   print(subStr2)
+
+   // 限制截取几个字符
+   let subStr3 = str1.substringToIndex(str1.startIndex.advancedBy(5, limit: str1.characters.endIndex))
+   print(subStr3)
+String 和 NSString 之间的 使用 as 无缝转换
+let helloString = "我们一起飞"
+(helloString as NSString).substringWithRange(NSMakeRange(2, 3))
+数组
+

可以放不同类型的元素
let不可变数组,var可变数组

+
// 初始化一个空数组 [String]声明是一个装有字符串的数组类型
+   var emptyArray: [String] = [String]()
+
+// 可变数组
+   var array = ["哈哈","呵呵","嘿嘿"]
+   array.append("咯咯")
+   print("array = \(array)")
+
+   // 不可变数组
+   let array2 = ["可可","噗噗"]
+
+   // 数组拼接
+   let arr = array + array2
+   print("arr = \(arr)")
+
+   array += array2
+   print("array = \(array)")
+

数组遍历

+
// 1. 
+   for i in 0 ..< tempArray.count {
+       print(tempArray[i])
+   }
+
+
+   // 2. 
+   for obj in tempArray {
+       print("第二种方法:\(obj)")
+   }
+
+
+   // 3. 快速便利
+   for (index,value) in tempArray.enumerate() {
+       print("index = \(index), value = \(value)")
+   }
+

字典和OC类似

+

函数调用

+

必须在函数声明的下面调用内部函数

+

可以定义函数的外部参数和内部参数

+

可以在参数前面加上 ‘# ’ 表明函数的外部参数和内部参数名称一样 swift3.0 以后废弃

+

无参无返回值
三种写法

+
func demo9() -> Void {
+       print("无参无返回值第一种写法")
+   }
+
+
+   func demo_9() -> () {
+       print("无参无返回值第二种写法")
+   }
+
+   func demo_99() {
+       print("无参无返回值第三种写法")
+   }
+

有参有返回值

+
func demo10(width a: Int, height b: Int) -> Int {
+   return a * b
+

}

+

闭包

+

和OC中block类似,
和在函数中调用一个内部函数原理相同
可当作参数传递
在需要时执行闭包实现回调

+
// 定义闭包
+let closure = {() -> () in
+       print("闭包实现")
+   }
+
+   // 执行闭包
+   closure()
+

注意循环引用

+

只有两个对象相互强引用或者三个对象相互强引用形成闭环时才会形成循环引用

+

weak 和 unsafe_unretained的区别

+

__weak iOS5.0 推出,当对象被系统回收时,对象的地址会自动指向nil

+

____unsafe_unretained iOS4.0 推出,当对象被系统回收时,对象的地址不会自动指向nil,会造成野指针访问
解决办法:(官方推荐)weak - strong -dance 解决循环引用,AFNetworking中

+

尾随闭包

+

函数的最后一个参数时闭包的时候,函数的参数 ‘()’可以提前关闭,闭包写在 ‘()’后面,当作尾随闭包来使用

+

swift 和 OC 区别

+

swift 和 OC语法的快速的对比

+

XXX.init(xxx) ==> XXX.(xxx)
selector 类型 ==> ‘函数名’
对象方法的调用 是 ‘.’ self是可以省略的
枚举 枚举名 + 枚举值的名 => .枚举值的名
常量 和变量

+

let 声明常量 var 声明变量
变量/常量的类型是自动推到的
不同类型之间不能够直接运算 需要手动转换数据类型
可选项
‘?’ 表示表示可选项 可能有值 可能为 nil
可选项会自动带上 Optional 字样
可选项不能够直接参与运算 需要强制解包
‘!’ 表示强制解包 获取可选项中具体的值
‘??’ 快速判断可选项是否为nil 如果为 nil 就去取 ‘??’后面的默认值
控制流
if 没有非零即真的概念 必须制定明确的条件
if let 快速赋值 并且判断赋值对象是否为 nil 如果不为nil 就进入分之执行相关逻辑代码
guard let 作用和 if let 相反 好处: 可以减少一层分支嵌套
where 多重判断 注意 和 && || 不一样的
循环

+

for in
0..<10
0…10
字符串

+

更加轻量级 更加高效 是 结构体
支持快速遍历
长度
拼接
截取 as
集合 let 声明不可变的集合 var 声明可变的集合

+

声明 []
声明一个 空的集合
增删改查
合并
遍历
函数

+

func 函数名称(外部参数1 内部参数1: 类型, 外部参数2 内部参数2: 类型) -> Int {执行的代码}
函数没有返回值的三种方式
函数内部函数
闭包 () -> () 没有参数没有返回值的闭包类型

+

提前准备好的一段可以执行代码块
可以当做参数传递
在需要的时候执行闭包产生回调的效果
在闭包中使用self 需要考虑循环引用
函数是一种特殊的闭包

+

block 的循环引用的解除
weak 和 weak关键字作用类似 当属性对象被回收是 会自动指向nil
_
unsafeunretained 和 assgin关键字作用类似 当属性对象被回收是 不会自动指向nil, 会造成野指针访问 weak -strong - dance

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2017/03/08/Swift3.0\345\255\246\344\271\240(\344\270\211)/index.html" "b/2017/03/08/Swift3.0\345\255\246\344\271\240(\344\270\211)/index.html" new file mode 100644 index 0000000..88429d7 --- /dev/null +++ "b/2017/03/08/Swift3.0\345\255\246\344\271\240(\344\270\211)/index.html" @@ -0,0 +1,248 @@ + + + + + + + + Swift3.0学习(三) | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ Swift3.0学习(三) +

+ + +
+ +
+ +

面向对象–必选属性

+

构造函数时是分段构造,先构造子类,给’必选属性’设置初始值,然后再构造父类,
定义全局变量(属性)时,需要在构造函数中设置初始化变量值
super.init()是一个类构造结束
重写: override 函数重写(覆盖),如果在重写的方法中 没有super, 父类相关的一些操作就不会被执行
重载:函数名相同, 参数的类型 或者参数的个数不同 就形成了函数的重载
注意:重载构造函数,并且父类默认的构造函数 init()不重写, 父类默认的构造函数就不能够被访问,不能够确保必选属性设置初始值,只能调用重载的构造函数
面向对象–可选属性

+

使用KVC给对象设置值
注意: 在swift中使用KVC 基本数据类型不能声明为可选项,必须设置为必选项给定初始值

+

KVC实现的流程
遍历字典的键值 给对象发送 setValue: forKey消息
如果key 对象的属性不存在就将消息转发给 setVale: forUndefinedKey:
如果存在就直接设置值
注意:setVale: forUndefinedKey: 默认抛出异常 不能够super,如果super 相当于没有实现此方法

+

面向对象–便利构造函数

+

作用:方便快捷的创建对象
场景:
可检查参数是否正确来实例化
实例化控件
特点
必须以self来调用构造函数
指定的构造函数不能被重写,也不能被super调用
便利构造函数可被子类继承
可以构造失败,返回nil
面向对象 – 描述信息

+

重写 description 方法,返回值是string类型
使用kvc讲对象属性转换为字典
将字典转化为string类型

+
override var description: String {
+        let keys = ["name","age","number","sex"]
+
+        // 对象转为字典
+        let dict = self.dictionaryWithValuesForKeys(keys)
+
+        // 每个对象都有描述属性
+        return dict.description
+    }
+

面向对象 – 存储属性

+

面向对象 – 计算属性(readOnly)

+

只有getter,没有setter
只能取值不能赋值
每次调用都会被执行,消耗cpu
不占内存空间,依赖其他的属性来计算
面向对象 – didset 属性设置监察器

+

能获取 oldValue 和 newValue 的值
通常用来重写setter方法,实现视图绑定模型
在一个属性的didset中计算其他属性

+

缺点:消耗内存,被计算的属性占内存
优点:减少了cpu消耗
懒加载

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2017/03/25/\347\237\245\350\257\206\347\202\271\346\200\273\347\273\223/index.html" "b/2017/03/25/\347\237\245\350\257\206\347\202\271\346\200\273\347\273\223/index.html" new file mode 100644 index 0000000..83c9cfa --- /dev/null +++ "b/2017/03/25/\347\237\245\350\257\206\347\202\271\346\200\273\347\273\223/index.html" @@ -0,0 +1,234 @@ + + + + + + + + 知识点总结 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ 知识点总结 +

+ + +
+ +
+ +

开发中的知识点总结

+

textField

+

使用textField自定义搜索框,textField及leftView等属性
使用leftView 时需要设置model为始终显示
可以设置leftView的frame 调整图片大小
IBInsepectable IBDesignable 的使用

+

给属性检查器面板中添加属性设置选项
IBDesignalbe 实时显示UI控件,需要讲对应的xib和对应类绑定

+

增加分类

+

新建一个swift 文件,

+

使用extension,重写 get set方法
使用newValue 时需要判断,注意返回值的类型是否匹配
试图切换

+

一个控制器中加载不同视图,可以在loadView 方法中自定义根视图

+

loadView 使用注意事项
如果实现loadView 未调用 super loadView ,sb/xib实效
loadView调用时,如果根视图是nil(本质是未调用super loadView),会自动调用loadView 方法初始化根视图,形成递归调用
判断当前登陆标识的状态确定是调用 super loadView 还是 把创建自定义的根视图赋值给 view
VFL 的使用

+

使用之前需要先 关闭frame 的布局才能手动的添加约束 translatesAutoresizingMaskIntoConstraints = false
基本动画使用

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2017/04/10/Alamofire\346\272\220\347\240\201\345\255\246\344\271\240/index.html" "b/2017/04/10/Alamofire\346\272\220\347\240\201\345\255\246\344\271\240/index.html" new file mode 100644 index 0000000..bf35087 --- /dev/null +++ "b/2017/04/10/Alamofire\346\272\220\347\240\201\345\255\246\344\271\240/index.html" @@ -0,0 +1,245 @@ + + + + + + + + Alamofire源码学习总结 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ Alamofire源码学习总结 +

+ + +
+ +
+ +

网络请求时Path和Query 之间是用 ‘?’ 号隔开,后边是传给服务器的参数,GET请求Query是放在URL之后,POST请求是放在Body中

    +
  • 如果参数是一个 key-value 形式,Query 格式为:key=value

    +
  • +
  • 如果参数是一个数组 key = [value1, value2, value3 ….],

    +
  • +
  • Query 格式为 key[]=value1&key[]=value2&key[]=value3

    +
  • +
  • 如果参数是一个字典

    +
  • +
+
1
key = [“subKey1”:”value1”, “subKey2”:”value2”, “subKey3”:”value3”….],
+
    +
  • Query 的格式为
  • +
+
1
key[subKey1]=value1&key[subKey2]=value2&key[subKey3]=value3
+
    +
  • Alamfire中的编码
  • +
+
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
private func query(_ parameters: [String: Any]) -> String {
var components: [(String, String)] = []
for key in parameters.keys.sorted(by: <) {
let value = parameters[key]!
components += queryComponents(fromKey: key, value: value)
}
return components.map { "\($0)=\($1)" }.joined(separator: "&")
}
public func queryComponents(fromKey key: String, value: Any) -> [(String, String)] {
var components: [(String, String)] = [] // 元祖数组
if let dictionary = value as? [String: Any] { // value 为字典,key[subKey]=value 形式
for (nestedKey, value) in dictionary {
components += queryComponents(fromKey: "\(key)[\(nestedKey)]", value: value)
}
} else if let array = value as? [Any] { // value为数组, key[]=value 形式
for value in array {
components += queryComponents(fromKey: "\(key)[]", value: value)
}
} else if let value = value as? NSNumber { // value为 NSNumber
if value.isBool {
components.append((escape(key), escape((value.boolValue ? "1" : "0"))))
} else {
components.append((escape(key), escape("\(value)")))
}
} else if let bool = value as? Bool { // value 为 Bool
components.append((escape(key), escape((bool ? "1" : "0"))))
} else { // value 为字符串时 直接转义
components.append((escape(key), escape("\(value)")))
}
return components
}
+ +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2017/11/29/CocoaPods\345\210\266\344\275\234\347\247\201\346\234\211pod/index.html" "b/2017/11/29/CocoaPods\345\210\266\344\275\234\347\247\201\346\234\211pod/index.html" new file mode 100644 index 0000000..84c7d9d --- /dev/null +++ "b/2017/11/29/CocoaPods\345\210\266\344\275\234\347\247\201\346\234\211pod/index.html" @@ -0,0 +1,269 @@ + + + + + + + + CocoaPods制作私有pod | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ CocoaPods制作私有pod +

+ + +
+ +
+ +

CocoaPods原理

本文主要记录制作自己的pod库过程

+

CocoaPods 是自动化工具,主要用来管理一些开源的第三方框架,或者是制作自己的私有库,开源库,也可以使用它讲自己的项目组件化。

+

具体可以参考:

+
    +
  1. 细聊 Cocoapods 与 Xcode 工程配置
  2. +
  3. 你真的会用 CocoaPods 吗?
  4. +
+

CocoaPods 安装

安装教程

+

创建私有索引仓库

每次使用第三方开源库的时候只需要在 Podfile 文件中指定所用的库然后 Pod install ,这是因为安装pod的时候 pod setup远程仓库(索引库) clone到了本地,此仓库中存放了所有支持pod框架的描述信息(包括每个库的各个版本)

+

文件目录:

+
~/.cocoapods/repos/master
+
    +
  • 码云 或者 coding 上创建一个私有仓库

    +
  • +
  • 添加私有库到CocoaPods

    +
      +
    • 格式 pod repo add [repoName] (URL)

      +
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      	pod repo add PrivateSpec https://gitee.com/aapenga/PrivateSpec.git 
      ```


      ## 创建本地私有库

      - `cd` 到任意一个文件夹下, 执行 `pod lib create [项目名]`

      ```
      pod lib create PPExcelView
      +
    • +
    +
  • +
+
    +
  • 将文件放入到生成的 Demo 文件夹 的 Classes 文件目录下
  • +
  • cdExample 下(含有 Podfile 文件) 执行

    +
    1
    pod install
    +

    或者

    +
    1
    pod update
    +
  • +
+

此时添加的文件出现在 Dome Pod 的 Development Pods 目录中

+
    +
  • 从本地验证 pod 是否通过

    +
    1
    pod lib lint
    +

    如果有警告,可以使用 --private 或者 --allow-warnings 忽略

    +
    1
    2
    pod lib lint --allow-warnings
    pod lib lint --private
    +

    出现 PPExcelView passed validation. 为通过

    +
  • +
  • 从远程仓库验证 pod

    +
    1
    pod spec lint
    +

    出现 PPExcelView.podspec passed validation 为通过

    +
  • +
+

推送 .podspec 文件到私有仓库

cd.podspec 所在的文件下

+
pod repo push MyGitSpec PPExcelView.podspec
+
+ +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2017/12/02/\346\234\254\345\234\260\345\267\262\346\234\211\351\241\271\347\233\256\346\267\273\345\212\240Pod/index.html" "b/2017/12/02/\346\234\254\345\234\260\345\267\262\346\234\211\351\241\271\347\233\256\346\267\273\345\212\240Pod/index.html" new file mode 100644 index 0000000..0174384 --- /dev/null +++ "b/2017/12/02/\346\234\254\345\234\260\345\267\262\346\234\211\351\241\271\347\233\256\346\267\273\345\212\240Pod/index.html" @@ -0,0 +1,240 @@ + + + + + + + + 本地已有项目添加Pod | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ 本地已有项目添加Pod +

+ + +
+ +
+ +

上一篇中主要记录了如何使用 pod 工具自动创建 pod 库并且生成一个测试Demo,这边主要记录给已有的项目添加 pod

+

创建 .podsepc 文件

    +
  • cd 到已有项目的根目录执行命令: pod spec create [ProjectName].podspec

    +
    1
    pod spec create EmotionKeyboard.podspec
    +
  • +
+

修改编辑 .podsepc 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
	s.name         = "EmotionKeyboard"
s.version = "0.0.1"
s.summary = "A Emotional Keyboard."

# 此处的格式不能修改
s.description = <<-DESC

Swift Emotional Keyboard
DESC

s.homepage = "https://github.com/aTreey/EmotionKeyboard"

# 两种写法,使用下面第二种
# s.license = "MIT (example)"
s.license = { :type => "MIT", :file => "LICENSE" }

s.author = { "aTree" => "480814177@qq.com" }

s.platform = :ios, "8.0"

s.source = { :git => "https://github.com/aTreey/EmotionKeyboard.git", :tag => "#{s.version}" }


# 从 .podspec 同级别的目录下起匹配
s.source_files = "EmotionKeyboard", "EmotionKeyboard/Classes/**/*.{h,m}"

# 设置自己项目中依赖的系统库
s.framework = "UIKit"

```

## 调整文件的目录结构

- 在项目根目录下新建一个放组件代码的文件夹
- 如果项目中包含 `bundle` 文件, 需要在组件文件夹下新建一个 `Assets` 文件夹,用来存放组件中的资源

最终结果如下:

![](https://ws1.sinaimg.cn/large/a1641e1bly1fm2jl7ykslj21gc0o80xt.jpg))

- 创建并添加必要文件

- `LICENSE` 文件: 开源许可文件, 如果创建项目的时候选择了开源许可就可以自动生成忽略此步骤,如果没有直接拷贝一份其他的工程中的即可
- `README.md` 文件: 主要用来说明你开源库的信息及使用方法


## 验证本地 `.podspec` 文件

- 错误1: `.podspec `中的文件路径设置错误

![](https://ws1.sinaimg.cn/large/a1641e1bly1fm2jlboq82j20v807ejug.jpg)

- 错误2: bundle 文件资源路径错误

![](https://ws1.sinaimg.cn/large/a1641e1bly1fm2jl4shnnj20zq0cwgqf.jpg)


- 本地验证结果

- **EmotionKeyboard passed validation.** 本地验证验证通过

## 网络验证 `.podspec` 文件

- 给 `master` 分支打上 `.podspec` 文件中对应的 `tag` 标签, 提交推送,
- 执行命令
+ +pod spec lint +
1
- 网络验证结果
+ +EmotionKeyboard.podspec passed validation. +``` +

本文参考

+

给 Pod 添加资源文件

+

基于 Swift 创建 CocoaPods 完全指南

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2017/12/14/Mac-\345\256\211\350\243\205MySQL/index.html" "b/2017/12/14/Mac-\345\256\211\350\243\205MySQL/index.html" new file mode 100644 index 0000000..1ee7a93 --- /dev/null +++ "b/2017/12/14/Mac-\345\256\211\350\243\205MySQL/index.html" @@ -0,0 +1,250 @@ + + + + + + + + Mac 安装MySQL | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ Mac 安装MySQL +

+ + +
+ +
+ +

搭建 XMPP 即使通讯

+

安装MySQL数据库

    +
  • 安装MySQL 数据库

    +
      +
    • 官网 下载安装包

      +

      特别强调:不要一直下一步最后的时候会有个弹窗提示你生成root的临时密码,记录一下

      +
    • +
    +
  • +
  • 安装数据库管理工具 MySQLWorkbench

    +

    官网安装包下载安装

    +
  • +
  • 修改数据库 root 密码,

    +

    打开MySQLWorkbench 随便打开一个SQL 输入安装时记录的密码,确认后提示你修改密码,

    +
  • +
  • 如果为记住安装时密码,参考下面链接修改

    +

    +
  • +
+

安装 openfire

    +
  • 官网 下载安装包安装

    +
  • +
  • 安装后需重启系统偏好设置

    +
  • +
  • 如果出现右边的管理员配置按钮不能点击,请使用 brew 安装 或者更新 Java 环境,openfire 需要 Java 环境
  • +
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2018/01/08/Swift\345\212\240\345\257\206\347\233\270\345\205\263/index.html" "b/2018/01/08/Swift\345\212\240\345\257\206\347\233\270\345\205\263/index.html" new file mode 100644 index 0000000..e36dc69 --- /dev/null +++ "b/2018/01/08/Swift\345\212\240\345\257\206\347\233\270\345\205\263/index.html" @@ -0,0 +1,450 @@ + + + + + + + + Swift加密相关 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ Swift加密相关 +

+ + +
+ +
+ +

最近公司项目开发中引用了 SwiftOC 混编, 涉及到了加密相关, 使用了第三方 CryptoSwift, 以下是使用过程中的简单记录

+

CryptoSwift简介

+

数组转换

    +
  • data 转化为字节数组

    +
    1
    2
    let bytes = data.bytes
    print(bytes)
    +
  • +
  • 字节数组转化为 data

    +
    1
    2
    let data = Data(bytes: [0x01, 0x02, 0x03])
    print(data)
    +
  • +
  • 十六进制创建字节

    +
    let bytes_Hex = Array<UInt8>(hex: "0x010203")
    +print(bytes_Hex)
    +
  • +
  • 字节转十六进制数

    +
    let hex = bytes_Hex.toHexString()
    +print(hex)       
    +
  • +
+
    +
  • 字符串生成字节数组

    +
    let string2bytes = "string".bytes
    +print(string2bytes)
    +
  • +
+
    +
  • 字节数组 base64

    +
    let bytes1: [UInt8] = [1, 2, 3]
    +let base64Str = bytes1.toBase64()
    +print(base64Str!)
    +
  • +
  • 字符串base64

    +
    let str0 = "test"
    +let base64Str1 = str0.bytes.toBase64()
    +print(base64Str1!)
    +
  • +
+

MD5加密

    +
  • MD5(RFC1321)诞生于 1991 年,全称是“Message-Digest Algorithm(信息摘要算法)5”,由 MIT 的计算机安全实验室和 RSA 安全公司共同提出。
  • +
  • 之前已经有 MD2、MD3 和 MD4 几种算法。MD5 克服了 MD4 的缺陷,生成 128bit 的摘要信息串,出现之后迅速成为主流算法

    +
  • +
  • 字节数组MD5

    +
    let str = "测试test"
    +let bytes = str.bytes
    +
    +// 写法一
    +let digest = bytes.md5().toHexString()
    +print(digest)
    +// 04f47b69e3573867a5b3c1e5edb00789
    +
    +// 写法二
    +let digest1 = Digest.md5(bytes).toHexString()
    +print(digest1)
    +// 04f47b69e3573867a5b3c1e5edb00789
    +
  • +
  • Data的MD5值

    +
    /// data MD5
    +let data = Data(str.bytes)
    +let digest3 = data.md5().toHexString()
    +print(digest3)
    +// 04f47b69e3573867a5b3c1e5edb00789
    +
  • +
  • 字符串MD5

    +
    ///写法 一
    +let digest4 = str.md5()
    +print(digest4)
    +// 04f47b69e3573867a5b3c1e5edb00789
    +
    +// 写法二
    +do {
    +    var digest4_2 = MD5()
    +
    +    let _ = try digest4_2.update(withBytes: "测试".bytes)
    +    let _ = try digest4_2.update(withBytes: "test".bytes)
    +    let result = try digest4_2.finish()
    +    print(result.toHexString())
    +    //04f47b69e3573867a5b3c1e5edb00789
    +} catch {
    +
    +}    
    +
  • +
+

哈希算法

    +
  • 字节数组SHA值

    +
    let str = "测试test"
    +let bytes = str.bytes
    +
    +//方式一
    +let digest1 = bytes.sha1().toHexString()
    +let digest2 = bytes.sha224().toHexString()
    +let digest3 = bytes.sha256().toHexString()
    +let digest4 = bytes.sha384().toHexString()
    +let digest5 = bytes.sha512().toHexString()
    +print(digest1, digest2, digest3, digest4 ,digest5, separator: "\n")
    +
    +//方式二
    +let digest6 = Digest.sha1(bytes).toHexString()
    +let digest7 = Digest.sha224(bytes).toHexString()
    +let digest8 = Digest.sha256(bytes).toHexString()
    +let digest9 = Digest.sha384(bytes).toHexString()
    +let digest10 = Digest.sha512(bytes).toHexString()
    +print(digest6, digest7, digest8, digest9 ,digest10, separator: "\n")
    +
  • +
+
    +
  • Data的SHA值

    +
    let data = Data(bytes: str.bytes)
    +let digest11 = data.sha1().toHexString()
    +let digest12 = data.sha224().toHexString()
    +let digest13 = data.sha256().toHexString()
    +let digest14 = data.sha384().toHexString()
    +let digest15 = data.sha512().toHexString()
    +print(digest11, digest12, digest13, digest14 ,digest15, separator: "\n")
    +
  • +
+
    +
  • 字符串的SHA值

    +
    let digest16 = str.sha1()
    +let digest17 = str.sha224()
    +let digest18 = str.sha256()
    +let digest19 = str.sha384()
    +let digest20 = str.sha512()
    +print(digest16, digest17, digest18, digest19 ,digest20, separator: "\n")
    +
  • +
+
// 方式一
+let digest_str = str.sha1()
+print(digest_str)
+
+// 方式二
+do {
+    var digest_str = SHA1()
+    let _ = try digest_str.update(withBytes: Array("测试".bytes))
+    let _ = try digest_str.update(withBytes: Array("test".bytes))
+    let result = try digest_str.finish()
+    print(result.toHexString())
+} catch {
+
+}
+

AES 加密

    +
  • key:公共密钥

    +
  • +
  • IV: 密钥偏移量

    +
  • +
  • padding:key 的补码方式

    +
    key 长度不够16 字节时,需要手动填充, zeroPadding 将其补齐至 blockSize 的整数倍
    +zeroPadding 补齐规则:
    +- 将长度补齐至 blockSize 参数的整数倍。比如我们将 blockSize 设置为 AES.blockSize(16)
    +- 如果长度小于 16 字节:则尾部补 0,直到满足 16 字节。
    +- 如果长度大于等于 16 字节,小于 32 字节:则尾部补 0,直到满足 32 字节。
    +- 如果长度大于等于 32 字节,小于 48 字节:则尾部补 0,直到满足 48 字节。 以此类推......
    +
  • +
  • 补码方式padding, noPadding, zeroPadding, pkcs7, pkcs5, 默认使用 pkcs7,两者写法结果相同

    +
  • +
+
let iv_paddding = "1234567890123456" // 默认是
+let aes_padding1 = try AES(key: key.bytes, blockMode: .CBC(iv: iv_paddding.bytes))
+let aes_padding2 = try AES(key: key.bytes, blockMode: .CBC(iv: iv_paddding.bytes), padding: .pkcs7)            
+
    +
  • ECB 模式

    +
    let key = "hangge.com123456"
    +let str = "E蜂通信"
    +print("加密前 \(str)")
    +
    +//MARK: 使用ECB 模式, , 使用aes128 加密
    +let aes = try AES(key: key.bytes, blockMode: .ECB)
    +let encrpyted = try aes.encrypt(str.bytes)
    +print("加密后 \(encrpyted)", separator: "-----")
    +
    + /// 解密
    + let decrypted = try aes.decrypt(encrpyted)
    + print("解密后 \(decrypted)")
    + let decryptedString = String(data: Data(decrypted), encoding: .utf8)!
    + print("解密后字符串 \(decryptedString)")
    +
  • +
+
    +
  • CBC 模式

    +
    //MARK: 使用CBC 模式, 需要提供一个额外的密钥偏移量 iv
    +
    +    /// 写法 1 --  便捷写法
    +    // 偏移量
    +    let iv = "1234567890123456" // 默认是
    +
    +    // TODO: 随机密钥偏移量
    +    let data = Data(bytes: AES.randomIV(AES.blockSize))
    +    let random_iv = String(data: data, encoding: .utf8)
    +
  • +
+
/// 写法 2 --
+// 创建密码器
+let cbc_aes = try AES(key: key.bytes, blockMode: .CBC(iv: iv.bytes))
+
+let cbc_aes2 = try AES(key: key, iv: iv)
+
+
+let cbc_aes3 = try AES(key: key, iv: iv)
+
+let cbc_encrypted = try cbc_aes.encrypt(str.bytes)
+
+let cbc_encrypted2 = try cbc_aes2.encrypt(str.bytes)
+
+let cbc_encrypted3 = try cbc_aes3.encrypt(str.bytes)
+
+
+// 便捷写法解密
+let cbc_decrypted = try cbc_aes.decrypt(cbc_encrypted)
+let cbc_decryptedData = Data(cbc_decrypted)
+let cbc_decryptedStr = String(data: cbc_decryptedData, encoding: .utf8)
+print(cbc_decryptedStr)
+
+// 正常写法2 解密
+let cbc_decrypted2 = try cbc_aes2.decrypt(cbc_encrypted2)
+let cbc_decryptedStr2 = String(data: Data(cbc_decrypted2), encoding: .utf8)
+print(cbc_decryptedStr2)
+
+
+// 随机密钥偏移量解密
+let cbc_decrypted3 = try cbc_aes3.decrypt(cbc_encrypted3)
+let cbc_decryptedStr3 = String(data: Data(cbc_decrypted3), encoding: .utf8)
+print(cbc_decryptedStr3)
+
+ +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2018/01/10/\347\275\221\347\273\234\344\270\203\345\261\202\345\215\217\350\256\256/index.html" "b/2018/01/10/\347\275\221\347\273\234\344\270\203\345\261\202\345\215\217\350\256\256/index.html" new file mode 100644 index 0000000..619dd57 --- /dev/null +++ "b/2018/01/10/\347\275\221\347\273\234\344\270\203\345\261\202\345\215\217\350\256\256/index.html" @@ -0,0 +1,262 @@ + + + + + + + + iOS 即使通讯网络知识篇 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ iOS 即使通讯网络知识篇 +

+ + +
+ +
+ +

网络七层协议

读《图解TCP/IP》第5版以及看过的关于网络文章之后自己的理解

+

OSI (Open System Interconnec) 译为 开发式系统互联 七层模型从下到上一次是:物理层 -> 数据链路层 —> 网络层 -> 传输层 -> 会话层 -> 表示层 -》 应用层

+

数据每次经过一层之后就会在数据头部增加一个首部信息

+
物理层:网卡,网线,集线器,中继器,调制解调器
    +
  • 定义设备标准,如网线,光纤接口类型,将0和1转化为电信号的强和弱,达到传输比特流目的,这层数据叫比特
  • +
+
数据链路层:网桥,交换机
    +
  • 传输的地址帧,并且有检测错误功能,保证数据可靠传输, 这层数据叫帧
  • +
+
网络层:路由器
    +
  • 两个计算机通信时可能会经过多个数据链路或者通信子网,网络层将数据链路层的帧组成数据包,包中有封装好的包头,其中含有逻辑地址信息(源站点和目的站点),此层为数据包选择合适的路由和交换结点,IP协议产生,这层数据叫着数据包
  • +
+
传输层:
    +
  • 解决数据如何在网络中传输,这个层负责获取全部信息,因此,它必须跟踪数据单元碎片、乱序到达的 数据包和其它在传输过程中可能发生的危险。第4层为上层提供端到端(最终用户到最终用户)的透明的、可靠的数据传输服务。所为透明的传输是指在通信过程中 传输层对上层屏蔽了通信传输系统的具体细节。传输层协议的代表包括:TCP、UDP、SPX等
  • +
+
会话层:
    +
  • 这一层也可以称为会晤层或对话层,在会话层及以上的高层次中,数据传送的单位不再另外命名,而是统称为报文。会话层不参与具体的传输,它提供包括访问验证和会话管理在内的建立和维护应用之间通信的机制。如服务器验证用户登录便是由会话层完成的
  • +
+
表示层:
    +
  • 这一层主要解决拥护信息的语法表示问题。它将欲交换的数据从适合于某一用户的抽象语法,转换为适合于OSI系统内部使用的传送语法。即提供格式化的表示和转换数据服务。数据的压缩和解压缩, 加密和解密等工作都由表示层负责。
  • +
+
应用层:HTTP 协议
    +
  • 如何包装数据
  • +
+

通常从上到下会分成四层

+

TCP/IP 通常是指TCP/IP协议族,是一组不同协议组合在一起构成的协议族

+
    +
  • 应用层:FTP 应用层协议
  • +
  • 传输层:TCP,UDP 传输层协议,TCP 提供了一个可靠的服务
  • +
  • 网络层:IP,ICMP 网络层协议,传输时不可靠的
  • +
  • 链路层:以太网协议
  • +
+

Socket

为了解决不同计算机上进程间通信问题
服务器在指定的端口上监听,然后生成一对儿新的socket 进行通讯,一个线程对应一个socket

+

英文翻译为“插座”通常称为”套接字”, 本质是对于TCP/IP的封装,有客户端 socket 和 服务端socket

+
    +
  • 流式 Socket (STREAM): 是一种面向连接的socket,针对于面向连接的TCP服务应用,因为安全所以效率低
  • +
  • 数据报式Socket (DATAGRAM): 是一种无连接的SOcket,正对于无连接的UDP服务应用,无序,不安全,需要在接收端分析重排或者要求重发,所以效率高
  • +
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2018/01/12/iOS\345\215\263\346\227\266\351\200\232\350\256\257\345\256\236\347\216\260\344\272\214/index.html" "b/2018/01/12/iOS\345\215\263\346\227\266\351\200\232\350\256\257\345\256\236\347\216\260\344\272\214/index.html" new file mode 100644 index 0000000..6f41204 --- /dev/null +++ "b/2018/01/12/iOS\345\215\263\346\227\266\351\200\232\350\256\257\345\256\236\347\216\260\344\272\214/index.html" @@ -0,0 +1,366 @@ + + + + + + + + iOS即时通讯实现二 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ iOS即时通讯实现二 +

+ + +
+ +
+ +

实现IM通信所用协议

几个协议理解

+

xmpp 是基于xml协议主要是易于扩展,早起用于PC时代的产品使用,流量大,耗电大交互复杂其实并不适合移动互联网时代的IM产品

+

MQTT 协议 适配多平台,需自己扩展好友,群组等功能

+

protobut 协议

+

封装socket,自己实现

所要考虑的问题

+
    +
  • 传输协议选择

    +
      +
    • TCP ? 一般公司都选用(技术不是很成熟)
    • +
    • UDP ?QQ 腾讯增加自己的私有协议,保证数据传递的可靠性,解决了丢包,乱序的问题
    • +
    +
  • +
  • 聊天协议选择及代表框架

    +
      +
    • Socket ? CocoaAsyncSocket
    • +
    • WebSockt ?是传输通讯协议,基于socket封装的一个协议 SocketRocket
    • +
    • MQTT ?MQTTKit 聊天协议为上层协议
    • +
    • protobut 协议 ?
    • +
    • XMPP ?XMPPFramework 聊天协议为上层协议
    • +
    • 自定义
    • +
    +
  • +
  • 传输数据格式

    +
      +
    • Json?
    • +
    • XML?
    • +
    • ProtocolBuffer?
    • +
    +
  • +
  • 细节相关

    +
      +
    • TCP 长连接如何保持,
    • +
    • 心跳机制
    • +
    • 重连机制
    • +
    • 数据安全机制
    • +
    +
  • +
+

CocoaAsyncSocket

    +
  • CocoaAsyncSocket 的 delegate 用来设置代理处理各个回调
  • +
  • delegateQueue
  • +
  • socketQueue 串行队列,贯穿全类并没有任何加锁,确保了socket 操作中每一步都是线程安全的
  • +
  • 创建了两个读写队列(本质是数组)

    +
      +
    • NSMutableArray *readQueue;
    • +
    • NSMutableArray *writeQueue;
    • +
    +
  • +
  • 全局数据缓冲: 当前socket未获取完的数据大小

    +
      +
    • GCDAsyncSocketPreBuffer *preBuffer;
    • +
    +
  • +
  • 交替延时变量: 用于进行另一服务端地址请求的延时

    +
      +
    • alternateAddressDelay
    • +
    +
  • +
  • connect

    +
  • +
  • disconnect

    +
  • +
+

数据粘包,断包

    +
  • 粘包: 如果客户端同一时间发送几条数据,而服务器只收到一大条数据

    +
      +
    • 原因:
    • +
    • 由于传输的是数据流,经过TCP传输后,TCP使用了优化算法将多次间隔较小且数据量小的数据合并成一个大的数据块,因此三条数据合并成了一条

      +

      TCP,UDP都可能造成粘包的原因

      +
    • +
    • 发送端需要等缓冲区满了才发送出去,做成粘包

      +
    • +
    • 接收方不及时接收缓冲区的包,造成多个包接收
    • +
    +
  • +
  • 断包: 因为一次发送很大的数据包,缓冲区有限,会分段发送或读取数据

    +
  • +
+

CocoaAsyncSocket的封包,拆包处理

    +
  • 读取数据方法:

    +
    // 有数据时调用,读取当前消息队列中的未读消息
    +- (void)readDataWithTimeout:(NSTimeInterval)timeout tag:(long)tag
    +
  • +
+
**每次读取数据,每次都必须手动调用上述 readData 方法超时设置为不超时才能触发消息回调的代理**
+
+**因此在第一连接时调用,再在接到消息时调用,上述方法就可以达到每次收到消息都会触发读取消息的代理**
+
+以上做法存在的问题就是没有考虑数据的拆包会有粘包情况,这时候需要用下面两个`read`方法,1. 读取指定长度、2.读取指定边界
+
+    // 读取特定长度数据时调用
+    - (void)readDataToLength:(NSUInteger)length withTimeout:(NSTimeInterval)timeout tag:(long)tag
+
+    // 读到特定的 data 边界时调用
+    - (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag
+
+
+**解决办法及具体思路**
+
+- 封包:给每个数据包增加一个长度或者加一个开始结束标记,表明数据的长度和类型(根据自己的项目:文本、图片、语音、红包、视频、话题、提问等等)
+    - 可以在数据包之后加一个结束标识符,解决了传输过程中丢包,丢失头部信息的错误包读取,读到这样的就丢弃直接读下一个数据包
+
+- 拆包:获取每个包的标记,根据标记的数据长度获取数据,最后根据类型处理数据,最后读取数据包头部的边界
+
    +
  • 利用缓冲区对数据进行读取

    +
      +
    • 创建读取数据包
    • +
    • 添加到读取的队列中
    • +
    • 从队列中取出读取任务包
    • +
    • 使用偏移量 maxLength 读取数据

      +
      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
      		- (void)readDataWithTimeout:(NSTimeInterval)timeout
      buffer:(NSMutableData *)buffer
      bufferOffset:(NSUInteger)offset
      maxLength:(NSUInteger)length
      tag:(long)tag
      {
      if (offset > [buffer length]) {
      LogWarn(@"Cannot read: offset > [buffer length]");
      return;
      }

      // 1. 创建读取数据包
      GCDAsyncReadPacket *packet = [[GCDAsyncReadPacket alloc] initWithData:buffer
      startOffset:offset
      maxLength:length
      timeout:timeout
      readLength:0
      terminator:nil
      tag:tag];

      dispatch_async(socketQueue, ^{ @autoreleasepool {

      LogTrace();

      if ((flags & kSocketStarted) && !(flags & kForbidReadsWrites))
      {
      // 2. 向读的队列添加任务包
      [readQueue addObject:packet];
      // 3. 从队列中取出读取任务包
      [self maybeDequeueRead];
      }
      }});

      // Do not rely on the block being run in order to release the packet,
      // as the queue might get released without the block completing.
      }
      +
    • +
    +
  • +
+

doReadData 方法时读取数据的核心方法

+
    +
  • 如果读取对列中没有数据,就去判是否是上次已经读取了数据,但是因为没有目前还没有读取到数据的标记,这是正好设置了 read 方法中的超时时间,所以要断开socket 连接
  • +
+

+
    +
  • 接收到数据后在 socket系统进程的数据缓冲区中
      +
    • (基于TLS的分为两种)他们在各自管道中流动,完成数据解密后又流向了全局数据缓冲区
        +
      • CFStreem
      • +
      • SSLPrebuffer
      • +
      +
    • +
    +
  • +
+

处理数据逻辑: 如果当前读取包长度给明了,则直接流向currentRead,如果数据长度不清楚,那么则去判断这一次读取的长度,和currentRead可用空间长度去对比,如果长度比currentRead可用空间小,则流向currentRead,否则先用prebuffer来缓冲。

+

心跳机制

    +
  • TCP 自带的keep-alive 使用空闲时间来发并且默认超时时间太长是2 小时,
  • +
  • 如果应用使用了socks ,socks proxy会让tcp keep-alive失效,因为socks 协议只管转发TCP层具体的数据包,并不会转发TCP协议内的实现具体细节数据包
  • +
  • TCP 的长连接理论上是一直保持连接的,可以设置 TCP keep-alive的时间 但是实际情况下中,可能出现故障或者是因为防火墙的原因会自动把一定时间段内没有数据交互的连接给断开,心跳机制就可以维持长连接,保持活跃状态

    +
  • +
  • iOS 中使用 NSTimer scheduledTimerWithTimeInterval 需要在调用之前的关键位置设置 fireDate 为未来的某个时间, 然后再需要的时候开启

    +
  • +
  • 或者直接使用 timerWithTimeInterval 创建,然后添加到runloop
  • +
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2018/01/17/\351\235\242\350\257\225\344\270\255\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223/index.html" "b/2018/01/17/\351\235\242\350\257\225\344\270\255\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223/index.html" new file mode 100644 index 0000000..e8dbe9c --- /dev/null +++ "b/2018/01/17/\351\235\242\350\257\225\344\270\255\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223/index.html" @@ -0,0 +1,298 @@ + + + + + + + + 面试中常见问题总结 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ 面试中常见问题总结 +

+ + +
+ +
+ +

OC 的动态性

OC是基于C语言的,C语言在编译阶段就会确定了调用具体的那个函数,OC它的底层是通过runtime 运行时机制来调用函数,在OC中成为消息发送,具体来说就是在编译阶段OC可以调用任何函数,即时这个函数是没有实现的,只有在运行的时候才会根据isa指针和已经注册的方法列表中找到 IMP(函数指针)真正要调用的那个函数,我们平时写的OC代码,在运行的时候也都是转换成了runtime 的方式进行的,不管是什么方法本质都是发送一个消息

+

runtime

    +
  • 消息机制原理: 根据方法编号列表 SEL 去映射表中查找对应方法的实现
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Person *p = [Person allo] init];

// 底层的写法
// alloc
Person *p = objc_msgSend(objc_getClass("Person"), sel_registerName("alloc"),);

// init
p = objc_msgSend(p, sel-registerName("init"));

// 调用对象方法本质就是:让对象发消息
objc_msgSend(p, @selector(eat));

// 调用类方法本质: 让类发消息
objc_msgSend([Person class], @selector(run:), 20);

// alloc init 也可以理解为:

id objc = objc_msgSend([NSObject class], @selector(alloc));

objc = objc_msgSend(objc, @selector(alloc));
+
    +
  • runtime 消息机制调用流程

    +

    每个对象内部都有一个isa指针,指向他自己真实的类来确定调用的是那个类的方法; 每个方法sel都有一个IMP指针(指向现实函数的指针),不管是对象方法还是类方法,都会有一个方法列表

    +
      +
    1. 发消息时根据对象内部的isa 指针去该类所对应的方法类表中查找方法,如果没有去父类中查找
    2. +
    3. 注册每个方法编号,为了以后快速查找调用
    4. +
    5. 根据方法编号查找对应方法
    6. +
    7. 通过isa指针和IMP指针确定最终调用的函数
    8. +
    +
  • +
  • runtime 常见使用

    +
      +
    1. 运行是动态添加方法
    2. +
    3. 给分类添加属性,(关联属性)
    4. +
    5. 字典转模型中从先遍历模型属性再从字典中查找相关值实现模型属性赋值
    6. +
    7. NSCoding 自动归档解档时对象属性过多时 encodeWithCoder:initWithCoder 两个方法实现
    8. +
    +
  • +
+

Runloop

    +
  • 概念

    +

    NSRunloop 是封装 CFRunloopRef 一套纯C的函数,

    +
  • +
  • 作用

    +
      +
    1. 保持程序持续运行
    2. +
    3. 处理App中各种事件,具体的有:toches事件, NSTimer 事件, Selector 事件, PerformSelector 事件,source 源等等
    4. +
    5. 有事件触发时就运行,没有就休眠
    6. +
    7. 渲染app 中唯一一个主线程上所有UI界面的更新
    8. +
    +
  • +
  • 使用和要点

    +
      +
    1. Runloop 在程序入口 main 函数中就会开启,并且开启的是主线程不会销毁除非退出程序,保证了程序一直运行并能响应事件和交互
    2. +
    3. 每一条线程都是唯一对应一个Runloop
    4. +
    5. 主线程不需要创建,因为在程序启动的时候就已经创建了,而子线程的需要手动创建 直接获取currentRunloop 方式(本质是使用了懒加载)
    6. +
    7. Runloop 对象是利用字典进行存储,因为一一对应,所有key 和value 就是线程和 他所对应的runloop

      +
    8. +
    9. Runloop 中 NSDefaultRunLoopMode 一般情况下主线程运行的 Mode

      +
    10. +
    11. UIInitializationRunLoopMode 初始化mode,通常不用
    12. +
    13. UITrackingRunLoopMode 界面跟踪, 用于scrollView 追踪触摸滑动,保证界面不受其他mode影响
    14. +
    15. GSEventReceiveRunLoopMode 用于接收系统事件内部 mode, 通常不用

      +
    16. +
    17. NSRunLoopCommonModes, 并不是某种具体的 mode ,开发中使用最多,他是一个组合 mode,组合了 NSDefaultRunLoopMode和 NSRunLoopCommonModes

      +
    18. +
    +
  • +
  • Runloop 相关类

    +
      +
    1. Source 事件源/输入源可以理解为 激发源
    2. +
    3. Timer 基于事件触发,CADisplayLink,NSTimer 都是加到了 Runloop,受 Mode 的影响,GCD 定时器不受Runloop 影响
    4. +
    5. Observer 相当于runloop 循环中的监听器,时刻监听当前Runloop的运行状态
    6. +
    7. 休眠没事情做的时候可以停下来,处于休眠状态
    8. +
    +
  • +
  • 使用场景

    +
      +
    1. 创建NSTimer
    2. +
    3. PersforSelector方法
    4. +
    5. 常驻线程:(创建后处于等待状态,有事件时响应事件)
        +
      • 实现后台收集用户停留时间或者是某个按钮的点击次数,如果使用主线程会不方便,使用runloop解决
      • +
      +
    6. +
    7. AutoreleasePool 自动释放池
    8. +
    9. UI更新
    10. +
    +
  • +
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2018/01/18/\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223-\344\272\214/index.html" "b/2018/01/18/\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223-\344\272\214/index.html" new file mode 100644 index 0000000..b608477 --- /dev/null +++ "b/2018/01/18/\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223-\344\272\214/index.html" @@ -0,0 +1,264 @@ + + + + + + + + 面试中常见问题总结(二) | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ 面试中常见问题总结(二) +

+ + +
+ +
+ +

非正式协议和正式协议

非正式协议:凡是给NSObject或者其子类添加的类别都是非正式协议
正式协议:使用 @protocol 方式声明的方法列表,有 @require 和 @optional 两种类型

+

KVC 键值编码

之所以可以使用就是因为给 NObject 增加了 NSKeyValueCoding 非正式协议,NSObject 实现了协议,OC中几乎所有的对象都支持KVC,获取值的方法是不通过属性的setter、getter 方法,而是通过属性名称的key间接访问了实例变量,可以访问私有变量

+
1
2
3
4
 // 官方注释
/* Send -setObject:forKey: to the receiver, unless the value is nil, in which case send -removeObjectForKey:.
*/
- (void)setValue:(nullable ObjectType)value forKey:(NSString *)key;
+

如果为nil, 就会自动过滤掉, 此方法常用在字典中过滤空值

+

// forKeyPath: 用于符合属性,能够通过层层访问内部属性,相当于调用属性点语法, 拥有forKey 的所有功能

+
1
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;
+
    +
  • 实现原理
      +
    • 利用 setValue: forKeyPath: 赋值
    • +
    +
      +
    1. 底层实现还是使用 runtime 给对象发消息,先去查找属性是否有setter方法,存在setter 就会通过 setter 方法复制,
    2. +
    3. 如果没有setter方法,接着查找下是否划线的成员变量,如果有直接给赋值
    4. +
    5. 如果没有下划线的成员变量,查找是否存在和key值相同的属性,如果有就直接给属性赋值
    6. +
    7. 如果没有和key值相同的属性,就会执行 setValue: forUndefinedKey: 抛出一个异常程序奔溃,这也是字典转模型中需要实现这个方法的原因
    8. +
    +
  • +
+

KVO 键值监听

利用一个key值来找到某个属性并监听某个属性的值的变化发送给观察者,可以理解为简单的观察者模式

+
    +
  • KVO 使用

    +
      +
    1. 为目标对象的属性添加观察者

      +
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      - (void)testKVO {
      Person *p = [[Person alloc] init];
      self.person = p;
      p.name = @"哈哈";

      [p addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];

      p.name = @"我不是原来的我!你能监听到我 ???";
      p.name = @"看我七十二变";
      }
      +
    2. +
    +
  • +
+
2. 实现监听的方法
+
+
1
2
3
4
5
#pragma mark - kvo 回调方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {

NSLog(@"KVO监听到的对象是 = %@, 值 = %@ , change = %@", [object class], keyPath, change);
}
+ +3. 移除观察者 + +
1
2
3
4
5
- (void)dealloc {

[self removeObserver:self forKeyPath:@"name"];
NSLog(@"-----控制器销毁-----");
}
+
    +
  • KVO原理

    +

    类的属性被添加了观察者后,OC底层就会通过Runtime 动态的创建一个这个类的派生类,当前类内部的isa 指针指向了这个派生类,在派生类中重写基类被观察属性的 setter 方法,调用前后会调用 willChangeValueForKey, setValueForKey, didChangeValue 方法, 从而达到监听属性值的方法

    +
  • +
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2018/01/19/Block\345\255\246\344\271\240\346\216\242\347\251\266/index.html" "b/2018/01/19/Block\345\255\246\344\271\240\346\216\242\347\251\266/index.html" new file mode 100644 index 0000000..6f1d52f --- /dev/null +++ "b/2018/01/19/Block\345\255\246\344\271\240\346\216\242\347\251\266/index.html" @@ -0,0 +1,244 @@ + + + + + + + + Block学习探究 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ Block学习探究 +

+ + +
+ +
+ +

代码及结果

    +
  • 普通局部变量

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    - (void)block1 {

    NSInteger num = 100;

    // 定义一个block
    dispatch_block_t block = ^ {
    NSLog(@"block1 -- numer = %zd", num);
    };

    num = 200;

    block();
    }
    +
  • +
  • 使用__block修饰变量

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    - (void)block2 {

    __block NSInteger num = 100;

    // 定义一个block
    dispatch_block_t block = ^ {
    NSLog(@"block2 -- numer = %zd", num);
    };

    num = 200;

    block();
    }
    +
  • +
  • 全局变量

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    - (void)block3 {


    // 定义一个block
    dispatch_block_t block = ^ {
    NSLog(@"block3 -- numer = %zd", blockNum);
    };

    blockNum = 200;

    block();
    }
    +
  • +
  • 静态常量

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    - (void)block4 {

    static NSInteger num = 100;

    // 定义一个block
    dispatch_block_t block = ^ {
    NSLog(@"block4 -- numer = %zd", num);
    };

    num = 200;

    block();
    }
    +
  • +
+

运行结果

2018-01-19 00:04:13.759416+0800 CycleRetain[91251:2382380] block1 -- numer = 100
+2018-01-19 00:04:13.760206+0800 CycleRetain[91251:2382380] block2 -- numer = 200
+2018-01-19 00:04:13.760473+0800 CycleRetain[91251:2382380] block3 -- numer = 200
+2018-01-19 00:04:13.760603+0800 CycleRetain[91251:2382380] block4 -- numer = 200
+

原理及本质

block 根据创建位置不同,共有三种:栈block,堆block,全局block

+

使用block本质就是为了保证栈上和堆上block内访问和修改的是同一个变量,具体的实现是将block 修饰的变动自动封装成一个结构体,让他在堆上创建
基于OC 底层的runtime 机制
block 在内部会有一个指向结构体的指针,当调用block的时候其实就是让block找出对应的指针所指的函数地址进行调用。并传入了block自己本身

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2018/02/03/Mac\345\256\211\350\243\205VirtualBox\350\277\236\346\216\245U\347\233\230/index.html" "b/2018/02/03/Mac\345\256\211\350\243\205VirtualBox\350\277\236\346\216\245U\347\233\230/index.html" new file mode 100644 index 0000000..eeb441a --- /dev/null +++ "b/2018/02/03/Mac\345\256\211\350\243\205VirtualBox\350\277\236\346\216\245U\347\233\230/index.html" @@ -0,0 +1,228 @@ + + + + + + + + Mac安装VirtualBox连接U盘 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ Mac安装VirtualBox连接U盘 +

+ + +
+ +
+ +

最近因为某些原因需要再Mac上安装虚拟机,所以选择了VirtualBox安装了
win10系统,但是需要再虚拟机的win10中访问U盘数据,遇到的问题是不能连接并且读取到U盘,错误提示不能分配USB内存。。。

+

网上找了好多办法都胡扯,根本没有解决…

+

解决方法:
1.升级VirtualBox到最新版
2.关闭当前的虚拟机中的操作系统
3.安装增强工具
4.重启VirtualBox,回到Mac桌面推出U盘即可

+

最重要的就是在第四步

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/03/Charles \346\212\223\345\214\205/index.html" "b/2019/06/03/Charles \346\212\223\345\214\205/index.html" new file mode 100644 index 0000000..fadf5dd --- /dev/null +++ "b/2019/06/03/Charles \346\212\223\345\214\205/index.html" @@ -0,0 +1,313 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

Charles 抓包

+

原理

简单理解如下:

+
    +
  • 正常请求

    +

    客户端 ——> 服务器

    +
  • +
+
    +
  • Charles 抓包

    +

    客户端 ——> 代理 (Charles)——> 服务器

    +
  • +
+

安装

Charles官网

+

破解版本请自行百度

+

重要说明安装之前请暂时关闭电脑的VPN

+

重要说明安装之前请暂时关闭电脑的VPN

+

重要说明安装之前请暂时关闭电脑的VPN

+

启动Charles

安装Charles证书

+
如果安装失败,请检查以前是否已经安装过,如果已经安装过并且已经失效,请删除失效证书后再安装
+

打开钥匙串–> 选择Charles CA -> 双击证书 –> 信任证书

+

设置 https

- Proxy -> SSL Proxying Settings -> SSL Proxying -> Add
+- Host: * 为需要过滤的域名地址,
+- *: 表示不过滤Port, 固定为443 `*`表示任意端口
+

+

Mac 抓包

    +
  • Proxy -> macOS Proxy (☑️)

    +

    +
  • +
+

真机抓包

此时可以将 macOS Proxy 选项中的 ✅ 去掉,

+

Mac与iPhone连接必须同一网络

设置代理端口号

+

手机安装证书

    +
  • 安装手机证书

    +

    +
  • +
  • 安装完成后弹窗

    +

    +
  • +
+
    +
  • 查看mac 地址,为手机设置代理

    +
      +
    1. 根据安装手机证书后的弹窗显示IP配置

      +
    2. +
    3. Charles –> Help –> Loca IP Address

      +

      +
    4. +
    5. 打开电脑网络设置查看

      +

      +
    6. +
    +
  • +
+
    +
  • 设置代理

    +

    第一步

    +

    第二步

    +

    第三步

    +
  • +
  • 根据弹窗提示,在手机浏览器下载并安装证书

    +

    +
  • +
+
    +
  • 手机信任证书

    +
      +
    • 设置 –> 通用 –> 关于本机 –> 证书信任设置

      +

      +
    • +
    +
  • +
+

模拟器抓包

    +
  • 安装模拟器证书

    +

    +
  • +
  • 勾选 macOS Proxy 选项

    +

    +
  • +
+

如果失败,请先关闭模拟器,重新启动Charles 再打开模拟器

+

使用浏览器打开网页提示不是私密链接

解决办法

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/03/ES6\350\257\255\346\263\225/index.html" "b/2019/06/03/ES6\350\257\255\346\263\225/index.html" new file mode 100644 index 0000000..e0f9cd8 --- /dev/null +++ "b/2019/06/03/ES6\350\257\255\346\263\225/index.html" @@ -0,0 +1,314 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

JS ES6 语法

1. let var 变量,作用域

var a = 10;
+
+if (a > 10) {
+    let b = 5; // let 只能在代码块内被访问
+
+    console.log(b);
+}
+
+// 访问 b 报错
+console.log(b);
+console.log(a);
+

2. const 只能分配一次值

{
const const_a = 1;
console.log(const_a)

+
// 报错
+const_a = 2;
+
+// 声明一个常量数组
+const const_array = [];
+const_array.push(1);
+const_array.push(2);
+// 再次赋值时会报错
+const_array = [3];
+

}

+

3. 解构语法

{
// 1.解构数组
function getArray() {
return [10, 20, 30];
}

+
let [a,b,c] = getArray()
+console.log(a, b, c)
+
+
+// 函数返回一个对象
+function getObj() {
+    return {dessert: '面包', drink: '水', play: 'Game'};
+}
+
+// 2.解构对象
+let {des: dessert,  dri: drink, pla: play} = getObj()
+console.log(des, dri, pla);
+

}

+

4. 模版字符串

{
let name = ‘姓名:’, password = ‘密码:’
let str_4 = name + ‘李老师’ + ‘—-‘ + password + ‘北京太热。。。’
console.log(str_4)

+
// 使用 `${}` 包裹会生成字符串
+let str_41 = `${name}李老师----${password}北京太热`
+console.log(str_41)
+
+// 多行显示时直接回车换行
+let str_42 = `${name}李老师
+----
+${password}北京太热`
+console.log(str_42)
+

}

+

5. 带标签的模版字符串

{
let name = ‘姓名:’, password = ‘密码:’
// 多行显示时直接回车换行
let str_5 = strTest${name}李老师 ---- ${password}北京太热
console.log(str_5)

+
// strings 字符中包含的被插值分割的子字符串已数组形式展现
+// values 字符串中的插值数组
+function strTest(strings, ...values) {
+    console.log(strings)
+    console.log(values)
+}
+
+function strTest1(strings, ...values) {
+    let result = '';
+    for (let i = 0; i < values.length; i++) {
+        result += strings[i]
+        result += values[i]
+    }
+    return result
+}
+

}

+

6. 判断字符串是否包含其他字符

{
let name = ‘姓名:’, password = ‘密码:’
let str_6 = ${name}李老师---${password}北京真热
console.log(str_6.startsWith(${name}))
console.log(str_6.endsWith(${name}))
console.log(str_6.includes(‘李老师’))
}

+

7. 函数默认参数

{
function funcDefaultParame(param1 = ‘a’, param2 = ‘b’, param3 = ‘c’) {
return ${param1}--${param2}--${param3}
}
console.log(funcDefaultParame(1, 2,3))
}

+

8. “…”展开操作符

{
let a = [1,2,3];
console.log(a);
console.log(…a)

+
let b = [...a, 4, 5];
+console.log(a);
+console.log(b)
+

}

+

9. ‘… ‘剩余操作符, 一般用在函数参数, 可以看作是一个可以展开的参数

//  ... param3表示函数可以接收除了param1, param2,两个参数外,多个参数,都会放在param3中
+function operator(param1, param2, ...param3) {
+    console.log(param1, param2, param3)
+}
+operator('a','b','c','d','e','f','g')
+

10. 解构对象解构函数

// 当函数参数是对象是可以按照对象解构
+function getObjcValue(param1, param2, {value1, value2} = {}) {
+    console.log(param1, param2, value1, value2)
+}
+
+getObjcValue(1,2,{value1: -3,value2: -4})
+

11. 获取函数名称

function getObjcValue(param1, param2, {value1, value2} = {}) {
+    console.log(param1, param2, value1, value2)
+}
+console.log(getObjcValue.name)
+
+let funName = function getfunctionName() {  }
+console.log(funName.name)
+

12. 箭头函数

// 原来普通写法
+var funType = function arrowfunction(param) {
+    return param
+}
+
+// 函数参数 => 函数返回值
+// '=>' 的左边表示箭头函数的参数,多个时表示为 (param1, param2, param3)
+// '=>' 的右边表示箭头函数的返回值,如果返回值是一个对象时用 { } 表示
+let arrowFunction = (param1, param2) => {
+    return `${param1}${param2}`
+

}

+

13

+ +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/03/GCD \344\277\241\345\217\267\351\207\217/index.html" "b/2019/06/03/GCD \344\277\241\345\217\267\351\207\217/index.html" new file mode 100644 index 0000000..632369d --- /dev/null +++ "b/2019/06/03/GCD \344\277\241\345\217\267\351\207\217/index.html" @@ -0,0 +1,256 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

GCD信号量用法

+

对于异步网络请求相互依赖问题一般用三种方式解决:

+
    +
  • 网络请求嵌套
  • +
  • 使用 BlockOperation
  • +
  • GCD DispatchGroup
  • +
  • GCD semaphore(信号量)
  • +
+

GCD 信号量

    +
  • semaphore 值 <= 0 阻塞
  • +
  • semaphore semaphore.signal() 使信号量值增加
  • +
  • semaphore.wait() 等待,可设置时长
  • +
+

request – 3 依赖 request – 1 和 request – 2,1 和 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
private func gcdSemaphore() {
let semaphore = DispatchSemaphore(value: 0)
DispatchQueue.global().async {
for i in 0...300 {
print("request -- 1")
}
semaphore.signal()
}

DispatchQueue.global().async {
for i in 0...200 {
print("request -- 2")
}
semaphore.signal()
}


DispatchQueue.global().async {
semaphore.wait()
semaphore.wait()
for i in 0...160 {
print("request -- 3")
}
}
}
+

request – 3 ,request – 2 和 request – 1,依次相互依赖

+
private func gcdSemaphore() {
+        let semaphore = DispatchSemaphore(value: 0)
+        DispatchQueue.global().async {
+            for i in 0...300 {
+                print("request -- 1")
+            }
+            semaphore.signal()
+        }
+
+        DispatchQueue.global().async {
+            semaphore.wait()
+            for i in 0...200 {
+                print("request -- 2")
+            }
+            semaphore.signal()
+        }
+
+
+        DispatchQueue.global().async {
+            semaphore.wait()
+            for i in 0...160 {
+                print("request -- 3")
+            }
+        }
+    }
+
+
+ +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/03/JavaScript\345\237\272\347\241\200/index.html" "b/2019/06/03/JavaScript\345\237\272\347\241\200/index.html" new file mode 100644 index 0000000..2681f8d --- /dev/null +++ "b/2019/06/03/JavaScript\345\237\272\347\241\200/index.html" @@ -0,0 +1,264 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

js 学习

+

数据类型

1
2
3
4
5
6
7
8
var fullName;
// undefined

fullName
// undefined

fullName + 2
// NaN
+
    +
  • 判断类型名称

    +
    1
    2
    3
    4
    fullName = '张'
    "张"
    typeof(fullName)
    // "string"
    +
  • +
+
1
2
3
4
var weight = 100;
undefined
typeof(weight)
// "number"
+ + + +
1
2
3
4
var firstName = '王', lastName = '张'
undefined
firstName + lastName
// "王张"
+
    +
  • 类型不相同时转换后在拼接

    +
    1
    2
    3
    4
    var weightIncrease = '2.5斤'
    undefined
    weight + weightIncrease
    // "1002.5斤"
    +
  • +
+

数组

var array = [];
+undefined
+array.length
+// 0
+typeof(array)
+// "object"
+
    +
  • 增加元素

    +
    1
    2
    3
    4
    array.push('1')
    // 4
    array
    // (4) ["ab", "cbd", "fcg", "1"]
    +
  • +
  • 数组前面添加

    +
  • +
+
1
2
3
4
array.unshift('0')
// 6
array
// (6) ["0", "ab", "cbd", "fcg", "1", Array(2)]
+
    +
  • 删除最后元素

    +
    1
    2
    3
    4
    array.pop() // 返回删除的元素
    // (2) ["2", "3"]
    array
    // (5) ["0", "ab", "cbd", "fcg", "1"]
    +
  • +
  • 删除第一个元素

    +
    1
    2
    3
    4
    5
    6
    	array.shift()
    // "0"
    array
    // (4) ["ab", "cbd", "fcg", "1"]
    ```
    - 删除指定的值,但数组元素没有被删
    +

    delete array[2]
    // true
    array
    // (4) [“ab”, “cbd”, empty, “1”]

    +
    1
    2
    	
    - 删除指定的元素
    +

    array.splice(1)
    // (3) [“cbd”, empty, “1”]
    array
    // [“ab”]

    +
    1
    2
    3
    4
    	
    ## 函数

    ### 调用调用
    +
  • +
+

alertMessage();

+

function alertMessageWithParamter(message) {
alert(message)
}

+

alertMessageWithParamter(‘有参数的函数’)
alertMessageWithParamter(250)
alertMessageWithParamter(
console.log(‘test’)
)

+
1
### 函数表达式,使用函数声明的方式
+

var alertMessage_expression = function expression_alert (message) {
alert(message)
}

+

alertMessage_expression(‘匿名函数调用’)

+

expression_alert(‘函数表达式’)
`

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/03/MasonrySnapkit\344\275\277\347\224\250\346\200\273\347\273\223/index.html" "b/2019/06/03/MasonrySnapkit\344\275\277\347\224\250\346\200\273\347\273\223/index.html" new file mode 100644 index 0000000..a2cab1f --- /dev/null +++ "b/2019/06/03/MasonrySnapkit\344\275\277\347\224\250\346\200\273\347\273\223/index.html" @@ -0,0 +1,369 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

Masonry使用

基本使用官网Demo非常详细,以下总结的是常见问题和技巧

+

安装遇到的问题

    +
  • pod 安装后出现⚠️,项目运行报错

    +
    1
    [UIView mas_makeConstraints:]: unrecognized selector sent to instance
    +

    github

    +

    解决方法

    +
  • +
  • 使用lastBaseline属性crash

    +

    解决办法

    +
  • +
+

使用时注意点

    +
  • edgesForExtendedLayout 方法

    +

    重写此方法的目的是为了确定布局位置从什么位置开始什么位置结束,

    +
    1
    2
    3
    - (UIRectEdge)edgesForExtendedLayout {
    return UIRectEdgeNone;
    }
    +
  • +
+
**重写后**
+![](https://ws1.sinaimg.cn/large/006tNc79ly1fsgcn3fmo0j30s61iyalk.jpg)
+
+**重写前**
+![](https://ws4.sinaimg.cn/large/006tNc79ly1fsgcokvpdwj30s61iy7eq.jpg)
+
    +
  • translucent 设置UINavigationBar是否半透明, 如果设置了导航栏的styleUIBarStyleBlackTranslucent 就为true

    +

    设置为false不透明效果

    +
  • +
+
    +
  • extendedLayoutIncludesOpaqueBars 导航栏不透明条件下是否可以扩展,默认是NO不可以扩展

    +
    1
    2
    self.navigationController?.navigationBar.isTranslucent = false
    self.extendedLayoutIncludesOpaqueBars = true
    +
  • +
+
**设置不透明,设置可以扩展此时从坐标的(0,0)点开始布局**
+![](https://ws2.sinaimg.cn/large/006tNc79ly1fsggem13mjj30ow0j83zy.jpg)    
+
    +
  • inset 的使用,当约束控件的属性相同时不用再考虑正负值的问题,例如要约束一个控件左边距离另一控件的右边位置时还需要使用负值情况

    +
    1
    2
    3
    4
    5
    6
    7
    [yellowdView makeConstraints:^(MASConstraintMaker *make) {
    make.height.equalTo(@150);
    make.top.equalTo(self).inset(220);
    make.leading.equalTo(self).inset(10);
    make.trailing.equalTo(greenView.leading).inset(10);
    make.width.equalTo(greenView);
    }];
    +
  • +
  • edges的使用,约束边距更方便

    +
    1
    2
    3
    [edgeView remakeConstraints:^(MASConstraintMaker *make) {
    make.edges.equalTo(lasView).insets(UIEdgeInsetsMake(10, 5, 5, 5));
    }];
    +
  • +
+
    +
  • Priority 设置约束的优先级

    +
      +
    • priorityHigh
    • +
    • priorityMedium
    • +
    • priorityLow
    • +
    +
  • +
  • makeConstraintsupdateConstraintsremakeConstraints三者区别

    +
      +
    • makeConstraints 添加约束
    • +
    • updateConstraints 更新约束,更新之前会查找一边控件已经存在的约束,没有就添加,有就更新到最新的约束
    • +
    • remakeConstraints删除控件以前的所有约束重新添加约束
    • +
    +
  • +
  • 设置控件或者某一约束的’key’ 方便冲突时⚠️查找

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    	greenView.mas_key = @"greenView";
    [greenView makeConstraints:^(MASConstraintMaker *make) {
    make.height.equalTo(yellowdView).key(@"greenView-height");
    make.top.equalTo(yellowdView).key(@"greenView-top");
    make.width.equalTo(yellowdView).key(@"greenView-width");
    make.trailing.equalTo(self).inset(10).key(@"greenView-trailing");
    // 冲突约束
    // make.leading.equalTo(greenView.trailing).inset(10).key(@"greenView-leading");
    }];
    ```

    - 使用 `MASAttachKeys`批量给`view`设置key
    +

    MASAttachKeys(greenView, yellowdView);

    +
    1
    2
    3
    4
    	
    ![](https://ws3.sinaimg.cn/large/006tNc79ly1fsgalvd4l6j31a20modnp.jpg)

    - `dividedBy`和 `multipliedBy` 约束宽高比
    +

    [topInnerView makeConstraints:^(MASConstraintMaker *make) {

    +
    // 设置自己的宽高比是3:1
    +make.width.equalTo( topInnerView.height).multipliedBy(3); // 乘因数
    +
    +// 设置宽高并且设置他们的优先级最低
    +make.width.height.lessThanOrEqualTo(topView);
    +make.width.height.equalTo(topView).priorityLow();
    +
    +// 设置位置
    +make.top.equalTo(topView);
    +

    }];

    +
    1
    - 多个控件批量约束
    +

    NSValue sizeValue = [NSValue valueWithCGSize:CGSizeMake(100, 100)];
    [@[blueView, redView, yellowView] makeConstraints:^(MASConstraintMaker
    make) {

    +
    make.size.equalTo(sizeValue);
    +

    }];

    +

    [@[blueView, redView, yellowView] mas_makeConstraints:^(MASConstraintMaker *make) {

    +
    make.top.equalTo(self).inset(20);
    +

    }];

    +

    [blueView makeConstraints:^(MASConstraintMaker *make) {

    +
    make.left.equalTo(self).inset(20);
    +

    }];

    +

    [redView makeConstraints:^(MASConstraintMaker *make) {

    +
    make.left.equalTo(blueView.right).inset(10);
    +

    }];

    +

    [yellowView makeConstraints:^(MASConstraintMaker *make) {

    +
    make.left.equalTo(redView.right).inset(10);
    +

    }];

    +
    1
    2
    3
    4

    ![批量约束](https://ws1.sinaimg.cn/large/006tNc79ly1fsgaz670hvj30s61iyn2l.jpg)

    **以上代码也可以简化为**
    +

    [redView makeConstraints:^(MASConstraintMaker *make) {

    +
    make.left.equalTo(blueView.right).inset(10);
    +

    }];

    +

    [yellowView makeConstraints:^(MASConstraintMaker *make) {

    +
    make.left.equalTo(redView.right).inset(10);
    +

    }];

    +
  • +
+
[blueView makeConstraints:^(MASConstraintMaker *make) {
+    make.top.left.equalTo(self);
+    make.size.equalTo(@[redView, yellowView, sizeValue]);
+}];
+
+
1
2
3
4
5
	![](https://ws2.sinaimg.cn/large/006tNc79ly1fsgbhz8lw5j30s61iyte5.jpg)

## Snapkit 使用

- Swift 中`edgesForExtendedLayout:UIRectEdge` 扩展布局的边缘是一个属性,默认是`All`
+ +self.edgesForExtendedLayout = [] +
1
2
3
4

在Swift中闭包参数可以使用$0、$1、$2 一次代表第一个,第二个,第三个参数

- 使用$0 来代替闭包参数
+ +nullDataImageView.snp.makeConstraints { + $0.top.equalTo(view).offset(44) + $0.centerX.equalTo(view) + $0.width.height.equalTo(200) +} +
1
2
3
4
5
6
7
	
- `deactivate` 和 `activate` 使用

- `activate` 激活指定的约束
- `deactivate` 移除指定的约束

添加约束
+ +nullDataImageView.snp.makeConstraints { + nullDataViewTopConstraint = $0.top.equalTo(view).offset(80).constraint + nullDataViewLeftConstraint = $0.left.equalTo(view).inset(10).constraint + $0.width.height.equalTo(200) +} +
1
2

移除对应约束,设置约束再激活约束
+ +nullDataViewLeftConstraint?.deactivate() +nullDataViewLeftConstraint?.update(inset: 100) +nullDataViewLeftConstraint?.activate() +UIView.animate(withDuration: 1.5) { + self.view.layoutIfNeeded() +} +
1
2
3
4

- 更新约束

- 添加约束
+ +nullDataImageView.snp.makeConstraints { + nullDataViewTopConstraint = $0.top.equalTo(view).offset(64).constraint + $0.centerX.equalTo(view) + $0.width.height.equalTo(200) +} +
1
2

- 更新约束
+ +nullDataViewTopConstraint?.update(inset: 84) +UIView.animate(withDuration: 0.5, delay: 0, options: .curveEaseOut, animations: { + self.view.layoutIfNeeded() +}) { (finished) in + print("动画执行完毕") +} +
1
2
	
- 添加`debug`模式中的 `key`使用`labeled()`方法
+ +nullDataImageView.snp.makeConstraints { + nullDataViewTopConstraint = $0.top.equalTo(view).offset(80).constraint + nullDataViewLeftConstraint = $0.left.equalTo(view).inset(10).constraint + $0.width.height.equalTo(200).labeled("width和height约束") +} +``` +
    +
  • 其他注意点都和Masnory相同
  • +
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/03/RN\346\212\245\351\224\231/index.html" "b/2019/06/03/RN\346\212\245\351\224\231/index.html" new file mode 100644 index 0000000..e8c59eb --- /dev/null +++ "b/2019/06/03/RN\346\212\245\351\224\231/index.html" @@ -0,0 +1,278 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

RN学习中遇到的错误总结

+

执行 npm install 时报错

错误1. Unexpected end of JSON input while parsing near '...er":"0.4.0"},"bin":{"'

本地有package.json文件,执行 npm install 报错,或者clone 的项目执行npm install 也报同样的错误, 可能是因为缓存导致,

+

解决办法:

+
    +
  1. 设置镜像源,以前可能大部分都是用的淘宝

    +
    1
    npm set registry https://registry.npmjs.org/
    +
  2. +
  3. 清除缓存

    +
    1
    npm cache clean –-force
    +
  4. +
  5. 如果提示失败,使用sudo

    +
    1
    sudo npm cache clean --force
    +
  6. +
+

错误2. xcrun: error: unable to find utility "simctl", not a developer tool or in PATH

可能是因为 Xcode 版本问题引起,XCode 偏好设置中 Command line Tools 中为选择版本问题导致(我的情况是安装了Xcode 10 beta版导致的)

+

解决办法:

+

+
1
2
3
4
5
6
7
8
9
10
11
12
The following build commands failed:
CompileC /Users/liepin/LiePinWorkspace/RNDemo/GitHubPopular/ios/build/Build/Intermediates.noindex/RCTWebSocket.build/Debug-iphonesimulator/RCTWebSocket.build/Objects-normal/x86_64/RCTSRWebSocket.o RCTSRWebSocket.m normal x86_64 objective-c com.apple.compilers.llvm.clang.1_0.compiler
(1 failure)
Installing build/Build/Products/Debug-iphonesimulator/GitHubPopular.app
An error was encountered processing the command (domain=NSPOSIXErrorDomain, code=2):
Failed to install the requested application
An application bundle was not found at the provided path.
Provide a valid path to the desired application bundle.
Print: Entry, ":CFBundleIdentifier", Does Not Exist

Command failed: /usr/libexec/PlistBuddy -c Print:CFBundleIdentifier build/Build/Products/Debug-iphonesimulator/GitHubPopular.app/Info.plist
Print: Entry, ":CFBundleIdentifier", Does Not Exist
+

错误3. Error: Cannot find module '../lib/utils/unsupported.js'

安装的node 版本不是稳定的版本,需要删除后重新安装

+
1
2
sudo rm -rf /usr/local/lib/node_modules/npm
brew reinstall node
+

错误4. Couldn't find preset "module:metro-react-native-babel-preset" when running jest

解决方法

+

已有项目集成RN

报错1. 文件路径错误,查看你的node_modules 文件夹是在当前目录还是上级目录

+
[!] No podspec found for `React` in `../node_modules/react-native
+
    +
  • ../ 是指父级目录

    +
  • +
  • ./ 是指当前目录

    +
  • +
+

报错 2. yoga Y 大小写为问题

+
[!] The name of the given podspec `yoga` doesn't match the expected one `Yoga`_modules/react-native/ReactCommon/yoga"
+

pod install 之后

报错 1.

+

解决办法: pod ‘React’, :subspecs 中加入 ‘CxxBridge’, ‘DevSupport’

+
pod 'React', :path => './node_modules/react-native', :subspecs => [
+'Core',
+'RCTText',
+'RCTNetwork',
+'RCTWebSocket', # 这个模块是用于调试功能的
+'CxxBridge', # Include this for RN >= 0.47
+'DevSupport', # Include this to enable In-App Devmenu if RN >= 0.43
+

]

+

报错 2.

RCTAnimation/RCTValueAnimatedNode.h' file not found

+

解决方法:

+

+
1
#import <RCTAnimation/RCTValueAnimatedNode.h>
+

替换为

+
1
#import "RCTValueAnimatedNode.h"
+

修改头文件导入方式

+

报错3 版本不匹配 react native version mismatch javascript version 0.54.3 native 0.57.2

    +
  • 关闭所有的 terminal,或者是集成开发环境的命令行窗口

    +
    1
    2
    watchman watch-del-all
    react-native start --reset-cache
    +
  • +
  • 重新打开一个terminal窗口

    +
    1
    react-native run-ios
    +

    问题描述和解决1

    +

    问题描述和解决2

    +
  • +
+

报错4 Unable to resolve module "schedule/tracking"

缺少一些开发依赖的库

+
npm i schedule@0.4.0 --save-dev
+

问题描述和解决

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/03/React-Native\345\274\200\345\217\221iOS\346\211\223\345\214\205/index.html" "b/2019/06/03/React-Native\345\274\200\345\217\221iOS\346\211\223\345\214\205/index.html" new file mode 100644 index 0000000..609a61b --- /dev/null +++ "b/2019/06/03/React-Native\345\274\200\345\217\221iOS\346\211\223\345\214\205/index.html" @@ -0,0 +1,259 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

React-Native开发iOS打包

打jsbudnle包

创建打包后存放文件目录

    +
  • cd 到 React-Native 项目根目录下 并且 进入 ios 文件目录下
  • +
  • 创建 bundle 文件夹

    +
    1
    mkdir bundle
    +
  • +
+

配置 React-Native 打包命令

    +
  • 打包命令

    +
    1
    react-native bundle
    +
  • +
  • 在React Native项目的根目录下执行命令

    +
    1
    react-native bundle --entry-file index.js --platform ios --dev false --bundle-output ./ios/bundle/index.jsbundle --assets-dest ./ios/bundle
    +
  • +
+
- `--entry-file`: ios或者android入口的js名称,比如 `index.js`
+
+- `--platform`: 平台名称(ios或者android)
+
+- `--dev`: 设置为`false`的时候将会对`JavaScript`代码进行优化处理。
+
+- `--bundle-output`: 生成的jsbundle文件的名称和路径,比如 `./ios/bundle/index.jsbundle`
+
+- `--assets-dest`: 图片以及其他资源存放的目录
+

使用打包命令

    +
  • 将打包命令添加到 packger.json

    +
    1
    2
    3
    4
    5
    "scripts": {
    "start": "node node_modules/react-native/local-cli/cli.js start",
    "test": "jest",
    "bundle-ios": "node node_modules/react-native/local-cli/cli.js bundle --entry-file index.js --platform ios --dev false --bundle-output ./ios/bundle/index.jsbundle --assets-dest ./ios/bundle"
    },
    +
  • +
+
    +
  • 再次打包只需要 React Native项目的根目录下执行命令,不用再次输入类似上面的命令

    +
    1
    npm run bundle-ios
    +
  • +
+

集成到Xcode(iOS原生项目中)

    +
  • 添加已经打好的.jsbundle 离线包

    +

    +
  • +
+
    +
  • iOS 项目代码配置

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    private func loadReactNativeTest() {
    // debug 测试加载本地文件
    let url = URL(string: "http://localhost:8081/index.bundle?platform=ios")!

    // 加载已经加入的离线包
    let url2 = Bundle.main.url(forResource: "index", withExtension: "jsbundle")

    let rootView = RCTRootView(
    bundleURL: url,
    moduleName: "MyApp", //这里的名字要和index.js中相同
    initialProperties: nil,
    launchOptions: nil
    )
    view = rootView
    }
    +
  • +
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/03/ReactNative\344\270\255ref\344\275\277\347\224\250/index.html" "b/2019/06/03/ReactNative\344\270\255ref\344\275\277\347\224\250/index.html" new file mode 100644 index 0000000..f208e12 --- /dev/null +++ "b/2019/06/03/ReactNative\344\270\255ref\344\275\277\347\224\250/index.html" @@ -0,0 +1,238 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

Reat Native ref 使用

+

State 状态的使用

在父控件中指定,整个生命周期讲不再改变
需求:一段闪烁的文字, Props 属性显示文字内容创建时初始化,State控制随时间变化是否显示,在父控件中

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Blink extends Component{
// 构造
constructor(props) {
super(props);
// 初始状态
this.state = {showWithText:true};
this.state = {}
setInterval(() => {
this.setState(previousState => {
return {showWithText: !previousState.showWithText}
});
}, 100);
}
render() {
let display= this.state.showWithText ? this.props.text : ' ';
return(
<Text>{display}</Text>
);
}
}
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
export default class App extends Component<Props> {
render() {
return(
<View style={styles.container}>
<Blink
text='I love to blink'
/>

<Blink
text='猜猜猜'
/>

<Blink
text='Blink is great'
/>
</View>
);
}
}
+

Props 属性的使用

1
2
3
4
5
6
7
8
9
// 定义一个组件
class Greeting extends Component {
render() {
return(
<Text> Hello {this.props.name}!
</Text>
);
}
}
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
export default class App extends Component<Props> {
render() {
return(
<View style={styles.container}>
<Image source={{uri:'https://upload.wikimedia.org/wikipedia/commons/d/de/Bananavarieties.jpg'}}
style={{width: 190, height: 110}}
/>
<Text style={styles.instructions}>
使用Props属性
</Text>
<View>
<Greeting name='React-Native'
/>
<Greeting name='iOS'
/>
<Greeting name='Swift'
/>
</View>

</View>
);
}
}
+

ref的使用

    +
  • 可以理解为组件渲染后指向组件的一个应用,可以通过ref获取到真实的组件(类似指针?)
  • +
+

定义

+
1
ref='scrollView'
+

获取

+
1
2
let scrollView = this.refs.scrollView
let scrollView = this.refs['scrollView']
+
    +
  • 可以通过ref访问组件的属性和方法
  • +
+

ES6 定时器

    +
  • 开启定时器
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
let scrollView = this.refs.scrollView;
// 2.4timer 定时器的写法
this.timer = setInterval(
()=>{
var tempPage = 0;
// 修改banner索引
if ((this.state.currentPage+1) >= ImageData.data.length) {
tempPage = 0;
} else {
tempPage = this.state.currentPage+1;
}

// 更新状态
this.setState({
currentPage: tempPage
})

let offSet_x = width * tempPage
scrollView.scrollResponderScrollTo({x:offSet_x, y:0, animated: true})
},
this.state.duration
);
+
    +
  • 暂停定时器
  • +
+
1
this.timer && clearInterval(this.timer)
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/03/Swift \345\215\217\350\256\256\344\275\277\347\224\250/index.html" "b/2019/06/03/Swift \345\215\217\350\256\256\344\275\277\347\224\250/index.html" new file mode 100644 index 0000000..ec43b25 --- /dev/null +++ "b/2019/06/03/Swift \345\215\217\350\256\256\344\275\277\347\224\250/index.html" @@ -0,0 +1,263 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

Swift 协议使用

对UIView 扩展

按钮/文本框抖动动画

    +
  • 声明协议

    +
      +
    • 协议中定义属性:遵循该协议的类型都具有此属性

      +
        +
      • 必须明确规定该属性是可读的 {get} 或者可写的 {set},或是可读可写的 {get set}
      • +
      • 使用static修饰声明一个类类型属性
      • +
      +
    • +
    • 协议中定义方法

      +
    • +
    +
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
protocol Shakable {}
extension Shakable where Self: UIView {
func shakeAnimation() {
let animation = CABasicAnimation(keyPath: "position")
animation.duration = 0.08
animation.repeatCount = 5
animation.autoreverses = true
animation.fromValue = NSValue(cgPoint: CGPoint(x: self.center.x - 4, y: self.center.y))
animation.toValue = NSValue(cgPoint: CGPoint(x: self.center.x + 4, y: self.center.y))
layer.add(animation, forKey: "position")
}
}
+
    +
  • 自定义UI控件并遵守协议
  • +
+
1
2
3
class MyButton: UIButton, Shakable {  }
class MySwitch: UISwitch, Shakable { }
class MyTextField: UITextField, Shakable { }
+
    +
  • 使用
  • +
+
1
2
3
4
5
6
7
8
9
10
let mySwitch = MySwitch()
mySwitch.isOn = true
self.mySwitch = mySwitch
view.addSubview(mySwitch)
mySwitch.translatesAutoresizingMaskIntoConstraints = false
mySwitch.topAnchor.constraint(equalTo: view.bottomAnchor, constant: 10).isActive = true
mySwitch.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10).isActive = true

// 调用
mySwitch.shakeAnimation()
+

UITableViewCell使用

定义协议

1
2
3
4
5
6
7
8
9
10
11
protocol ReusableView: class {	}

extension ReusableView where Self: UIView {
static var reuseIdentifier: String {
return String(describing: self)
}
}

```

### 对tableView扩展
+

extension UITableView {
/// 注册cell
func register<T: UITableViewCell>(T: T.Type) where T: ReusableView {
print(T.reuseIdentifier)
self.register(T.self, forCellReuseIdentifier: T.reuseIdentifier)
}

+
/// 调用cell
+func dequeueReusableCell<T: UITableViewCell>(_ indexPath: IndexPath) -> T where T: ReusableView {
+    print(T.reuseIdentifier)
+    return self.dequeueReusableCell(withIdentifier: T.reuseIdentifier, for: indexPath) as! T
+}
+

}

1
2
3
4

### 用法

- 创建自定义的cell,需要遵守协议

+

class TableProtocolCell: UITableViewCell, ReusableView {
override var reuseIdentifier: String? {
return “cell”
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.backgroundColor = .cyan
self.textLabel?.text = “通过协议和泛型创建的cell”
self.textLabel?.textColor = .blue
}
required init?(coder aDecoder: NSCoder) {
fatalError(“init(coder:) has not been implemented”)
}
}

+

class TableProtocolCell2: UITableViewCell, ReusableView {
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.backgroundColor = .red
self.textLabel?.text = “cell1 - 通过协议和泛型创建的”
self.textLabel?.textAlignment = .right
}
required init?(coder aDecoder: NSCoder) {
fatalError(“init(coder:) has not been implemented”)
}
}

1
2

- 注册和使用

+

class TableViewProtocolController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(T: TableProtocolCell.self)
tableView.register(T: TableProtocolCell2.self)
}

+
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+    return 10
+}
+
+override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+    var cell = UITableViewCell()
+    if indexPath.row % 2 == 0 {
+        cell = tableView.dequeueReusableCell(indexPath) as TableProtocolCell
+    } else {
+        cell = tableView.dequeueReusableCell(indexPath) as TableProtocolCell2
+    }
+    return cell
+}
+

}

+

`

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/03/Swift \345\237\272\347\241\200\347\237\245\350\257\206/index.html" "b/2019/06/03/Swift \345\237\272\347\241\200\347\237\245\350\257\206/index.html" new file mode 100644 index 0000000..ba5278d --- /dev/null +++ "b/2019/06/03/Swift \345\237\272\347\241\200\347\237\245\350\257\206/index.html" @@ -0,0 +1,310 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

Swift 学习

+

枚举

1. 定义 rawValue 为 Int 类型,初始值为1,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
enum Month: Int {
case January = 1
case February
case March
case April
case May
case June
case July
case August
case September
case October
case November
case December
}
+

2. rawValue 的整型值可以不连续

1
2
3
4
5
6
enum Season: Int {
case Spring = 1
case Summer = 5
case Autumn = 10
case Winter = 40
}
+

3. 枚举可以是字符串,字符串是变量名rawValue 的值相等

1
2
3
4
5
6
7
enum ProgrammerLanguae: String {
case Swift
case OC = "Objective-C"
case C = "语言"
case RN = "React-Native"
case Java
}
+

使用rawValue

+
1
2
3
func residueNewYear(month: Month) -> Int {
return 12 - month.rawValue
}
+

调用枚举的Moth的构造函数生成一个Month

+
1
2
3
4
let month = Month(rawValue: 5)
let swift = ProgrammerLanguae.Swift.rawValue
let oc = ProgrammerLanguae.OC
let RN = ProgrammerLanguae.RN.rawValue
+

4. 枚举关联值 associate value

1
2
3
4
5
6
7
8
9
10
11
12
enum Status {
case success(Int)
case fail(String)
case null // 未关联值
}

func associateValueTest(isSuccess: Bool) -> Status {
if isSuccess {
return .success(200)
}
return .fail("失败")
}
+

5. 枚举associateValue多个值

    +
  • 本质是关联了一个元祖(value0, value1, value2…)
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
enum Shape {
case Square(width: Double)
case Reactangle(width: Double, height: Double)
case Circle(x: Double, y: Double, radius: Double)
case Point
}

let square = Shape.Square(width: 20)
let reactangle = Shape.Reactangle(width: 10, height: 20)
let circle = Shape.Circle(x: 10, y: 10, radius: 20)
let point = Shape.Point

private func area(shape: Shape) -> Double {
switch shape {
case let .Square(width):
return width * width
case let .Reactangle(width, height):
return width * height
case let .Circle(_, _, radius):
return radius * radius * Double.pi
case .Point:
return 0
}
}
+

6. 递归枚举

    +
  • 定义一个递归算术表达式
  • +
  • 使用 indirect 来修饰
  • +
+
1
2
3
4
5
6
7
8
indirect enum ArithmeticalExpression {
case Number(Int)
case Addition(ArithmeticalExpression, ArithmeticalExpression) // + 时两边也是一个表达式
case Multiplication(ArithmeticalExpression, ArithmeticalExpression) // * 时两边也是一个表达式

// indirect case Addition(ArithmeticalExpression, ArithmeticalExpression) // + 时两边也是一个表达式
// indirect case Multiplication(ArithmeticalExpression, ArithmeticalExpression) // * 时两边也是一个表达式
}
+
    +
  • 递归表达式使用 (2+3) * 4
  • +
+
1
2
3
4
let two = ArithmeticalExpression.Number(2)
let one = ArithmeticalExpression.Number(3)
let sum = ArithmeticalExpression.Addition(two, one)
let indirectEnumResult = ArithmeticalExpression.Multiplication(sum, ArithmeticalExpression.Number(4))
+
    +
  • 计算表达式值的函数
  • +
+
1
2
3
4
5
6
7
8
9
10
private func calculate(expression: ArithmeticalExpression) -> Int {
switch expression {
case let .Number(value):
return value
case let .Addition(left, right):
return calculate(expression: left) + calculate(expression: right)
case let .Multiplication(left, right):
return calculate(expression: left) * calculate(expression: right)
}
}
+

结构体

    +
  • 结构体中的属性值没有初始化时必须使用构造函数来初始化,否则报错
  • +
  • +
+
1
2
3
4
5
6
7
struct Location {
var latitude: Double
var longitude: Double
var placeName: String?
}

let location1 = Location(latitude: 37.3230, longitude: -122.0322, placeName: "测试")
+
    +
  • 结构体中属性如果都赋了初始值就可以直接初始化
  • +
+
1
2
3
4
5
6
struct Location2 {
var latitude: Double = 0
var longitude: Double = 0
}

let location2 = Location2()
+
    +
  • 如果未指定初始值,swift 就不会默认初始化为(不初始化)
  • +
  • 当属性值为可选值时此时允许值为nil,那么就允许不通过构造函数来初始化赋值
  • +
  • let 只有一次赋值机会
  • +
  • 不管是类还是结构体都应该提供一个全参数的构造函数 init(latitude: Double, longitude: Double, placeName: String?)
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct Location3 {
let latitude: Double
let longtitude: Double
var placeName: String?

init(coordinateString: String) {
let index = coordinateString.index(of: ",")
let index1 = coordinateString.index(after: index!)
let test_1 = coordinateString.prefix(upTo: index!)
let test_2 = coordinateString.suffix(from: index1)

latitude = Double(test_1)!
longtitude = Double(test_2)!
}

init(latitude: Double, longitude: Double, placeName: String?) {
self.latitude = latitude
self.longtitude = longitude
self.placeName = placeName
}
}

let test3 = Location3(coordinateString: "12,45")
+

属性和方法

计算型属性

类型属性

类型方法

属性管擦器

延迟属性

访问控制

单利模式

继承和构造函数

swift 中继承

多态性

属性和函数重载

子类两段式构造

    +
  • 子类构造函数分为两段,第一段是构造自己,第二段是构造父类

    +
      +
    • 父类中构造函数
    • +
    • 子类要设置一个自己的构造函数
    • +
    • 子类构造函数中需要先初始化自己的属性
    • +
    • 然后在构造函数中调用父类初始化父类构造函数中的相关属性

      +

      父类

      +
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      class Father {
      var name: String
      var sex: Int = 0
      var old: Int = 32
      var desc: String {
      return "我是\(name)"
      }
      init(name: String) {
      self.name = name
      }

      func run() {
      print("Father can Running")
      }
      }
      +
    • +
    +
  • +
+
子类
+
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Son: Father {

var isSwimming = true

var computer: String

override var desc: String {
return "Son is \(name)"
}

init(name: String, computer: String) {
self.computer = computer
super.init(name: name)
}
}
+
    +
  • 子类两段构造都完成后子类的初始化才完成,可以使用self调用属性或者方法

    +
    1
    2
    3
    4
    5
    init(name: String, computer: String) {
    self.computer = computer
    self.getToys() // 此代码报错,子类初始化未完成
    super.init(name: name)
    }
    +
  • +
+

便利构造函数和指定构造函数

    +
  • 便利构造函数只能调用自己的指定构造函数
  • +
  • 指定构造函数通过一系列的调用都会调用super.init()
  • +
  • 便利构造函数无法调用super.init()
  • +
+
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
class Father {
var name: String
var sex: Int = 0
var old: Int = 32
var desc: String {
return "我是\(name)"
}
init(name: String) {
self.name = name
}

init(name: String, old: Int) {
self.name = name
self.old = old
}

func run() {
print("Father can Running")
}
}

class Son: Father {
var isSwimming = true
var computer: String

override var desc: String {
return "Son is \(name)"
}

// 子类指定构造函数,调用了父类的指定构造函数
init(name: String, computer: String) {
self.computer = computer
super.init(name: name)
}

// 子类便利构造函数,调用了指定构造函数
convenience override init(name: String) {
let computer = "iMac"
self.init(name: name, computer: computer)
}
}
+

构造函数继承

    +
  • 子类有可能会继承父类的构造函数
  • +
  • 子类没有实现父类的任何指定构造函数;则自动继承父类的所有指定构造函数, 因为继承了指定构造函数所以同时便利构造函数也被继承
  • +
+

父类

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
class Father {
var name: String
var sex: Int = 0
var old: Int = 32
var desc: String {
return "我是\(name)"
}

/// Father指定构造函数-1
init(name: String) {
self.name = name
}

/// Father指定构造函数-2
init(name: String, old: Int) {
self.name = name
self.old = old
}

/// Father便利构造函数
convenience init(old: Int) {
self.init(name: "Father", old: old)
}

func run() {
print("Father can Running")
}
}

+

子类

+
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
class Son: Father {
var isSwimming = true
var computer: String
var job: String

override var desc: String {
return "Son is \(name)"
}

/// 子类重载的指定构造函数-1
convenience override init(name: String) {
self.init(name: name, computer: "Dell")
}

/// 子类重载的指定构造函数-2
override convenience init(name: String, old: Int) {
self.init(name: name, computer: "acer")
}


/// 子类自己的指定构造函数,调用了父类的指定构造函数
init(name: String, computer: String) {
self.computer = computer
self.job = "C#"
super.init(name: name)
}

// 子类便利构造函数,调用了自己指定构造函数
convenience init(computer: String) {
let name = "小张"
self.init(name: name, computer: computer)
}
}
+

未实现父类任何的指定构造函数

+
1
2
3
class Grandson: Son {
var toy: String = "dog toys"
}
+

子类可以调用的构造函数

+
1
2
3
4
5
let grandSon0 = Grandson(old: 4)
let grandSon1 = Grandson(computer: "Mi")
let grandSon2 = Grandson(name: "小王")
let grandSon3 = Grandson(name: "小虎", computer: "👽")
let grandSon4 = Grandson(name: "小李", old: 8)
+
    +
  • 子类实现了父类所有的指定构造函数,则自动继承父类的所有便利构造函数
  • +
+
1
2
3
4
5
let son0 = Son(old: 30)
let son1 = Son(computer: "Mi")
let son2 = Son(name: "小王")
let son3 = Son(name: "小虎", computer: "👽")
let son4 = Son(name: "小李", old: 8)
+

required 构造函数

    +
  • 父类中有被 required 关键词修饰的构造函数子类必须实现此构造函数
  • +
  • 子类实现的此构造函数不需要再使用 override 关键词修饰,需要 required 修饰
  • +
+

子类

+
1
2
3
4
5
6
7
8
class Father {
var name: String
/// Father指定构造函数-1
// required 修饰的指定构造函数子类中必须实现
required init(name: String) {
self.name = name
}
}
+

父类

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Son: Father {
var isSwimming = true
var computer: String

/// 子类重载的指定构造函数-1
convenience required init(name: String) {
self.init(name: name, computer: "Dell")
}

/// 子类自己的指定构造函数,调用了父类的指定构造函数
init(name: String, computer: String) {
self.computer = computer
self.job = "C#"
super.init(name: name)
}
}
+
    +
  • 子类如果实现了自己的指定构造函数,那么 required 修饰指定构造函数就初始化失败,因为自己实现了指定构造函数所以不能继承父类中的构造函数,此时可以自己实现被required 修饰的便利构造函数
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Son1: Son {
var sport: String

// 指定构造函数,需要调用父类的指定构造函数
init(name: String, sport: String) {
self.sport = sport
Son.init(name: name)
}

// 父类中有被 required 关键词修饰的必须实现的构造函数
convenience required init(name: String) {
// fatalError("init(name:) has not been implemented")
// 调用自己的指定构造函数,
self.init(name: name, sport: "")
}
}
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/03/SwiftLint\344\275\277\347\224\250/index.html" "b/2019/06/03/SwiftLint\344\275\277\347\224\250/index.html" new file mode 100644 index 0000000..6fcb53c --- /dev/null +++ "b/2019/06/03/SwiftLint\344\275\277\347\224\250/index.html" @@ -0,0 +1,216 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/03/Swift\344\270\213\346\240\207\345\222\214\350\277\220\347\256\227\347\254\246\351\207\215\350\275\275/index.html" "b/2019/06/03/Swift\344\270\213\346\240\207\345\222\214\350\277\220\347\256\227\347\254\246\351\207\215\350\275\275/index.html" new file mode 100644 index 0000000..716d749 --- /dev/null +++ "b/2019/06/03/Swift\344\270\213\346\240\207\345\222\214\350\277\220\347\256\227\347\254\246\351\207\215\350\275\275/index.html" @@ -0,0 +1,266 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

Swift下标和运算符重载

+

字典数组下标

var array = [1,2,3,4,5,6]
+let temp = array[0]     // 通过下标访问
+let temp2 = array[2]    // 通过下标访问
+

结构体下标

    +
  • 对于结构体不是直接使用下标访问,会直接报错
    直接使用下标获取属性值报错: Type 'Vector_3' has no subscript members

    +
  • +
  • 可以通过实现 subscript(index: Int) -> Double?方法让结构体支持下标访问,可以理解为一个特殊的函数,需要传入参数和返回值

    +
  • +
  • 可以增加第二种下标–根据坐标轴获取属性值
  • +
  • 重写 subscript 的 set 方法 可以使用下标修改属性值
  • +
+
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
struct Vector_3 {
var x = 0.0
var y = 0.0
var z = 0.0

// 根据索引获取
subscript(index: Int) -> Double? {

// 重写了get方法 实现通过下标获取属性值
get {
switch index {
case 0: return x
case 1: return y
case 2: return z
default: return nil
}
}

// 重写 set 方法,实现g通过下标设置属性值,使用系统默认的newValue
set {
guard let newValue = newValue else {return}
switch index {
case 0: x = newValue
case 1: y = newValue
case 2: z = newValue
default: return
}
}
}

// 增加第二种下标--根据坐标轴获取属性值
subscript(axis: String) -> Double? {

get {
switch axis {
case "X", "x": return x
case "Y", "y": return y
case "Z", "z": return z
default: return nil
}
}

set {
guard let newValue = newValue else {return}
switch axis {
case "X", "x": x = newValue
case "Y", "y": y = newValue
case "Z", "z": z = newValue
default: return
}
}
}
}

```

### 下标访问

结构体使用下标获取值
+

var v = Vector_3(x: 1, y: 2, z: 3)
print(v[0], v[1], v[2], v[3], v[100]) // Optional(1.0) Optional(2.0) Optional(3.0) nil nil
print(v[“x”], v[“y”], v[“z”], v[“i”]) // Optional(1.0) Optional(2.0) Optional(3.0) nil

+
1
2

结构体使用下标修改值
+

v[0] = 101
v[1] = 202
v[2] = 303
v[3] = 400
v[100] = 51
print(v[0], v[1], v[2], v[3], v[100]) // Optional(101.0) Optional(202.0) Optional(303.0) nil nil

1
 

+

v[“x”] = 100
v[“y”] = 200
v[“z”] = 300
v[“i”] = 50
print(v[“x”], v[“y”], v[“z”], v[“i”]) // Optional(100.0) Optional(200.0) Optional(300.0) nil

+
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

### 多维下标

定义一个关于矩阵的结构体

struct Matrix {
var data: [[Double]]
let row: Int
let column: Int

init(row: Int, column: Int) {
self.row = row
self.column = column
data = [[Double]]()
for _ in 0..<row {
let aRow = Array(repeating: 1.0, count: column)
data.append(aRow)
}
}

// 通过下标 [row,column] 方式访问
subscript(row: Int, column: Int) -> Double {
get {
assert(row >= 0 && row < self.row && column >= 0 && column < self.column, "下标不合法")
return data[row][column]
}

set {
assert(row >= 0 && row < self.row && column >= 0 && column < self.column, "下标不合法")
data[row][column] = newValue
}
}

// 通过下标 [row][column] 方式访问
subscript(row: Int) -> [Double] {
get {
assert(row >= 0 && row < self.row, "下标不合法")
// 直接返回数组,数组本身有下标
return data[row]
}

set {
assert(newValue.count == column, "下标不合法")
data[row] = newValue
}
}
}


### 运算符重载

- 重载 + 运算符
+

func +(one: Vector_3, other: Vector_3) -> Vector_3 {
return Vector_3(x: one[0]! + other[0]!, y: one[1]! + other[1]!, z: one[2]! + other[2]!)

+

// return Vector_3(x: one.x + other.x, y: one.y + other.y, z: one.z + other.z)
}

+
1
2

- 两个参数时相减
+

func -(one: Vector_3, other: Vector_3) -> Vector_3 {
return Vector_3(x: one.x - other.x, y: one.y - other.y, z: one.z - other.z)
}

+
1
- 一个参数时去反, 需要 prefix 修饰
+

prefix func -(a: Vector_3) -> Vector_3 {
return Vector_3(x: -a.x, y: -a.y, z: -a.z)
}

1
2

- 向量相乘/向量和常量相乘

+

func (one: Vector_3, other: Vector_3) -> Double {
return (one.x
other.x) + (one.y other.y) + (one.z other.z)
}

+
1
2

- 两个参数不能交换,需要重载两次 `*`
+

func (one: Vector_3, a: Double) -> Vector_3 {
return Vector_3(x: a
one.x, y: a one.y, z: a one.z)
}

+

func (a: Double, one: Vector_3) -> Vector_3 {
return one
a

+
// 也可采用下面写法
+

// return Vector_3(x: a one.x, y: a one.y, z: a * one.z)
}

+
1
2

- 修改自身参数,不需要返回值
+

func +=(one: inout Vector_3, other: Vector_3) {
// 已经重载过 + 运算符,可以直接调用
one = one + other
}

+

func ==(one: Vector_3, other: Vector_3) -> Bool {
return one.x == other.x &&
one.y == other.y &&
one.z == other.z
}

+

func !=(one: Vector_3, other: Vector_3) -> Bool {
return !(one == other)

+
// 也可采用下面写法
+return one.x != other.x ||
+        one.y != other.y ||
+        one.z != other.z
+

}

+

func <(one: Vector_3, other: Vector_3) -> Bool {
if one.x != other.x {return one.x < other.x}
if one.y != other.y {return one.y < other.y}
if one.z != other.z {return one.z < other.z}
return false
}

+

func <=(one: Vector_3, other: Vector_3) -> Bool {
return one < other || one == other

+
// 也可采用下面写法
+return one.x > other.x &&
+        one.y > other.x &&
+        one.z > other.z
+

}

+

func >(one: Vector_3, other: Vector_3) -> Bool {
return (one <= other)
}

+
1
2
3
4
5
6

### 自定义操作符

`postfix` 声明前后缀关键词, `operator ` 操作符关键词

- a+++
+

声明后置操作符
postfix operator +++
postfix func +++(vector: inout Vector_3) -> Vector_3 {
vector += Vector_3(x: 1.0, y: 1.0, z: 1.0)
return vector
}

+
1
2

- +++a
+

// 声明前置操作符
prefix operator +++
prefix func +++(vector: inout Vector_3) -> Vector_3 {
let temp = vector
vector += Vector_3(x: 1.0, y: 1.0, z: 1.0)
return temp
}

+

`

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/03/Swift\345\215\217\350\256\256\345\210\253\345\220\215/index.html" "b/2019/06/03/Swift\345\215\217\350\256\256\345\210\253\345\220\215/index.html" new file mode 100644 index 0000000..2089deb --- /dev/null +++ "b/2019/06/03/Swift\345\215\217\350\256\256\345\210\253\345\220\215/index.html" @@ -0,0 +1,350 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

Swift协议和别名

+

协议

某些属性的集合,可以有多个协议组成,具有的特点

+
    +
  1. 可以声明函数,但是不能实现,由实现这协议的具体结构或者类来实现
  2. +
  3. 声明的函数如果有参数也不能设置参数的默认值, 默认参数被认为也是函的一种实现
  4. +
  5. 可以声明属性,必须是计算型属性 只能使用var 修饰,不能是存储型属,必须在 {} 中明确指出属性的读取类型 例如 {get set}
  6. +
  7. 遵守协议的类可选择存储型属性或者计算型属性来实现协议中定义的属性
  8. +
  9. protocol Person: class { } 表示此协议只能被类来遵守,结构体能遵守
  10. +
  11. 协议可以看着是一种类型,如果协议中的属性是只读的,如果是协议类型只能访问get 方法, 但是遵守协议的具体类或者结构可以设置属性的读和写

    +
  12. +
  13. 协议中的构造函数

    +
    - 如果父类中遵守了协议中的构造函数,子类中必须重写父类中的构造函数并且使用require修饰构造函数,表示继承此类的子类也必须实现该构造函数
    +- 如果class 使用了final 修饰,表示不能被继承,此时不需要 require 关键词修饰
    +
  14. +
+

类型别名

    +
  • 定义别名 typealias, 供扩展使用

    +
    1
    2
    3
    4
    5
    6
    7
    8
    typealias Length = Double
    extension Double {
    var km: Length {return self * 1_000.0}
    var m: Length {return self * 100.0}
    var cm: Length {return self / 10.0}
    /// 英尺
    var ft: Length { return self / 3.28084}
    }
    +
  • +
+
使用
+
+
1
let distance: Length = 10.5.km
+
    +
  • 定义别名防止硬编码

    +
    1
    2
    	// 音频采样率
    typealias AudioSample = UInt64
    +
  • +
+

协议中 typealias

协议中定义一个属性,该属性的类型根据不同的情况返回不同的类型(我理解为类似泛型),具体的类型由遵守此协议的类或者结构指定

+
    +
  • 定义协议中别名
  • +
+
1
2
3
4
5
protocol WeightCalculable {
// 属性重量根据不同体积返回的可能是Int,可以可能是double类型,所以使用别名, 协议中使用associatedtype关键词,
associatedtype WeightType
var weight: WeightType { get }
}
+

扩展 Int 中的吨单位

+
1
2
3
4
extension Int {
typealias Weight = Int
var t: Weight {return self * 1_000}
}
+
    +
  • 使用协议中别名
  • +
+
    +
  • 一部手机的时使用该协议WeightCalculable
  • +
+
1
2
3
4
5
6
class iPhone: WeightCalculable {
typealias WeightType = Double
var weight: WeightType {
return 0.5
}
}
+
    +
  • 一辆大卡时使用该协议WeightCalculable
  • +
+
1
2
3
4
5
6
7
8
class Car: WeightCalculable {
typealias WeightType = Int
let weight: WeightType

init(weight: Int) {
self.weight = weight
}
}
+
1
let bigCar = Car(weight: 8_000.t) // 8 吨
+

面向协议编程

    +
  • 定义比赛协议,具有 赢/输/总场次/胜率 等特性
  • +
+
1
2
3
4
5
6
protocol Recordable: CustomStringConvertible {
var wins: Int { get }
var losses: Int { get }

func winningPerent() -> Double
}
+
    +
  • 协议中方法或者属性的默认实现
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
extension Recordable {
// CustomStringConvertible 协议默认实现
var description: String {
return String(format: "WINS: %d---LOSSES: %d", [wins, losses])
}

// 默认实现总场数计算型属性
var totalGames: Int {
return wins + losses
}

// 默认实现的方法
func shoutWins() {
print("come on", wins, "times!")
}
}
+

篮球比赛:

    +
  • Equatable 协议 - 相等比较 需要重载 ==
  • +
  • Comparable 协议 - 是否可比较 ,重载 < 可直接调用sort()函数
  • +
  • CustomStringConvertible 协议 - 打印信息,需重写 description
  • +
  • Recordable协议 - 比赛协议,没有平局
  • +
+
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
struct BasketballRecord:Equatable, Comparable, CustomStringConvertible, Recordable {
var wins: Int
var losses: Int


/// 在具体定义的类型中实现 协议中的属性或者方法就会覆盖协议中的
var totalGames: Int = 200

var description: String {
return "本次比赛胜: " + String(wins) + "负" + "\(losses)"
}

// 胜率
func winningPerent() -> Double {
return (Double(wins) / Double(totalGames))
}

// swift4.1 后,不需要再重写此方法,swift 会自动合成,如果不想让某一个属性参与比较,就需要重写该方法
static func == (lhs: BasketballRecord, rhs: BasketballRecord) -> Bool {
return lhs.wins == rhs.wins && lhs.losses == rhs.losses
}

// Comparable
static func < (lhs: BasketballRecord, rhs: BasketballRecord) -> Bool {
if lhs.wins != rhs.wins {
return lhs.wins < rhs.wins
}
return lhs.losses > rhs.losses
}
}
```

#### 足球比赛:

有平局的情况,原来的协议不能满足,需要增加一个平局的协议

- 定义平局协议
+

protocol Tieable {
/// 平局,可读写
var ties: Int { get set }

+

}

1
2
3
4

- 足球比赛需要遵守的协议 `Recordable` 和 `Tieable`

- 总场数需要加上平局的场数,需要实现totalGames的get方法

+

struct FootableRecord: Recordable, Tieable {
var wins: Int
var losses: Int
// 平局数
var ties: Int

+
// 总场数
+var totalGames: Int {
+    return wins + losses + ties
+}
+
+var description: String {
+    return "本次比赛胜: " + String(wins) + "负" + "\(losses)"
+}
+
+func winningPerent() -> Double {
+    return (Double(wins) / Double(totalGames))
+}
+

}

1
2
3
4
5
6
7
8
9
10
11
12

- 足球比赛中以上写法存在的问题

- 所有有平局的比赛计算总场次的逻辑都是一样,每次都要写一次实现
- 单一修改 Recordable 协议不能解决问题,
- 存在平局和没有平局两种情况 totalGames 的计算逻辑不相同

- 解决办法:
- 扩展遵守了`Recordable`协议的类型,前提条件是:这个类型遵守了 Tieable 同时也遵守了 `Recordable ` 协议,可以理解为如果该类型遵守了 `Tieable `协议之后,`Recordable` 协议中的 totalGames 属性实现是另一种方式了,不在是以前的 `totalGames` 中直接返回 `wins + losses` 的值


定义协议

+

protocol Recordable: CustomStringConvertible {
var wins: Int { get }
var losses: Int { get }

+
func winningPerent() -> Double
+

}

1
2

协议默认实现

+

extension Recordable {
var description: String {
return String(format: “WINS: %d—LOSSES: %d”, [wins, losses])
}

+
// 默认实现总场数计算型属性
+var totalGames: Int {
+    return wins + losses
+}
+
+// 默认实现胜率
+func winningPerent() -> Double {
+    return (Double(wins) / Double(totalGames))
+}
+
+// 默认实现的方法
+func shoutWins() {
+    print("come on", wins, "times!")
+}
+

}

+
1
2

扩展遵守了Tieable 的 类型的 Recordable 协议
+

extension Recordable where Self: Tieable {
var totalGames: Int {
return wins + losses + ties
}
}

+
1
2
3
4
5
6

### 协议聚合

如果对某一类型需要限制他必须遵守某些协议,可以使用协议聚合来定义

比如: 有个奖赏协议
+

protocol Prizable {
func isPrizable() -> Bool
}

+
1
2

篮球比赛遵守此协议
+

// 篮球比赛奖赏
extension BasketballRecord: Prizable {
func isPrizable() -> Bool {
return totalGames > 10 && winningPerent() > 0.5
}
}

+
1
2

足球比赛奖赏遵守此协议
+

extension FootableRecord: Prizable {
func isPrizable() -> Bool {
return wins > 1
}
}

1
2

现在有某一个学生也遵守了此协议

+

struct Student: Prizable {
var name: String
var score: Int

+
func isPrizable() -> Bool {
+    return score >= 60
+}
+

}

+
1
2

定义奖赏的方法,参数类型必须是遵守了此协议的结构或者类型
+

private func award(_ one: Prizable) {
if one.isPrizable() {
print(one)
print(“恭喜获得奖励”)
} else {
print(one)
print(“很遗憾”)
}
}

1
2

如果说 BasketballRecord 这个类还遵守了其他的协议,例如遵守了 `Recordable` 协议, 并且这个协议也遵守了 `CustomStringConvertible` 并且默认实现了`description` 的`get` 方法

+

// MARK: - 比赛协议,具有 赢。输。总场次。胜率 特性
protocol Recordable: CustomStringConvertible {
var wins: Int { get }
var losses: Int { get }

+
func winningPerent() -> Double
+

}

+
1
2

`Recordable` 默认实现
+

extension Recordable {
var description: String {
return String(format: “WINS: %d—LOSSES: %d”, [wins, losses])
}

+
// 默认实现总场数计算型属性
+var totalGames: Int {
+    return wins + losses
+}
+
+// 默认实现胜率
+func winningPerent() -> Double {
+    return (Double(wins) / Double(totalGames))
+}
+
+// 默认实现的方法
+func shoutWins() {
+    print("come on", wins, "times!")
+}
+

}

1
2
3
4
5

此时如果 `Student` 类还是调用 `award` 方法的话,print(one) 打印的信息将是`Recordable `中默认实现的内容,因此需要约束`award`函数的参数必须遵守两个协议让`Student` 也重写自己的`description `属性的`get`方法,不能再让 `Prizable` 扩展 默认实现

- swift 3 写法: protocol<A, B>
- swift 4 写法: A & B

+

private func award2(_ one: Prizable & CustomStringConvertible) {
if one.isPrizable() {
print(one)
print(“恭喜获得奖励”)
} else {
print(one)
print(“很遗憾”)
}
}

1
2
3
4
5
6
7
8
9
10

### 泛型约束


- 定义一个函数,找出一个学生数组中分数最大的
- 参数:一个学生数组,都遵守了 Comparable 的类型
- 返回值:某个遵守了 Comparable 的类型实例
- 此时函数报错 `Protocol 'Comparable' can only be used as a generic constraint because it has Self or associated type requirements`,

因为 Comparable 协议中定义的方法 public static func < (lhs: Self, rhs: Self) -> Bool 的参数类型是Self,是具体的某个类型

+

func maxScore(seq: [Comparable]) -> Comparable { }

1
2
3
4
5


- 如果需要定义一个函数实现在一个数组中找出需要奖励的人的名字该如何实现呢
- 参数:遵守两个协议 Comparable 和 Prizable 协议, 并且使用泛型
- 返回值:返回值是可选值,有可能没有任何奖励的对象

+

func topPrizable<T: Comparable & Prizable>(seq: [T]) -> T? {
return seq.reduce(nil) { (tempTop: T?, condender: T) in
guard condender.isPrizable() else { return tempTop }
// 解包 condender 失败, 上一层验证了他必须是奖励的那个
guard let tempTop = tempTop else { return condender }
return max(tempTop, condender)
}
}

+

`

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/03/Swift\345\215\217\350\256\256\346\211\251\345\261\225/index.html" "b/2019/06/03/Swift\345\215\217\350\256\256\346\211\251\345\261\225/index.html" new file mode 100644 index 0000000..cffef66 --- /dev/null +++ "b/2019/06/03/Swift\345\215\217\350\256\256\346\211\251\345\261\225/index.html" @@ -0,0 +1,244 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

Swift协议扩展

+

定义结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct Point {
var x = 0.0
var y = 0.0

}

struct Size {
var height = 0.0
var width = 0.0
}

struct Rectangle {
var origin = Point()
var size = Size()

init(origin: Point, size: Size) {
self.origin = origin
self.size = size
}
}
+

扩展

    +
  • 扩展方法
  • +
+
1
2
3
4
5
6
extension Rectangle {
mutating func translate(x: Double, y: Double) {
self.origin.x += x
self.origin.y += y
}
}
+
    +
  • 只能扩展计算型的属性,不能扩展存储型属性, 存储型属性需要在定义类或者结构体时声明
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
extension Rectangle {
var center: Point {
get {
let center_x = origin.x + size.width / 2.0
let center_y = origin.y + size.height / 2.0
return Point(x: center_x, y: center_y)
}

set {
origin.x = newValue.x - size.width / 2.0
origin.y = newValue.y - size.height / 2.0
}
}
}
+
    +
  • 扩展构造方法

    +
      +
    • 类中不能扩展指定构造方法,只能在结构体中扩展
    • +
    • 结构体中不能扩展便利构造方法m,只能在类中扩展
    • +
    +
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

extension Rectangle {
init(center: Point, size: Size) {
let origin_x = center.x - size.width / 2.0
let origin_y = center.y - size.height / 2.0
self.origin = Point(x: origin_x, y: origin_y)
self.size = size
}

// 结构体中不能扩展便利构造函数 Delegating initializers in structs are not marked with 'convenience'
// convenience init(center: Point, size: Size) {
// let origin_x = center.x - size.width / 2.0
// let origin_y = center.y - size.height / 2.0
// self.origin = Point(x: origin_x, y: origin_y)
// self.size = size
// }
}
+
    +
  • 扩展嵌套类型
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
extension Rectangle {
enum Vertex: Int {
case left_top
case left_bottom
case right_bottom
case right_top
}

// 获取某一个顶点坐标
func point(of vertex: Vertex) -> Point {
switch vertex {
case .left_top:
return origin
case .left_bottom:
return Point(x: origin.x, y: origin.y + size.height)
case .right_bottom:
return Point(x: origin.x + size.width, y: origin.y + size.height)
case .right_top:
return Point(x: origin.x + size.width, y: origin.y)
}
}
}
+
    +
  • 扩展下标,根据传入的索引获取对应顶点坐标
  • +
+
1
2
3
4
5
6
extension Rectangle {
subscript(index: Int) -> Point? {
assert(0 <= index && index < 4, "传入值非法")
return point(of: Vertex(rawValue: index)!)
}
}
+

扩展系统方法

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
extension Int {
/// 平方
var square: Int {
return self * self
}

/// 立方
var cube: Int {
return self * self * self
}

/// 判断数组是否在某一个范围内
func inRange(clousedLeft left: Int, openRight right: Int) -> Bool {
return self >= left && self > right
}

/// 重复执行操作
func repeatitions(task: () -> ()) {
for _ in 0..<self {
task()
}
}

/// 变长函数
func stride(from: Int, to: Int, by: Int, task: () -> ()) {
// 系统变长函数
for i in Swift.stride(from: 0, to: 21, by: 3) {
print(i)
}

for _ in Swift.stride(from: from, to: to, by: by) {
task()
}
}
}
+

扩展后使用

+
1
2
3
4
5
6
7
8
9
10
11
12
print(2.square)
print(3.cube)
print(4.inRange(clousedLeft: 0, openRight: 4))
4.repeatitions {
print("extension")
}


// 使用变长函数
4.stride(from: 0, to: 8, by: 4) {
print("stride")
}
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/03/Swift\346\263\233\345\236\213/index.html" "b/2019/06/03/Swift\346\263\233\345\236\213/index.html" new file mode 100644 index 0000000..1d7cc80 --- /dev/null +++ "b/2019/06/03/Swift\346\263\233\345\236\213/index.html" @@ -0,0 +1,240 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

Swift 泛型

泛型定义–wikipedia

    +
  1. 在程序编码中一些包含类型参数的类型,也就是说泛型的参数只可以代表类,不能代表个别对象。(这是当今较常见的定义)
  2. +
  3. 在程序编码中一些包含参数的类。其参数可以代表类或对象等等。(现在人们大多把这称作模板)
  4. +
+

泛型的用处

泛型可以实现传入一个指定类型后做一些操作然后返回后还是原来指定的类型,你可以指定特定的类型(特定是指约束)

+

泛型和 Any 的区别

区别:Any能代表任何类型,但是不能返回某个具体的类型,都需要使用 as 去转化,而泛型正好相反

+
    +
  • Any 可以代表任何类型,除了class之外还可以代表struct,enum,
  • +
  • AnyObject 可以代码任何 class 的类型, swift 中基本类型, ArrayDictionary 都是 struct 类型,都不能使用 AnyObject 来接受
  • +
+

Swift中Any 和 AnyObject用法

+

泛型函数和泛型参数

swapTwoValues是一个泛型函数,可以接收或者返回任何类型

+
1
2
3
4
5
func swapTwoValues<T>(inout a: T, inout b: T) {
let temporaryA = a
a = b
b = temporaryA
}
+
    +
  • swapTwoValues 函数名后面的 <T> 表示这个 T 类型是函数所定义的一个类型, T 只是一个类型的占位符,不用关心具体类型

    +
  • +
  • (inout a: T, inout b: T) 中a和b两个参数类型T是泛型参数

    +
  • +
+

类型约束

两个泛型参数的泛型函数

+
1
2
3
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
// function body goes here
}
+
    +
  • 泛型参数T约束条件:必须是SomeClass类的子类
  • +
  • 泛型参数U约束条件:必须遵守SomeProtocol协议
  • +
  • 使用where指定约束
  • +
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/03/Swift\351\224\231\350\257\257\345\244\204\347\220\206/index.html" "b/2019/06/03/Swift\351\224\231\350\257\257\345\244\204\347\220\206/index.html" new file mode 100644 index 0000000..68dc857 --- /dev/null +++ "b/2019/06/03/Swift\351\224\231\350\257\257\345\244\204\347\220\206/index.html" @@ -0,0 +1,221 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

Swift 中错误处理

+

assert 断言

只在debug 模式下程序会自动退出

+
1
public func assert(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)
+

assertionFailure 断言错误

只在debug 模式下程序会自动退出, 没有条件

+
1
public func assertionFailure(_ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)
+

precondition

releas 模式程序也会退出

+
1
public func precondition(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)
+

throw

+ +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/03/Swift\351\241\271\347\233\256\344\270\255\351\233\206\346\210\220RN/index.html" "b/2019/06/03/Swift\351\241\271\347\233\256\344\270\255\351\233\206\346\210\220RN/index.html" new file mode 100644 index 0000000..87742cf --- /dev/null +++ "b/2019/06/03/Swift\351\241\271\347\233\256\344\270\255\351\233\206\346\210\220RN/index.html" @@ -0,0 +1,243 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

Swift已有项目中集成ReactNative

+

本教程针对于已经有的Swift项目集成ReactNative

+

创建新的空目录

为了不影响原有的swift项目目录结构,建议先创建一个空的文件夹,例如SwiftRN

+

+

创建package.json文件

终端进入创建的SwiftRN文件目录下,创建package.json文件

+
1
touch package.json
+
修改package.json文件,指定各个版本
1
2
3
4
5
6
7
8
9
10
11
12
13
{
"name": "SwiftRN",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start",
"test": "jest"
},
"dependencies": {
"react": "16.3.1",
"react-native": "^0.56.0"
}
}
+
执行npm install

在创建package.json所在的文件目录下执行,在此过程中可以遇到的错误可能较多

+
1
npm install
+
添加index.js文件
1
touch index.js
+
修改文件内容
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
import {
AppRegistry,
Text,
View,
} from 'react-native';

import React, { Component } from 'react';

// 用于最后在项目中测试
class SwiftRNHomeView extends Component {
render() {
return(
<View style={{backgroundColor:'white'}}>
<Text style={{fontSize: 20, marginTop: 200}}>Hellow world</Text>
</View>
);
}
}


class MyApp extends Component {
render() {
return <SwiftRNHomeView></SwiftRNHomeView>;
}
}

AppRegistry.registerComponent('SwiftRN', () => MyApp);
+

配置swift项目

将新创建的Swift文件目录下的所有文件拷贝到Swift项目所在的根目录下

+

+

使用Pod集成RN

swift项目采用了pod管理第三方库,在Podfile文件中加入下面代码

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
pod 'React', :path => './node_modules/react-native', :subspecs => [
'Core',
'RCTText',
'RCTNetwork',
'RCTWebSocket', # 这个模块是用于调试功能的
'CxxBridge', # Include this for RN >= 0.47,不引入的话会报错误或者警告
'DevSupport', # Include this to enable In-App Devmenu if RN >= 0.43,不引入的话会报错误或者警告
'RCTAnimation', 不引入的话会报错误或者警告
]


# 如果你的RN版本 >= 0.42.0,请加入下面这行,不引入的话会报错误或者警告
pod "yoga", :path => "./node_modules/react-native/ReactCommon/yoga"
pod 'DoubleConversion', :podspec => './node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
pod 'glog', :podspec => './node_modules/react-native/third-party-podspecs/glog.podspec'
pod 'Folly', :podspec => './node_modules/react-native/third-party-podspecs/Folly.podspec'
+

创建桥接文件

    +
  • 如果原项目中有SwiftDemo-Bridging-Header.h类似这个文件可以跳过。
  • +
  • 如果从来没有创建过桥接文件,创建一个OC的类会自动弹出是否需要创建桥接文件,直接创建即可,最后把无用的OC文件删掉
  • +
+

引用RN头文件

在桥接文件中导入头文件

+
1
2
#import <React/RCTRootView.h>
#import <React/RCTBundleURLProvider.h>
+

使用RN页面

在模拟器中使用RN的页面, 确保在 info.plist 文件中添加了Allow Arbitrary Loads=YES

+
    +
  • moduleName 对应的名字和index.js中 AppRegistry.registerComponent(‘SwiftRN’, () => MyApp)注册的名字必须相同
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import UIKit

class RNTestController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "http://localhost:8081/index.bundle?platform=ios")!
let rootView = RCTRootView(
bundleURL: url,
moduleName: "SwiftRN", //这里的名字必须和AppRegistry.registerComponent('MyApp', () => MyApp)中注册的名字一样
initialProperties: nil,
launchOptions: nil
)
view = rootView
}
}
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/03/Xcode\344\270\255\344\275\277\347\224\250Debug Memory Graph\346\243\200\346\237\245\345\206\205\345\255\230\346\263\204\346\274\217/index.html" "b/2019/06/03/Xcode\344\270\255\344\275\277\347\224\250Debug Memory Graph\346\243\200\346\237\245\345\206\205\345\255\230\346\263\204\346\274\217/index.html" new file mode 100644 index 0000000..fd05738 --- /dev/null +++ "b/2019/06/03/Xcode\344\270\255\344\275\277\347\224\250Debug Memory Graph\346\243\200\346\237\245\345\206\205\345\255\230\346\263\204\346\274\217/index.html" @@ -0,0 +1,223 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/03/iOS \345\212\250\346\200\201\350\256\276\347\275\256TableHeaderView\351\253\230\345\272\246/index.html" "b/2019/06/03/iOS \345\212\250\346\200\201\350\256\276\347\275\256TableHeaderView\351\253\230\345\272\246/index.html" new file mode 100644 index 0000000..8af5a9a --- /dev/null +++ "b/2019/06/03/iOS \345\212\250\346\200\201\350\256\276\347\275\256TableHeaderView\351\253\230\345\272\246/index.html" @@ -0,0 +1,230 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

iOS动态设置tableHeaderView高度

需求

动态的设置tableHeaderView的高度

+

遇到的问题:

    +
  • 设置tableHeaderViewframe无法实现,有时候会出现tableView 中间的 cell 不显示
  • +
  • 有时候会导致headerViewcell内容重叠

    +
  • +
  • 通过计算控件高度设置frame方式麻烦

    +
  • +
+

正确的步骤

    +
  • 创建HeaderView
  • +
  • 取出UITableViewtableHeaderView
  • +
  • 设置frame大小,调整headerView布局
  • +
  • 重新给UITableViewtableHeaderView
  • +
+

使用AutoLayout

UITableView增加分类

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
extension UITableView {
/// set tableHeaderView
func setTableHeaderView(_ headerView: UIView) {
headerView.translatesAutoresizingMaskIntoConstraints = false
self.tableHeaderView = headerView
// autolayout
NSLayoutConstraint(item: headerView, attribute: .centerX, relatedBy: .equal, toItem: self, attribute: .centerX, multiplier: 1.0, constant: 0).isActive = true
NSLayoutConstraint(item: headerView, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1.0, constant: 0).isActive = true
NSLayoutConstraint(item: headerView, attribute: .width, relatedBy: .equal, toItem: self, attribute: .width, multiplier: 1.0, constant: 0).isActive = true
}
/// update
func updateHeaderView() {
guard let headerView = self.tableHeaderView else { return }
headerView.layoutIfNeeded()
let header = self.tableHeaderView
self.tableHeaderView = header
}
}
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/03/iOS\345\274\200\345\217\221_\344\272\216\351\270\277\351\271\217/index.html" "b/2019/06/03/iOS\345\274\200\345\217\221_\344\272\216\351\270\277\351\271\217/index.html" new file mode 100644 index 0000000..f4d93e2 --- /dev/null +++ "b/2019/06/03/iOS\345\274\200\345\217\221_\344\272\216\351\270\277\351\271\217/index.html" @@ -0,0 +1,264 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

个人信息

+

工作经历

猎聘网 (2018.2 ~ 至今)

Swift重构猎聘App中应聘记录模块,推进项目组使用AutoLayout技术,后期负责开发迭代乐班班项目,参与每版需求分析和主要代码编写,解决复杂功能页面卡顿,内存优化,BUG修复及iOS新特性适配等。

+

北京万朝科技有限公司(2015.5 ~ 2018.2)

在该项目中担任iOS开发⼯作,参与项目需求分析、核⼼代码编写以及常用工具类的封装,BUG修复,版本迭代更新,推进组内swift的使用。

+

中盎炬华科技发展有限公司(2014.4月 ~ 2015.5)

参与iOS客户端的开发维护完成开发工作,实现一些交互动画和效果;研究最新技术,优化程序代码,集成第三方SDK。

+

项⽬经历

1. 猎聘APP (2018.2 ~ 至今)

项⽬目描述: 为中高端人才和经理人提供招聘的平台

+
    +
  • 使用 swift 重构应聘记录模块中相关页面及功能
  • +
  • 推进整个项目组使用 AutoLayout 技术
  • +
+

2. 乐班班

项⽬目描述: 音/视频播放、直播,内购,即时通信等功能,由OC、Swift,RN 技术混编,项⽬应⽤技术

+
    +
  • 使用ReactNative技术重构和开发部分页面
  • +
  • 使用Xcode、Instruments工具查找、分析、解决项目内存泄露,修复BUG
  • +
  • Swift重构通信模块,用collectionView构建消息页面,自定义emoj Keyboard,封装常用UI控件,自定义控制器转场动画
  • +
  • 使用AVFoundation 实现视频录制,上传,播放功能
  • +
  • 通过GCD dispatch semaphors 解决并发网络请求问题
  • +
  • 使用collectionView构建复杂的视频播放页面,企业大学首页
  • +
+

3. e蜂通信 (2015.5 ~ 2018.2)

项⽬目描述:整合了办公OA、CRM系统,新增即时通信、分享、远程监控、解密外发、红包、私聊等功能;由OC和swift混编, 项⽬目应⽤用技术:

+
    +
  • 使⽤CocoaAsyncSocket自定义通信协议,实现聊天,特定业务消息触达功能
  • +
  • 使⽤cocoaPod实现组件化,
  • +
  • 封装swift基础库,完善swift和Objective-C混编
  • +
  • 实现分享⼩视频、图片、点赞、评论功能
  • +
  • ⾃定义相机实现人脸识别打卡、扫描二维码功能
  • +
  • 封装常⽤UI控件,使用Core Animation实现动画效果,集成百度地图SDK
  • +
  • 利⽤JavaScriptCore完成H5和Native之间的交互
  • +
+

4. 四万公里 (2014.4 ~ 2015.5)

项目描述:针对境外旅游群体提供便捷服务,项⽬目应⽤用技术:

+
    +
  • 封装 TableViewController基类,对接服务端接口等
  • +
  • 封装基础UI控件
  • +
  • 集成科大讯飞SDK, 实现了实时翻译功能
  • +
  • 集成滴滴打车SDK, 实现用户可在国外打车功能
  • +
+

专业技能

    +
  1. 熟练使用Objective-C、Swift,及Swift与Objective-C混编,有良好的编程习惯
  2. +
  3. 熟练掌握iOS内存管理机制以及ARC技术,结合Instruments对项目进行内存优化
  4. +
  5. 熟练掌握NSThread/GCD/NSOperation等并发编程技术
  6. +
  7. 熟练掌握iOS中事件链和响应链的传递方式
  8. +
  9. 熟练使⽤AFNetworking,Moya,FMDB,SDWebImage,Masonry等第三⽅开源框架
  10. +
  11. 熟练掌握各种UI控件封装,熟悉多视图开发,能实现复杂的界⾯面交互
  12. +
  13. 掌握KVO原理理、KVC原理理、SDWebImage内部实现过程、RunLoop的应⽤
  14. +
  15. 有团队开发经验,同时能够独立完成APP开发,代码编写,调试和发布
  16. +
+

自我介绍

工作之余了解使⽤ JavaScript,RxSwift 函数响应式编程,小程序开发等知识,经常关注iOS博客和Github 上优秀代码,以及在raywenderlich上学习新知识,在个人博客做些技术积累。 计划在iOS领域深⼊同时也扩展⾃己的知识⾯,了解使⽤后端和前端技术。

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/03/iOS\351\200\232\350\277\207URL\350\216\267\345\217\226HTML\344\270\255\346\240\207\351\242\230/index.html" "b/2019/06/03/iOS\351\200\232\350\277\207URL\350\216\267\345\217\226HTML\344\270\255\346\240\207\351\242\230/index.html" new file mode 100644 index 0000000..08f6849 --- /dev/null +++ "b/2019/06/03/iOS\351\200\232\350\277\207URL\350\216\267\345\217\226HTML\344\270\255\346\240\207\351\242\230/index.html" @@ -0,0 +1,258 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

iOS通过URL获取HTML中标题

+

获取标题

String扩展一个方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private let patternTitle = "(?<=title>).*(?=</title)"
extension String {
static func parseHTMLTitle(_ urlString: String) -> String? {
let url = URL(string: urlString)!
do {
let html = try String(contentsOf: url, encoding: .utf8)
let range = html.range(of: patternTitle, options: String.CompareOptions.regularExpression, range: html.startIndex..<html.endIndex, locale: nil)
guard let tempRange = range else { return "" }
let titleStr = html[tempRange]
return String(titleStr)
} catch {
return ""
}
}
}
+
iOS 中简单正则表达式

iOS 中 正则表达式中的\ 需要使用 \\ 来表示

+
// 获取
+let testStr = "我到底是[iOS]开发还是[产品]经理"
+
+// 获取 "[" 和 "]" 之间的所有内容, 左边[ 起一直到 右边 ] 结束的所有内容 
+let pattern1 = "\\[.*\\]" // [iOS]开发还是[产品]
+
+// 获取 "[" 和 "]" 之间的所有内容,包括[]
+let pattern2 = "\\[.*?\\]"    // [iOS]
+
+// 获取第一个 "[" 和 "]" 之间的所有内容 不包括[] 
+let pattern3 = "(?<=\\[).*?(?=\\])" // iOS
+
+// 获取html中 title 标签中内容
+let pattern5 = "(?<=title>).*(?=</title)" 
+
获取范围截取子串
let testRange1 = testStr.range(of: pattern1, options: String.CompareOptions.regularExpression, range: testStr.startIndex..<testStr.endIndex, locale: nil)
+    let result1 = String(testStr[testRange1!])
+
+
+let testRange2 = testStr.range(of: pattern2, options: String.CompareOptions.regularExpression, range: testStr.startIndex..<testStr.endIndex, locale: nil)
+let result2 = String(testStr[testRange2!])
+
+
+let testRange3 = testStr.range(of: pattern3, options: String.CompareOptions.regularExpression, range: testStr.startIndex..<testStr.endIndex, locale: nil)
+let result3 = String(testStr[testRange3!])
+
+print(result1) 
+print(result2) 
+print(result3) 
+
正则表达式

通过URL解析html中的标题

+
    +
  • 获取html字符串
  • +
  • 正则匹配
  • +
  • 生成字符串Range
  • +
  • 截取字符串
  • +
+

匹配一次直接返回结果

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 生成表达式
let regular = try NSRegularExpression(pattern: pattern5, options: .caseInsensitive)


// 开始匹配
let matchResult1 = regular.firstMatch(in: html, options: NSRegularExpression.MatchingOptions.reportProgress, range: NSMakeRange(0, html.count))

// 获取匹配结果范围
let matchRange = matchResult1?.range
let range = Range(matchRange!)!

// 生成String.Index 类型范围
let startIndex = html.index(html.startIndex, offsetBy: range.lowerBound)
let endIndex = html.index(html.startIndex, offsetBy: range.upperBound)
let range3 = Range(uncheckedBounds: (lower: startIndex, upper: endIndex))

// 截取字符串
let result1 = String(html[range3])
print(result1)
+

全部匹配后返回结果时一个集合

+
1
2
3
4
5
6
7
8
9
10
let matchResult2 = regular.matches(in: html, options: .reportProgress, range: NSMakeRange(0, html.count))
matchResult2.forEach { (result) in
let range = Range(result.range)!

let startIndex = html.index(html.startIndex, offsetBy: range.lowerBound)
let endIndex = html.index(html.startIndex, offsetBy: range.upperBound)

let subStr2 = html[Range(uncheckedBounds: (startIndex, endIndex))]
print(String(subStr2))
}
+

将匹配到的内容提还

+
1
let str3 = regular.stringByReplacingMatches(in: html, options: .reportProgress, range: NSMakeRange(0, html.count), withTemplate: "====")
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/03/iOS\351\235\242\350\257\225OC\350\257\255\350\250\200\347\211\271\346\200\247/index.html" "b/2019/06/03/iOS\351\235\242\350\257\225OC\350\257\255\350\250\200\347\211\271\346\200\247/index.html" new file mode 100644 index 0000000..71319c6 --- /dev/null +++ "b/2019/06/03/iOS\351\235\242\350\257\225OC\350\257\255\350\250\200\347\211\271\346\200\247/index.html" @@ -0,0 +1,330 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

OC 语言特性

分类

原理

由运行时来决议的,不同分类当中含有同名方法 谁最终生效取决于谁最终参与编译,最后参编译的同名分类方法会最终生效,假如分类方法中添加的方法和宿主类中的某一个方法名相同,分类中的方法会覆盖宿主类中的同名方法,覆盖是指在消息传递过程中优先查找数组靠前的元素,如果查找到了同名方法就直接调用,实际上宿主类的同名方法实现仍然是存在的,我们可可以通过一些手段调用到原有类的同名方法的实现

+

使用场景

    +
  • 申明私有方法,分类的 .m 文件中申明私有方法啊,对外不暴露
  • +
  • 对类中的代码进行抽取分解
  • +
  • 把Framework的私有方法公开化
  • +
+

特点

    +
  • 运行时决议,运行时通过runtime 才把分类中的方法添加到了宿主类上
  • +
  • 可以为系统类添加方法
  • +
  • 有多个分类中存在同名方法时,最后编译的方法会最先生效
  • +
  • 分类中有和宿主类同名方法时,底层中通过内存copy将分类方法“覆盖”宿主类方法
  • +
  • 名字相同的分类编译会报错
  • +
+

分类中可以添加的内容

    +
  • 实例方法
  • +
  • 类方法
  • +
  • 协议
  • +
  • 属性,实际上只是声明了 getter / setter 方法,并没有添加成员变量
  • +
+

关联对象技术

关联对象由 AssocicationsManager 管理并在 AssociationsHashMap 上存储
所有对象的关联内容都在同一个全局容器中

+
    +
  • 给分类添加成员变量,通过关联对象的方式
  • +
+

扩展

使用场景

    +
  • 声明私有属性
  • +
  • 声明私有方法
  • +
  • 声明私有成员变量
  • +
+

特点

    +
  • 编译时决议
  • +
  • 只以声明的形式存在,多数情况下寄生在宿主类的.m中
  • +
  • 不能为系统添加扩展
  • +
+

代理

    +
  • 代理设计模式,传递方式是一对一
  • +
  • 使用weak 避免循环引用
  • +
+

通知

使用观察者模式来实现的用于跨层传递消息的机制

+

KVO

KVO 是系统对观察者模式的又一实现,使用isa 混写技术(isa - swizzling)来动态运行时为某一个类添加一个子类重写了它的setter 方法,同时将原有类的isa指针指向了新创建的类上

+
    +
  • KVO 是OC对观察者模式的又一实现
  • +
  • apple 使用isa 混写技术(isa - swizzling)来实现KVO
      +
    • 给我A类注册一个观察者时本质上是调用系统的 Observer for keyPath 方法,系统会创建一个NSKVONotifiying_A 的类
    • +
    +
  • +
  • 使用 setter 方法设置值KVO才能生效
  • +
  • 使用setValue:forKey: 改变值KVO才能生效
  • +
  • 使用下划线的成员变量直接修改值需要手动添加KVO才能生效(增加 willChangeValueForKey 和 didChangeValueForKey 两个方法)
  • +
+

KVC

apple提供的键值编码技术

+
    +
  • (nullable id)valueForKey:(NSString *)key;
  • +
  • (void)setValue:(nullable id)value forKey:(NSString *)key;

    +

    以上两个方法中的key是没有任何限制的,只要知道对应的成员变量名称,就可以对私有的成员变量设置和操作,这违背了面向对象的编程思想

    +
  • +
+

(void)setValue:(nullable id)value forKey:(NSString *)key; 方法调用流程

+
    +
  • 先判断是否有跟key 相关的setter 方法,如果有就直接调用,结束调用
  • +
  • 如果没有再判断是否存在实例变量,如果存在直接给赋值,结束调用
  • +
  • 如果说实例变量不存在,会去调用 - (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key; 抛出异常
  • +
+

属性关键字

读写权限

    +
  • readonly

    +
  • +
  • readwrite 系统默认

    +
  • +
+

原子性

    +
  • atomic (系统默认)保证赋值和获取(对成员属性的直接获取和赋值)时线程安全,并不代表操作和访问,如果atomic修饰的一个数组,只能保证独具改数组的读取和赋值时线程安全,并不能保证对数据操作(增加/删除数组元素)时是安全的
  • +
  • nonatomic
  • +
+

引用技术

    +
  • assign
      +
    • 修饰基本数据类型,
    • +
    • 修饰对象时不改变其引用计数,
    • +
    • 会产生悬垂指针
    • +
    +
  • +
  • weak
      +
    • 不改变被修饰对象的引用计数,
    • +
    • 所指对象被释放之后指针自动置为nil
    • +
    +
  • +
+

两者区别:

+
    +
  • weak可以修饰对象,而assign 既可以修饰对象也可以修饰基本数据类型
  • +
  • assign 修饰的对象,对象释放后指针仍然指向原对象的内存地址,而weak 修饰的对象,释放后会自动置为nil
  • +
+

问题1

+

weak 修饰的对象为什么释放后会自动置为nil

+

问题2

+

@property (copy) NSMutableArray *array; 有什么问题

+
    +
  • 被copy修饰后,如果赋值过来的是NSMutableArray,copy之后就是NSArray,如果赋值过来的是NSAarray,copy 之后仍然是NSArray,有可能会调用array的添加元素方法会导致crash
  • +
+

copy 关键词

+
    +
  • 浅拷贝:内存地址的复制,让目标对象指针和源对象指向同一块内存空间,
      +
    • 会增加对象的引用计数
    • +
    • 并没有一个新的内存分配
    • +
    +
  • +
+
    +
  • 深拷贝:目标对象指针和源对象指针分别指向两块内容相同的内存空间
      +
    • 不会增加源对象的引用计数
    • +
    • 有新对象的内存分配
    • +
    +
  • +
+

MRC 重写 retain修饰的变量的setter 方法

1
2
3
4
5
6
7
8

- (void)setObj:(id)obj {
if (_obj != obj) {
[_obj release];
}
_obj = [obj retain];

}
+

判断是为了防止异常处理,如果不做 if 判断,当传入的 obj 对象正好是原来的_obj 对象,对原对象尽行releas 操作,实际上也会对传入的对象进行releas 操作进行释放,此时如果再通过obj指针访问废弃的对象时就会导致carsh

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/03/is \345\205\263\351\224\256\345\255\227\347\261\273\345\236\213\346\243\200\346\237\245/index.html" "b/2019/06/03/is \345\205\263\351\224\256\345\255\227\347\261\273\345\236\213\346\243\200\346\237\245/index.html" new file mode 100644 index 0000000..fad874f --- /dev/null +++ "b/2019/06/03/is \345\205\263\351\224\256\345\255\227\347\261\273\345\236\213\346\243\200\346\237\245/index.html" @@ -0,0 +1,225 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

swift类型检查和类型转换

+

is 关键字类型检查

判断父类和子类之间的关系

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
func typeCheck() {
let programmer = Programmer(name: "老张-php")
if dev is iOSDeveloper {
print("iOS 开发者")
} else {
print("其他开发者")
}

if programmer is Coder {
print("老张-php是一农个码农")
} else {
print("")
}
}
+

as 关键字类型转换

1
2
let ios = iosDev as? Programmer
let ios2 = iosDev as! Coder
+

判断是否遵守了协议

swift 中协议可以当作是一个类型使用

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
et ios = iosDev as? Programmer
let ios2 = iosDev as Coder

// 是否遵守了 Person 协议
if iosDev is Person {
print("遵守了Person协议")
}

let devs = [dev, iosDev, programmer] as [Any]

for dev in devs {
if let dev = dev as? Person {
print(dev)
print("遵守Person协议")
} else {
print("未遵守Person协议")
}
}
+

AnyObject Any

1
2
3
4
var anyObjectArray: [AnyObject] = [CGFloat(0.5) as AnyObject,
1 as AnyObject,
"string" as AnyObject,
iOSODev]
+

swift是面向函数编程, 函数是一等公民,数组中可以存入一个函数,函数表达的是一个过程,不是一个名词或者物体,所以函数不是一个对象,需要使用 any 关键字

+
1
2
3
4
var anyArray: [Any] = [CGFloat(0.5),
1,
"string",
iOSODev]
+

放入函数

+
1
anyArray.append({ (a: Int) -> (Int) in return a * a })
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/03/npm\345\222\214cnpm/index.html" "b/2019/06/03/npm\345\222\214cnpm/index.html" new file mode 100644 index 0000000..1184e95 --- /dev/null +++ "b/2019/06/03/npm\345\222\214cnpm/index.html" @@ -0,0 +1,244 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

ReactNative配置.npm 和cnpm 相关

npm

Nodejs的包管理工具

+

npm 常用命令

    +
  • 更新或卸载

    +
    1
    npm update/uninstall moduleName
    +
  • +
  • 查看当前目录下已安装的包

    +
    1
    npm list
    +
  • +
  • 查看全局安装的包的路径

    +
    1
    npm root -g
    +
  • +
  • 全部命令

    +
    1
    npm help
    +
  • +
+

cnpm

因为npm安装插件是从国外服务器下载,受网络影响大,可能出现异常,cnpm 淘宝镜像

+

安装

1
npm install -g cnpm --registry=https://registry.npm.taobao.org
+

设置镜像源

1
2
cnpm set registry http://registry.cnpm.taobao.com
npm set registry http://registry.cnpm.taobao.com
+

配置 .npmrc 文件

前端项目开发离不开安装各种npm依赖包,可以选择远程的仓库也可以选择本地的仓库,但更改仓库地址需要在安装时控制台打命令,比较麻烦, 而npmrc可以很方便地解决上面问题

+

.npmrc 原理

当安装项目的依赖包时,会查找并读取项目根目录下的.npmrc文件的配置,自动指定仓库地址

+
    +
  • 打开 .npmrc

    +
    1
    open .npmrc
    +
  • +
+
    +
  • 编辑文件, 指定特定的库的镜像源, 比如以@test开头的库镜像为:http://registry.cnpm.test.com/ 而其他的正常

    +
    1
    2
    @test:registry=http://registry.cnpm.lietou.com/
    registry=https://registry.npm.taobao.org/
    +
  • +
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/03/swift\344\270\255\345\274\272\345\274\225\347\224\250\345\276\252\347\216\257\345\222\214weak\345\205\263\351\224\256\345\255\227/index.html" "b/2019/06/03/swift\344\270\255\345\274\272\345\274\225\347\224\250\345\276\252\347\216\257\345\222\214weak\345\205\263\351\224\256\345\255\227/index.html" new file mode 100644 index 0000000..853a7e6 --- /dev/null +++ "b/2019/06/03/swift\344\270\255\345\274\272\345\274\225\347\224\250\345\276\252\347\216\257\345\222\214weak\345\205\263\351\224\256\345\255\227/index.html" @@ -0,0 +1,299 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

Swift中内存管理

+

强引用循环和 weak 关键字

房东

+
1
2
3
4
5
6
7
8
9
10
11
12
class Landlord {
var name: String
var house: House?

init(name: String) {
self.name = name
}

deinit {
print("Landlord内存释放")
}
}
+

房子

+
1
2
3
4
5
6
7
8
9
10
11
12
class House {
var name: String
var landlord: Landlord?

init(person: String) {
self.name = person
}

deinit {
print("House内存释放")
}
}
+

循环引用

+
1
2
3
4
5
6
7
8
9
10
11
func strongRetain() {
var landlord: Landlord? = Landlord(name: "老李")
var house: House? = House(person: "望京府邸")
// 房东有个房子
landlord?.house = house
// 房子有个主人
house?.landlord = landlord

landlord = nil
house = nil
}
+
    +
  • 虽然 landlord = nil 和 house = nil,但是 Landlord 和 House 内存空间并没有释放 (并未执行 deinit 函数)

    +
  • +
  • 原因:因为两个内存空间相互引用,引用计数不为0,形成了强循环引用

    +
  • +
  • 解决办法:使用weak 关键字来修饰两者中其中一个变量

    +
      +
    • 使用weak修饰的变量必须是可选类型,可以赋值为nil
    • +
    • 使用weak修饰必须是var 类型变量
    • +
    +
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
class House {
var name: String
weak var landlord: Landlord?

init(person: String) {
self.name = person
}

deinit {
print("House内存释放")
}
}
+

unowned 关键字的使用

相互循环引用的两个变量有一个是可选的

有个身份证的类,每个身份证肯定对应一个人

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class ID {
var number: String
let person: Landlord

init(person: Landlord, number: String) {
self.person = person
self.number = number
}

deinit {
print("ID内存释放")
}
}

```

房东
+

class Landlord {
var name: String
var house: House?
/// 身份证可以为nil,
var id: ID?

+
init(name: String) {
+    self.name = name
+}
+
+deinit {
+    print("Landlord内存释放")
+}
+

}

+
1
2

循环引用
+

func unownedTest() {
var person: Landlord? = Landlord(name: “老王”)
var id: ID? = ID(person: person!, number: “10010”)

+
person?.id = id
+person = nil
+id = nil
+

}

+
1
2
3
4
5
6
7
8

- person 和 id 之间有强引用关系
- 每个id必须有一个对应的人并且是固定的只能使用let 修饰,不能为nil, 而id可以为空
- unowned和 weak 作用相同都是弱引用,只是unowned 修改的变量不能是nil (可选型的)
- unowned只能使用在 class 类型上,不能修饰函数类型,例如闭包存在循环引用时不能使用unowned修饰
- 解决方法:
- 此时可以通过使用 weak 修饰 id 的方式,因为 id 可以为nil
- let 修饰并且不能为nil 这种情况可以使用 unowned 修饰解决
+
class ID {
+    var number: String
+    unowned let person: Landlord
+
+    init(person: Landlord, number: String) {
+        self.person = person
+        self.number = number
+    }
+
+    deinit {
+        print("ID内存释放")
+    }
+}
+
1
2
3
4
	
**unowned有一定的危险性**

因为unowned 修饰的对象不能赋值为nil,所以执行下面的代码会crash,
+

func unownedTest() {
var person: Landlord? = Landlord(name: “老王”)
var id: ID? = ID(person: person!, number: “10010”)

+
person?.id = id
+
+// 提前释放 person 内存
+person = nil
+
+// 获取身份证对应的人
+print(id?.person) // 此处奔溃,
+let owner = id?.person
+id = nil
+

}

1
2

正确的写法因该是先将🆔设置为nil,再将 Person 设置为nil,再调用 print(id?.person) 就不会再报错

+

func unownedTest() {
var person: Landlord? = Landlord(name: “老王”)
var id: ID? = ID(person: person!, number: “10010”)

+
person?.id = id
+
+id = nil
+
+// 提前释放 person 内存
+person = nil
+
+// 获取身份证对应的人
+print(id?.person) // 此处奔溃,
+let owner = id?.person
+

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

#### 相互循环引用的两个变量都不是可选的

使用隐式可选类型

### 闭包中的强引用循环

- 并不是所有的闭包都会产生循环引用,只有相互引用的两个对象会产生循环引用

- 使用swift闭包捕获列表,在闭包的声明参数之前使用 [unowned self], 不需要解包

- 使用 unowned表示这个对象不能为空,可能存在风险,如果对象为空就会crash


使用 unowned 对象不能为空,可能存在风险

+

class Closure {
var closure: ((Int) -> Void)?

+
init() {
+    closure = { [unowned self] index in
+        print(self)
+        print(index)
+    }
+}
+

}

1
2

使用 weak, 此时需要解包

+

closure = { [weak self] index in
// 解包
if let self = self {
print(self)
print(index)
}
}

1
2

强制解包

+

closure = { [weak self] index in
// 强制解包
print(self!)
print(index)
}
`

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/03/typescripe \345\256\211\350\243\205\345\217\212\350\257\255\346\263\225/index.html" "b/2019/06/03/typescripe \345\256\211\350\243\205\345\217\212\350\257\255\346\263\225/index.html" new file mode 100644 index 0000000..00fa8b9 --- /dev/null +++ "b/2019/06/03/typescripe \345\256\211\350\243\205\345\217\212\350\257\255\346\263\225/index.html" @@ -0,0 +1,290 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

ReactNative 中使用 TypeScript

+

从RN0.57版本开始已经可以直接支持typescript,无需任何配置。在保留根入口文件index.js的前提下,其他文件都可以直接使用.ts或.tsx后缀。但这一由babel进行的转码过程并不进行实际的类型检查,因此仍然需要编辑器和插件来共同进行类型检查

+
+

TypeScript和JavaScript对比

两者深度对比

+

最大的区别:

+
    +
  • TypeScript是强类型语⾔和静态类型语⾔(需要指定类型)
  • +
  • JavaScript是弱类型语⾔和动态类型语⾔(不需要指定类型)
  • +
+

TypeScript

由微软开发的自由和开源的编程语言,是JavaScript的一个严格超集,并添加了可选的静态类型和基于类的面向对象编程

+

TypeScript定义

+

安装及语法

教程

+
安装
1
npm install -g typescript
+
安装 typescript 依赖管理器 typings
1
npm install -g typings
+
安装 typings 依赖
1
2
typings install npm~react --save
typings install dt~react-native --globals --save
+
使用 yarn 安装

如果已经安装了 yarn 安装如下:

+
1
2
yarn add tslib @types/react @types/react-native
yarn add --dev react-native-typescript-transformer typescript
+
创建 tsconfig.json 配置文件
    +
  • cd 到 ReactNative 项目根文件夹下

    +
    1
    tsc --init
    +
  • +
  • 修改 tsconfig.json 文件

    +

    如果一个目录下存在一个tsconfig.json文件,那么它意味着这个目录是TypeScript项目的根目录。 tsconfig.json文件中指定了用来编译这个项目的根文件和编译选项

    +

    tsconfig.json文件配置详解

    +

    TypeScript配置文件tsconfig简析

    +

    务必设置”jsx”:”react”
    注意多余的注释可能会不兼容,需要移除

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    {
    "compilerOptions": {
    "target": "es6",
    "allowJs": true,
    "jsx": "react",
    "outDir": "./dist",
    "sourceMap": true,
    "noImplicitAny": false
    },

    // "files"属性 :指定一个包含相对或绝对文件路径的列表。 "include"和"exclude"属性指定一个文件glob匹配模式列表

    "include": [
    "typings/**/*.d.ts",
    "src/**/*.ts",
    "src/**/*.tsx"
    ],
    "exclude": [
    "node_modules"
    ]
    }
    +
  • +
  • cd 到项目根目录中新建存放 typescripe 源代码的文件夹

    +
  • +
+
建立 rn-cli.config.js 文件,使用支持typescript的transfomer
1
2
3
4
5
6
7
8
module.exports = {
getTransformModulePath() {
return require.resolve('react-native-typescript-transformer');
},
getSourceExts() {
return ['ts', 'tsx'];
}
}
+
修改 .babelrc 文件
1
2
3
4
{
"presets": ["react-native"],
"plugins": ["transform-decorators-legacy"]
}
+
修改项目中文件导入
    +
  • 入口index.jsApp.js的文件名请不要修改,项目根目录下新建src文件夹
  • +
  • src文件夹下新建 .tsx 后缀的文件
  • +
  • .tsx 后缀的文件引用react的写法有所区别

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    import * as React from 'react'
    import { Text, View } from 'react-native';
    import { Component } from 'react';

    export default class HelloWorld extends Component{
    render() {
    return (
    <View>
    <Text>Hello!</Text>
    <Text>Typescript</Text>
    </View>
    );
    }
    }
    +
  • +
  • App.js中使用

    +
      +
    • App.js 导入方式修改为如下方式

      +
      1
      2
      3
      4
      	import './src';
      ```

      - 导入 .tsx 类型的组建
      +

      import HelloWorld from “./src/HelloWorld”;

      +
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11

      ### 配置jsconfig

      `jsconfig.json`目录中存在文件表明该目录是JavaScript项目的根目录。该jsconfig.json文件指定根文件和JavaScript语言服务提供的功能选项

      [jsconfig详解](https://code.visualstudio.com/docs/languages/jsconfig)

      [jsconfig.json](https://jeasonstudio.gitbooks.io/vscode-cn-doc/content/md/%E8%AF%AD%E8%A8%80/javascript.html?q=)


      ##### 新建 `jsconfig.json` 文件
      +
    • +
    +
  • +
+

touch jsconfig.json

1
2

##### 编辑 `jsconfig.json` 文件

+

{
“compilerOptions”: {
“allowJs”: true,
“allowSyntheticDefaultImports”: true,
“emitDecoratorMetadata”: true
},
“exclude”: [
“node_modules”,
“rnApp/Tools” // 项目根目录下新建的存放RN中所用的一些工具类
]
}

+
1
2
3


### package.json 文件配置
+

{
“name”: “RNProject”,
“version”: “0.0.1”,
“private”: true,
“scripts”: { // 通过设置这个可以使npm调用一些命令脚本,封装一些功能
“start”: “node node_modules/react-native/local-cli/cli.js start”,
“test”: “jest”
},
“dependencies”: { // 发布后依赖的包
“@types/react”: “^16.4.16”,
“@types/react-native”: “^0.57.4”,
“react”: “16.5.0”,
“react-native”: “0.57.2”,
“react-transform-hmr”: “^1.0.4”,
“tslib”: “^1.9.3”
},

+

“devDependencies”: { // 开发中依赖的其它包
“babel-jest”: “23.6.0”,
“jest”: “23.6.0”,
“metro-react-native-babel-preset”: “0.48.0”,
“react-native-typescript-transformer”: “^1.2.10”,
“react-test-renderer”: “16.5.0”,
“typescript”: “^3.1.2”
},

+

“jest”: { // JavaScript 的单元测试框架
“preset”: “react-native”
}
}

1
2
3
4
5
6
7
8
9

##### `–save` 和 `-–save-dev` 区别

- --save参数表示将该模块写入dependencies属性,
- --save-dev表示将该模块写入devDependencies属性。

##### 添加开发依赖库

- 类型检查

+
npm install @types/jest --save-dev
+npm install @types/react --save-dev
+npm install @types/react-native --save-dev 
+
+
1
- 装饰器转化核心插件
+ +npm install babel-plugin-transform-decorators-legacy --save-dev +
1
2
	
- 测试框架
+ +npm install react-addons-test-utils --save-dev +npm install react-native-mock --save-dev +
1
2

##### 删除依赖库
+

npm uninstall @types/jest –save

1
2


+

npm uninstall @types/jest –save-dev

1
2

##### 彻底删除,-d 表示 devDependencies, -O 表示 optionalDependencies 中

+

npm uninstall -s -D -O @types/jest

+
1
2
3
4
5
6
7
8
9
10

### 报错汇总

##### 错误1 `Cannot find module 'react-transform-hmr/lib/index.js'`

[解决1](https://stackoverflow.com/questions/50142561/cannot-find-module-react-transform-hmr-lib-index-js?noredirect=1&lq=1)

[解决2](https://github.com/facebook/react-native/issues/21530)

- 安装 `react-transform-hmr`
+
npm install react-transform-hmr --save
+
1
2

- 清除缓存重新启动服务
+ +npm start --reset-cache +``` +
+ +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/03/\343\200\21252\344\270\252\346\226\271\346\263\225\347\254\254\344\270\200\347\253\240\343\200\213/index.html" "b/2019/06/03/\343\200\21252\344\270\252\346\226\271\346\263\225\347\254\254\344\270\200\347\253\240\343\200\213/index.html" new file mode 100644 index 0000000..7fdf92b --- /dev/null +++ "b/2019/06/03/\343\200\21252\344\270\252\346\226\271\346\263\225\347\254\254\344\270\200\347\253\240\343\200\213/index.html" @@ -0,0 +1,402 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

《52个方法-第一章》

+

一、OC起源

    +
  • OC使用的是“消息结构”而不是“函数调用”
  • +
  • 两者区别:
      +
    • 消息结构:在运行时所执行的代码由运行环境来决定,无论是否多态总会在运行时才会去查找所要执行的方法
      1
      2
      NSObject *objc = [NSObject new];
      [objc performWith: parameter1 and: parameter2];
      +
    • +
    +
  • +
+
- 函数调用:由编译器决定,如果函数多态,编译时就要查处到底应该执行那个函数实现
+
+
1
2
Object *objc = new Object;
objc -> perform(parameter1, parameter2);
+
    +
  • 消息结构中编译器不会关心接收消息的对象是何种类型,接收消息的对象问题也要在运行时处理,此过程叫做“动态绑定”
  • +
+

要点:

+
    +
  1. OC语言使用的动态绑定的消息结构,在运行时所执行的代码由运行环境来决定,无论是否多态总会在运行时才会去查找所要执行的方法,
  2. +
  3. OC 中对象总是分配在“堆空间”,而绝不会分配在”栈“上,不带 “*”的变量或者结构体等可能会使用“栈空间”
  4. +
+

二、类头文件尽量少导入其他头文件

要点:

+
    +
  1. 除非必要,否则不要引入头文件,使用向前声明降低类之间的耦合
  2. +
  3. 有时无法使用向前声明,应该把该类遵守协议的声明放到“class-continuation分类”(匿名分类)中。
  4. +
  5. 如果“class-continuation分类”也不行的话,就把协议单独放在一个文件中,再将其引入### 三、多用字面量语法,少用与之等价的方法
  6. +
+

三、多用字面量语法,少用与之等价的方法

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
- (void)tempMethond {
// OC1.0 起,支持简单方式创建基本类型对象

// - 使用字符串字面量:简介易读
NSString *someString = @"Effective Objective-C 2.0";
// 传统方式
NSString *someStrongOld = [NSString stringWithFormat:@"Effective Objective-C 2.0"];


// - 字面数值
NSNumber *someNumber = @1;
NSNumber *floatNumber = @2.5f;
NSNumber *doubleNumber = @3.1415926;
NSNumber *boolNumber = @YES;
NSNumber *charNumber = @'a';
// 字面量语法用于表达式
int x = 5;
float y = 6.23f;
NSNumber *expressionNumber = @(x * y);


// - 字面量数组
NSArray *animals = @[@"cat", @"dog", @"mouse", @"badger"];
NSString *dog = animals[1];

// arrayWithObjects 和 字面量 的区别:
// arrayWithObjects 方法依次处理各个参数,直到发现nil为止
// 字面量数组遇到nil直接抛出异常
NSArray *animalsOld = [NSArray arrayWithObjects:@"cat", @"dog", @"mouse", @"badger", nil];
NSString *dogOld = [animalsOld objectAtIndex:1];


// - 字面量字典
// 如果有nil直接抛出异常
NSDictionary *personData = @{@"firtName": @"Matt",
@"lastName": @"Galloway",
@"age": @28};
NSString *lastName = personData[@"lastName"];

// dictionaryWithObjectsAndKeys 方法依次处理各个参数,直到发现nil为止
NSDictionary *personDataOld = [NSDictionary dictionaryWithObjectsAndKeys:@"Matt",@"firtName",
@"Galloway", @"lastName",
[NSNumber numberWithInteger:28], @"age", nil];

NSString *lastNameOld = [personData objectForKey:@"lastName"];


// - 可变数组和字典
NSMutableArray *mutableArray = animals.mutableCopy;
mutableArray[1] = @"dog";

NSMutableDictionary *mutableDict = [personData mutableCopy];
mutableDict[@"lastName"] = @"Galloway";
}
+

要点:

+
    +
  1. 使用字面量语法创建字符串,数值,数组,字典简明扼要
  2. +
  3. 通过下标操作获取数组,字典对应的元素
  4. +
  5. 使用字面量创建数组,字典时若有nil ,会抛出异常,确保值里不含nil### 五、用枚举表示状态、选项、状态码
  6. +
+

四、多用类型常量,少用#define预处理指令

宏定义
    +
  • 缺点:
      +
    • 可以把所有相同的字符串值都替换为定义的值,
        +
      • 定义出来的常量没有具体的类型信息
      • +
      • 如果有人重新定义了宏定义,不会有警告或者提醒,导致程序中值不一样
      • +
      +
    • +
    +
  • +
+
类型常量
    +
  • 命名规则:

    +
      +
    • 如果只是在实现文件.m 中使用,则在前面加字母k
    • +
    • 如果此常量在类之外也可见,则以类名为前缀,(第19条详细了解命名习惯)
    • +
    +
  • +
  • 声明位置:

    +
      +
    • 总是喜欢在头文件中声明宏定义,可能和常量名冲突,
    • +
    • 所有引入这个头文件的类都会出现宏定义或者常量,相当于声明了一个名叫kAnimationDuration的全局变量
    • +
    • 应该加上前缀,表明其所属的具体类,例如:EOCViewClassAnimationDuration
    • +
    +
  • +
  • 常量从右往左解读依次是: 具体类型,不可修改

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    	// const 修饰的是常量不希望别人修改, NSString * 指向NSString对象
    extern NSString *const EOCStringConstant;


    // 两者写法都可以
    extern const NSTimeInterval EOCAnimatedViewAnimationDuration;
    extern NSTimeInterval const EOCAnimatedViewAnimationDuration1;
    ```

    - 如果不打算公开常量,可以定义在 .m 实现文件里
    +

    static const NSTimeInterval kAnimationDuration = 0.3;

    +
    1
    2
    3

    - static关键字
    - static 修饰意味着该变量仅在定义此变量的编译单元(实现文件.m)中可见,作用域就是当前.m文件
    +
    static const NSTimeInterval kAnimationDuration = 0.3;
    +
    1
    2
    3
    - 如果需要声明一个外部可见的常值变量,需要在头文件中声明并使用 extern 关键词修饰 "extern 

    在 .h 文件中
    + +extern NSString *const EOCStringConstant; + +// 两者写法都可以 +extern const NSTimeInterval EOCAnimatedViewAnimationDuration; +extern NSTimeInterval const EOCAnimatedViewAnimationDuration1; +
    1
    2

    在 .m 文件中
    + +NSString *const EOCStringConstant = @"VALUE"; +const NSTimeInterval EOCAnimatedViewAnimationDuration = 0.3; +NSTimeInterval const EOCAnimatedViewAnimationDuration1 = 0.3; +
    1
    2
    3
    4
    5
    6
    7
    8
    9

    **要点:**

    1. 定义常量时static const定义编译单元(实现文件.m)中可见的常量,无需为其名称加前缀
    2. 在头文件中使用extern 声明全局常量,并在实现文件中定义其值,这种常量要出现在全局符号表中,所以加相关类作为前缀

    ### 五、用枚举表示状态、选项、状态码

    - 普通枚举
    +

    enum EOCConnectionState {

    +
    EOCConnectionStateDisconnected, // 断开连接
    +EOCConnectionStateConnecting,   // 连接中...
    +EOCConnectionStateConnected,    // 已连接
    +

    };

    +
    1
    普通枚举如果要使用如下:
    +

    enum EOCConnectionState status = EOCConnectionStateConnected;

    +
    1
    2

    如果每次都不需要输入enum关键字,只需要typedef关键字重新定义枚举类型
    +

    EOCConnectionState status1 = EOCConnectionStateConnecting;

    +
    1
    2

    - 设置枚举的初始值
    +

    enum EOCConnectionStateConnectionState {

    +
    EOCConnectionStateConnectionStateDisconnected = 1, // 断开连接
    +EOCConnectionStateConnectionStateConnecting,   // 连接中...
    +EOCConnectionStateConnectionStateConnected,    // 已连接
    +

    };

    +
    1
    2
    3
    4
    5

    - 可选枚举

    - << 位操作符(左移), 表示往左移动N位,使用 位运算符 | (或)来组合使用
    - 使用位运算符 &(与) 判断是否开启了某个选项
    +

    enum UIViewAutoresizing {

    +
    UIViewAutoresizingNone                 = 0,
    +UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,
    +UIViewAutoresizingFlexibleWidth        = 1 << 1,
    +UIViewAutoresizingFlexibleRightMargin  = 1 << 2,
    +UIViewAutoresizingFlexibleTopMargin    = 1 << 3,
    +UIViewAutoresizingFlexibleHeight       = 1 << 4,
    +UIViewAutoresizingFlexibleBottomMargin = 1 << 5
    +

    }

    +
    1
    2
    3
    4

    - 使用宏定义枚举

    普通枚举类型(老式语法定义枚举)
    +

    typedef enum : NSUInteger {

    +
    EOCConnectionState2DisConnected,
    +EOCConnectionState2DisConnecting,
    +EOCConnectionState2Connected,
    +

    } EOCConnectionState2;

    +
    1
    2

    NS_ENUM 宏定义的枚举展开后:
    +

    typedef enum EOCConnectionState_3 : NSUInteger EOCConnectionState_3;
    enum EOCConnectionState_3 : NSUInteger {

    +
    EOCConnectionState_3DisConnected,
    +EOCConnectionState_3DisConnecting,
    +EOCConnectionState_3Connected,
    +

    };

    +
    1
    2

    - 定义新特性枚举
    +

    typedef NS_ENUM(NSUInteger, EOCConnectionState3) {

    +
    EOCConnectionState3DisConnected,
    +EOCConnectionState3DisConnecting,
    +EOCConnectionState3Connected,
    +

    };

    +
    1
    2
    3
    4

    - 枚举使用

    .h文件
    +

    #import <UIKit/UIKit.h>

    +

    typedef enum EOCConnectionState EOCConnectionState;

    +

    typedef NS_ENUM(NSUInteger, EOCConnectionState3) {

    +
    EOCConnectionState3DisConnected,
    +EOCConnectionState3DisConnecting,
    +EOCConnectionState3Connected,
    +

    };

    +
  • +
+
@interface EOCEnum : NSObject
+@property (nonatomic, assign) enum EOCConnectionState state;
+@property (nonatomic, assign) EOCConnectionState3 state3;
+@end
+
+
1
2

.m 文件中
+ +#import "EOCEnum.h" + +@implementation EOCEnum + +- (void)testEnum { + // 未使用typedef 关键字 + enum EOCConnectionState status = EOCConnectionStateConnected; + NSLog(@"%u", status); + + // 使用typedef 关键字 + EOCConnectionState status1 = EOCConnectionStateConnecting; + NSLog(@"%u", status1); + + UIViewAutoresizing resizing = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + if (resizing & UIViewAutoresizingFlexibleWidth) { + // 设置了 UIViewAutoresizingFlexibleWidth 约束 + } +} + +// UIKit 试图所支持的设置显示方向 +- (UIInterfaceOrientationMask)supportedInterfaceOrientations { + return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft; +} + +// 枚举使用 +- (void)userEnum { + switch (_state3) { + case EOCConnectionState3DisConnected: + // ... + break; + + case EOCConnectionState3DisConnecting: + // ... + break; + + case EOCConnectionState3Connected: + // ... + break; + + default: // 最好不用,防止以后增加一种状态机之后,编译器不发出警告 + break; + } +} + +@end +``` +

要点:

+
    +
  1. 使用枚举表示状态机的状态、传递给方法的选项以及状态码等值,给枚举值起名时要注重易懂
  2. +
  3. 如果一个类型可以使用状态机的状态来表示,并且多个选项可同时使用,那么就定义为可选的枚举类型,枚举各值定义为2的幂,以便通过“按位或”操作组合
  4. +
  5. 使用NS_ENUM 和 NS_OPTIONS 宏来定义枚举,并指明底层的数据类型。这样可以确保枚举是用开发者所选的底层数据类型实现出来的,而不会采用编译器所选的类型
  6. +
  7. switch语句处理枚举类型时不要实现default 分支,如果以后加入枚举的新状态之后,编译器会发出警告
  8. +
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/03/\344\270\200\350\207\252\345\212\250\345\274\225\347\224\250\350\256\241\346\225\260/index.html" "b/2019/06/03/\344\270\200\350\207\252\345\212\250\345\274\225\347\224\250\350\256\241\346\225\260/index.html" new file mode 100644 index 0000000..b09963a --- /dev/null +++ "b/2019/06/03/\344\270\200\350\207\252\345\212\250\345\274\225\347\224\250\350\256\241\346\225\260/index.html" @@ -0,0 +1,246 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

自动引用计数

+

什么是自动引用计数

内存管理中对引用采取自动计数的技术

+

内存管理

    +
  • 自己生成的对象,自己所持有
      +
    • alloc
    • +
    • new
    • +
    • copy
    • +
    • mutableCopy
    • +
    +
  • +
  • 非自己生成的对象,自己也能持有
      +
    • retain
    • +
    +
  • +
  • 不再需要自己持有的对象时释放
      +
    • release
    • +
    +
  • +
  • 非自己持有的对象无法释放

    +
  • +
  • 废弃对象

    +
      +
    • dealloc
    • +
    +
  • +
+

__strong

__strong 修饰符表示对对象的“强引用”

+

__unsafe_unretained 修饰符

__unsafe_unretained 是不安全的所有权修饰符, 被它修饰的变量不属于编译器的内存管理对象

+
1
id __unsafe_unretained objc = [[NSObject alloc] init];
+

Xcode 提示:Assigning retained object to unsafe_unretained variable; object will be released after assignment

+

将保留对象赋给unsafe_unretain变量;对象将在赋值后释放

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
id __unsafe_unretained obj1 = nil;

{
id __unsafe_unretained obj = [[NSObject alloc] init];

// 自己生成并持有对象
// 因为obj0 变量是__strong,强引用,所以自己持有对象
id __strong obj0 = [[NSObject alloc] init];

// obj0 变量赋值给你obj1
// ojb1 变量不持有对象的强应用,也不持有弱引用
obj1 = obj0;


// 输出 obj1 变量表示的对象
NSLog(@"A: %@", obj1);
} // obj0变量 超出了其作用域,强引用失效自动释放自己持有的对象,除此之外没有其他持有者,所以废弃该对象




// 输出 obj1 变量表示的对象
// obj1 变量表示的对象因为无持有者已经销毁,坏的内存访问
NSLog(@"B: %@", obj1);
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/03/\347\254\254\344\270\203\347\253\240Blocks\351\235\242\350\257\225/index.html" "b/2019/06/03/\347\254\254\344\270\203\347\253\240Blocks\351\235\242\350\257\225/index.html" new file mode 100644 index 0000000..bb162bc --- /dev/null +++ "b/2019/06/03/\347\254\254\344\270\203\347\253\240Blocks\351\235\242\350\257\225/index.html" @@ -0,0 +1,296 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

Blocks

Block 是什么?

Blocks 是 C 语言的扩充功能,将函数及其执行上下文封装起来的对象,带有自动变量(局部变量)的匿名函数

+
    +
  • 因为block 底层C++ 结构体中存在着 isa 指针,所以是一个对象
  • +
  • 因为block 底层C++ 结构体中存在着一个函数指针 FuncPtr,所以是一个函数
  • +
+

什么是Block 调用

Block 的调用就是函数的调用
在终端使用 clang 编译之后查看文件内容可得出以下结论:

+
    +
  • 对其进行一个强制转换,转换之后取出成员变量 FuncPtr
  • +
+

截获变量

以下代码运行结果是 12

+

+
原因:block 对基本数据类型的局部变量会截获其值,在定义 block 时以值的方式传到了block 对应的结构体当中,而调用block 时直接使用的是block对应结构体当中已经传过来的那个值,所以值为12
+
    +
  • 局部变量截获

    +
      +
    • 基本数据类型的局部变量截获其值
    • +
    • 对象类型的局部变量连同所有权修饰符一起截获
    • +
    +
  • +
  • 局部静态变量: 已指针形式截获

    +
  • +
  • 全局变量:不截获

    +
  • +
  • 静态全局变量: 不截获

    +
  • +
+

以下代码运行结果是 8

+

+
原因:block 对局部静态变量会以指针形式截获,所以值为8
+

__block 修饰符

    +
  • __block 修饰后的变量最后变成了对象,底层c++ 结构体中存在isa指针
  • +
  • 一般情况下,对被截获变量进行赋值操作需要添加block 修饰符,而操作是不需要添加block 修饰符的
  • +
  • 不需要__block 修饰符,全局变量和静态全局变量 block 是不截获的,所以不需要修饰,静态局部变量是通过指针截获的,修改外部的变量,也不需要添加
      +
    • 静态局部变量
    • +
    • 全局变量
    • +
    • 静态全局变量
    • +
    +
  • +
+

不需要添加__Block 修饰符

+
1
2
3
4
5
6
7
8
- (void)test {
NSMutableArray *array = [NSMutableArray array];
void(^block)(void) = ^{
[array addObject:@1234];
};

block();
}
+

需要添加__Block 修饰符

+
1
2
3
4
5
6
7
8
- (void)test {
__block NSMutableArray *tempArray = nil;
void(^block)(void) = ^{
tempArray = [NSMutableArray array];
};

block();
}
+

以下代码运行结果是 8

+
1
2
3
4
5
6
7
8
- (void)test__Block3 {
__block int multiplier = 6;
int(^Block)(int) = ^int(int num){
return multiplier * num;
};
multiplier = 4;
NSLog(@"__block--test__Block3---result is %d", Block(2));
}
+

原因

+

+

+

栈上Block 的block 变量中的 forwarding 指针指向的是自身

+

Block 的内存管理

Block 的 内存分布

+

+

Block 的 Copy 操作

+

+
    +
  • 全局block
  • +
  • 堆block
  • +
  • 栈block
  • +
+

Block 循环引用

    +
  • 如果当前block对当前对象的某一变量进行截获的话,block 会对对应变量有一个强引用,当前block因为当前的对象对其有个强引用,产生了自循环引用,通过声明为__weak变量来解决
  • +
  • 如果定义了__block 修饰符的话也会产生循环引用,在ARC 下会产生循环引用,而在MRC 下不会,在ARC 下通过打破环来消除,但是存在弊端时当block 没有机会执行时,
  • +
+

循环引用1

+

解决方法:

+

+

大环循环引用2

+

+

以上代码,在MRC 下,不会产生循环引用,在ARC 下,会产生循环应用,引起内存泄露

+

解决方法

+

如果block 没有机会执行,那么循环引用将不会被打破

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/03/\347\254\254\344\270\211\347\253\240UI\350\247\206\345\233\276\345\223\215\345\272\224\351\223\276\347\246\273\345\261\217\346\270\262\346\237\223\351\235\242\350\257\225/index.html" "b/2019/06/03/\347\254\254\344\270\211\347\253\240UI\350\247\206\345\233\276\345\223\215\345\272\224\351\223\276\347\246\273\345\261\217\346\270\262\346\237\223\351\235\242\350\257\225/index.html" new file mode 100644 index 0000000..e4d9426 --- /dev/null +++ "b/2019/06/03/\347\254\254\344\270\211\347\253\240UI\350\247\206\345\233\276\345\223\215\345\272\224\351\223\276\347\246\273\345\261\217\346\270\262\346\237\223\351\235\242\350\257\225/index.html" @@ -0,0 +1,289 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

UITableView 相关

    +
  • 重用机制
  • +
  • 并发访问,数据拷贝问题:主线程删除了一条数据,子线程数据由删除之前的数据源copy而来,子线程网络请求,数据解析,预排版等操作后,回到主线程刷新时已经删除的数据又会重现

    +
      +
    • 主线程数据源删除时对数据进行记录,子线程的数据回来后再进行一次同步删除操作,然后再刷新数据

      +
    • +
    • 使用GCD中的串行队列,将数据删除和数据解析在此队列中进行

      +
    • +
    +
  • +
+

UIView 和CALayer 的关系

    +
  • UIView 为 CALayer 提供内容,负责处理触摸事件,参与响应链
  • +
  • CALayer 负责显示内容,分工明确,UIView 的Layer 实际上就是CALayer, backgroundColor 等属性本质是对CALayer 做了一层包装
  • +
+

事件传递

+
    +
  • hitTest 方法是返回响应时间的试图
  • +
  • pointInside withEvent 方法判断点击位置是否在当前视图范围内
  • +
  • 倒序遍历最终响应事件的试图,最后添加的试图会最优先被遍历到
  • +
+

需求案例

+

只让正方形的view中圆形区域内可以响应事件,四个角的位置不响应事件

+
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
class CircularAreaResponseButton: UIButton {

override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if self.isHidden || !self.isUserInteractionEnabled || self.alpha <= 0.01 {
return nil
}

var hitView: UIView?
if self.point(inside: point, with: event) {
// 遍历当前视图的子试图
for subView in subviews {
// 坐标转换
let convertPoint = self.convert(point, to: subView)
if let hitTestView = subView.hitTest(convertPoint, with: event) {
// 找到了最终响应事件的试图
hitView = hitTestView
break;
}
}

if hitView != nil {
return hitView
}
return self
}
return nil
}


override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
let x1 = point.x
let y1 = point.y

let x2 = self.frame.width / 2.0
let y2 = self.frame.height / 2.0
// 使用平面内两点见距离公式计算距离, 并且判断是否 <= 圆的半径
return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)) <= x2
}
}
+

视图响应

UIView 都继承于 UIResponder,都有以下方法

+
1
2
3
4
func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?)
func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?)
func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?)
+

如果事件沿着视图响应链一直传递到 UIApplicationDelegate 仍然没有任何一个视图处理事件,程序将会发生什么?

+
    +
  • 忽略掉了这个事件当作什么都没有发生
  • +
+

图像显示原理

CALayer 的 contents 是位图

+

+
    +
  • CPU的工作

    +

    Layout | Display | Prepare | Commite |
    ———|———–|————|———|
    UI布局 | 绘制 | 图片编解码 | 提交位图 |
    文本计算 |

    +
  • +
  • GPU渲染管线

    +

    定点着色 | 图元转配 | 光栅化 | 片段着色 | 片段处理 |
    ——–|——–|——-|———|———|

    +
  • +
+
提交到帧缓冲区(frameBuffer)
+

UI卡顿掉帧的原因

1/60 ms 时间内, 由cpu 和GPU 协同产生最终的数据,当 CPU UI布局,文本计算所用的时间较长时,留给GPU渲染,提交到帧缓冲区的时间就会变短,这样以来就会发生掉帧情况

+

UIView 的绘制原理

CPU优化方案

+
    +
  • 对象的创建、调整、销毁放在子线程,节省cpu的一部分时间
  • +
  • 预排版(布局计算,文本计算)放在子线程中
  • +
  • 预渲染(文本等异步绘制,图片编解码)等
  • +
+

GPU优化方案

+

在屏渲染

指GPU的渲染操作是在当前用于显示的屏幕缓冲区域中进行

+

离屏渲染

指GPU在当前屏幕缓冲区域外新开劈了一个缓冲区域进行渲染操作
指定了UI视图图层的某些属性,标记为视图在未预合成之前不能用于在当前屏幕上直接显示的时候就会发生离屏渲染,例如图层的圆角设置和蒙层设置

+
    +
  • 离屏渲染何时触发

    +
      +
    • 设置layer 的圆角并且和 maskToBounds 一起使用时
    • +
    • 图层蒙版
    • +
    • 阴影
    • +
    • 光栅化
    • +
    +
  • +
  • 为何要避免离屏渲染

    +
      +
    • 离屏渲染,会增加 GPU 的工作量,GPU工作量的增加就很有可能导致CPU和GPU的总耗时超过1/60s,导致UI卡顿和掉帧
    • +
    +
  • +
  • 避免离屏渲染

    +
  • +
+

###异步绘制

+

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/03/\347\254\254\344\271\235\347\253\240Runloop\351\235\242\350\257\225/index.html" "b/2019/06/03/\347\254\254\344\271\235\347\253\240Runloop\351\235\242\350\257\225/index.html" new file mode 100644 index 0000000..e6657d5 --- /dev/null +++ "b/2019/06/03/\347\254\254\344\271\235\347\253\240Runloop\351\235\242\350\257\225/index.html" @@ -0,0 +1,218 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/03/\347\254\254\344\272\224\347\253\240Runtime\347\233\270\345\205\263\351\235\242\350\257\225/index.html" "b/2019/06/03/\347\254\254\344\272\224\347\253\240Runtime\347\233\270\345\205\263\351\235\242\350\257\225/index.html" new file mode 100644 index 0000000..82deba0 --- /dev/null +++ "b/2019/06/03/\347\254\254\344\272\224\347\253\240Runtime\347\233\270\345\205\263\351\235\242\350\257\225/index.html" @@ -0,0 +1,303 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

Runtime 相关问题

数据结构

objc_object 结构体

objc_class

对象、类对象、元类对象的区别

    +
  • 类对象存储实例方法列表等信息
  • +
  • 元类对象存锤类方法列表等信息
  • +
+

消息传递

void objc_msgSend(void /* id self, SEL op, ... */)

+

经过编译器转变后
[self class] <==> objc_msgSend(self, @selector(class))
第一个参数是消息传递的接收者self, 第二个参数是传递的消息名称(选择器)

+

void objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */)
第一个参数是一个结构体指针

+

//
struuch objc_super {
__unsafe_unretained id receiver; // 就是当前的对象
}

+

[superclass] <==> objc_msgSendSuper(super, @selector(class))

+

无论调用[super class] 还是 [self class] 最终这条消息的接收者都是当前对象

+

图1

+

+

消息传递过程

图2

+

+

问题1

+

+

打印结果都是 Phone

+

原因:

+
    +
  • 无论调用[super class] 还是 [self class] 最终这条消息的接收者都是当前对象
  • +
  • [self class] 在Phone的类对象中查找class 方法,没有去父类查找,父类也没有,如图1顺次往上查找到根类,调用根类中class 的具体实现
  • +
  • [super class]只是跨越了当前类,直接从当前类的父类中查找 class 方法,父类中没有class 方法,如图1所示只能继续往上查找到根类对象,在NSObject 中有class 方法实现,所以接受者任然是当前的对象
  • +
+

缓存查找

根据给定的SEL(方法选择器)通过一个函数来映射出bucket_t 在数组当中的位置,本质上是哈希查找

+

+

当前类中查找

当前类中存在着对应的方法列表

+
    +
  • 对于已经排序好的列表,采用二分查找算法查找对应的执行函数
  • +
  • 对于没有排序的列表,采用一般遍历查找方法查找对应的执行函数
  • +
+

父类逐级查找

图3

+
    +
  • 最关键是通过当前类的 superclass 成员变量去查找父类,或者访问父类
  • +
  • curClass 的父类是否为 nil?如果是NSObject 类,他的父类为空,所以会结束查找
  • +
  • 如果当前类有父类就要去该父类的缓存中查找,如果在缓存中命中,那么就结束查找,如果缓存中没有,就需要遍历当前类的父类的方法列表,如果没有就遍历父类的父类查找,沿着superclass指针逐级向上查找,直到查找到NSObject,再去Superclass。如果还是没有为nil时就结束父类逐级查找
  • +
+

消息转发

resolveInstanceMethod:

    +
  • 该方法是类方法不是实例方法,参数是SEL 方法选择器类型
  • +
  • 返回值是一个BOOL值
  • +
  • 告诉系统是否要解决当前实例方法的实现
  • +
  • 如果返回YES,结束消息转发
  • +
  • 如果返回NO,系统会给第二次机会处理这个消息,会回掉forwardingTargetForSelector:
  • +
+

forwardingTargetForSelector:

    +
  • 参数是 SEL 方法选择器
  • +
  • 返回值是 id 类型,说明有哪个对象来处理,转发的对象是谁,如果返回了转发目标就会结束当前调用
  • +
  • 如果返回为nil,系统会调用 methodSignatureForSelector:方法,最后一次机会处理这个消息
  • +
+

methodSignatureForSelector:

    +
  • 参数是 SEL 方法选择器
  • +
  • 返回值是一个对象,实际上这个对象是对methodSignatureForSelector 方法的参数,参数个数,参数类型和返回值的包装
  • +
  • 如果返回一个方法签名的话,就会调用 forwardInvocation: 方法
  • +
  • 如果返回为nil,标记为消息无法处理,程序crash
  • +
+

forwardInvocation:

    +
  • 如果此方法不能处理消息,程序crash
  • +
+

代码示例

+

.h 文件

+
1
2
3
4
@interface RunTimeObject : NSObject
// 只声明,不实现,验证消息转发机制
- (void)test;
@end
+

.m 文件

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
@implementation RunTimeObject

// 实现消息转发流程 中的方法
/**
是否要解决当前实例方法的实现
*/
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(test)) {
NSLog(@"resolveInstanceMethod:");
// 如果返回YES,消息转发就会结束
// return YES;

return NO;
} else {

NSLog(@"super resolveInstanceMethod:");
// 返回父类的默认调用
return [super resolveInstanceMethod:sel];
}
}


/**
- 参数是 SEL 方法选择器
- 返回值是 id 类型,说明有哪个对象来处理,转发的对象是谁,如果返回了转发目标就会结束当前调用
- 如果返回为nil,系统会调用 methodSignatureForSelector:方法,最后一次机会处理这个消息
*/
- (id)forwardingTargetForSelector:(SEL)aSelector {
NSLog(@" forwardingTargetForSelector: ");
return nil;
}

/**
- 参数是 SEL 方法选择器
- 返回值是一个对象,实际上这个对象是对`methodSignatureForSelector` 方法的参数,参数个数,参数类型和返回值的包装
- 如果返回一个方法签名的话,就会调用 forwardInvocation: 方法
- 如果返回为nil,标记为消息无法处理,程序crash

*/
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if (aSelector == @selector(test)) {
NSLog(@"methodSignatureForSelector:");
/**
v: 表示这个方法返回值是 void

固定参数@: 表示参数类型是 id, 即self
固定参数`:` : 表示参数类型是选择器类型,即 @selector(test)
*/
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
} else {

NSLog(@"super methodSignatureForSelector:");
// 返回父类的默认调用
return [super methodSignatureForSelector:aSelector];
}
}


- (void)forwardInvocation:(NSInvocation *)anInvocation {
NSLog(@"forwardInvocation:");
}

@end
+

调用

+
1
2
3
4
- (void)runTiemTest {
RunTimeObject *obj = [[RunTimeObject alloc] init];
[obj test];
}
+

结果:

+
1
2
3
4
5
resolveInstanceMethod:
forwardingTargetForSelector:
methodSignatureForSelector:
super resolveInstanceMethod:
forwardInvocation:
+

为类动态添加方法

methond Swizzling

+

使用场景

    +
  • 页面中的进出添加统计信息,使用 methond Swizzling 替换viewillAppear 方法
  • +
+

动态添加方法

    +
  • perforSelector: 实际上是考察class_addMethod 的方法使用
  • +
+

动态方法解析

    +
  • @dynamic关键字
  • +
  • 动态运行时语言将函数决议推迟到运行时
  • +
  • 编译时不生成setter/getter 方法,而是在运行时才添加具体的执行函数
  • +
+

问题

[obj foo] 和 obj_msgSend()函数之间有什么关系?

    +
  • [obj foo]经过编译器处理过后会变成 obj_msgSend()有两个参数,一个是obj,第二个参数是foo 选择器
  • +
+

runtime 如何通过Selector找到对应的IMP地址的?

消息传递机制

+

能否向编译后的类增加实例变量

编译之前就完成了实例变量的布局,
runtime_r_t 表示是readonly 的,编译后的类是不能添加实例变量的,可以向动态添加的类中添加实例变量

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/03/\347\254\254\345\205\253\347\253\240\345\244\232\347\272\277\347\250\213\351\235\242\350\257\225/index.html" "b/2019/06/03/\347\254\254\345\205\253\347\253\240\345\244\232\347\272\277\347\250\213\351\235\242\350\257\225/index.html" new file mode 100644 index 0000000..9809872 --- /dev/null +++ "b/2019/06/03/\347\254\254\345\205\253\347\253\240\345\244\232\347\272\277\347\250\213\351\235\242\350\257\225/index.html" @@ -0,0 +1,215 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/03/\347\254\254\345\205\255\347\253\240\345\206\205\345\255\230\347\256\241\347\220\206\351\235\242\350\257\225/index.html" "b/2019/06/03/\347\254\254\345\205\255\347\253\240\345\206\205\345\255\230\347\256\241\347\220\206\351\235\242\350\257\225/index.html" new file mode 100644 index 0000000..cfacd80 --- /dev/null +++ "b/2019/06/03/\347\254\254\345\205\255\347\253\240\345\206\205\345\255\230\347\256\241\347\220\206\351\235\242\350\257\225/index.html" @@ -0,0 +1,298 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

内存管理

内存布局

+
    +
  • stack 栈是从高地址向低地址扩展,所以栈是向下增长的,对象和block copy 后都会放在堆上
  • +
  • heap 堆区向上增长
  • +
+
    +
  • 散列表方式

    +
      +
    • 自旋锁
        +
      • 自旋锁是“忙等” 的锁
      • +
      +
    • +
    • 引用计数表
    • +
    • 弱应用表
    • +
    +
  • +
+

内存管理方案

    +
  • 小对象使用 TaggedPointer
  • +
  • 64 位架构下面使用的是 NONPONINT_ISA 内存管理方案,非指针型的 isa,内部存储了一些
  • +
  • 散列表结构 (Side Tables() 结构)
      +
    • 自旋锁
    • +
    • 引用计数表
    • +
    • 弱引用表
    • +
    • Side Tables() 结构
    • +
    +
  • +
+

为什么不是一个SideTable,而是多个组成了Side Tables

如果所有对象的存储都放在一张大表当中,因为每个对象是在不同的线程中创建的,要操作其中一个对象时,需要将表加锁处理保证数据安全,要等锁释放之后才能操作下一个对象,此时存在效率问题,引入分离锁技术方案

+
    +
  • 分离锁:把引用计数表分成多张表进行,
  • +
  • Side Tables 本质是一张Hash 表
  • +
+

+

自旋锁

是“忙等”的锁,如果当前锁已被其他线程获取,当前线程会不断探测是否被释放,会第一时间获取,而其他锁比如信号量当它获取不到锁时,会把自己的线程阻塞休眠,等到其他线程释放这个锁时再唤醒这个锁

+
    +
  • 适用于轻量访问
  • +
+

引用计数表

使用过Hash表来实现,hash查找提高了查找高效率,插入和获取是通过Hash 算法来是实现的,避免了for循环

+

alloc 实现

经过一系列调用,最终调用了C函数 calloc, 并没有引用计数+1

+

retain 实现

底层经过了2次Hash表查找

+

+

release 实现

和 retain 实现相反

+

dealloc 实现原理

+

+

objc_destructInstance() 实现

+

clearDeallocating() 实现

+

MRC

手动引用计数

+

ARC

    +
  • 编译器在对应位置自动插入retain 和 release 操作,并和runtime协作达到自动引用计数管理内存的效果

    +
  • +
  • ARC 中禁止手动调用 retain/release

    +
  • +
  • ARC 中新增 weeak、strong 等关键字
  • +
+

弱引用管理

被声明为__weak 的对象指针经过编译后会进过调用以下方法,在最终的weak_register_no_lock() 方法中进行弱引用变量的添加。添加的位置是通过Hash算法查找的

+

+

weak 修饰的对象,释放时置为nil 如何实现的

当一个对象被 dealloc 之后,在dealloc内部实现当中会调用弱引用清除的相关函数,在相关函数当中会根据当前对象指针查找弱引用表,把当前对象下对应的弱应用都取出,遍历弱应用指针并置为nil。

+

自动释放池

是以栈为结点通过双向链表的形式组合而成,和线程一一对应

+

Autoreleasepool

+

循环引用

自循环引用

一个对象中有拥有一个 强持有他的 obj,如果给 obj 赋值为原对象就会造成自循环引用

+

相互循环引用

    +
  • 代理
  • +
  • Block
  • +
  • NSTimer
  • +
  • 大环多循环
  • +
+

多循环引用

+

解决循环引用的方案

__weak

解决相互循环引用

+

__block

+

__unsafe_unretained

一般不建议使用

+

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/03/\347\254\254\345\215\201\344\270\200\347\253\240\350\256\276\350\256\241\346\250\241\345\274\217\351\235\242\350\257\225/index.html" "b/2019/06/03/\347\254\254\345\215\201\344\270\200\347\253\240\350\256\276\350\256\241\346\250\241\345\274\217\351\235\242\350\257\225/index.html" new file mode 100644 index 0000000..7af59b8 --- /dev/null +++ "b/2019/06/03/\347\254\254\345\215\201\344\270\200\347\253\240\350\256\276\350\256\241\346\250\241\345\274\217\351\235\242\350\257\225/index.html" @@ -0,0 +1,242 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

第十一章 设计模式

+

六大设计原则

单一职责原则:一个类只负责一件事

+

开闭原则: 对修改关闭,对扩展开放

+

接口隔离原则:使用多个专门的协议,而不是一个庞大臃肿的协议,iOS系统的UITableView的数据和代理协议分开

+

依赖倒置原则:抽象不应该依赖于具体实现,具体实现可以依赖于抽象

+

里氏替换原则:父类可以被子类无缝替换,且原有功能不受任何影响,iOS系统 的 KVO 机制遵守了这一原则,当调用 addObserver 方法时,因为系统在动态运行时为我们创建了一个子类,直观感受是仍然使用的是父类,实际上系统已经替换为创建的子类,所以KVO 机制 不但使用了管擦这模式,并且遵从了 里氏替换原则

+

迪米特法则: 一个对象应对其他对象有尽可能少的了解,平时所说的高内聚,低耦合

+

常用设计模式

责任链

有三个业务分别为A/B/C,以前的调用顺序为 A -> B -> C ,现在顺序有调整,

+

定义某一个类,有一个成员变量他的类型跟原有类类型一致,组成一个责任链,让处理具体业务的责任者去处理

+

+

桥接模式

业务解耦的问题:同一个列表和多套数据显示,多套数据通过后端来控制是共存,

+

+

类构成

+

抽象类A ——> class B

+

+

适配器模式

一个现有类需要适应变化的问题, 年代比较久远的类或者对象

+

类构成

+
    +
  • 对象适配
  • +
  • 类适配
  • +
+

单例模式

命令模式

行为参数化

+

降低代码重合度

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/03/\347\254\254\345\215\201\344\270\211\347\253\240\347\256\227\346\263\225\351\235\242\350\257\225/index.html" "b/2019/06/03/\347\254\254\345\215\201\344\270\211\347\253\240\347\256\227\346\263\225\351\235\242\350\257\225/index.html" new file mode 100644 index 0000000..0bb7fa5 --- /dev/null +++ "b/2019/06/03/\347\254\254\345\215\201\344\270\211\347\253\240\347\256\227\346\263\225\351\235\242\350\257\225/index.html" @@ -0,0 +1,215 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/03/\347\254\254\345\215\201\344\272\214\347\253\240\346\236\266\346\236\204\345\222\214\346\241\206\346\236\266\351\235\242\350\257\225/index.html" "b/2019/06/03/\347\254\254\345\215\201\344\272\214\347\253\240\346\236\266\346\236\204\345\222\214\346\241\206\346\236\266\351\235\242\350\257\225/index.html" new file mode 100644 index 0000000..9a845f9 --- /dev/null +++ "b/2019/06/03/\347\254\254\345\215\201\344\272\214\347\253\240\346\236\266\346\236\204\345\222\214\346\241\206\346\236\266\351\235\242\350\257\225/index.html" @@ -0,0 +1,293 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

第十二章 架构和框架

+

目的

+
    +
  • 实现模块化
  • +
  • 分层
  • +
  • 解耦
  • +
+

图片缓存

图片缓存框架设计方案?

+

图片缓存时内存设计上需要考虑的问题

    +
  • 内存存储的size

    +
      +
    • 10kb的图片开辟的内存空间 50 张
    • +
    • 100kb图片的 20 张
    • +
    • 100kb 以上的 10 张
      通过队列的方式存储,先进先出的方式淘汰
    • +
    +
  • +
  • 淘汰策略

    +
      +
    • 以队列先进先出的方式淘汰
    • +
    • 已LRU 算法 (如 30 min 之内是否使用过)
    • +
    +
  • +
+

磁盘设计考虑的问题

    +
  • 存储方式
  • +
  • 大小限制
  • +
  • 淘汰策略(超过 7 天)
  • +
+

网络部分的设计需要考虑的问题

    +
  • 图片请求最大并发量
  • +
  • 请求超时策略,一旦超时了,可以重试,如果重试2次失败,是否还下载
  • +
  • 请求优先级,下载或者缓存的图片是否紧急需要
  • +
+

图片通过什么方式进行读写,过程是怎样的?

    +
  • 以图片的url 的单向哈希值作为key 存储
  • +
  • 读取过程
  • +
+

+

图片解码

    +
  • 对于不同格式的图片,解码采用什么方式来做?

    +
      +
    • 应用策略模式对于不同图片格式进行解码
    • +
    +
  • +
  • 在那个阶段做图片解码处理?

    +
      +
    • 再磁盘读取后未解码或者网络请求返回后,解码后再放到内存中,可以减轻主线程的压力,解码在主线程中
    • +
    +
  • +
+

线程处理

+

阅读时长统计

怎样设计一个时长统计框架?

+

为何要有不同类型的记录器,处于什么考虑?

基于不同分类场景提供的关于记录的封装、适配

+

记录的数据由于程序杀死或者断电,关机等丢失,你是怎样处理的?

    +
  • 定时写磁盘
  • +
  • 限定内存缓存条数,超过该条数就写如磁盘
  • +
+

记录器上传 中延时上传的具体场景有哪些?上传时机如何把握?

上传时机

+
    +
  • 立刻上传
  • +
  • 延时上传
  • +
  • 定时上传
  • +
+

延时上传场景

+

统计结果立即上传会重复调用接口导致资源浪费

+
    +
  • 前后台切换
  • +
  • 从无网到有网的变化时
  • +
  • 通过其他的接口捎带着上传
  • +
+

复杂页面的架构

MVVM 框架思想

+

RN 的数据流思想

+

任何一个子节点是没有权力做自己的变化更新的,它必须把这个消息传递给根结点,根结点通过自顶向下的遍历查找需要更新的节点

+

系统UIView 更新机制的思想

AsyncDisplayKit 关于预排版的设计思想

客户端整体架构

需要独立于APP 的通用层
通用的业务层
中间层
业务层

+

+

业务之间的解耦通信方式

+
    +
  • OpenURL
  • +
  • 依赖注入方式, 在中间层通过代理 的方式实现业务A 和业务B之间的通信
  • +
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/03/\347\254\254\345\215\201\345\233\233\347\253\240\347\254\254\344\270\211\346\226\271\345\272\223\351\235\242\350\257\225/index.html" "b/2019/06/03/\347\254\254\345\215\201\345\233\233\347\253\240\347\254\254\344\270\211\346\226\271\345\272\223\351\235\242\350\257\225/index.html" new file mode 100644 index 0000000..7feef9d --- /dev/null +++ "b/2019/06/03/\347\254\254\345\215\201\345\233\233\347\253\240\347\254\254\344\270\211\346\226\271\345\272\223\351\235\242\350\257\225/index.html" @@ -0,0 +1,242 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

第十四章 第三方库

+

AFNetworking

框架图

会话NSURSession

+

主要类关系图

+

AFURLSessionManager

    +
  • 创建和管理 NSURLSession, NSURLSessionTask
  • +
  • 实现NSURLSessionDelegate 等协议的代理方法
  • +
  • 引入AFSecurityPolicy 保证请求安全,https 请求时 证书检验,公钥验证
  • +
  • 引入AFNetworkReachabilityManager 监控网络状态
  • +
+

SDWebImage

架构图

+

+

加载图片流程

+

Reactive Cocoa

+

RACStream

+

+

AsyncDisplayKit

主要解决的问题

+

+

基本原理

+

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/03/\347\254\254\345\215\201\347\253\240\347\275\221\347\273\234\347\233\270\345\205\263/index.html" "b/2019/06/03/\347\254\254\345\215\201\347\253\240\347\275\221\347\273\234\347\233\270\345\205\263/index.html" new file mode 100644 index 0000000..9473462 --- /dev/null +++ "b/2019/06/03/\347\254\254\345\215\201\347\253\240\347\275\221\347\273\234\347\233\270\345\205\263/index.html" @@ -0,0 +1,360 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

第十章 网络请求相关

+

HTTP协议

超文本传输协议

+

请求/响应报文

请求报文:

+
    +
  • 请求行
    方法 URL 协议版本(1.1版本) CRLF(回车)

    +
  • +
  • 首部字段区域
    首部字段名(key) :值(value )CRLF
    首部字段名(key) :值(value )CRLF

    +
  • +
  • 实体主体
    get 请求没有
    post 有实体主体

    +
  • +
+

响应报文:

+
    +
  • 响应行
    版本 状态码 状态码描述(短语) CRLF

    +
  • +
  • 首部字段区

    +
  • +
  • +
  • 响应实体主体
  • +
+

连接建立流程

三次握手和四次挥手

+

三次握手

+
    +
  • 客户端 发送 syn 同步报文到Server,
  • +
  • server 端收到syn报文后会,链接建立,server 端会返回一个 syn,ack 报文给客户端,
  • +
  • 客户端收到 server 端的ask 报文后,会再次回应一个确认ack 的报文
  • +
+

数据请求

+
    +
  • 接下来在已经建立的tcp 通道上进行http 的请求报文
  • +
  • server 端回复给客户端一个http响应报文
  • +
+

四次挥手

+
    +
  • 客户端发送 fin 终止报文到server 端
  • +
  • server 端收到报文后会回回复一个ack 报文到客户端
  • +
  • 客户端到server 端的tcp的链接已经断开,但是 server 端到客户端的链接还会传输数据
  • +
  • server 端向客户端发送一个fin ack 报文 ,然后客户端回应给server 一个ack 报文,释放链接
  • +
+

HTTP 的特点

    +
  • 无连接
    非持久链接 需要多次断开/建立链接,就会经历 三次握手,四次挥手

    +

    HTTP 的持久链接: 多个http请求在同一个tcp 通道上, 一定时间内不会断开tcp通道,提高了网络请求的效率

    +

    头部字段:

    +
    coonection : keep-alive 客户端是否采用持久链接
    +time :20 多长时间有效
    +max:10 最多发生多少个http请求和响应对
    +
  • +
  • 无状态
    同一个用户多次发送http请求,server 端是不知道是同一个用户的,server 端通过 Cookie/Session 规避这个问题

    +
      +
    • cookie
      cookie 主要是用来记录用户状态,区分用户,cookie由server端返回给客户端,cookie 状态保存在客户端

      +

      保证cookie 的安全

      +
        +
      • cookie 加密处理
      • +
      • 只在https 上携带Cookie
      • +
      • 设置Cookie为 httpOnly,防止跨站脚本攻击
      • +
      +
    • +
    • session 用来记录用户的状态,区分用户,状态存放在服务端

      +
    • +
    +
  • +
+

HTTPS 与网络安全

HTTPS 都使用了那些加密手段,为什么?

+
    +
  • 建立链接过程使用非对称加密,因为耗时
  • +
  • 后续通信数据传输过程中使用对称性加密
  • +
+

对称性加密

+

TCP: 传输控制协议

    +
  • 面向链接
    TCP 是全双工的所以数据传输之前需要三次握手,传输完毕需要四次挥手,
  • +
  • 可靠传输

    +
      +
    • 无差错
    • +
    • 按序到达
    • +
    • +
    +
  • +
  • 面向字节流

    +

    发送方发送数据时不管一次性提交给TCP的缓冲是多大的数据,TCP本身会根据实际情况来划分数据包再发送给接受数据方

    +
  • +
  • 流量控制

    +

    滑动窗口协议

    +
  • +
+

UDP : 用户数据协议

    +
  • 无连接
    发送数据时不需要建立链接,传输完毕也不需要释放链接

    +
  • +
  • 尽最大努力交付
    是不保证可靠传输的,

    +
  • +
  • 面向报文
    既不合并,也不拆分

    +
  • +
+

问题

GET 和post 请求的区别

    +
  • GET 获取资源的

    +
      +
    • 安全的: 不因该引起Server 端的任何状态变化的
    • +
    • 幂等的:同一个请求方法执行多次和执行一次的效果完全相同
    • +
    • 可缓存的:请求是可以缓存的,代理服务器可以缓存
    • +
    • 请求参数以? 分割拼接到URL 后面
    • +
    +
  • +
  • post 请求处理资源

    +
      +
    • 不安全的:
    • +
    • 非幂等的
    • +
    • 不可缓存的
    • +
    +
  • +
+

GET 请求参数有长度限制,是2048 个字符,post 一般没有限制

+

HTTPS 链接建立流程

客户端会发送服务端一个支持的加密算法列表
包括 TLS 的版本号 + 随机数C,
服务端再回给客户端一个证书
包括 商定的加密算法 后续首先通过非对称加密进行对称性加密的密钥传输,http的网络就通过被非对称密钥保护的对称密钥访问和传输数据

+

TCP 和 UDP的区别

tcp是面向链接的,并且支持可靠的传输,支持面向字节流, tcp 提供了流浪上的控制和拥塞的控制
UDP 提供了简单的复用/分用及差错检测的传输层的功能,

+

状态吗有哪些

    +
  • 1XX
  • +
  • 2XX:成功
  • +
  • 3XX:重定向
  • +
  • 4XX:客户端请求错误
  • +
  • 5XX: 服务器端请求错误
  • +
+

为什么需要三次握手,两次行不行?

解决同步请求链接建立时超时的情况,如果只有两次握手,

+

持久链接怎么判断一个请求是否结束

    +
  • 请求/响应报文的头部字段
  • +
+

响应报文 content-length: 1024 头部字段是由server 端返回数据大小,客户端根据所接收的数据的字节数是否达到了头部字段 cotent-length 的大小判断

+
    +
  • 通过post请求时server 端返回数据有可能需要多次响应才能返回,此时需要根据chunked 字段名 是否为空来判断
  • +
+

Charels 抓包原理是什么?

中间人攻击

+

正常步骤是 客户端 —- server

+

抓包:发送请求和响应请求都可以通过中间人修改再返回给某一端

+

客户端 —中间人—- server

+

DNS 解析

域名到IP地址的映射,DNS解析请求采用UDP数据报,且明文

+

NDS 解析存在的常见问题

    +
  • DNS 劫持问题
    钓鱼网站
  • +
  • DNS 解析转发问题
  • +
+

DNS 劫持和HTTP 的关系时怎么样的

没有关系,

+
    +
  • DNS 解析发在HTTP 建立连接之前
  • +
  • DNS 解析请求使用UDP数据报, 端口是53,跟HTTP没有任何关系
  • +
+

解决DNS 劫持问题

+
    +
  • httpDNS
  • +
  • 长连接
  • +
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/03/\347\254\254\345\233\233\347\253\240OC\350\257\255\350\250\200\347\211\271\346\200\247\351\235\242\350\257\225/index.html" "b/2019/06/03/\347\254\254\345\233\233\347\253\240OC\350\257\255\350\250\200\347\211\271\346\200\247\351\235\242\350\257\225/index.html" new file mode 100644 index 0000000..d57a74f --- /dev/null +++ "b/2019/06/03/\347\254\254\345\233\233\347\253\240OC\350\257\255\350\250\200\347\211\271\346\200\247\351\235\242\350\257\225/index.html" @@ -0,0 +1,330 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

OC 语言特性

分类

原理

由运行时来决议的,不同分类当中含有同名方法 谁最终生效取决于谁最终参与编译,最后参编译的同名分类方法会最终生效,假如分类方法中添加的方法和宿主类中的某一个方法名相同,分类中的方法会覆盖宿主类中的同名方法,覆盖是指在消息传递过程中优先查找数组靠前的元素,如果查找到了同名方法就直接调用,实际上宿主类的同名方法实现仍然是存在的,我们可可以通过一些手段调用到原有类的同名方法的实现

+

使用场景

    +
  • 申明私有方法,分类的 .m 文件中申明私有方法啊,对外不暴露
  • +
  • 对类中的代码进行抽取分解
  • +
  • 把Framework的私有方法公开化
  • +
+

特点

    +
  • 运行时决议,运行时通过runtime 才把分类中的方法添加到了宿主类上
  • +
  • 可以为系统类添加方法
  • +
  • 有多个分类中存在同名方法时,最后编译的方法会最先生效
  • +
  • 分类中有和宿主类同名方法时,底层中通过内存copy将分类方法“覆盖”宿主类方法
  • +
  • 名字相同的分类编译会报错
  • +
+

分类中可以添加的内容

    +
  • 实例方法
  • +
  • 类方法
  • +
  • 协议
  • +
  • 属性,实际上只是声明了 getter / setter 方法,并没有添加成员变量
  • +
+

关联对象技术

关联对象由 AssocicationsManager 管理并在 AssociationsHashMap 上存储
所有对象的关联内容都在同一个全局容器中

+
    +
  • 给分类添加成员变量,通过关联对象的方式
  • +
+

扩展

使用场景

    +
  • 声明私有属性
  • +
  • 声明私有方法
  • +
  • 声明私有成员变量
  • +
+

特点

    +
  • 编译时决议
  • +
  • 只以声明的形式存在,多数情况下寄生在宿主类的.m中
  • +
  • 不能为系统添加扩展
  • +
+

代理

    +
  • 代理设计模式,传递方式是一对一
  • +
  • 使用weak 避免循环引用
  • +
+

通知

使用观察者模式来实现的用于跨层传递消息的机制

+

KVO

KVO 是系统对观察者模式的又一实现,使用isa 混写技术(isa - swizzling)来动态运行时为某一个类添加一个子类重写了它的setter 方法,同时将原有类的isa指针指向了新创建的类上

+
    +
  • KVO 是OC对观察者模式的又一实现
  • +
  • apple 使用isa 混写技术(isa - swizzling)来实现KVO
      +
    • 给我A类注册一个观察者时本质上是调用系统的 Observer for keyPath 方法,系统会创建一个NSKVONotifiying_A 的类
    • +
    +
  • +
  • 使用 setter 方法设置值KVO才能生效
  • +
  • 使用setValue:forKey: 改变值KVO才能生效
  • +
  • 使用下划线的成员变量直接修改值需要手动添加KVO才能生效(增加 willChangeValueForKey 和 didChangeValueForKey 两个方法)
  • +
+

KVC

apple提供的键值编码技术

+
    +
  • (nullable id)valueForKey:(NSString *)key;
  • +
  • (void)setValue:(nullable id)value forKey:(NSString *)key;

    +

    以上两个方法中的key是没有任何限制的,只要知道对应的成员变量名称,就可以对私有的成员变量设置和操作,这违背了面向对象的编程思想

    +
  • +
+

(void)setValue:(nullable id)value forKey:(NSString *)key; 方法调用流程

+
    +
  • 先判断是否有跟key 相关的setter 方法,如果有就直接调用,结束调用
  • +
  • 如果没有再判断是否存在实例变量,如果存在直接给赋值,结束调用
  • +
  • 如果说实例变量不存在,会去调用 - (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key; 抛出异常
  • +
+

属性关键字

读写权限

    +
  • readonly

    +
  • +
  • readwrite 系统默认

    +
  • +
+

原子性

    +
  • atomic (系统默认)保证赋值和获取(对成员属性的直接获取和赋值)时线程安全,并不代表操作和访问,如果atomic修饰的一个数组,只能保证独具改数组的读取和赋值时线程安全,并不能保证对数据操作(增加/删除数组元素)时是安全的
  • +
  • nonatomic
  • +
+

引用技术

    +
  • assign
      +
    • 修饰基本数据类型,
    • +
    • 修饰对象时不改变其引用计数,
    • +
    • 会产生悬垂指针
    • +
    +
  • +
  • weak
      +
    • 不改变被修饰对象的引用计数,
    • +
    • 所指对象被释放之后指针自动置为nil
    • +
    +
  • +
+

两者区别:

+
    +
  • weak可以修饰对象,而assign 既可以修饰对象也可以修饰基本数据类型
  • +
  • assign 修饰的对象,对象释放后指针仍然指向原对象的内存地址,而weak 修饰的对象,释放后会自动置为nil
  • +
+

问题1

+

weak 修饰的对象为什么释放后会自动置为nil

+

问题2

+

@property (copy) NSMutableArray *array; 有什么问题

+
    +
  • 被copy修饰后,如果赋值过来的是NSMutableArray,copy之后就是NSArray,如果赋值过来的是NSAarray,copy 之后仍然是NSArray,有可能会调用array的添加元素方法会导致crash
  • +
+

copy 关键词

+
    +
  • 浅拷贝:内存地址的复制,让目标对象指针和源对象指向同一块内存空间,
      +
    • 会增加对象的引用计数
    • +
    • 并没有一个新的内存分配
    • +
    +
  • +
+
    +
  • 深拷贝:目标对象指针和源对象指针分别指向两块内容相同的内存空间
      +
    • 不会增加源对象的引用计数
    • +
    • 有新对象的内存分配
    • +
    +
  • +
+

MRC 重写 retain修饰的变量的setter 方法

1
2
3
4
5
6
7
8

- (void)setObj:(id)obj {
if (_obj != obj) {
[_obj release];
}
_obj = [obj retain];

}
+

判断是为了防止异常处理,如果不做 if 判断,当传入的 obj 对象正好是原来的_obj 对象,对原对象尽行releas 操作,实际上也会对传入的对象进行releas 操作进行释放,此时如果再通过obj指针访问废弃的对象时就会导致carsh

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2016/10/index.html b/archives/2016/10/index.html new file mode 100644 index 0000000..dd25617 --- /dev/null +++ b/archives/2016/10/index.html @@ -0,0 +1,238 @@ + + + + + + + + Archives: 2016/10 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2016/11/index.html b/archives/2016/11/index.html new file mode 100644 index 0000000..1e118ed --- /dev/null +++ b/archives/2016/11/index.html @@ -0,0 +1,333 @@ + + + + + + + + Archives: 2016/11 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + +
+
+ 2016 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2016/12/index.html b/archives/2016/12/index.html new file mode 100644 index 0000000..e0643ff --- /dev/null +++ b/archives/2016/12/index.html @@ -0,0 +1,257 @@ + + + + + + + + Archives: 2016/12 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2016/index.html b/archives/2016/index.html new file mode 100644 index 0000000..ae46205 --- /dev/null +++ b/archives/2016/index.html @@ -0,0 +1,376 @@ + + + + + + + + Archives: 2016 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + +
+
+ 2016 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2016/page/2/index.html b/archives/2016/page/2/index.html new file mode 100644 index 0000000..d67fa56 --- /dev/null +++ b/archives/2016/page/2/index.html @@ -0,0 +1,281 @@ + + + + + + + + Archives: 2016 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + +
+
+ 2016 +
+
+ + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2017/01/index.html b/archives/2017/01/index.html new file mode 100644 index 0000000..e2394da --- /dev/null +++ b/archives/2017/01/index.html @@ -0,0 +1,200 @@ + + + + + + + + Archives: 2017/1 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2017/02/index.html b/archives/2017/02/index.html new file mode 100644 index 0000000..dd10b61 --- /dev/null +++ b/archives/2017/02/index.html @@ -0,0 +1,238 @@ + + + + + + + + Archives: 2017/2 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2017/03/index.html b/archives/2017/03/index.html new file mode 100644 index 0000000..9f9e7de --- /dev/null +++ b/archives/2017/03/index.html @@ -0,0 +1,257 @@ + + + + + + + + Archives: 2017/3 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2017/04/index.html b/archives/2017/04/index.html new file mode 100644 index 0000000..9bb560e --- /dev/null +++ b/archives/2017/04/index.html @@ -0,0 +1,200 @@ + + + + + + + + Archives: 2017/4 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2017/11/index.html b/archives/2017/11/index.html new file mode 100644 index 0000000..51b5c71 --- /dev/null +++ b/archives/2017/11/index.html @@ -0,0 +1,200 @@ + + + + + + + + Archives: 2017/11 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2017/12/index.html b/archives/2017/12/index.html new file mode 100644 index 0000000..8a021e2 --- /dev/null +++ b/archives/2017/12/index.html @@ -0,0 +1,219 @@ + + + + + + + + Archives: 2017/12 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2017/index.html b/archives/2017/index.html new file mode 100644 index 0000000..8f23158 --- /dev/null +++ b/archives/2017/index.html @@ -0,0 +1,376 @@ + + + + + + + + Archives: 2017 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + +
+
+ 2017 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2017/page/2/index.html b/archives/2017/page/2/index.html new file mode 100644 index 0000000..7a14666 --- /dev/null +++ b/archives/2017/page/2/index.html @@ -0,0 +1,224 @@ + + + + + + + + Archives: 2017 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2018/01/index.html b/archives/2018/01/index.html new file mode 100644 index 0000000..25d5204 --- /dev/null +++ b/archives/2018/01/index.html @@ -0,0 +1,295 @@ + + + + + + + + Archives: 2018/1 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2018/02/index.html b/archives/2018/02/index.html new file mode 100644 index 0000000..aabffc7 --- /dev/null +++ b/archives/2018/02/index.html @@ -0,0 +1,200 @@ + + + + + + + + Archives: 2018/2 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2018/index.html b/archives/2018/index.html new file mode 100644 index 0000000..7e321db --- /dev/null +++ b/archives/2018/index.html @@ -0,0 +1,314 @@ + + + + + + + + Archives: 2018 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + +
+
+ 2018 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2019/06/index.html b/archives/2019/06/index.html new file mode 100644 index 0000000..4fa44f1 --- /dev/null +++ b/archives/2019/06/index.html @@ -0,0 +1,316 @@ + + + + + + + + Archives: 2019/6 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + +
+
+ 2019 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2019/06/page/2/index.html b/archives/2019/06/page/2/index.html new file mode 100644 index 0000000..5a844ce --- /dev/null +++ b/archives/2019/06/page/2/index.html @@ -0,0 +1,316 @@ + + + + + + + + Archives: 2019/6 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + +
+
+ 2019 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2019/06/page/3/index.html b/archives/2019/06/page/3/index.html new file mode 100644 index 0000000..6b70c26 --- /dev/null +++ b/archives/2019/06/page/3/index.html @@ -0,0 +1,316 @@ + + + + + + + + Archives: 2019/6 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + +
+
+ 2019 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2019/06/page/4/index.html b/archives/2019/06/page/4/index.html new file mode 100644 index 0000000..4ed4029 --- /dev/null +++ b/archives/2019/06/page/4/index.html @@ -0,0 +1,316 @@ + + + + + + + + Archives: 2019/6 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + +
+
+ 2019 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2019/index.html b/archives/2019/index.html new file mode 100644 index 0000000..8629928 --- /dev/null +++ b/archives/2019/index.html @@ -0,0 +1,316 @@ + + + + + + + + Archives: 2019 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + +
+
+ 2019 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2019/page/2/index.html b/archives/2019/page/2/index.html new file mode 100644 index 0000000..bbf7ec7 --- /dev/null +++ b/archives/2019/page/2/index.html @@ -0,0 +1,316 @@ + + + + + + + + Archives: 2019 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + +
+
+ 2019 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2019/page/3/index.html b/archives/2019/page/3/index.html new file mode 100644 index 0000000..4a5b305 --- /dev/null +++ b/archives/2019/page/3/index.html @@ -0,0 +1,316 @@ + + + + + + + + Archives: 2019 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + +
+
+ 2019 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2019/page/4/index.html b/archives/2019/page/4/index.html new file mode 100644 index 0000000..d989444 --- /dev/null +++ b/archives/2019/page/4/index.html @@ -0,0 +1,316 @@ + + + + + + + + Archives: 2019 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + +
+
+ 2019 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/index.html b/archives/index.html new file mode 100644 index 0000000..452a18f --- /dev/null +++ b/archives/index.html @@ -0,0 +1,316 @@ + + + + + + + + Archives | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + +
+
+ 2019 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/page/2/index.html b/archives/page/2/index.html new file mode 100644 index 0000000..b1f2a93 --- /dev/null +++ b/archives/page/2/index.html @@ -0,0 +1,316 @@ + + + + + + + + Archives | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + +
+
+ 2019 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/page/3/index.html b/archives/page/3/index.html new file mode 100644 index 0000000..b92b3e3 --- /dev/null +++ b/archives/page/3/index.html @@ -0,0 +1,316 @@ + + + + + + + + Archives | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + +
+
+ 2019 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/page/4/index.html b/archives/page/4/index.html new file mode 100644 index 0000000..a4fdcc1 --- /dev/null +++ b/archives/page/4/index.html @@ -0,0 +1,316 @@ + + + + + + + + Archives | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + +
+
+ 2019 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/page/5/index.html b/archives/page/5/index.html new file mode 100644 index 0000000..6f34856 --- /dev/null +++ b/archives/page/5/index.html @@ -0,0 +1,386 @@ + + + + + + + + Archives | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + +
+
+ 2018 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ 2017 +
+
+ + + + + + + + + + + + +
+ + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/page/6/index.html b/archives/page/6/index.html new file mode 100644 index 0000000..9639f85 --- /dev/null +++ b/archives/page/6/index.html @@ -0,0 +1,386 @@ + + + + + + + + Archives | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + +
+
+ 2017 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ 2016 +
+
+ + + + +
+ + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/page/7/index.html b/archives/page/7/index.html new file mode 100644 index 0000000..b4781fb --- /dev/null +++ b/archives/page/7/index.html @@ -0,0 +1,376 @@ + + + + + + + + Archives | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + +
+
+ 2016 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/page/8/index.html b/archives/page/8/index.html new file mode 100644 index 0000000..f6ad6c5 --- /dev/null +++ b/archives/page/8/index.html @@ -0,0 +1,262 @@ + + + + + + + + Archives | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + +
+
+ 2016 +
+
+ + + + + + + + + + + + + + + + +
+ + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/css/fonts/FontAwesome.otf b/css/fonts/FontAwesome.otf new file mode 100644 index 0000000000000000000000000000000000000000..8b0f54e47e1d356dcf1496942a50e228e0f1ee14 GIT binary patch literal 62856 zcmcfp2Y3_5)&LBzEbU6(wGF`%u_do$I-wUs=poc3^xzP>t859|l91%ydy%{4ZewH9 zLNU#OK%5)jlp7M#adH#VlN(Y~MSVYG)7F`Dsts8mQIv>+ztD)dFw+9OVG%`1 zdML`ns?&x=Qnp|IfM+dm&(}ePcdqmf37+Ghm#p%f+FVKQ2*chjkzF#ZB~9w-bef!xGBr6D7h{6UGOP@t%*!8rhr zqTX&D_txFJckW8F88SgJDOYWQiq1}9HpST zU`<34PZ)C!_3}_&M2)6kC53tq%16Wv<;B!kk^fL$a$g&o8ZTNrRL|U3FQqy}Aw%^t z%FjbIl=r0M9>Z`rYKq77t>{++@-k0@oM~*1+}p2(7`Q4V*n=HYq=vsI?g5v}-nP z3|{}}ibb1(*R0;YdDD}@+q7nj-e?F6nlWp}oWMD=X3yOms||yGW^I(#9B4HL0`>*2 zG{Pq6qjlCmi#Eba+D94TAv}p9V_D5%k=nR0b4*~E)oRv<#|upiMk~z0GGmR=Yz-V5 ze^pq5HgIj2Au?HKwVD>qoJsnJx#u=RZ=|+Tk5lVmJ2z1#N=q3aw}vu8YK7c-N>4=y zwHEjdq-Iky;2wVdD3u7c7HAy@>636rQ}I+R6-Jq%%_eFi6$}s_rB+ajpcD*stEugP zo136*FtrWZo1wQ}7%h+r0@$R$MYWppE&yKBVk^ODoieQIXI-PMCWPv3^jr9p7*cDDu9q6%xx{?3;;b@n3omixrmwx*YNmZf9p3xm@i;8 zp?TpJjUB@J0D^@;Vq@WEgcj}}s2gf=U*-SLs=qz||El20$!O-RlsfnS_J9)6lK^rf z@F|+|fem;DctSVzuQ6lCs>g=*`}C{(m-TP#-`gM6ukSbXXY`l%AL#GuKiB_u|L6U` z^xwJVb4z_|(yht2X53nKYvZlGw+y#3Zk69U@CS95u-8E9*x%q${UiIw^e^w<+#lK> z-M_Ej)SuN~+27uOroXrU-Tp88`)^UVM&1epcn{s0b!+*p&9_2tnQmp>swD94ennAt zcir7`_tDR9d~W}I%Sf-0+(^%nvXRn}u#+RjBRxinMp7g0j<_@8_K4p{{5Im&i2f13 zj`+pr(-A+9_-Vw=5kHRjVZ`?%z8i6aJ1^|@`u}w?=l`!y{JYkcahKF7zYy(4XAHaLAh7>kswf;WDJ8 zodnW*&mk}LA4ATyzs;HS z&jMIk)X1SUY8WQ8mk8qz!5gX{ac?|#KNXah-`{R{t;jx;+arrw4mTM?C=b`)g9B|K zKbe$=Z!xqbc>xxr!#G3cIJ_43-sk>0XiMsaXE3e+56S@N-W&nebhy1GS=0t{!`!CB zeXl$`20SDCO)=z#yl@A)%foXM<_FJ&aY(!S?qN9ajLc&>wDpF%>BD`=97%ujZX|^{ zkUJb;(Bvllh3Ak$Tkm1o9O@S+z@h#=rtsbrEayd0}DguL&kx00m+ja=Bpt$)C)Jj(+GE#@N5{qN_YooPx`~Xe7HP3 z{%{$_+eqqQIN>I3Ngv^P)=&zdhx-v8M)G7X!|w&{r;s|*7v>g7Gy(!cXqP3lRov@8 zR1fWh=MwT9Zqok0{>Y@@?`{gwSN{7?L`gvE7m2*?lX6LUm1893w2Pdz9?n{^!(W2e zdWpaFl9b@u0BLprBcj#q)KgjW@7iqlGG5Yvz*k2E1b+8G7f(?i1&vA9XxDLyUk5nmBs6~80?xA;He-^DJ8RN^C1NybWMO6ExxOV&s>OP-SKlxQUu zNxCEtRJdwMgQQb(MDmQ}tmIiqujCEMHOY0!HkBMipnS7>{u``WKCv$?i#JtM9$^4u7g87d5nYqQ>kup*r>4Q>U zI$1hRI!8KRx>mYFs*@&5bEW0dI%&J~sPvTdy!1usRp|%PFQwl}f0q6xb;-PBD%k|t zY}tI-V%aj;YS{+aQ?dwIjLaxYk`>BoWsR~9*)iEk*+tn)va7OpWS_{smHjSrdP+V0 zJk_4#J?D9@_1xwe?HTK7@=Wl|@+|Uf_B`o%#`BWri=J_T=4`v|*&UBhl-L)Zv5p0%+J>@(~s_AL7X`wDx7eUJT&{SSMK z9pETV%t<)~r{X4Z^SBk<7A}m7;^H_fm&|2x`CJ88%QbUt++pq*cal5LUErSMUf^El zUgJLCKIVSme)FQdBwi!E`Us0Q z%p9T98WOazMw1pS4`!>y8fGSUh&Ik-O^&x{%~AT;IIAusHq0EYwdzPtZ?PI<%-T3( zf;Poyj0@2lgv1zcHAY2Q^wEZ}*a%}ZXpR=04ir-WpbZI&wOaLYTC*`MGSZl6h=r8Y z4d>%cq(*NDHzt{4!;(WH^yY|Ityyc*hFL*fHES(8GA!v5YmA7AiVce8e_;!6kC&7Z?Hyy8O0n%G}drq zY^2^A7ORi2YLl!XIxW$Sg>0fe(yD_8(T0#%Z4_w&Inczd&{N0@YP37MFWzF+MkX06M(8q>71~9GMQF*2ge2%AwMG*R7f)W-5CO{_W(pxQ1Gtd{5P-01VNw=dm{|+^ z6%j+0-eT37Lc+r$ViLp5kx^l=IKzeEl&qvF4E7NA%LH2ey@o@10m4vTyAQN~fSq7A zx?gWNFHF`H8*d3AI~%7r4CUPWFH{<1gk*m_30u(tfF`iWB#nqQTC}hv2E8F#m?SuDFTQn3UEkkc8@TWC!-F{GC^ww z>q*$~q;*EKK82V{VgW}(B4CfL)4q56 z4)D)xH0hF~^)O1fFcUYy3iJruY7hufKutIFVd8R^gr`Ecp*I_TDL24)U$r5ORbRg-pCjNXR?8@hRjlg!)^B z(D!dOu%iM74)q`)qGOHW+C($Zqs|&;iLn3^gGC89>$Oo4U_&EF=f-R>g=zQ41JxU% z^ai~(IaX`22o=$0BPn|0z*CK8 zK%DqkW2^;?Z85-a0Z6ni9$1JOKmq#-j|FR7G;j-Zd_)ZF6-)}K?p{V%Lg*B4TBUeba0p4h(`{lkhnUa;!S@mlEwb3uRAAna%X|R34lqnNUbFX_%$pF{0bXxjWdRmGt^CFZcG*MWq&*% zpD-JDPJjsSWiSA$4WFQ~!(L z(g@%$q;&`!M=`(;0H;FcJiPEeUTy)bGXu%#O;$^MxH}UvXTe-kd`b#g8@(3xP*30x znc%M+5eqCjy*4&-n6xnX2oC%!5s^Uj?t@SuO@S=#uW(bx z{WX6b2|^FDjXG;w?7RqzWiB8Wa4|QJBTGftngtFZz*C@qy(Q$Y1K?iO@DUL*ch+1% z9wK1j&>$1McLEb&Zk8+5#cF{jf&aTxfx3yPAYib-S%s<1oju2WfRYkWB~Tuak9)I+ z(-1(skh!xT*2bHo!{JN-dNJ<8yjM5m zG60rH7zk-~uZGNixK`kLe=CruA#>*j!96b-j;Z)?t?(j4`6Spia^GJE{4Ojx680Zt zNWe8%t069;H$XAk92OS^LR}2VREDV856=$Q!%mO|6<}C_6UCa{zd}W<5upDiblg`Y z4Cvl7f*bc0-6U;-JxByu&zNWdaxxqBk$}(fNs-__0UlzBNj3priZ@%}*dQl4?7A@u zxFO-}z(C>X2fTOs4u7+;J0*%HiJsMQxqoBiu59bC{I)* zIwpEv)GK;ZbY1kl=qJ%1q5%)ugY$R_l;6D`VIDej?~k_t(Uq#ab(*CcOB-jjSFxlRYtLG(g8nl{qO zbOHT5{ZCLqIVOM^&rD@zGV_^TOav3dn3%)Nr_5K(_smbsZ;XR+Nxh{3(y`L%(je&q z=^E)esaBdKO_%0LE2WLn1JX|EJJNqkKa+kfy&=6R{Z;m$EI>A1Hd!`RHd8iFwn+Af zOe@pN;$&u7o$Qe8lVqKiD_fkJ-=Jui1W386V`Pb1S)E zZZ{Xs={O@7&!utMTpf3Udy%`wead~q-Q@bYKfGjKDz6z{L0&7o9`}0EYlm03m(I)J zmEe`?mG4#O)#laVb=0fN>w?#dUN3vS=Jl4>2VS3feeLyw*Uw(Rc{#l9deh#V_egJz z_ayH*-iy4Kd2jIE?ESR2*4ylzxhxHlZ~0u+4bSNe2Avwqk&^$DHRv=KS#CD3;S~8SQm|;x zN%uXOg<%H!6sOWpT07MECb~&~iaal%Kr~kA@W=0ly z{t+$Uxdi~XHN7!e%}J9R(_7UXGlAu{@LgPTdU`T9mC4D=%h61g=2Yj|)i)V?b+ui? zE#uW(1@DS-MfI`{o?I@T&abi;)~M_?7x@=n*uipt?Z;r>c-GlBp66Pcnp(J_b~W~k zJU4;W8IE;z9Xr-_5FpZ3`8gH2s@$By{Co|!66RIRN3*C1^>ST?V>+@U!LTF2up`?- zL$|?lw4^nqr~{nKnUu7&6b%lRrZlCsr~{Z@h76@~^htykcl!R`V4$yrCB3Hbq$wn746_@NOa-3Klzp2l^gn2VQjbAuo0?#JQLL z$Mz}bSE*b<%<3&$R%={A(pBfD{9}jO88R43TRRf@j!umu(~;H5a&uR%M853YmDj$} zIQyjET)Xy-no~>!4446Ue9XYDW$(ym^9NXsBiI!j&bBmH*VjYd5uCtsQXS7>`8HO> zDbN}`0?ouLy46Rz8=vn%p8Uqm@ezB}D0m6pght^=)w6thX?kgz2G3qG5zoOZl-P#$ z;62Eu9_V9|U>i5{jy^LBsJUYYou6NrldH_F$f?R#6Z}L^@PMpQjwrgSs={8Q zoOChE&E(fDVqJZ+_^S(9K%?|z4Qv@&$Gd6owP0l%>_y%&IxVx)7#jOLcGPC4#d!g42=Yrv!#JYwQRKph}ax;`_tIz`20);H(1 zsJH++i<8d1wvyoE7px2R-tQK>V~5{WU|KHT4=~~?>;J-zTfD!37u?D8Q>s%Z8#$yy z%h5wD_x>xdywB+ughWP$WMyPzRwT*3=TpiXGn-0FZKbMbDvnhisqR1g!-dcPCCh&K zU-?&5z+T@$$>=nPF5$IkC4LdF#0#)`=@RwFOYj1u#w%4&w-#zI;XGu*dusADPKoOm z8YZ0Itm0}4+W;2`1!=edNfwuq23(9Y^AiBwidZ$*g5O$1LZ$6+E(!Uc|#A>nDKry|{>zcC#+K%kF13+aeB` z9VD9p6UpVd$^V7B9CH{zE9`mIIchS3J(9JvNG|5m;2dy7E#^4~49g)Y8pA2@Lg!dK zg2BOf!)Nnef3=~Zrna)izq+0-OJ%Z4GBT8|Rd_LG9C|4SxZ~=3jfW$p9$pYw$y_dg z$>JhlV>uJMiW^X%#R@E9a470Q>roqx9zaWQErSDbk~yp(uQ0DT&%cNvuP5iE^LQ+u z26PNWna=x2;dpDwYtF2PX<;eXb5R_ zZZpZ*jjdH0&h{xRQ82^3_v)+fai0dznTkb#fpNA>TZj!$wMBp(y(a5G+OcF=O-IX7 zI1yn7^P5|gEmh6+^=fi-zRxzcYPfTi=c-TFqDL>HS)ZW?kxW)_xu>W{<;ZnRKUuRK|0& z{yIfL1XJ`OLv>qeQ+d6Ac^h59pu}O!d{)1 zv*gVuu9H;FWrMuddxQ0v#UA3Pz#$I+SM%g3Mhc$GgAw6?7&+-zJQ9zbG>QEFIth(L zBY*uBja2)zlewX3ESktVZS|5(mkM&oHz$Xv$b>E&ZkH^c3ZkKeyP{@`J>81Zl|K725KKL~og7cTUw&+r2C zUk9>oB)d(Z#5JNP*mUmDq4TywX6_8%+DKj@yYsN}P;F;x zs~Sy06X}*#uDQ7i4t1y4@e^&gBNN(#@|4_eym;lN^{dj7Q_?EUGMmj-qU3N8NR(vr zL5@U0AW!DyaDfW~n7L>qoU7ycb%~=uC}_($bO;~RAg|+gl_}Tm%SPM9pFM`C+p(U`f$Ogj39`p#D49F9Oe2B)Y(1=eW zw)bneg>cL|gV(T-@p*5{tE=Jcu_#{Qxp*GXIvt3kkYHpQ3rMZzl>31_u>s6-4t1k$ z+%4rq9}T342VUdi$!t^dQ!_JRmu7%?geCz#$k7y78#|!3og3_v;<;Rny}YW5!%{qk zYr=}g#4>emYj$g9vy8LVs?h8`L_|TiBLNz~6T}mIn`7Q#x%%eXmYM^ywlbt>Y*KQW ztPgGNM5|#@Lho##(bo(L9oRr~qe#cANDc%f=kjIw`MHHTDlBJG(mA{ekB4g&=UR+@ z#y>k2b08anAWukZCeRZa(ch0ofCOX(Es0wN+K`%qt+#QuZ7_-y0m}#2?n`dsD*wD% zU9TxGD=jNm!ZzETgs?z(%&2dH6S29assTs?*$2o*DW}7G$(=zkCn=n0K=g91j%PTP zO^O&KdH%vD8V)3XPz7L>;2B8w07~qv;%G|;IoyGV`0yOvTG|Z!pBsQ#a448*<@V{7 zdf2gEhBIedl9SbV5}wF0Z(rH8R)gfF3J%|GPxzE<#INuQA;=Fuj>54gr^1)E;a_nA zo)4mW8(@oc8NVA2@UCNk;D%})%w{#z2H@ok=K_g?v+@cKVge`%egi3pAfR$7s)V8% zDeAC@I!=iS?|Kv_iSmi9WFEB;;){P5Rf%dKM4(>OC~6j+5}g+P=`qz~g~xw9Zi~l? z6U67mcO<+dT5?YEC%uhsrC(z|gAE zO*vJ0Soy8esY(oZgqQLER6n4etX{4*s1K;GsNYi~jhAMuW{;*_b1QI4;QGKH$2>CT zA7i<(=f?Sr+dQskyn1}e_?r{PPpF*GHsRt#zlr~zR50n=$@LGNnX+igA5%|F+cqs@ z+S}6~n7(}aZ!^p@%4hsObLz||W*(ijYF6oN$QX$5KDr7zAHmywn^DlpJ_O|_m=Lh-A{Et-MyoGSNERokiok) zBnhB3NFqWKByj{Ii5OXtL=iv-I)VcRzH|jku>?yL&Y*4VU{JsS#rOmaeBcup%p(vg z?BW3W4M&OsA3!q@+*i8Vuj{V(uR|WXD@)op>iqEmJe@|bq0uaUO$x21Z|quaWJ_xUXAmZ_~hhx4bGFsw0wse^@d)0B zL-DjAP%gua%Yc&7*ptG~HMb>n%yYV^Ir+quNu8Y~X zOsAO}fxX6IZ{=QTe4}1~-O+ORpvERWcIMrGol^hUixhq6Nu^Kwy$j!Uz@hXT4-9Ss z-^eat$rCh}7lHN*%g%HL&}$Su8|+c)fPpL~YD3OWLx-U)QRDO)^r8pth-2Z11unc6 zgng%-ae6tu=(e_wW5-~S1W_f(E39}MY+<0HH}t}`?3|LK9Q9xyw$l+A#;7pmon0@m z&K*)1ESq+ndV%!`g!5xSUcduLyEub)22bZfY4K@?Qx%R1r~Nu#$Db%*0|u7If<;f- zZs~|Wl!(S*4>TT2kOs?S>p%Q{+3%`Sh&B5C`;XrEP=ho`23o%ajYA%X+By!lcghCs z(t*>G`3tf5iS25v9E+7>u>TlY=(eddSF1{x5@z+(?=Ec9VE;d`68_zm&3^yMUl5~Q z0Git}{%n4T8P1e5L>?Gep2ptkLk#cJzMcm|(|{by6<_nIywA5V(E)G8Gcom+3bm`G z563%p(Fbx;4q8>~c*j#Xi_WWWENE06tM5GgA^R;KAldIYrnu%>=<-IpTt0YLpJO5Z z7ka_5=ykNkF$!&QjdCo4<9+{Y{}-4YM?Pfn-Sr?2iLE?(P=OM*pd0w2DX66fl@N?-1iD^%I(}!F>Y{#DE3uA#DGd2hEe5<#MzbG*8eJ9rAVS*a7>X z{S`8p!61R*K0CV=3?EN|rl+Y>-AblM$u#nWsCFL|0B zfQG|)pZ4~I6JVA_-Cz?4mQ3W`hJitlTLhF*gLObK6@qDS+lA0x(4E2J0agpr&cu^; zCO{MD_+OBcSu~yntMX9y*I=$xBgAa|S3PuJ@wbLP?TrDFLn7oI!1w?W6b|fFfXJWR zs>T5*;3zvdesBW5jGjNr;s6}*4v+5OI|y>`@(7+gbxs`u84}+uPY@vw00iu76xufo z;xcky3)%Z&;>+Yhm+!$8%J?!scS9CB;mhtZ2z){+m9XdqJo!a-xeFw$i9EJ~O~`HB z##U^V3ifpbIY!5;!OjkR*D9R>68VYgd@_*MUtkE$$-fkUxcc07c}E{~7;XvDpX)Cb|1|XFuvZq>JsB#)PveQe{;jxBiN^8{5K0jUrRqVzDg~18#Ciz@>FQUv zymy! z&*Od810Fl&u{>a&NYRqnoKmjF>yBohOh1`&!vECeGZ#-?l2ulhSKE~}#We+0>ac&U zetlbytST=DEOI$HMPT2?V*?FMarLpa{zkN(ZYfS}NLFDp%px@Hdbg?*+HWKXULd8 zkEK16c|6zUdZ=x9l%!V#N--vs)1Y?7`7@ zUn0ko6}wEv0^s#bf$8Y;nt{g#G6c;O9Rxkp~37xp$cQT7Cj!TNVhT`^& zI&4Hw_&KKS_Q{rzgsVT3nbUxjS!=s=ByFFeTQM)>Kqhz5aopk1G=ntHm(bZMG8dQ$BhNn1}_Fh1}7Nti)0c zsT@ogRyZ#PtP12$h;{@IwrJG15JZTZim@zu2-s#H3a(^DF9b*f!~-`SXB4TWX_;v% zT*RcM)i;-FDx{sz1Pp>3(E_#;_tAw?r_B|uIG=Ss?X=o8Z{QexDBE<7`o%{7?Ua9oUL)qyK{_Ai_VIOP#S7N&Z?ckpe>SiZNU9u zm_q=i4bJZ5(sVGj!PB!f7mo=XL{82L5inMgk&7V{T*SK~8Nwgw=%`(Z+g00lwVjUA zU=<3WUD{k?Dq6tekKu^y$hJ1`S7AGt=)v}92iHh2woB0rmiQX{&w_)RM|6e?WpRxG1qwgX1Z!msyPF7Ub7d7P6Vlc}3fyKQX z{8za}`FR?A4PT@4^9plwl!99goGkcu9*=ILU}-~rO?{;X|K@0ah;2_8fQ@>SAE*Hu zm0Ehb1*Q3A1^#G9oZ@s=Z~7@U&T;h6C(|Pi z>r_B2x`_Sz(lt28)kCN2v$jPmT?xPQJ9rqtDh3Y{nDII?+Y{^5u5Q$qRByH=X89*( zW+qsbz#re{>&mNY!JH4q<+i%|_71QcjvmY20Be`s_Y9ba=Ca)^9*q@#$RFGQTd(6C zD%WBR767mVjOD@V9ovsqp^2K>2HSzmI?N+AtVd2c@Vk*_I(IXT8ZbX?y>VB zUjx`hNA3vvLF4-_R%7+suyd>U8$5c5_dOFpf9J3&TGE@)C^juSC%r(E5|OF3M9T2A z8F=ALyha5M-v?g!X1a!$w-VTSu>AxDq`vRwfu|HHXh4~0-SQeQgF!}1ZYz~VPn9c zflBaRv=`n3Qn*Usc#Ek45eF0^LSR7lb6Mh?HnDpSg`cyk1F(JR%Ob?7Vgyf{qpy_(zgvuS>Vj=cLo{pa z>7>`QufDBBFQFGv3;F@B7jX-I>9Oo}NgLE_GwF{*7W7V4osfp`C!~n`D{ zw)N2Ge`)&ziIhHfGEX#uH_&MpKf(LB?vesIuAl_mzgzL^#-FF3QCH;Vl;)~*24l45 z5hQEJ5XpdL?T;vL1Qt`RP}9%>a6BA^|X!|NjdB_-jxI_CZ_l=Idxa zYiv&H$kZH3Ka|;-Ec<2Ut6=@}QDUDhSUP#7+LCO}G^NX|nW;%eh5%56KxP0ZU4iv*KA7w1xTwa7;q_g#*D8$PI$hF$~8E;@fbZi2er?M%mste&UVe zXw>l^U;pv=3AlcEd7Zho235`~JX|gRb zKMD8VG5SSkg(gI)?#yI@*VMn7sL4H8YOkr6)!UoP8&pmwgM1I4LNhLF(2)Uk4S`SY@Fxs`Oc(;0h69>rvKnWwBS-<;xgEr(x6DibxmxA2GpmIW%yoQloTB&TirQB-&)3iy;JKCM^{C2fZQ!-8vmGcos@_>` zs?06jUahZ9ZjxoybQv>rMOIl>wlW*yIdawc z1=gI%9Q>fsugF}o-=uuC4DGI?OOHNR`nu}nH;VJ$(-gdSwdhq6NdZ#d`u?6~~Z{9B`t z1-wD7iVv{1TrJ$)^S%f-D(W5jPFReasvb;xyJU+{ge@XLF!sW1Y>t#pxHf&n1 zT#>nH|1Pz8XL!_BlgzYrRr(xN=QBka^;w~<(os*A)DqVV3{f`x~wu*<2rlCTY(;`{I>jL zIg(cYQuReK+EM8DP0?Fb7i+$1ey6Rcv#0a&>5I>wJl%P&@mbk{muvs|59Qaf*EhbW z_U+#I{v1%Pj(mLjABWnTWxgjboH*Xqepc3gw(i1Z<%PWN^t0;pv+-Sq_cH?QCUG% zdPQ{U<|=F`!^+a9%Ut<>^NXIy4^bDT=A~pM$7FvlUt%w-s(;S!0?Is#=3GHno8CWo>lpI)FKe$jT79zST+OkX zwj*_?YR}i6x1XsyQCHPo(E_mQ%IeFS(o1y3!G*H?$*YP&RM{3=S)>NP*O)ZkUffX9 zT;l&u;qy61(`3n|nI*aE+#T^)mAc-5XO|S1md4@P{+a8x;&v0(YMUovWmkUrJ&Pu zXoQi+mlzyVO8Y8*2502splvA@57<9pE;b(RGHHC@z@yN7Q&))11UB+fcs{K&H5xCf zKDlFG%!H&Hbw@N1lr{f|?xO7oSi+$#0O~rDel$eo146*S?V*`hq6(0H%NP%`pACJIXr6*_&%wUIKAOx$>g;p&(WnhH6fYKMq71sza*elGHFyzT zNPIVF5n6Pb9n8$&3wSgMoXv3B$C6Mh1fewGk~#e>zp;A#;b65xG}uIkv|TbiuX_H{ zk&Epb2jy&{55H9X#uX)4CZOX@#Zq2#rw<$&plbvIOi;aXCP=0bJUn3c-RxUQ+%1X* z{>fL~SNpafs_Cq6Q#Z8rzSI7;tgaj)tW-6%1zF{q_Q!hHHYCdG6KgDHrSE2tnfv2@ z*#3!n`zLrG>Rg06WEV2S+hbHQ5ecCgnnkz+d`6wy7t4G@cPx&bJ`uY72A&*2kiR() z6bXoV6U+i~@qib)t=M{V>dOo`ML-S4(`fXOqhDdqDM`!8!N1|({Bm;AN^(==Jist4j@u&|VHkfH@Du$@Qy2AQ$ zyS=B!4Apu-Qm z??=AR!Q1>cw5nx=g{6hW@|2gSS+|amKUv#qsXH{+_oKfB=iXcIlJfGBa)=elxEVFOi~iUHd&I=pcASXucdT%& zI1%%L?ZgRx=S$9)Xz&P5Vg--jbHH8UD3D7bnD#I%oeT0z8Q3~q@{90U0|W>Iq7TOh z1NXBNgAP&M96-(t7<7ax5CV`lsF`;0Kr{)mF%V-31dg>2)dn!v5Y0Px-e3)^bLR_u zAk-tD0EPi=Wb4oq5)tMOdh~ZfmOf-|vv(;;YY^!I0+^8?SJRo`dC@ukP#kZu9gS@X z7R zCS-&8Ac`H_`5nyExf3wSe-KjId?+zTryShb!;;qltDAkOl@Z$Z084;cCoF^bIV@Ee zi3{;N-Umb2864mq;zq|m6=t(Nu}cM>#x8r?A+v@+MLw**Gn*WdKniw(tq8euTdsi8Zq0W~rrMOat z%m0Qa9T0xxB&|C-8&94BV}cy@fj6lSv`8TpH^P5~fbH1MJPwr1O5YI>fq5L>0N%zO zpw)L380LDgt&xsGhe10dgc}3xt5^u(a<_ofE8Q_ik&>4J5mvKj)0vr&g(IvQf*&EM z=Wz@dRD$rSN=YG=v%iJN&b$_g?5u8v$WA1*LC~f?kA!H=1=V$Z2@4m*i z!)jf11|vI|n8CTKI0gr=6lqxSh(fRxsD;zUZFwYAz1w8iX;p%+pFb`A>8H=%KcT*I z^vK~Cl@~X6uZ!LX%cM?9PfXsuNtT-rdYCFNudJd#gZ+NZs4Z-@H~OP-Um>6O(8DSS zoDRl3UI$DI2g5tT@K!iGt*{MN6a;gygZes?bp@Y!A_yRcap%RV1Aj6_&7Kx;2d?wJhEtaB~olpbt#z|334}xAjCm}zo^*y)xKLutVI8W?{JDyFB1Q@ zZ_8I|ht9Q2;aCbEKK)ESZ-CDnes(Q&ErZV-ejfVF;b+G(wNC)OE>Uz9__G-Nz3=RO zZ6z2L7<36;qB{jz2UcO}R4@MkgsPa&d5c9es2Nn#RuU84VO2XdgMo>XE1Z^x!2y&xJLkH-3zbN3m%kH8KljihAJNb-ug>0nsnuBd*6X?d6;)zd+r*T zW2CS(mmnq)+H`6@{E%?I6J&tp0rb`DATh%L%b^w|O)E&6u#ND-5T68qh?oB|I~X|p z2@cFJ@H7ifZHSfthPe--wSjaqP6Yd#K)hyrfmUFjYbnTCJU^_5+x3N53hR# z%hh$(x|pT}S$1`GUZbk5zWG3NVQWdVrl`BPyIbklk4}H?SP7qr0PoF%gUtaaGMsqM zLWgx1?>y+dy%z!%qyh8|Q3L#d1ncPA3r`1b?*eB7@SU5^Ai{UTK*kTiV-(5hX({SM zd~#Y-s|GzOZEb1-=Sncs(wLU4DMm9C=_P4d;9uOpB&F3gYEqmc8a&F?73#_=d%0bO zOpM)LR8XaQxY8$jL6_Ykc&_$lHY{ri9Qr?lgOz-=rM)PkfMXZbcU8L&C61U zPD*?Y2U(X+x>f4h?fglZc;v8 z4XQz@C<#qQf2!cj1MkmH#g|cl&Gf^j-P?oJ;GFSuJ$4<3t(D<3({U9}#P2J0<+>`p zx+3xLwwx_^=b~}Sgz9{Iih9qH1F>&>{Td2=L3RG-`qbw&u{VB6y{SUe(A4wqAe9D; z`f9Wr?Y)Yw${Ma#zj>8d_#v(fJp@s(pg{&fWG{s1xT8FPC^iG04cu0s8#oI-dO3!C z)ukmxrS$QQT{BkW8dtF1<*URuP!?W^j$vPQNohq19dkwZ{d=g!5q!$w3*la{n*$Ow zUgQWyI(rdKs&+03P}IdMxon^wJ+EegJG^7B0Xxyc%CLKZ^bQ;6Uhr6Dl5U z*PMIqT+i`;$Qlk-w;v`8L*z602~b(lJVNvDvqSXW2=x9Z55$h2lomT!MMg4@`|!bbNtJ)t8(lGj!JyO57)!Bt(Pt>F0vKDH>o6MXX+Gi=;uJYQV7SX zDF7jBiywIBDywp93TsRJOKtE~7}!oUH*Z3GK79S*zYT3e^>CeVRgw<&V*iqIh%Zr9 zSC>^(g0^$Bwx+V7sNNq3IoG3kXx`16S5eTqtNx(10=0Et1*sM6Fn;`rt0#cl1;ImD zSRpS5K1Zw^3dHeOM zu@muwpA$d5brnd044QhC_)A~aod2Qw`&c>N|F)9h5%!0F8W~ zOX7qE><;<;HLE}y1wH9Hs3Sy80@-H}q@3Y{UXUS<^Hw5*49O3md?gc|=`UFU{A{4D zfsjB9Qhx~vM5zLGEd^u)kVD*p1(97&Lo5)Q4r>Qeb258EQC(D1Sf$265MffCpAA7} zu0Bx7gPCP)Q$bU99Yk<~t)Ve9xh6@Kl$@ImT2Y@%PG@Hoq@^K<+=iYnHXFSjIS=0spgd563i}N>f zk6XpVsBFQsxjg;O?JtUpi3k7a-Q)VbjFxT zvu)6pLrfF{lxH+gg0LQH5P-V>h`o9|_GVmVuA$1Ut2S;}6C%w{$x2C4(R#2LTireA zGXTz?AH*3;N=>Ee2jA~L^BMn|dECX&Z;-VqG#0AMi!9bMen9!STMt!W*k*AJ@r}uQ zOwxJ#0$W;D`|_L0>bXB)X}$J3c{4?dR8nb)ib(I>Bhm|}!`AHMjyMjLHP^%~-Mo6` zw)brZ^7oZWu@o)zM-Yj0asEV>kgepk&VHgHWG&VNHI`!fX8XTrvGZR*G;ak; z_W2{SfrA;dl|CgNoxWurPdk&P60(Nu^~V4|r@17&e~&0W^3bDNU~(%E9)-op%uY-c z!!*o*9Hxl@^o{X&85^7#&^;#N47#r>34Hv6m?MO%%Dp&A&K~$gK==z0Z!KOreIzYJ zA#wr=C8jcPn25upDggj}Cvm6@vF=Xfc`&lY418P3?p#c^TJ*y6+{M}Iawy-Ig>1DK zY~u>H*|&zM-k0?pe*4j*+qWO>+>w@4$0gOJ?bxYe?;qVB-jj3QZPzMy(gsqpp^5YA zFX&!-O}Fjd=*mbQYb6XH(N}FJ(GedN384c>e;Q10bUcFbZU6}(KwzBws*Q6FYaiCZ zZ#>h|a>fHt=4mJiy?OObZ6j8`8bz?L28{2 zw?jE)-rUJk=AOM;r}^|8;JYqI*Z+LN$?fbzkl5X$ltsyf3BcYCtWMdHv^{aV?~eVu z_U_y-&9MQ@s@g$iq|>$<&YF(d2q6oj0kB)y(C~t={B60uI#4%?j0yP(YC21tkd&N| z!6z;?Xbnq3Q^JzN5~<{SpB&GQAwU;D7aGMQZ2-R`&61Xr&NZyxwPDBF#4vqW>NfgX zxDR65@rf!rQ<9LESY+hLz;MUbg3zK+-;i~|8$#AgK|X~5LkN-i*M)PyeIgfQ&ov|Y zKxE(5B-QHcQhlqzLP;5J54mbj=OuLx1%qt?^bw&`B{My_)@>-2gp*gR(Pz9{PZ%WcbGeJfMYUJa}R{xq( z!4Wm+0@+>hv3$}5nLGtwdB2d)!dJ|$Z2BieX4oF0#rORpS2BDwoUT1t*y&<5l|L z6PbO#Ve63PCayBPXnBxIzSa7(#u8(Wjs~D}bToL~v?1%ZN$GZW z!(kqL9+nsmT)E>$aPm%m1+I3V)#N2Ly7HrVueeoKd$91>F;#VDO?nmAaHRC?IaN1U zZ&vTC^W|P??H8 zt(!nK+>8$!$*cVzZrvGPA673t_b$aqj8zAT<+D#>a3p8$?kzvX?;}qU@g5?BC5kU9 zNte%;U|{64t-UaPaW-@T5p?cToA-<*J~B<&ohWw)w!cW5@;|KTS&P zdM@^C&=Jm7WvQuF;Sk3XkA)rN%thJ7MXHv_mUYKCt3-bAB$=I!*|QU!uBKhZbP#=E z{Sx{zpByqec&nOX;AWqEGK|~B`?q~EWY@agEBCD0xAy$>Ep+Iw{iNP-%OAfs{d|!=I z%ex;^FJ#^vx*H}$k2uZ0HJ)?}>4_CsabMZA&Jc#Ys@R)F(Rw9Lnly(JKiTo73>MNq zq;8P#^nSs+0)*yGh>sxm?VNs(q>+3~)5-AR<@jg7zvM1>+fC`5PU709ONw3o%D0y+ z7|mswByTJ^_0cCMPF%l!bkVeIUby+#Unxi=_cmXCea8A#Yhts;gSNn2s#9Pz3USvXoF>* z1qz5+X8?tr|2n`1gQ*WEI3#r%uqSZ+d-PuzdxCevO7{WvelUFa4`d{OX2>D4?1)DchD@fD zkx%dkAp|kmQ5vKI{Ml#3kIgO2u;~m?lEMpM-UP%pX}gRT#qSnQ+qz-D6$q_np!we% z#v?kG2bBWvH=AG#w*FfNQ__W`u+YjV21KEFU3k~oQ%RRJQ(xlui|RfS2y{pT?e^Yl zoa-{#q3lO}fkjxdhI{XB1CWzLfSViu(}yU&meJ<>;tZL)HC{G=GR2dFGCGgM(hcOp zc<#XBrr@#!>B(h9OJ=BM1i{H1Fk=7*NWK%0{1(am0WAXt1hurZ6dgNxgexm*+I8T# zlzdnWQp*O$sKYg~>3mgubySt5{$3Fhd@G5fmb|miIhNGRb505zc}JO(V|1k3puUlv zVK8KvQ|##wWHRMgrSb{-)fbf+_Ed`@!;qN;Vuv*?H#5f~&5~GivT_Y}>8uM%b55o; z-2&{m$(U)(uo!Ha)=Zn(Y?0OnDswC*yTN9#rXh)#k(r%lO}85C#+)1}!T?>BW?Q-) z$N&gO7?C!&r8$gJd2c<)gch?+dfA|~r&?1?TuPcDJ&%jV_J>m7EhjX#&CG}$0P zV@ffmr)Q^Sg970&18-w9*`%(;t~pG_3l3q!?yMtxnd!T?G&{m;R=oLg7VQ$ITGp7= z0HX<~kKqLViyF`ZX25vy#L&qLUWauretq((&qI0l`2SD>mMinB4LhRCn7V~eVN$Fu zP8}EPK`3b5+K*vxxV7R}@zhr)XmR%Is!M9}cy4h%WV1ykvRAQnh@pe{fv& z4*p=(dxuqWYvqlw>o-&+{ZrCN-X*Vc=MP?M_+-0u_wDcZ{HT^2{IRNumXT-n?|1B1 z=UB5$IlSCH!4a1o75#4VyDL-+@C;qngg&E|n?r_%!H$Fxa>!;Y#Q zJ9 +

###异步绘制

+

@@ -302,10 +254,10 @@

+
@@ -314,41 +266,87 @@

-

ReactNative配置.npm 和cnpm 相关

npm

Nodejs的包管理工具

-

npm 常用命令

    -
  • 更新或卸载

    -
    1
    npm update/uninstall moduleName
    -
  • -
  • 查看当前目录下已安装的包

    -
    1
    npm list
    +

    ReactNative 中使用 TypeScript

    +

    从RN0.57版本开始已经可以直接支持typescript,无需任何配置。在保留根入口文件index.js的前提下,其他文件都可以直接使用.ts或.tsx后缀。但这一由babel进行的转码过程并不进行实际的类型检查,因此仍然需要编辑器和插件来共同进行类型检查

    +
    +

    TypeScript和JavaScript对比

    两者深度对比

    +

    最大的区别:

    +
      +
    • TypeScript是强类型语⾔和静态类型语⾔(需要指定类型)
    • +
    • JavaScript是弱类型语⾔和动态类型语⾔(不需要指定类型)
    • +
    +

    TypeScript

    由微软开发的自由和开源的编程语言,是JavaScript的一个严格超集,并添加了可选的静态类型和基于类的面向对象编程

    +

    TypeScript定义

    +

    安装及语法

    教程

    +
    安装
    1
    npm install -g typescript
    +
    安装 typescript 依赖管理器 typings
    1
    npm install -g typings
    +
    安装 typings 依赖
    1
    2
    typings install npm~react --save
    typings install dt~react-native --globals --save
    +
    使用 yarn 安装

    如果已经安装了 yarn 安装如下:

    +
    1
    2
    yarn add tslib @types/react @types/react-native
    yarn add --dev react-native-typescript-transformer typescript
    +
    创建 tsconfig.json 配置文件
      +
    • cd 到 ReactNative 项目根文件夹下

      +
      1
      tsc --init
    • -
    • 查看全局安装的包的路径

      -
      1
      npm root -g
      +
    • 修改 tsconfig.json 文件

      +

      如果一个目录下存在一个tsconfig.json文件,那么它意味着这个目录是TypeScript项目的根目录。 tsconfig.json文件中指定了用来编译这个项目的根文件和编译选项

      +

      tsconfig.json文件配置详解

      +

      TypeScript配置文件tsconfig简析

      +

      务必设置”jsx”:”react”
      注意多余的注释可能会不兼容,需要移除

      +
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      {
      "compilerOptions": {
      "target": "es6",
      "allowJs": true,
      "jsx": "react",
      "outDir": "./dist",
      "sourceMap": true,
      "noImplicitAny": false
      },

      // "files"属性 :指定一个包含相对或绝对文件路径的列表。 "include"和"exclude"属性指定一个文件glob匹配模式列表

      "include": [
      "typings/**/*.d.ts",
      "src/**/*.ts",
      "src/**/*.tsx"
      ],
      "exclude": [
      "node_modules"
      ]
      }
    • -
    • 全部命令

      -
      1
      npm help
      +
    • cd 到项目根目录中新建存放 typescripe 源代码的文件夹

    -

    cnpm

    因为npm安装插件是从国外服务器下载,受网络影响大,可能出现异常,cnpm 淘宝镜像

    -

    安装

    1
    npm install -g cnpm --registry=https://registry.npm.taobao.org
    -

    设置镜像源

    1
    2
    cnpm set registry http://registry.cnpm.taobao.com
    npm set registry http://registry.cnpm.taobao.com
    -

    配置 .npmrc 文件

    前端项目开发离不开安装各种npm依赖包,可以选择远程的仓库也可以选择本地的仓库,但更改仓库地址需要在安装时控制台打命令,比较麻烦, 而npmrc可以很方便地解决上面问题

    -

    .npmrc 原理

    当安装项目的依赖包时,会查找并读取项目根目录下的.npmrc文件的配置,自动指定仓库地址

    +
    建立 rn-cli.config.js 文件,使用支持typescript的transfomer
    1
    2
    3
    4
    5
    6
    7
    8
    module.exports = {
    getTransformModulePath() {
    return require.resolve('react-native-typescript-transformer');
    },
    getSourceExts() {
    return ['ts', 'tsx'];
    }
    }
    +
    修改 .babelrc 文件
    1
    2
    3
    4
    {
    "presets": ["react-native"],
    "plugins": ["transform-decorators-legacy"]
    }
    +
    修改项目中文件导入
      +
    • 入口index.jsApp.js的文件名请不要修改,项目根目录下新建src文件夹
    • +
    • src文件夹下新建 .tsx 后缀的文件
    • +
    • .tsx 后缀的文件引用react的写法有所区别

      +
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      import * as React from 'react'
      import { Text, View } from 'react-native';
      import { Component } from 'react';

      export default class HelloWorld extends Component{
      render() {
      return (
      <View>
      <Text>Hello!</Text>
      <Text>Typescript</Text>
      </View>
      );
      }
      }
      +
    • +
    • App.js中使用

        -
      • 打开 .npmrc

        -
        1
        open .npmrc
        +
      • App.js 导入方式修改为如下方式

        +
        1
        2
        3
        4
        	import './src';
        ```

        - 导入 .tsx 类型的组建
        +

        import HelloWorld from “./src/HelloWorld”;

        +
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11

        ### 配置jsconfig

        `jsconfig.json`目录中存在文件表明该目录是JavaScript项目的根目录。该jsconfig.json文件指定根文件和JavaScript语言服务提供的功能选项

        [jsconfig详解](https://code.visualstudio.com/docs/languages/jsconfig)

        [jsconfig.json](https://jeasonstudio.gitbooks.io/vscode-cn-doc/content/md/%E8%AF%AD%E8%A8%80/javascript.html?q=)


        ##### 新建 `jsconfig.json` 文件
      -
        -
      • 编辑文件, 指定特定的库的镜像源, 比如以@test开头的库镜像为:http://registry.cnpm.test.com/ 而其他的正常

        -
        1
        2
        @test:registry=http://registry.cnpm.lietou.com/
        registry=https://registry.npm.taobao.org/
      +

      touch jsconfig.json

      1
      2

      ##### 编辑 `jsconfig.json` 文件

      +

      {
      “compilerOptions”: {
      “allowJs”: true,
      “allowSyntheticDefaultImports”: true,
      “emitDecoratorMetadata”: true
      },
      “exclude”: [
      “node_modules”,
      “rnApp/Tools” // 项目根目录下新建的存放RN中所用的一些工具类
      ]
      }

      +
      1
      2
      3


      ### package.json 文件配置
      +

      {
      “name”: “RNProject”,
      “version”: “0.0.1”,
      “private”: true,
      “scripts”: { // 通过设置这个可以使npm调用一些命令脚本,封装一些功能
      “start”: “node node_modules/react-native/local-cli/cli.js start”,
      “test”: “jest”
      },
      “dependencies”: { // 发布后依赖的包
      “@types/react”: “^16.4.16”,
      “@types/react-native”: “^0.57.4”,
      “react”: “16.5.0”,
      “react-native”: “0.57.2”,
      “react-transform-hmr”: “^1.0.4”,
      “tslib”: “^1.9.3”
      },

      +

      “devDependencies”: { // 开发中依赖的其它包
      “babel-jest”: “23.6.0”,
      “jest”: “23.6.0”,
      “metro-react-native-babel-preset”: “0.48.0”,
      “react-native-typescript-transformer”: “^1.2.10”,
      “react-test-renderer”: “16.5.0”,
      “typescript”: “^3.1.2”
      },

      +

      “jest”: { // JavaScript 的单元测试框架
      “preset”: “react-native”
      }
      }

      1
      2
      3
      4
      5
      6
      7
      8
      9

      ##### `–save` 和 `-–save-dev` 区别

      - --save参数表示将该模块写入dependencies属性,
      - --save-dev表示将该模块写入devDependencies属性。

      ##### 添加开发依赖库

      - 类型检查

      +
      npm install @types/jest --save-dev
      +npm install @types/react --save-dev
      +npm install @types/react-native --save-dev 
      +
      +
      1
      - 装饰器转化核心插件
      + +npm install babel-plugin-transform-decorators-legacy --save-dev +
      1
      2
      	
      - 测试框架
      + +npm install react-addons-test-utils --save-dev +npm install react-native-mock --save-dev +
      1
      2

      ##### 删除依赖库
      +

      npm uninstall @types/jest –save

      1
      2


      +

      npm uninstall @types/jest –save-dev

      1
      2

      ##### 彻底删除,-d 表示 devDependencies, -O 表示 optionalDependencies 中

      +

      npm uninstall -s -D -O @types/jest

      +
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10

      ### 报错汇总

      ##### 错误1 `Cannot find module 'react-transform-hmr/lib/index.js'`

      [解决1](https://stackoverflow.com/questions/50142561/cannot-find-module-react-transform-hmr-lib-index-js?noredirect=1&lq=1)

      [解决2](https://github.com/facebook/react-native/issues/21530)

      - 安装 `react-transform-hmr`
      +
      npm install react-transform-hmr --save
      +
      1
      2

      - 清除缓存重新启动服务
      +npm start --reset-cache +``` +
      @@ -361,7 +359,7 @@

      @@ -443,125 +441,23 @@

      }

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15

      #### 相互循环引用的两个变量都不是可选的

      使用隐式可选类型

      ### 闭包中的强引用循环

      - 并不是所有的闭包都会产生循环引用,只有相互引用的两个对象会产生循环引用

      - 使用swift闭包捕获列表,在闭包的声明参数之前使用 [unowned self], 不需要解包

      - 使用 unowned表示这个对象不能为空,可能存在风险,如果对象为空就会crash


      使用 unowned 对象不能为空,可能存在风险

      -

      class Closure {
      var closure: ((Int) -> Void)?

      -
      init() {
      -    closure = { [unowned self] index in
      -        print(self)
      -        print(index)
      -    }
      -}
      -

      }

      1
      2

      使用 weak, 此时需要解包

      -

      closure = { [weak self] index in
      // 解包
      if let self = self {
      print(self)
      print(index)
      }
      }

      1
      2

      强制解包

      -

      closure = { [weak self] index in
      // 强制解包
      print(self!)
      print(index)
      }
      `

      - - - -
      - - -

- - - -
- -
- - -
- -

ReactNative 中使用 TypeScript

-

从RN0.57版本开始已经可以直接支持typescript,无需任何配置。在保留根入口文件index.js的前提下,其他文件都可以直接使用.ts或.tsx后缀。但这一由babel进行的转码过程并不进行实际的类型检查,因此仍然需要编辑器和插件来共同进行类型检查

-
-

TypeScript和JavaScript对比

两者深度对比

-

最大的区别:

-
    -
  • TypeScript是强类型语⾔和静态类型语⾔(需要指定类型)
  • -
  • JavaScript是弱类型语⾔和动态类型语⾔(不需要指定类型)
  • -
-

TypeScript

由微软开发的自由和开源的编程语言,是JavaScript的一个严格超集,并添加了可选的静态类型和基于类的面向对象编程

-

TypeScript定义

-

安装及语法

教程

-
安装
1
npm install -g typescript
-
安装 typescript 依赖管理器 typings
1
npm install -g typings
-
安装 typings 依赖
1
2
typings install npm~react --save
typings install dt~react-native --globals --save
-
使用 yarn 安装

如果已经安装了 yarn 安装如下:

-
1
2
yarn add tslib @types/react @types/react-native
yarn add --dev react-native-typescript-transformer typescript
-
创建 tsconfig.json 配置文件
    -
  • cd 到 ReactNative 项目根文件夹下

    -
    1
    tsc --init
    -
  • -
  • 修改 tsconfig.json 文件

    -

    如果一个目录下存在一个tsconfig.json文件,那么它意味着这个目录是TypeScript项目的根目录。 tsconfig.json文件中指定了用来编译这个项目的根文件和编译选项

    -

    tsconfig.json文件配置详解

    -

    TypeScript配置文件tsconfig简析

    -

    务必设置”jsx”:”react”
    注意多余的注释可能会不兼容,需要移除

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    {
    "compilerOptions": {
    "target": "es6",
    "allowJs": true,
    "jsx": "react",
    "outDir": "./dist",
    "sourceMap": true,
    "noImplicitAny": false
    },

    // "files"属性 :指定一个包含相对或绝对文件路径的列表。 "include"和"exclude"属性指定一个文件glob匹配模式列表

    "include": [
    "typings/**/*.d.ts",
    "src/**/*.ts",
    "src/**/*.tsx"
    ],
    "exclude": [
    "node_modules"
    ]
    }
    -
  • -
  • cd 到项目根目录中新建存放 typescripe 源代码的文件夹

    -
  • -
-
建立 rn-cli.config.js 文件,使用支持typescript的transfomer
1
2
3
4
5
6
7
8
module.exports = {
getTransformModulePath() {
return require.resolve('react-native-typescript-transformer');
},
getSourceExts() {
return ['ts', 'tsx'];
}
}
-
修改 .babelrc 文件
1
2
3
4
{
"presets": ["react-native"],
"plugins": ["transform-decorators-legacy"]
}
-
修改项目中文件导入
    -
  • 入口index.jsApp.js的文件名请不要修改,项目根目录下新建src文件夹
  • -
  • src文件夹下新建 .tsx 后缀的文件
  • -
  • .tsx 后缀的文件引用react的写法有所区别

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    import * as React from 'react'
    import { Text, View } from 'react-native';
    import { Component } from 'react';

    export default class HelloWorld extends Component{
    render() {
    return (
    <View>
    <Text>Hello!</Text>
    <Text>Typescript</Text>
    </View>
    );
    }
    }
    -
  • -
  • App.js中使用

    -
      -
    • App.js 导入方式修改为如下方式

      -
      1
      2
      3
      4
      	import './src';
      ```

      - 导入 .tsx 类型的组建
      -

      import HelloWorld from “./src/HelloWorld”;

      -
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11

      ### 配置jsconfig

      `jsconfig.json`目录中存在文件表明该目录是JavaScript项目的根目录。该jsconfig.json文件指定根文件和JavaScript语言服务提供的功能选项

      [jsconfig详解](https://code.visualstudio.com/docs/languages/jsconfig)

      [jsconfig.json](https://jeasonstudio.gitbooks.io/vscode-cn-doc/content/md/%E8%AF%AD%E8%A8%80/javascript.html?q=)


      ##### 新建 `jsconfig.json` 文件
      -
    • -
    -
  • -
-

touch jsconfig.json

1
2

##### 编辑 `jsconfig.json` 文件

-

{
“compilerOptions”: {
“allowJs”: true,
“allowSyntheticDefaultImports”: true,
“emitDecoratorMetadata”: true
},
“exclude”: [
“node_modules”,
“rnApp/Tools” // 项目根目录下新建的存放RN中所用的一些工具类
]
}

-
1
2
3


### package.json 文件配置
-

{
“name”: “RNProject”,
“version”: “0.0.1”,
“private”: true,
“scripts”: { // 通过设置这个可以使npm调用一些命令脚本,封装一些功能
“start”: “node node_modules/react-native/local-cli/cli.js start”,
“test”: “jest”
},
“dependencies”: { // 发布后依赖的包
“@types/react”: “^16.4.16”,
“@types/react-native”: “^0.57.4”,
“react”: “16.5.0”,
“react-native”: “0.57.2”,
“react-transform-hmr”: “^1.0.4”,
“tslib”: “^1.9.3”
},

-

“devDependencies”: { // 开发中依赖的其它包
“babel-jest”: “23.6.0”,
“jest”: “23.6.0”,
“metro-react-native-babel-preset”: “0.48.0”,
“react-native-typescript-transformer”: “^1.2.10”,
“react-test-renderer”: “16.5.0”,
“typescript”: “^3.1.2”
},

-

“jest”: { // JavaScript 的单元测试框架
“preset”: “react-native”
}
}

1
2
3
4
5
6
7
8
9

##### `–save` 和 `-–save-dev` 区别

- --save参数表示将该模块写入dependencies属性,
- --save-dev表示将该模块写入devDependencies属性。

##### 添加开发依赖库

- 类型检查

-
npm install @types/jest --save-dev
-npm install @types/react --save-dev
-npm install @types/react-native --save-dev 
-
-
1
- 装饰器转化核心插件
- -npm install babel-plugin-transform-decorators-legacy --save-dev -
1
2
	
- 测试框架
- -npm install react-addons-test-utils --save-dev -npm install react-native-mock --save-dev -
1
2

##### 删除依赖库
-

npm uninstall @types/jest –save

1
2


-

npm uninstall @types/jest –save-dev

1
2

##### 彻底删除,-d 表示 devDependencies, -O 表示 optionalDependencies 中

-

npm uninstall -s -D -O @types/jest

-
1
2
3
4
5
6
7
8
9
10

### 报错汇总

##### 错误1 `Cannot find module 'react-transform-hmr/lib/index.js'`

[解决1](https://stackoverflow.com/questions/50142561/cannot-find-module-react-transform-hmr-lib-index-js?noredirect=1&lq=1)

[解决2](https://github.com/facebook/react-native/issues/21530)

- 安装 `react-transform-hmr`
-
npm install react-transform-hmr --save
-
1
2

- 清除缓存重新启动服务
+let owner = id?.person +

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

#### 相互循环引用的两个变量都不是可选的

使用隐式可选类型

### 闭包中的强引用循环

- 并不是所有的闭包都会产生循环引用,只有相互引用的两个对象会产生循环引用

- 使用swift闭包捕获列表,在闭包的声明参数之前使用 [unowned self], 不需要解包

- 使用 unowned表示这个对象不能为空,可能存在风险,如果对象为空就会crash


使用 unowned 对象不能为空,可能存在风险

+

class Closure {
var closure: ((Int) -> Void)?

+
init() {
+    closure = { [unowned self] index in
+        print(self)
+        print(index)
+    }
+}
+

}

1
2

使用 weak, 此时需要解包

+

closure = { [weak self] index in
// 解包
if let self = self {
print(self)
print(index)
}
}

1
2

强制解包

+

closure = { [weak self] index in
// 强制解包
print(self!)
print(index)
}
`

-npm start --reset-cache -``` -
@@ -574,7 +470,7 @@
@@ -775,7 +671,7 @@
- Share + Share @@ -785,10 +681,10 @@
+
@@ -797,55 +693,43 @@
-

iOS通过URL获取HTML中标题

-

获取标题

String扩展一个方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private let patternTitle = "(?<=title>).*(?=</title)"
extension String {
static func parseHTMLTitle(_ urlString: String) -> String? {
let url = URL(string: urlString)!
do {
let html = try String(contentsOf: url, encoding: .utf8)
let range = html.range(of: patternTitle, options: String.CompareOptions.regularExpression, range: html.startIndex..<html.endIndex, locale: nil)
guard let tempRange = range else { return "" }
let titleStr = html[tempRange]
return String(titleStr)
} catch {
return ""
}
}
}
-
iOS 中简单正则表达式

iOS 中 正则表达式中的\ 需要使用 \\ 来表示

-
// 获取
-let testStr = "我到底是[iOS]开发还是[产品]经理"
-
-// 获取 "[" 和 "]" 之间的所有内容, 左边[ 起一直到 右边 ] 结束的所有内容 
-let pattern1 = "\\[.*\\]" // [iOS]开发还是[产品]
-
-// 获取 "[" 和 "]" 之间的所有内容,包括[]
-let pattern2 = "\\[.*?\\]"    // [iOS]
-
-// 获取第一个 "[" 和 "]" 之间的所有内容 不包括[] 
-let pattern3 = "(?<=\\[).*?(?=\\])" // iOS
-
-// 获取html中 title 标签中内容
-let pattern5 = "(?<=title>).*(?=</title)" 
-
获取范围截取子串
let testRange1 = testStr.range(of: pattern1, options: String.CompareOptions.regularExpression, range: testStr.startIndex..<testStr.endIndex, locale: nil)
-    let result1 = String(testStr[testRange1!])
-
-
-let testRange2 = testStr.range(of: pattern2, options: String.CompareOptions.regularExpression, range: testStr.startIndex..<testStr.endIndex, locale: nil)
-let result2 = String(testStr[testRange2!])
-
-
-let testRange3 = testStr.range(of: pattern3, options: String.CompareOptions.regularExpression, range: testStr.startIndex..<testStr.endIndex, locale: nil)
-let result3 = String(testStr[testRange3!])
-
-print(result1) 
-print(result2) 
-print(result3) 
-
正则表达式

通过URL解析html中的标题

+

自动引用计数

+

什么是自动引用计数

内存管理中对引用采取自动计数的技术

+

内存管理

    +
  • 自己生成的对象,自己所持有
      +
    • alloc
    • +
    • new
    • +
    • copy
    • +
    • mutableCopy
    • +
    +
  • +
  • 非自己生成的对象,自己也能持有
      +
    • retain
    • +
    +
  • +
  • 不再需要自己持有的对象时释放
      +
    • release
    • +
    +
  • +
  • 非自己持有的对象无法释放

    +
  • +
  • 废弃对象

      -
    • 获取html字符串
    • -
    • 正则匹配
    • -
    • 生成字符串Range
    • -
    • 截取字符串
    • +
    • dealloc
    -

    匹配一次直接返回结果

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // 生成表达式
    let regular = try NSRegularExpression(pattern: pattern5, options: .caseInsensitive)


    // 开始匹配
    let matchResult1 = regular.firstMatch(in: html, options: NSRegularExpression.MatchingOptions.reportProgress, range: NSMakeRange(0, html.count))

    // 获取匹配结果范围
    let matchRange = matchResult1?.range
    let range = Range(matchRange!)!

    // 生成String.Index 类型范围
    let startIndex = html.index(html.startIndex, offsetBy: range.lowerBound)
    let endIndex = html.index(html.startIndex, offsetBy: range.upperBound)
    let range3 = Range(uncheckedBounds: (lower: startIndex, upper: endIndex))

    // 截取字符串
    let result1 = String(html[range3])
    print(result1)
    -

    全部匹配后返回结果时一个集合

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    let matchResult2 = regular.matches(in: html, options: .reportProgress, range: NSMakeRange(0, html.count))
    matchResult2.forEach { (result) in
    let range = Range(result.range)!

    let startIndex = html.index(html.startIndex, offsetBy: range.lowerBound)
    let endIndex = html.index(html.startIndex, offsetBy: range.upperBound)

    let subStr2 = html[Range(uncheckedBounds: (startIndex, endIndex))]
    print(String(subStr2))
    }
    -

    将匹配到的内容提还

    -
    1
    let str3 = regular.stringByReplacingMatches(in: html, options: .reportProgress, range: NSMakeRange(0, html.count), withTemplate: "====")
    +
  • +
+

__strong

__strong 修饰符表示对对象的“强引用”

+

__unsafe_unretained 修饰符

__unsafe_unretained 是不安全的所有权修饰符, 被它修饰的变量不属于编译器的内存管理对象

+
1
id __unsafe_unretained objc = [[NSObject alloc] init];
+

Xcode 提示:Assigning retained object to unsafe_unretained variable; object will be released after assignment

+

将保留对象赋给unsafe_unretain变量;对象将在赋值后释放

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
id __unsafe_unretained obj1 = nil;

{
id __unsafe_unretained obj = [[NSObject alloc] init];

// 自己生成并持有对象
// 因为obj0 变量是__strong,强引用,所以自己持有对象
id __strong obj0 = [[NSObject alloc] init];

// obj0 变量赋值给你obj1
// ojb1 变量不持有对象的强应用,也不持有弱引用
obj1 = obj0;


// 输出 obj1 变量表示的对象
NSLog(@"A: %@", obj1);
} // obj0变量 超出了其作用域,强引用失效自动释放自己持有的对象,除此之外没有其他持有者,所以废弃该对象




// 输出 obj1 变量表示的对象
// obj1 变量表示的对象因为无持有者已经销毁,坏的内存访问
NSLog(@"B: %@", obj1);
@@ -858,7 +742,7 @@
@@ -985,7 +869,170 @@

- Share + Share + + + + + +

+ + + +
+ +
+ + +
+ +

swift类型检查和类型转换

+

is 关键字类型检查

判断父类和子类之间的关系

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
func typeCheck() {
let programmer = Programmer(name: "老张-php")
if dev is iOSDeveloper {
print("iOS 开发者")
} else {
print("其他开发者")
}

if programmer is Coder {
print("老张-php是一农个码农")
} else {
print("")
}
}
+

as 关键字类型转换

1
2
let ios = iosDev as? Programmer
let ios2 = iosDev as! Coder
+

判断是否遵守了协议

swift 中协议可以当作是一个类型使用

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
et ios = iosDev as? Programmer
let ios2 = iosDev as Coder

// 是否遵守了 Person 协议
if iosDev is Person {
print("遵守了Person协议")
}

let devs = [dev, iosDev, programmer] as [Any]

for dev in devs {
if let dev = dev as? Person {
print(dev)
print("遵守Person协议")
} else {
print("未遵守Person协议")
}
}
+

AnyObject Any

1
2
3
4
var anyObjectArray: [AnyObject] = [CGFloat(0.5) as AnyObject,
1 as AnyObject,
"string" as AnyObject,
iOSODev]
+

swift是面向函数编程, 函数是一等公民,数组中可以存入一个函数,函数表达的是一个过程,不是一个名词或者物体,所以函数不是一个对象,需要使用 any 关键字

+
1
2
3
4
var anyArray: [Any] = [CGFloat(0.5),
1,
"string",
iOSODev]
+

放入函数

+
1
anyArray.append({ (a: Int) -> (Int) in return a * a })
+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ +

ReactNative配置.npm 和cnpm 相关

npm

Nodejs的包管理工具

+

npm 常用命令

    +
  • 更新或卸载

    +
    1
    npm update/uninstall moduleName
    +
  • +
  • 查看当前目录下已安装的包

    +
    1
    npm list
    +
  • +
  • 查看全局安装的包的路径

    +
    1
    npm root -g
    +
  • +
  • 全部命令

    +
    1
    npm help
    +
  • +
+

cnpm

因为npm安装插件是从国外服务器下载,受网络影响大,可能出现异常,cnpm 淘宝镜像

+

安装

1
npm install -g cnpm --registry=https://registry.npm.taobao.org
+

设置镜像源

1
2
cnpm set registry http://registry.cnpm.taobao.com
npm set registry http://registry.cnpm.taobao.com
+

配置 .npmrc 文件

前端项目开发离不开安装各种npm依赖包,可以选择远程的仓库也可以选择本地的仓库,但更改仓库地址需要在安装时控制台打命令,比较麻烦, 而npmrc可以很方便地解决上面问题

+

.npmrc 原理

当安装项目的依赖包时,会查找并读取项目根目录下的.npmrc文件的配置,自动指定仓库地址

+
    +
  • 打开 .npmrc

    +
    1
    open .npmrc
    +
  • +
+
    +
  • 编辑文件, 指定特定的库的镜像源, 比如以@test开头的库镜像为:http://registry.cnpm.test.com/ 而其他的正常

    +
    1
    2
    @test:registry=http://registry.cnpm.lietou.com/
    registry=https://registry.npm.taobao.org/
    +
  • +
+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ +

iOS通过URL获取HTML中标题

+

获取标题

String扩展一个方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private let patternTitle = "(?<=title>).*(?=</title)"
extension String {
static func parseHTMLTitle(_ urlString: String) -> String? {
let url = URL(string: urlString)!
do {
let html = try String(contentsOf: url, encoding: .utf8)
let range = html.range(of: patternTitle, options: String.CompareOptions.regularExpression, range: html.startIndex..<html.endIndex, locale: nil)
guard let tempRange = range else { return "" }
let titleStr = html[tempRange]
return String(titleStr)
} catch {
return ""
}
}
}
+
iOS 中简单正则表达式

iOS 中 正则表达式中的\ 需要使用 \\ 来表示

+
// 获取
+let testStr = "我到底是[iOS]开发还是[产品]经理"
+
+// 获取 "[" 和 "]" 之间的所有内容, 左边[ 起一直到 右边 ] 结束的所有内容 
+let pattern1 = "\\[.*\\]" // [iOS]开发还是[产品]
+
+// 获取 "[" 和 "]" 之间的所有内容,包括[]
+let pattern2 = "\\[.*?\\]"    // [iOS]
+
+// 获取第一个 "[" 和 "]" 之间的所有内容 不包括[] 
+let pattern3 = "(?<=\\[).*?(?=\\])" // iOS
+
+// 获取html中 title 标签中内容
+let pattern5 = "(?<=title>).*(?=</title)" 
+
获取范围截取子串
let testRange1 = testStr.range(of: pattern1, options: String.CompareOptions.regularExpression, range: testStr.startIndex..<testStr.endIndex, locale: nil)
+    let result1 = String(testStr[testRange1!])
+
+
+let testRange2 = testStr.range(of: pattern2, options: String.CompareOptions.regularExpression, range: testStr.startIndex..<testStr.endIndex, locale: nil)
+let result2 = String(testStr[testRange2!])
+
+
+let testRange3 = testStr.range(of: pattern3, options: String.CompareOptions.regularExpression, range: testStr.startIndex..<testStr.endIndex, locale: nil)
+let result3 = String(testStr[testRange3!])
+
+print(result1) 
+print(result2) 
+print(result3) 
+
正则表达式

通过URL解析html中的标题

+
    +
  • 获取html字符串
  • +
  • 正则匹配
  • +
  • 生成字符串Range
  • +
  • 截取字符串
  • +
+

匹配一次直接返回结果

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 生成表达式
let regular = try NSRegularExpression(pattern: pattern5, options: .caseInsensitive)


// 开始匹配
let matchResult1 = regular.firstMatch(in: html, options: NSRegularExpression.MatchingOptions.reportProgress, range: NSMakeRange(0, html.count))

// 获取匹配结果范围
let matchRange = matchResult1?.range
let range = Range(matchRange!)!

// 生成String.Index 类型范围
let startIndex = html.index(html.startIndex, offsetBy: range.lowerBound)
let endIndex = html.index(html.startIndex, offsetBy: range.upperBound)
let range3 = Range(uncheckedBounds: (lower: startIndex, upper: endIndex))

// 截取字符串
let result1 = String(html[range3])
print(result1)
+

全部匹配后返回结果时一个集合

+
1
2
3
4
5
6
7
8
9
10
let matchResult2 = regular.matches(in: html, options: .reportProgress, range: NSMakeRange(0, html.count))
matchResult2.forEach { (result) in
let range = Range(result.range)!

let startIndex = html.index(html.startIndex, offsetBy: range.lowerBound)
let endIndex = html.index(html.startIndex, offsetBy: range.upperBound)

let subStr2 = html[Range(uncheckedBounds: (startIndex, endIndex))]
print(String(subStr2))
}
+

将匹配到的内容提还

+
1
let str3 = regular.stringByReplacingMatches(in: html, options: .reportProgress, range: NSMakeRange(0, html.count), withTemplate: "====")
+ + +
+ @@ -1045,23 +1092,23 @@

Recent Posts

diff --git a/page/3/index.html b/page/3/index.html index 40d4a4f..4a2869f 100644 --- a/page/3/index.html +++ b/page/3/index.html @@ -65,7 +65,7 @@

+ +
+ + + +
+ +
+ + +
+ +

flutter 常用组件

组件

    +
  • StatefulWidget
      +
    • 具有可变状态的窗口部件,使用应用时可随时变化,例如进度条
    • +
    +
  • +
  • StatelessWidget
      +
    • 不可变窗口部件
    • +
    +
  • +
+

Text 组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//  Text 组件
class MyText extends StatelessWidget{
@override
Widget build(BuildContext context){
return Text('reacctNative、 flutter 学习之路是非常坚信不疑的,作为一个程序员,因该对于技术有着追求,不断的提高自己才是王道,可是道理都懂依然过不好这一生',
maxLines: 2,
overflow: TextOverflow.fade,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20.0,
color: Colors.deepOrange,
decoration: TextDecoration.underline,
decorationColor: Colors.redAccent,
decorationStyle: TextDecorationStyle.dotted
),
);
}
}
+

COntainer 组件

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

// Container容器组件
class MyContainer extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
child: Text('Container 容器中有文字',
style: TextStyle(
backgroundColor: Colors.cyan,
),
),
alignment: Alignment.center, // 容器中内容对其方式
width: 300.0,
height: 200.0,
padding: const EdgeInsets.all(10), // 容器内边距
margin: const EdgeInsets.fromLTRB(10, 5, 10, 5), // container 和外部控件的边距
decoration: new BoxDecoration(
gradient: const LinearGradient(
colors: [Colors.lightBlue, Colors.greenAccent, Colors.yellow]
),

// 设置 border 的宽度
border: Border.all(width: 5, color: Colors.black)

),
);
}
}
+

Image 组件

fit属性

    +
  • BoxFit.fill:全图显示,图片会被拉伸,并充满父容器。
  • +
  • BoxFit.contain:全图显示,显示原比例,可能会有空隙。
  • +
  • BoxFit.cover:显示可能拉伸,可能裁切,充满(图片要充满整个容器,还不变形)。
  • +
  • BoxFit.fitWidth:宽度充满(横向充满),显示可能拉伸,可能裁切。
  • +
  • BoxFit.fitHeight :高度充满(竖向充满),显示可能拉伸,可能裁切。
  • +
  • BoxFit.scaleDown:效果和contain差不多,但是此属性不允许显示超过源图片大小,可小不可大。
  • +
+

图片的混合模式

    +
  • color:是要混合的颜色,如果你只设置color是没有意义的。
  • +
  • colorBlendMode: 是混合模式。
  • +
+

repeat 图片填充模式

    +
  • ImageRepeat.repeat : 横向和纵向都进行重复,直到铺满整个画布。
  • +
  • ImageRepeat.repeatX: 横向重复,纵向不重复。
  • +
  • ImageRepeat.repeatY:纵向重复,横向不重复
  • +
+
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
class MyImage extends StatelessWidget {
@override
Widget build(BuildContext context){
return Container(
child: new Image.network(
// 'https://images.xiaozhuanlan.com/photo/2019/28ed76123023e46483fcf0ae5c2ba11b.png',
'https://p.ampmake.com/mall/product/9666f00e-f02a-46ce-9b7c-090bf6aba9aa.png',

// 1. 设置填充模式
// fit: BoxFit.scaleDown,
// fit: BoxFit.none,
// fit: BoxFit.fill,
// fit: BoxFit.fitHeight,
// fit: BoxFit.fitWidth,

// 保持原图比例
// fit: BoxFit.contain,

// 充满整个容器,图片被裁切
// fit: BoxFit.cover,
// scale: 1.0,

// 2. 图片混合
color: Colors.red,
colorBlendMode: BlendMode.overlay,

// 3. reap
repeat: ImageRepeat.repeat,
),
width: 300.0,
height: 200.0,
color: Colors.lightBlue,
);
}
}
+

ListView 列表组件

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
class MyListView extends StatelessWidget{
@override
Widget build(BuildContext contex){

return ListView(
children: <Widget>[
new ListTile(
leading: new Icon(Icons.camera),
title: Text('相册'),
),

new ListTile(
leading: new Icon(Icons.camera_alt),
title: Text('相机'),
),

new ListTile(
leading: new Icon(Icons.camera_enhance),
title: Text('相机1'),
),

new ListTile(
leading: new Icon(Icons.camera_front),
title: Text('相机2'),
),

new ListTile(
leading: new Icon(Icons.camera_rear),
title: Text('相机3'),
),
],
);
}
}
+

ListView.builder 适合列表项比较多(或者无限)的情况

1
2
3
4
5
6
7
8
9
10
11
12
13
class MyListViewBuilder extends StatelessWidget {

@override
Widget build(BuildContext context){
return ListView.builder(
itemCount: 100, // 数量
itemExtent: 40, // Tile 高度
itemBuilder: (BuildContext context, int index){
return ListTile(title: Text("$index"));
}
);
}
}
+

横向列表

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
class MyHorizontalListView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
height: 100,
child: ListView(
// 设置滚动方式
scrollDirection: Axis.horizontal,
children: <Widget>[
new Container(
width: 180.0,
color: Colors.green,
),
new Container(
width: 180.0,
color: Colors.yellow,
),
new Container(
width: 180.0,
color: Colors.blue,
),
new Container(
width: 180.0,
color: Colors.cyan,
),
new Container(
width: 180.0,
color: Colors.purple,
),
],
),
);
}
}
+

动态列表和构造方法的创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class MyDynamicListView extends StatelessWidget {
// 声明参数
final List<String> items;
// 构造方法
MyDynamicListView({Key key, @required this.items}):super(key:key);
@override
Widget build(BuildContext context) {
return Container(
child: new ListView.builder(
itemCount: items.length,
itemBuilder: (context, index){
return ListTile(
title: new Text('通过构造方法创建的动态列表${items[index]}'),
);
},
),
);
}
}
+

GridView 网格列表的使用

+ +
+ @@ -183,7 +243,7 @@

@@ -197,7 +257,7 @@

- Share + Share @@ -207,10 +267,10 @@

+
@@ -225,7 +285,7 @@

- Share + Share @@ -235,10 +295,10 @@

+
@@ -247,45 +307,48 @@

-

flutter 常用组件

组件

    -
  • StatefulWidget
      -
    • 具有可变状态的窗口部件,使用应用时可随时变化,例如进度条
    • -
    -
  • -
  • StatelessWidget
      -
    • 不可变窗口部件
    • -
    -
  • -
-

Text 组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//  Text 组件
class MyText extends StatelessWidget{
@override
Widget build(BuildContext context){
return Text('reacctNative、 flutter 学习之路是非常坚信不疑的,作为一个程序员,因该对于技术有着追求,不断的提高自己才是王道,可是道理都懂依然过不好这一生',
maxLines: 2,
overflow: TextOverflow.fade,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20.0,
color: Colors.deepOrange,
decoration: TextDecoration.underline,
decorationColor: Colors.redAccent,
decorationStyle: TextDecorationStyle.dotted
),
);
}
}
-

COntainer 组件

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

// Container容器组件
class MyContainer extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
child: Text('Container 容器中有文字',
style: TextStyle(
backgroundColor: Colors.cyan,
),
),
alignment: Alignment.center, // 容器中内容对其方式
width: 300.0,
height: 200.0,
padding: const EdgeInsets.all(10), // 容器内边距
margin: const EdgeInsets.fromLTRB(10, 5, 10, 5), // container 和外部控件的边距
decoration: new BoxDecoration(
gradient: const LinearGradient(
colors: [Colors.lightBlue, Colors.greenAccent, Colors.yellow]
),

// 设置 border 的宽度
border: Border.all(width: 5, color: Colors.black)

),
);
}
}
-

Image 组件

fit属性

    -
  • BoxFit.fill:全图显示,图片会被拉伸,并充满父容器。
  • -
  • BoxFit.contain:全图显示,显示原比例,可能会有空隙。
  • -
  • BoxFit.cover:显示可能拉伸,可能裁切,充满(图片要充满整个容器,还不变形)。
  • -
  • BoxFit.fitWidth:宽度充满(横向充满),显示可能拉伸,可能裁切。
  • -
  • BoxFit.fitHeight :高度充满(竖向充满),显示可能拉伸,可能裁切。
  • -
  • BoxFit.scaleDown:效果和contain差不多,但是此属性不允许显示超过源图片大小,可小不可大。
  • -
-

图片的混合模式

    -
  • color:是要混合的颜色,如果你只设置color是没有意义的。
  • -
  • colorBlendMode: 是混合模式。
  • -
-

repeat 图片填充模式

    -
  • ImageRepeat.repeat : 横向和纵向都进行重复,直到铺满整个画布。
  • -
  • ImageRepeat.repeatX: 横向重复,纵向不重复。
  • -
  • ImageRepeat.repeatY:纵向重复,横向不重复
  • -
-
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
class MyImage extends StatelessWidget {
@override
Widget build(BuildContext context){
return Container(
child: new Image.network(
// 'https://images.xiaozhuanlan.com/photo/2019/28ed76123023e46483fcf0ae5c2ba11b.png',
'https://p.ampmake.com/mall/product/9666f00e-f02a-46ce-9b7c-090bf6aba9aa.png',

// 1. 设置填充模式
// fit: BoxFit.scaleDown,
// fit: BoxFit.none,
// fit: BoxFit.fill,
// fit: BoxFit.fitHeight,
// fit: BoxFit.fitWidth,

// 保持原图比例
// fit: BoxFit.contain,

// 充满整个容器,图片被裁切
// fit: BoxFit.cover,
// scale: 1.0,

// 2. 图片混合
color: Colors.red,
colorBlendMode: BlendMode.overlay,

// 3. reap
repeat: ImageRepeat.repeat,
),
width: 300.0,
height: 200.0,
color: Colors.lightBlue,
);
}
}
-

ListView 列表组件

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
class MyListView extends StatelessWidget{
@override
Widget build(BuildContext contex){

return ListView(
children: <Widget>[
new ListTile(
leading: new Icon(Icons.camera),
title: Text('相册'),
),

new ListTile(
leading: new Icon(Icons.camera_alt),
title: Text('相机'),
),

new ListTile(
leading: new Icon(Icons.camera_enhance),
title: Text('相机1'),
),

new ListTile(
leading: new Icon(Icons.camera_front),
title: Text('相机2'),
),

new ListTile(
leading: new Icon(Icons.camera_rear),
title: Text('相机3'),
),
],
);
}
}
-

ListView.builder 适合列表项比较多(或者无限)的情况

1
2
3
4
5
6
7
8
9
10
11
12
13
class MyListViewBuilder extends StatelessWidget {

@override
Widget build(BuildContext context){
return ListView.builder(
itemCount: 100, // 数量
itemExtent: 40, // Tile 高度
itemBuilder: (BuildContext context, int index){
return ListTile(title: Text("$index"));
}
);
}
}
-

横向列表

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
class MyHorizontalListView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
height: 100,
child: ListView(
// 设置滚动方式
scrollDirection: Axis.horizontal,
children: <Widget>[
new Container(
width: 180.0,
color: Colors.green,
),
new Container(
width: 180.0,
color: Colors.yellow,
),
new Container(
width: 180.0,
color: Colors.blue,
),
new Container(
width: 180.0,
color: Colors.cyan,
),
new Container(
width: 180.0,
color: Colors.purple,
),
],
),
);
}
}
-

动态列表和构造方法的创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class MyDynamicListView extends StatelessWidget {
// 声明参数
final List<String> items;
// 构造方法
MyDynamicListView({Key key, @required this.items}):super(key:key);
@override
Widget build(BuildContext context) {
return Container(
child: new ListView.builder(
itemCount: items.length,
itemBuilder: (context, index){
return ListTile(
title: new Text('通过构造方法创建的动态列表${items[index]}'),
);
},
),
);
}
}
-

GridView 网格列表的使用

+

Xcode中使用Debug Memory Graph检查内存泄漏

勾选如下图所示选项

+

运行程序后点击

+

内存泄露定位

+

+ + +

+ + + +
+ + + +
+ +
+ + +
+ +

Swift 中错误处理

+

assert 断言

只在debug 模式下程序会自动退出

+
1
public func assert(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)
+

assertionFailure 断言错误

只在debug 模式下程序会自动退出, 没有条件

+
1
public func assertionFailure(_ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)
+

precondition

releas 模式程序也会退出

+
1
public func precondition(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)
+

throw

@@ -298,7 +361,7 @@

@@ -337,7 +400,7 @@

- Share + Share @@ -347,10 +410,10 @@

+
@@ -359,15 +422,147 @@

-

Xcode中使用Debug Memory Graph检查内存泄漏

勾选如下图所示选项

-

运行程序后点击

-

内存泄露定位

-

+

Swift协议和别名

+

协议

某些属性的集合,可以有多个协议组成,具有的特点

+
    +
  1. 可以声明函数,但是不能实现,由实现这协议的具体结构或者类来实现
  2. +
  3. 声明的函数如果有参数也不能设置参数的默认值, 默认参数被认为也是函的一种实现
  4. +
  5. 可以声明属性,必须是计算型属性 只能使用var 修饰,不能是存储型属,必须在 {} 中明确指出属性的读取类型 例如 {get set}
  6. +
  7. 遵守协议的类可选择存储型属性或者计算型属性来实现协议中定义的属性
  8. +
  9. protocol Person: class { } 表示此协议只能被类来遵守,结构体能遵守
  10. +
  11. 协议可以看着是一种类型,如果协议中的属性是只读的,如果是协议类型只能访问get 方法, 但是遵守协议的具体类或者结构可以设置属性的读和写

    +
  12. +
  13. 协议中的构造函数

    +
    - 如果父类中遵守了协议中的构造函数,子类中必须重写父类中的构造函数并且使用require修饰构造函数,表示继承此类的子类也必须实现该构造函数
    +- 如果class 使用了final 修饰,表示不能被继承,此时不需要 require 关键词修饰
    +
  14. +
+

类型别名

    +
  • 定义别名 typealias, 供扩展使用

    +
    1
    2
    3
    4
    5
    6
    7
    8
    typealias Length = Double
    extension Double {
    var km: Length {return self * 1_000.0}
    var m: Length {return self * 100.0}
    var cm: Length {return self / 10.0}
    /// 英尺
    var ft: Length { return self / 3.28084}
    }
    +
  • +
+
使用
+
+
1
let distance: Length = 10.5.km
+
    +
  • 定义别名防止硬编码

    +
    1
    2
    	// 音频采样率
    typealias AudioSample = UInt64
    +
  • +
+

协议中 typealias

协议中定义一个属性,该属性的类型根据不同的情况返回不同的类型(我理解为类似泛型),具体的类型由遵守此协议的类或者结构指定

+
    +
  • 定义协议中别名
  • +
+
1
2
3
4
5
protocol WeightCalculable {
// 属性重量根据不同体积返回的可能是Int,可以可能是double类型,所以使用别名, 协议中使用associatedtype关键词,
associatedtype WeightType
var weight: WeightType { get }
}
+

扩展 Int 中的吨单位

+
1
2
3
4
extension Int {
typealias Weight = Int
var t: Weight {return self * 1_000}
}
+
    +
  • 使用协议中别名
  • +
+
    +
  • 一部手机的时使用该协议WeightCalculable
  • +
+
1
2
3
4
5
6
class iPhone: WeightCalculable {
typealias WeightType = Double
var weight: WeightType {
return 0.5
}
}
+
    +
  • 一辆大卡时使用该协议WeightCalculable
  • +
+
1
2
3
4
5
6
7
8
class Car: WeightCalculable {
typealias WeightType = Int
let weight: WeightType

init(weight: Int) {
self.weight = weight
}
}
+
1
let bigCar = Car(weight: 8_000.t) // 8 吨
+

面向协议编程

    +
  • 定义比赛协议,具有 赢/输/总场次/胜率 等特性
  • +
+
1
2
3
4
5
6
protocol Recordable: CustomStringConvertible {
var wins: Int { get }
var losses: Int { get }

func winningPerent() -> Double
}
+
    +
  • 协议中方法或者属性的默认实现
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
extension Recordable {
// CustomStringConvertible 协议默认实现
var description: String {
return String(format: "WINS: %d---LOSSES: %d", [wins, losses])
}

// 默认实现总场数计算型属性
var totalGames: Int {
return wins + losses
}

// 默认实现的方法
func shoutWins() {
print("come on", wins, "times!")
}
}
+

篮球比赛:

    +
  • Equatable 协议 - 相等比较 需要重载 ==
  • +
  • Comparable 协议 - 是否可比较 ,重载 < 可直接调用sort()函数
  • +
  • CustomStringConvertible 协议 - 打印信息,需重写 description
  • +
  • Recordable协议 - 比赛协议,没有平局
  • +
+
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
struct BasketballRecord:Equatable, Comparable, CustomStringConvertible, Recordable {
var wins: Int
var losses: Int


/// 在具体定义的类型中实现 协议中的属性或者方法就会覆盖协议中的
var totalGames: Int = 200

var description: String {
return "本次比赛胜: " + String(wins) + "负" + "\(losses)"
}

// 胜率
func winningPerent() -> Double {
return (Double(wins) / Double(totalGames))
}

// swift4.1 后,不需要再重写此方法,swift 会自动合成,如果不想让某一个属性参与比较,就需要重写该方法
static func == (lhs: BasketballRecord, rhs: BasketballRecord) -> Bool {
return lhs.wins == rhs.wins && lhs.losses == rhs.losses
}

// Comparable
static func < (lhs: BasketballRecord, rhs: BasketballRecord) -> Bool {
if lhs.wins != rhs.wins {
return lhs.wins < rhs.wins
}
return lhs.losses > rhs.losses
}
}
```

#### 足球比赛:

有平局的情况,原来的协议不能满足,需要增加一个平局的协议

- 定义平局协议
+

protocol Tieable {
/// 平局,可读写
var ties: Int { get set }

+

}

1
2
3
4

- 足球比赛需要遵守的协议 `Recordable` 和 `Tieable`

- 总场数需要加上平局的场数,需要实现totalGames的get方法

+

struct FootableRecord: Recordable, Tieable {
var wins: Int
var losses: Int
// 平局数
var ties: Int

+
// 总场数
+var totalGames: Int {
+    return wins + losses + ties
+}
+
+var description: String {
+    return "本次比赛胜: " + String(wins) + "负" + "\(losses)"
+}
+
+func winningPerent() -> Double {
+    return (Double(wins) / Double(totalGames))
+}
+

}

1
2
3
4
5
6
7
8
9
10
11
12

- 足球比赛中以上写法存在的问题

- 所有有平局的比赛计算总场次的逻辑都是一样,每次都要写一次实现
- 单一修改 Recordable 协议不能解决问题,
- 存在平局和没有平局两种情况 totalGames 的计算逻辑不相同

- 解决办法:
- 扩展遵守了`Recordable`协议的类型,前提条件是:这个类型遵守了 Tieable 同时也遵守了 `Recordable ` 协议,可以理解为如果该类型遵守了 `Tieable `协议之后,`Recordable` 协议中的 totalGames 属性实现是另一种方式了,不在是以前的 `totalGames` 中直接返回 `wins + losses` 的值


定义协议

+

protocol Recordable: CustomStringConvertible {
var wins: Int { get }
var losses: Int { get }

+
func winningPerent() -> Double
+

}

1
2

协议默认实现

+

extension Recordable {
var description: String {
return String(format: “WINS: %d—LOSSES: %d”, [wins, losses])
}

+
// 默认实现总场数计算型属性
+var totalGames: Int {
+    return wins + losses
+}
+
+// 默认实现胜率
+func winningPerent() -> Double {
+    return (Double(wins) / Double(totalGames))
+}
+
+// 默认实现的方法
+func shoutWins() {
+    print("come on", wins, "times!")
+}
+

}

+
1
2

扩展遵守了Tieable 的 类型的 Recordable 协议
+

extension Recordable where Self: Tieable {
var totalGames: Int {
return wins + losses + ties
}
}

+
1
2
3
4
5
6

### 协议聚合

如果对某一类型需要限制他必须遵守某些协议,可以使用协议聚合来定义

比如: 有个奖赏协议
+

protocol Prizable {
func isPrizable() -> Bool
}

+
1
2

篮球比赛遵守此协议
+

// 篮球比赛奖赏
extension BasketballRecord: Prizable {
func isPrizable() -> Bool {
return totalGames > 10 && winningPerent() > 0.5
}
}

+
1
2

足球比赛奖赏遵守此协议
+

extension FootableRecord: Prizable {
func isPrizable() -> Bool {
return wins > 1
}
}

1
2

现在有某一个学生也遵守了此协议

+

struct Student: Prizable {
var name: String
var score: Int

+
func isPrizable() -> Bool {
+    return score >= 60
+}
+

}

+
1
2

定义奖赏的方法,参数类型必须是遵守了此协议的结构或者类型
+

private func award(_ one: Prizable) {
if one.isPrizable() {
print(one)
print(“恭喜获得奖励”)
} else {
print(one)
print(“很遗憾”)
}
}

1
2

如果说 BasketballRecord 这个类还遵守了其他的协议,例如遵守了 `Recordable` 协议, 并且这个协议也遵守了 `CustomStringConvertible` 并且默认实现了`description` 的`get` 方法

+

// MARK: - 比赛协议,具有 赢。输。总场次。胜率 特性
protocol Recordable: CustomStringConvertible {
var wins: Int { get }
var losses: Int { get }

+
func winningPerent() -> Double
+

}

+
1
2

`Recordable` 默认实现
+

extension Recordable {
var description: String {
return String(format: “WINS: %d—LOSSES: %d”, [wins, losses])
}

+
// 默认实现总场数计算型属性
+var totalGames: Int {
+    return wins + losses
+}
+
+// 默认实现胜率
+func winningPerent() -> Double {
+    return (Double(wins) / Double(totalGames))
+}
+
+// 默认实现的方法
+func shoutWins() {
+    print("come on", wins, "times!")
+}
+

}

1
2
3
4
5

此时如果 `Student` 类还是调用 `award` 方法的话,print(one) 打印的信息将是`Recordable `中默认实现的内容,因此需要约束`award`函数的参数必须遵守两个协议让`Student` 也重写自己的`description `属性的`get`方法,不能再让 `Prizable` 扩展 默认实现

- swift 3 写法: protocol<A, B>
- swift 4 写法: A & B

+

private func award2(_ one: Prizable & CustomStringConvertible) {
if one.isPrizable() {
print(one)
print(“恭喜获得奖励”)
} else {
print(one)
print(“很遗憾”)
}
}

1
2
3
4
5
6
7
8
9
10

### 泛型约束


- 定义一个函数,找出一个学生数组中分数最大的
- 参数:一个学生数组,都遵守了 Comparable 的类型
- 返回值:某个遵守了 Comparable 的类型实例
- 此时函数报错 `Protocol 'Comparable' can only be used as a generic constraint because it has Self or associated type requirements`,

因为 Comparable 协议中定义的方法 public static func < (lhs: Self, rhs: Self) -> Bool 的参数类型是Self,是具体的某个类型

+

func maxScore(seq: [Comparable]) -> Comparable { }

1
2
3
4
5


- 如果需要定义一个函数实现在一个数组中找出需要奖励的人的名字该如何实现呢
- 参数:遵守两个协议 Comparable 和 Prizable 协议, 并且使用泛型
- 返回值:返回值是可选值,有可能没有任何奖励的对象

+

func topPrizable<T: Comparable & Prizable>(seq: [T]) -> T? {
return seq.reduce(nil) { (tempTop: T?, condender: T) in
guard condender.isPrizable() else { return tempTop }
// 解包 condender 失败, 上一层验证了他必须是奖励的那个
guard let tempTop = tempTop else { return condender }
return max(tempTop, condender)
}
}

+

`

@@ -380,7 +575,7 @@

@@ -423,92 +618,7 @@

- - -

- - - -
- -
- - -
- -

Swift 泛型

泛型定义–wikipedia

    -
  1. 在程序编码中一些包含类型参数的类型,也就是说泛型的参数只可以代表类,不能代表个别对象。(这是当今较常见的定义)
  2. -
  3. 在程序编码中一些包含参数的类。其参数可以代表类或对象等等。(现在人们大多把这称作模板)
  4. -
-

泛型的用处

泛型可以实现传入一个指定类型后做一些操作然后返回后还是原来指定的类型,你可以指定特定的类型(特定是指约束)

-

泛型和 Any 的区别

区别:Any能代表任何类型,但是不能返回某个具体的类型,都需要使用 as 去转化,而泛型正好相反

-
    -
  • Any 可以代表任何类型,除了class之外还可以代表struct,enum,
  • -
  • AnyObject 可以代码任何 class 的类型, swift 中基本类型, ArrayDictionary 都是 struct 类型,都不能使用 AnyObject 来接受
  • -
-

Swift中Any 和 AnyObject用法

-

泛型函数和泛型参数

swapTwoValues是一个泛型函数,可以接收或者返回任何类型

-
1
2
3
4
5
func swapTwoValues<T>(inout a: T, inout b: T) {
let temporaryA = a
a = b
b = temporaryA
}
-
    -
  • swapTwoValues 函数名后面的 <T> 表示这个 T 类型是函数所定义的一个类型, T 只是一个类型的占位符,不用关心具体类型

    -
  • -
  • (inout a: T, inout b: T) 中a和b两个参数类型T是泛型参数

    -
  • -
-

类型约束

两个泛型参数的泛型函数

-
1
2
3
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
// function body goes here
}
-
    -
  • 泛型参数T约束条件:必须是SomeClass类的子类
  • -
  • 泛型参数U约束条件:必须遵守SomeProtocol协议
  • -
  • 使用where指定约束
  • -
- - -
- -
- -
- - - -
- -
- - -
- -

Swift 中错误处理

-

assert 断言

只在debug 模式下程序会自动退出

-
1
public func assert(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)
-

assertionFailure 断言错误

只在debug 模式下程序会自动退出, 没有条件

-
1
public func assertionFailure(_ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)
-

precondition

releas 模式程序也会退出

-
1
public func precondition(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)
-

throw

- -
- @@ -568,23 +678,23 @@

Recent Posts

diff --git a/page/4/index.html b/page/4/index.html index 1d79f19..093f875 100644 --- a/page/4/index.html +++ b/page/4/index.html @@ -62,10 +62,62 @@

+
+ +
+ + +
+ +

Swift 泛型

泛型定义–wikipedia

    +
  1. 在程序编码中一些包含类型参数的类型,也就是说泛型的参数只可以代表类,不能代表个别对象。(这是当今较常见的定义)
  2. +
  3. 在程序编码中一些包含参数的类。其参数可以代表类或对象等等。(现在人们大多把这称作模板)
  4. +
+

泛型的用处

泛型可以实现传入一个指定类型后做一些操作然后返回后还是原来指定的类型,你可以指定特定的类型(特定是指约束)

+

泛型和 Any 的区别

区别:Any能代表任何类型,但是不能返回某个具体的类型,都需要使用 as 去转化,而泛型正好相反

+
    +
  • Any 可以代表任何类型,除了class之外还可以代表struct,enum,
  • +
  • AnyObject 可以代码任何 class 的类型, swift 中基本类型, ArrayDictionary 都是 struct 类型,都不能使用 AnyObject 来接受
  • +
+

Swift中Any 和 AnyObject用法

+

泛型函数和泛型参数

swapTwoValues是一个泛型函数,可以接收或者返回任何类型

+
1
2
3
4
5
func swapTwoValues<T>(inout a: T, inout b: T) {
let temporaryA = a
a = b
b = temporaryA
}
+
    +
  • swapTwoValues 函数名后面的 <T> 表示这个 T 类型是函数所定义的一个类型, T 只是一个类型的占位符,不用关心具体类型

    +
  • +
  • (inout a: T, inout b: T) 中a和b两个参数类型T是泛型参数

    +
  • +
+

类型约束

两个泛型参数的泛型函数

+
1
2
3
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
// function body goes here
}
+
    +
  • 泛型参数T约束条件:必须是SomeClass类的子类
  • +
  • 泛型参数U约束条件:必须遵守SomeProtocol协议
  • +
  • 使用where指定约束
  • +
+ + +
+ +
+ +
+ + +
@@ -93,7 +145,7 @@

- -
- - - -
- -
- - -
- -

Swift协议和别名

-

协议

某些属性的集合,可以有多个协议组成,具有的特点

-
    -
  1. 可以声明函数,但是不能实现,由实现这协议的具体结构或者类来实现
  2. -
  3. 声明的函数如果有参数也不能设置参数的默认值, 默认参数被认为也是函的一种实现
  4. -
  5. 可以声明属性,必须是计算型属性 只能使用var 修饰,不能是存储型属,必须在 {} 中明确指出属性的读取类型 例如 {get set}
  6. -
  7. 遵守协议的类可选择存储型属性或者计算型属性来实现协议中定义的属性
  8. -
  9. protocol Person: class { } 表示此协议只能被类来遵守,结构体能遵守
  10. -
  11. 协议可以看着是一种类型,如果协议中的属性是只读的,如果是协议类型只能访问get 方法, 但是遵守协议的具体类或者结构可以设置属性的读和写

    -
  12. -
  13. 协议中的构造函数

    -
    - 如果父类中遵守了协议中的构造函数,子类中必须重写父类中的构造函数并且使用require修饰构造函数,表示继承此类的子类也必须实现该构造函数
    -- 如果class 使用了final 修饰,表示不能被继承,此时不需要 require 关键词修饰
    -
  14. -
-

类型别名

    -
  • 定义别名 typealias, 供扩展使用

    -
    1
    2
    3
    4
    5
    6
    7
    8
    typealias Length = Double
    extension Double {
    var km: Length {return self * 1_000.0}
    var m: Length {return self * 100.0}
    var cm: Length {return self / 10.0}
    /// 英尺
    var ft: Length { return self / 3.28084}
    }
    -
  • -
-
使用
-
-
1
let distance: Length = 10.5.km
-
    -
  • 定义别名防止硬编码

    -
    1
    2
    	// 音频采样率
    typealias AudioSample = UInt64
    -
  • -
-

协议中 typealias

协议中定义一个属性,该属性的类型根据不同的情况返回不同的类型(我理解为类似泛型),具体的类型由遵守此协议的类或者结构指定

-
    -
  • 定义协议中别名
  • -
-
1
2
3
4
5
protocol WeightCalculable {
// 属性重量根据不同体积返回的可能是Int,可以可能是double类型,所以使用别名, 协议中使用associatedtype关键词,
associatedtype WeightType
var weight: WeightType { get }
}
-

扩展 Int 中的吨单位

-
1
2
3
4
extension Int {
typealias Weight = Int
var t: Weight {return self * 1_000}
}
-
    -
  • 使用协议中别名
  • -
-
    -
  • 一部手机的时使用该协议WeightCalculable
  • -
-
1
2
3
4
5
6
class iPhone: WeightCalculable {
typealias WeightType = Double
var weight: WeightType {
return 0.5
}
}
-
    -
  • 一辆大卡时使用该协议WeightCalculable
  • -
-
1
2
3
4
5
6
7
8
class Car: WeightCalculable {
typealias WeightType = Int
let weight: WeightType

init(weight: Int) {
self.weight = weight
}
}
-
1
let bigCar = Car(weight: 8_000.t) // 8 吨
-

面向协议编程

    -
  • 定义比赛协议,具有 赢/输/总场次/胜率 等特性
  • -
-
1
2
3
4
5
6
protocol Recordable: CustomStringConvertible {
var wins: Int { get }
var losses: Int { get }

func winningPerent() -> Double
}
-
    -
  • 协议中方法或者属性的默认实现
  • -
-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
extension Recordable {
// CustomStringConvertible 协议默认实现
var description: String {
return String(format: "WINS: %d---LOSSES: %d", [wins, losses])
}

// 默认实现总场数计算型属性
var totalGames: Int {
return wins + losses
}

// 默认实现的方法
func shoutWins() {
print("come on", wins, "times!")
}
}
-

篮球比赛:

    -
  • Equatable 协议 - 相等比较 需要重载 ==
  • -
  • Comparable 协议 - 是否可比较 ,重载 < 可直接调用sort()函数
  • -
  • CustomStringConvertible 协议 - 打印信息,需重写 description
  • -
  • Recordable协议 - 比赛协议,没有平局
  • -
-
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
struct BasketballRecord:Equatable, Comparable, CustomStringConvertible, Recordable {
var wins: Int
var losses: Int


/// 在具体定义的类型中实现 协议中的属性或者方法就会覆盖协议中的
var totalGames: Int = 200

var description: String {
return "本次比赛胜: " + String(wins) + "负" + "\(losses)"
}

// 胜率
func winningPerent() -> Double {
return (Double(wins) / Double(totalGames))
}

// swift4.1 后,不需要再重写此方法,swift 会自动合成,如果不想让某一个属性参与比较,就需要重写该方法
static func == (lhs: BasketballRecord, rhs: BasketballRecord) -> Bool {
return lhs.wins == rhs.wins && lhs.losses == rhs.losses
}

// Comparable
static func < (lhs: BasketballRecord, rhs: BasketballRecord) -> Bool {
if lhs.wins != rhs.wins {
return lhs.wins < rhs.wins
}
return lhs.losses > rhs.losses
}
}
```

#### 足球比赛:

有平局的情况,原来的协议不能满足,需要增加一个平局的协议

- 定义平局协议
-

protocol Tieable {
/// 平局,可读写
var ties: Int { get set }

-

}

1
2
3
4

- 足球比赛需要遵守的协议 `Recordable` 和 `Tieable`

- 总场数需要加上平局的场数,需要实现totalGames的get方法

-

struct FootableRecord: Recordable, Tieable {
var wins: Int
var losses: Int
// 平局数
var ties: Int

-
// 总场数
-var totalGames: Int {
-    return wins + losses + ties
-}
-
-var description: String {
-    return "本次比赛胜: " + String(wins) + "负" + "\(losses)"
-}
-
-func winningPerent() -> Double {
-    return (Double(wins) / Double(totalGames))
-}
-

}

1
2
3
4
5
6
7
8
9
10
11
12

- 足球比赛中以上写法存在的问题

- 所有有平局的比赛计算总场次的逻辑都是一样,每次都要写一次实现
- 单一修改 Recordable 协议不能解决问题,
- 存在平局和没有平局两种情况 totalGames 的计算逻辑不相同

- 解决办法:
- 扩展遵守了`Recordable`协议的类型,前提条件是:这个类型遵守了 Tieable 同时也遵守了 `Recordable ` 协议,可以理解为如果该类型遵守了 `Tieable `协议之后,`Recordable` 协议中的 totalGames 属性实现是另一种方式了,不在是以前的 `totalGames` 中直接返回 `wins + losses` 的值


定义协议

-

protocol Recordable: CustomStringConvertible {
var wins: Int { get }
var losses: Int { get }

-
func winningPerent() -> Double
-

}

1
2

协议默认实现

-

extension Recordable {
var description: String {
return String(format: “WINS: %d—LOSSES: %d”, [wins, losses])
}

-
// 默认实现总场数计算型属性
-var totalGames: Int {
-    return wins + losses
-}
-
-// 默认实现胜率
-func winningPerent() -> Double {
-    return (Double(wins) / Double(totalGames))
-}
-
-// 默认实现的方法
-func shoutWins() {
-    print("come on", wins, "times!")
-}
-

}

-
1
2

扩展遵守了Tieable 的 类型的 Recordable 协议
-

extension Recordable where Self: Tieable {
var totalGames: Int {
return wins + losses + ties
}
}

-
1
2
3
4
5
6

### 协议聚合

如果对某一类型需要限制他必须遵守某些协议,可以使用协议聚合来定义

比如: 有个奖赏协议
-

protocol Prizable {
func isPrizable() -> Bool
}

-
1
2

篮球比赛遵守此协议
-

// 篮球比赛奖赏
extension BasketballRecord: Prizable {
func isPrizable() -> Bool {
return totalGames > 10 && winningPerent() > 0.5
}
}

-
1
2

足球比赛奖赏遵守此协议
-

extension FootableRecord: Prizable {
func isPrizable() -> Bool {
return wins > 1
}
}

1
2

现在有某一个学生也遵守了此协议

-

struct Student: Prizable {
var name: String
var score: Int

-
func isPrizable() -> Bool {
-    return score >= 60
-}
-

}

-
1
2

定义奖赏的方法,参数类型必须是遵守了此协议的结构或者类型
-

private func award(_ one: Prizable) {
if one.isPrizable() {
print(one)
print(“恭喜获得奖励”)
} else {
print(one)
print(“很遗憾”)
}
}

1
2

如果说 BasketballRecord 这个类还遵守了其他的协议,例如遵守了 `Recordable` 协议, 并且这个协议也遵守了 `CustomStringConvertible` 并且默认实现了`description` 的`get` 方法

-

// MARK: - 比赛协议,具有 赢。输。总场次。胜率 特性
protocol Recordable: CustomStringConvertible {
var wins: Int { get }
var losses: Int { get }

-
func winningPerent() -> Double
-

}

-
1
2

`Recordable` 默认实现
-

extension Recordable {
var description: String {
return String(format: “WINS: %d—LOSSES: %d”, [wins, losses])
}

-
// 默认实现总场数计算型属性
-var totalGames: Int {
-    return wins + losses
-}
-
-// 默认实现胜率
-func winningPerent() -> Double {
-    return (Double(wins) / Double(totalGames))
-}
-
-// 默认实现的方法
-func shoutWins() {
-    print("come on", wins, "times!")
-}
-

}

1
2
3
4
5

此时如果 `Student` 类还是调用 `award` 方法的话,print(one) 打印的信息将是`Recordable `中默认实现的内容,因此需要约束`award`函数的参数必须遵守两个协议让`Student` 也重写自己的`description `属性的`get`方法,不能再让 `Prizable` 扩展 默认实现

- swift 3 写法: protocol<A, B>
- swift 4 写法: A & B

-

private func award2(_ one: Prizable & CustomStringConvertible) {
if one.isPrizable() {
print(one)
print(“恭喜获得奖励”)
} else {
print(one)
print(“很遗憾”)
}
}

1
2
3
4
5
6
7
8
9
10

### 泛型约束


- 定义一个函数,找出一个学生数组中分数最大的
- 参数:一个学生数组,都遵守了 Comparable 的类型
- 返回值:某个遵守了 Comparable 的类型实例
- 此时函数报错 `Protocol 'Comparable' can only be used as a generic constraint because it has Self or associated type requirements`,

因为 Comparable 协议中定义的方法 public static func < (lhs: Self, rhs: Self) -> Bool 的参数类型是Self,是具体的某个类型

-

func maxScore(seq: [Comparable]) -> Comparable { }

1
2
3
4
5


- 如果需要定义一个函数实现在一个数组中找出需要奖励的人的名字该如何实现呢
- 参数:遵守两个协议 Comparable 和 Prizable 协议, 并且使用泛型
- 返回值:返回值是可选值,有可能没有任何奖励的对象

-

func topPrizable<T: Comparable & Prizable>(seq: [T]) -> T? {
return seq.reduce(nil) { (tempTop: T?, condender: T) in
guard condender.isPrizable() else { return tempTop }
// 解包 condender 失败, 上一层验证了他必须是奖励的那个
guard let tempTop = tempTop else { return condender }
return max(tempTop, condender)
}
}

-

`

- - -
- -
- -
- - - -
- -
- - -
- -

Reat Native ref 使用

-

State 状态的使用

在父控件中指定,整个生命周期讲不再改变
需求:一段闪烁的文字, Props 属性显示文字内容创建时初始化,State控制随时间变化是否显示,在父控件中

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Blink extends Component{
// 构造
constructor(props) {
super(props);
// 初始状态
this.state = {showWithText:true};
this.state = {}
setInterval(() => {
this.setState(previousState => {
return {showWithText: !previousState.showWithText}
});
}, 100);
}
render() {
let display= this.state.showWithText ? this.props.text : ' ';
return(
<Text>{display}</Text>
);
}
}
-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
export default class App extends Component<Props> {
render() {
return(
<View style={styles.container}>
<Blink
text='I love to blink'
/>

<Blink
text='猜猜猜'
/>

<Blink
text='Blink is great'
/>
</View>
);
}
}
-

Props 属性的使用

1
2
3
4
5
6
7
8
9
// 定义一个组件
class Greeting extends Component {
render() {
return(
<Text> Hello {this.props.name}!
</Text>
);
}
}
-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
export default class App extends Component<Props> {
render() {
return(
<View style={styles.container}>
<Image source={{uri:'https://upload.wikimedia.org/wikipedia/commons/d/de/Bananavarieties.jpg'}}
style={{width: 190, height: 110}}
/>
<Text style={styles.instructions}>
使用Props属性
</Text>
<View>
<Greeting name='React-Native'
/>
<Greeting name='iOS'
/>
<Greeting name='Swift'
/>
</View>

</View>
);
}
}
-

ref的使用

    -
  • 可以理解为组件渲染后指向组件的一个应用,可以通过ref获取到真实的组件(类似指针?)
  • -
-

定义

-
1
ref='scrollView'
-

获取

-
1
2
let scrollView = this.refs.scrollView
let scrollView = this.refs['scrollView']
-
    -
  • 可以通过ref访问组件的属性和方法
  • -
-

ES6 定时器

    -
  • 开启定时器
  • -
-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
let scrollView = this.refs.scrollView;
// 2.4timer 定时器的写法
this.timer = setInterval(
()=>{
var tempPage = 0;
// 修改banner索引
if ((this.state.currentPage+1) >= ImageData.data.length) {
tempPage = 0;
} else {
tempPage = this.state.currentPage+1;
}

// 更新状态
this.setState({
currentPage: tempPage
})

let offSet_x = width * tempPage
scrollView.scrollResponderScrollTo({x:offSet_x, y:0, animated: true})
},
this.state.duration
);
-
    -
  • 暂停定时器
  • -
-
1
this.timer && clearInterval(this.timer)
- - -
- @@ -383,7 +223,7 @@

@@ -445,7 +285,7 @@

- Share + Share @@ -458,7 +298,7 @@

@@ -567,7 +407,7 @@

- Share + Share @@ -577,10 +417,10 @@

+
@@ -589,72 +429,35 @@

-

RN学习中遇到的错误总结

-

执行 npm install 时报错

错误1. Unexpected end of JSON input while parsing near '...er":"0.4.0"},"bin":{"'

本地有package.json文件,执行 npm install 报错,或者clone 的项目执行npm install 也报同样的错误, 可能是因为缓存导致,

-

解决办法:

-
    -
  1. 设置镜像源,以前可能大部分都是用的淘宝

    -
    1
    npm set registry https://registry.npmjs.org/
    -
  2. -
  3. 清除缓存

    -
    1
    npm cache clean –-force
    -
  4. -
  5. 如果提示失败,使用sudo

    -
    1
    sudo npm cache clean --force
    -
  6. -
-

错误2. xcrun: error: unable to find utility "simctl", not a developer tool or in PATH

可能是因为 Xcode 版本问题引起,XCode 偏好设置中 Command line Tools 中为选择版本问题导致(我的情况是安装了Xcode 10 beta版导致的)

-

解决办法:

-

-
1
2
3
4
5
6
7
8
9
10
11
12
The following build commands failed:
CompileC /Users/liepin/LiePinWorkspace/RNDemo/GitHubPopular/ios/build/Build/Intermediates.noindex/RCTWebSocket.build/Debug-iphonesimulator/RCTWebSocket.build/Objects-normal/x86_64/RCTSRWebSocket.o RCTSRWebSocket.m normal x86_64 objective-c com.apple.compilers.llvm.clang.1_0.compiler
(1 failure)
Installing build/Build/Products/Debug-iphonesimulator/GitHubPopular.app
An error was encountered processing the command (domain=NSPOSIXErrorDomain, code=2):
Failed to install the requested application
An application bundle was not found at the provided path.
Provide a valid path to the desired application bundle.
Print: Entry, ":CFBundleIdentifier", Does Not Exist

Command failed: /usr/libexec/PlistBuddy -c Print:CFBundleIdentifier build/Build/Products/Debug-iphonesimulator/GitHubPopular.app/Info.plist
Print: Entry, ":CFBundleIdentifier", Does Not Exist
-

错误3. Error: Cannot find module '../lib/utils/unsupported.js'

安装的node 版本不是稳定的版本,需要删除后重新安装

-
1
2
sudo rm -rf /usr/local/lib/node_modules/npm
brew reinstall node
-

错误4. Couldn't find preset "module:metro-react-native-babel-preset" when running jest

解决方法

-

已有项目集成RN

报错1. 文件路径错误,查看你的node_modules 文件夹是在当前目录还是上级目录

-
[!] No podspec found for `React` in `../node_modules/react-native
-
    -
  • ../ 是指父级目录

    -
  • -
  • ./ 是指当前目录

    -
  • +

    Reat Native ref 使用

    +

    State 状态的使用

    在父控件中指定,整个生命周期讲不再改变
    需求:一段闪烁的文字, Props 属性显示文字内容创建时初始化,State控制随时间变化是否显示,在父控件中

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    class Blink extends Component{
    // 构造
    constructor(props) {
    super(props);
    // 初始状态
    this.state = {showWithText:true};
    this.state = {}
    setInterval(() => {
    this.setState(previousState => {
    return {showWithText: !previousState.showWithText}
    });
    }, 100);
    }
    render() {
    let display= this.state.showWithText ? this.props.text : ' ';
    return(
    <Text>{display}</Text>
    );
    }
    }
    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    export default class App extends Component<Props> {
    render() {
    return(
    <View style={styles.container}>
    <Blink
    text='I love to blink'
    />

    <Blink
    text='猜猜猜'
    />

    <Blink
    text='Blink is great'
    />
    </View>
    );
    }
    }
    +

    Props 属性的使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 定义一个组件
    class Greeting extends Component {
    render() {
    return(
    <Text> Hello {this.props.name}!
    </Text>
    );
    }
    }
    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    export default class App extends Component<Props> {
    render() {
    return(
    <View style={styles.container}>
    <Image source={{uri:'https://upload.wikimedia.org/wikipedia/commons/d/de/Bananavarieties.jpg'}}
    style={{width: 190, height: 110}}
    />
    <Text style={styles.instructions}>
    使用Props属性
    </Text>
    <View>
    <Greeting name='React-Native'
    />
    <Greeting name='iOS'
    />
    <Greeting name='Swift'
    />
    </View>

    </View>
    );
    }
    }
    +

    ref的使用

      +
    • 可以理解为组件渲染后指向组件的一个应用,可以通过ref获取到真实的组件(类似指针?)
    -

    报错 2. yoga Y 大小写为问题

    -
    [!] The name of the given podspec `yoga` doesn't match the expected one `Yoga`_modules/react-native/ReactCommon/yoga"
    -

    pod install 之后

    报错 1.

    -

    解决办法: pod ‘React’, :subspecs 中加入 ‘CxxBridge’, ‘DevSupport’

    -
    pod 'React', :path => './node_modules/react-native', :subspecs => [
    -'Core',
    -'RCTText',
    -'RCTNetwork',
    -'RCTWebSocket', # 这个模块是用于调试功能的
    -'CxxBridge', # Include this for RN >= 0.47
    -'DevSupport', # Include this to enable In-App Devmenu if RN >= 0.43
    -

    ]

    -

    报错 2.

    RCTAnimation/RCTValueAnimatedNode.h' file not found

    -

    解决方法:

    -

    -
    1
    #import <RCTAnimation/RCTValueAnimatedNode.h>
    -

    替换为

    -
    1
    #import "RCTValueAnimatedNode.h"
    -

    修改头文件导入方式

    -

    报错3 版本不匹配 react native version mismatch javascript version 0.54.3 native 0.57.2

      -
    • 关闭所有的 terminal,或者是集成开发环境的命令行窗口

      -
      1
      2
      watchman watch-del-all
      react-native start --reset-cache
      -
    • -
    • 重新打开一个terminal窗口

      -
      1
      react-native run-ios
      -

      问题描述和解决1

      -

      问题描述和解决2

      -
    • +

      定义

      +
      1
      ref='scrollView'
      +

      获取

      +
      1
      2
      let scrollView = this.refs.scrollView
      let scrollView = this.refs['scrollView']
      +
        +
      • 可以通过ref访问组件的属性和方法
      -

      报错4 Unable to resolve module "schedule/tracking"

      缺少一些开发依赖的库

      -
      npm i schedule@0.4.0 --save-dev
      -

      问题描述和解决

      +

      ES6 定时器

        +
      • 开启定时器
      • +
      +
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      let scrollView = this.refs.scrollView;
      // 2.4timer 定时器的写法
      this.timer = setInterval(
      ()=>{
      var tempPage = 0;
      // 修改banner索引
      if ((this.state.currentPage+1) >= ImageData.data.length) {
      tempPage = 0;
      } else {
      tempPage = this.state.currentPage+1;
      }

      // 更新状态
      this.setState({
      currentPage: tempPage
      })

      let offSet_x = width * tempPage
      scrollView.scrollResponderScrollTo({x:offSet_x, y:0, animated: true})
      },
      this.state.duration
      );
      +
        +
      • 暂停定时器
      • +
      +
      1
      this.timer && clearInterval(this.timer)

@@ -667,7 +470,7 @@

@@ -723,7 +526,7 @@

- Share + Share @@ -733,10 +536,10 @@

+
@@ -745,61 +548,72 @@

-

js 学习

-

数据类型

1
2
3
4
5
6
7
8
var fullName;
// undefined

fullName
// undefined

fullName + 2
// NaN
-
    -
  • 判断类型名称

    -
    1
    2
    3
    4
    fullName = '张'
    "张"
    typeof(fullName)
    // "string"
    +

    RN学习中遇到的错误总结

    +

    执行 npm install 时报错

    错误1. Unexpected end of JSON input while parsing near '...er":"0.4.0"},"bin":{"'

    本地有package.json文件,执行 npm install 报错,或者clone 的项目执行npm install 也报同样的错误, 可能是因为缓存导致,

    +

    解决办法:

    +
      +
    1. 设置镜像源,以前可能大部分都是用的淘宝

      +
      1
      npm set registry https://registry.npmjs.org/
    2. -
-
1
2
3
4
var weight = 100;
undefined
typeof(weight)
// "number"
- - - -
1
2
3
4
var firstName = '王', lastName = '张'
undefined
firstName + lastName
// "王张"
-
    -
  • 类型不相同时转换后在拼接

    -
    1
    2
    3
    4
    var weightIncrease = '2.5斤'
    undefined
    weight + weightIncrease
    // "1002.5斤"
    +
  • 清除缓存

    +
    1
    npm cache clean –-force
  • -
-

数组

var array = [];
-undefined
-array.length
-// 0
-typeof(array)
-// "object"
+
  • 如果提示失败,使用sudo

    +
    1
    sudo npm cache clean --force
    +
  • + +

    错误2. xcrun: error: unable to find utility "simctl", not a developer tool or in PATH

    可能是因为 Xcode 版本问题引起,XCode 偏好设置中 Command line Tools 中为选择版本问题导致(我的情况是安装了Xcode 10 beta版导致的)

    +

    解决办法:

    +

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    The following build commands failed:
    CompileC /Users/liepin/LiePinWorkspace/RNDemo/GitHubPopular/ios/build/Build/Intermediates.noindex/RCTWebSocket.build/Debug-iphonesimulator/RCTWebSocket.build/Objects-normal/x86_64/RCTSRWebSocket.o RCTSRWebSocket.m normal x86_64 objective-c com.apple.compilers.llvm.clang.1_0.compiler
    (1 failure)
    Installing build/Build/Products/Debug-iphonesimulator/GitHubPopular.app
    An error was encountered processing the command (domain=NSPOSIXErrorDomain, code=2):
    Failed to install the requested application
    An application bundle was not found at the provided path.
    Provide a valid path to the desired application bundle.
    Print: Entry, ":CFBundleIdentifier", Does Not Exist

    Command failed: /usr/libexec/PlistBuddy -c Print:CFBundleIdentifier build/Build/Products/Debug-iphonesimulator/GitHubPopular.app/Info.plist
    Print: Entry, ":CFBundleIdentifier", Does Not Exist
    +

    错误3. Error: Cannot find module '../lib/utils/unsupported.js'

    安装的node 版本不是稳定的版本,需要删除后重新安装

    +
    1
    2
    sudo rm -rf /usr/local/lib/node_modules/npm
    brew reinstall node
    +

    错误4. Couldn't find preset "module:metro-react-native-babel-preset" when running jest

    解决方法

    +

    已有项目集成RN

    报错1. 文件路径错误,查看你的node_modules 文件夹是在当前目录还是上级目录

    +
    [!] No podspec found for `React` in `../node_modules/react-native
     
      -
    • 增加元素

      -
      1
      2
      3
      4
      array.push('1')
      // 4
      array
      // (4) ["ab", "cbd", "fcg", "1"]
      +
    • ../ 是指父级目录

    • -
    • 数组前面添加

      +
    • ./ 是指当前目录

    -
    1
    2
    3
    4
    array.unshift('0')
    // 6
    array
    // (6) ["0", "ab", "cbd", "fcg", "1", Array(2)]
    -
      -
    • 删除最后元素

      -
      1
      2
      3
      4
      array.pop() // 返回删除的元素
      // (2) ["2", "3"]
      array
      // (5) ["0", "ab", "cbd", "fcg", "1"]
      +

      报错 2. yoga Y 大小写为问题

      +
      [!] The name of the given podspec `yoga` doesn't match the expected one `Yoga`_modules/react-native/ReactCommon/yoga"
      +

      pod install 之后

      报错 1.

      +

      解决办法: pod ‘React’, :subspecs 中加入 ‘CxxBridge’, ‘DevSupport’

      +
      pod 'React', :path => './node_modules/react-native', :subspecs => [
      +'Core',
      +'RCTText',
      +'RCTNetwork',
      +'RCTWebSocket', # 这个模块是用于调试功能的
      +'CxxBridge', # Include this for RN >= 0.47
      +'DevSupport', # Include this to enable In-App Devmenu if RN >= 0.43
      +

      ]

      +

      报错 2.

      RCTAnimation/RCTValueAnimatedNode.h' file not found

      +

      解决方法:

      +

      +
      1
      #import <RCTAnimation/RCTValueAnimatedNode.h>
      +

      替换为

      +
      1
      #import "RCTValueAnimatedNode.h"
      +

      修改头文件导入方式

      +

      报错3 版本不匹配 react native version mismatch javascript version 0.54.3 native 0.57.2

        +
      • 关闭所有的 terminal,或者是集成开发环境的命令行窗口

        +
        1
        2
        watchman watch-del-all
        react-native start --reset-cache
      • -
      • 删除第一个元素

        -
        1
        2
        3
        4
        5
        6
        	array.shift()
        // "0"
        array
        // (4) ["ab", "cbd", "fcg", "1"]
        ```
        - 删除指定的值,但数组元素没有被删
        -

        delete array[2]
        // true
        array
        // (4) [“ab”, “cbd”, empty, “1”]

        -
        1
        2
        	
        - 删除指定的元素
        -

        array.splice(1)
        // (3) [“cbd”, empty, “1”]
        array
        // [“ab”]

        -
        1
        2
        3
        4
        	
        ## 函数

        ### 调用调用
        +
      • 重新打开一个terminal窗口

        +
        1
        react-native run-ios
        +

        问题描述和解决1

        +

        问题描述和解决2

      -

      alertMessage();

      -

      function alertMessageWithParamter(message) {
      alert(message)
      }

      -

      alertMessageWithParamter(‘有参数的函数’)
      alertMessageWithParamter(250)
      alertMessageWithParamter(
      console.log(‘test’)
      )

      -
      1
      ### 函数表达式,使用函数声明的方式
      -

      var alertMessage_expression = function expression_alert (message) {
      alert(message)
      }

      -

      alertMessage_expression(‘匿名函数调用’)

      -

      expression_alert(‘函数表达式’)
      `

      +

      报错4 Unable to resolve module "schedule/tracking"

      缺少一些开发依赖的库

      +
      npm i schedule@0.4.0 --save-dev
      +

      问题描述和解决

      @@ -812,7 +626,7 @@

      数组 @@ -978,7 +792,83 @@

      + + +

    + + + +
    + +
    + + +
    + +

    js 学习

    +

    数据类型

    1
    2
    3
    4
    5
    6
    7
    8
    var fullName;
    // undefined

    fullName
    // undefined

    fullName + 2
    // NaN
    +
      +
    • 判断类型名称

      +
      1
      2
      3
      4
      fullName = '张'
      "张"
      typeof(fullName)
      // "string"
      +
    • +
    +
    1
    2
    3
    4
    var weight = 100;
    undefined
    typeof(weight)
    // "number"
    + + + +
    1
    2
    3
    4
    var firstName = '王', lastName = '张'
    undefined
    firstName + lastName
    // "王张"
    +
      +
    • 类型不相同时转换后在拼接

      +
      1
      2
      3
      4
      var weightIncrease = '2.5斤'
      undefined
      weight + weightIncrease
      // "1002.5斤"
      +
    • +
    +

    数组

    var array = [];
    +undefined
    +array.length
    +// 0
    +typeof(array)
    +// "object"
    +
      +
    • 增加元素

      +
      1
      2
      3
      4
      array.push('1')
      // 4
      array
      // (4) ["ab", "cbd", "fcg", "1"]
      +
    • +
    • 数组前面添加

      +
    • +
    +
    1
    2
    3
    4
    array.unshift('0')
    // 6
    array
    // (6) ["0", "ab", "cbd", "fcg", "1", Array(2)]
    +
      +
    • 删除最后元素

      +
      1
      2
      3
      4
      array.pop() // 返回删除的元素
      // (2) ["2", "3"]
      array
      // (5) ["0", "ab", "cbd", "fcg", "1"]
      +
    • +
    • 删除第一个元素

      +
      1
      2
      3
      4
      5
      6
      	array.shift()
      // "0"
      array
      // (4) ["ab", "cbd", "fcg", "1"]
      ```
      - 删除指定的值,但数组元素没有被删
      +

      delete array[2]
      // true
      array
      // (4) [“ab”, “cbd”, empty, “1”]

      +
      1
      2
      	
      - 删除指定的元素
      +

      array.splice(1)
      // (3) [“cbd”, empty, “1”]
      array
      // [“ab”]

      +
      1
      2
      3
      4
      	
      ## 函数

      ### 调用调用
      +
    • +
    +

    alertMessage();

    +

    function alertMessageWithParamter(message) {
    alert(message)
    }

    +

    alertMessageWithParamter(‘有参数的函数’)
    alertMessageWithParamter(250)
    alertMessageWithParamter(
    console.log(‘test’)
    )

    +
    1
    ### 函数表达式,使用函数声明的方式
    +

    var alertMessage_expression = function expression_alert (message) {
    alert(message)
    }

    +

    alertMessage_expression(‘匿名函数调用’)

    +

    expression_alert(‘函数表达式’)
    `

    + + +
    + @@ -1038,23 +928,23 @@

    Recent Posts

    diff --git a/page/5/index.html b/page/5/index.html index ef2b6e7..d8a2970 100644 --- a/page/5/index.html +++ b/page/5/index.html @@ -65,7 +65,7 @@

    - -
    - - - -
    - -
    - - -
    - -

    Charles 抓包

    -

    原理

    简单理解如下:

    -
      -
    • 正常请求

      -

      客户端 ——> 服务器

      -
    • -
    -
      -
    • Charles 抓包

      -

      客户端 ——> 代理 (Charles)——> 服务器

      -
    • -
    -

    安装

    Charles官网

    -

    破解版本请自行百度

    -

    重要说明安装之前请暂时关闭电脑的VPN

    -

    重要说明安装之前请暂时关闭电脑的VPN

    -

    重要说明安装之前请暂时关闭电脑的VPN

    -

    启动Charles

    安装Charles证书

    -
    如果安装失败,请检查以前是否已经安装过,如果已经安装过并且已经失效,请删除失效证书后再安装
    -

    打开钥匙串–> 选择Charles CA -> 双击证书 –> 信任证书

    -

    设置 https

    - Proxy -> SSL Proxying Settings -> SSL Proxying -> Add
    -- Host: * 为需要过滤的域名地址,
    -- *: 表示不过滤Port, 固定为443 `*`表示任意端口
    -

    -

    Mac 抓包

      -
    • Proxy -> macOS Proxy (☑️)

      -

      -
    • -
    -

    真机抓包

    此时可以将 macOS Proxy 选项中的 ✅ 去掉,

    -

    Mac与iPhone连接必须同一网络

    设置代理端口号

    -

    手机安装证书

      -
    • 安装手机证书

      -

      -
    • -
    • 安装完成后弹窗

      -

      -
    • -
    -
      -
    • 查看mac 地址,为手机设置代理

      -
        -
      1. 根据安装手机证书后的弹窗显示IP配置

        -
      2. -
      3. Charles –> Help –> Loca IP Address

        -

        -
      4. -
      5. 打开电脑网络设置查看

        -

        -
      6. -
      -
    • -
    -
      -
    • 设置代理

      -

      第一步

      -

      第二步

      -

      第三步

      -
    • -
    • 根据弹窗提示,在手机浏览器下载并安装证书

      -

      -
    • -
    -
      -
    • 手机信任证书

      -
        -
      • 设置 –> 通用 –> 关于本机 –> 证书信任设置

        -

        -
      • -
      -
    • -
    -

    模拟器抓包

      -
    • 安装模拟器证书

      -

      -
    • -
    • 勾选 macOS Proxy 选项

      -

      -
    • -
    -

    如果失败,请先关闭模拟器,重新启动Charles 再打开模拟器

    -

    使用浏览器打开网页提示不是私密链接

    解决办法

    - - -
    - @@ -241,7 +133,7 @@

    @@ -354,7 +246,115 @@

    13

    + + +
    + + + +
    + +
    + + +
    + +

    Charles 抓包

    +

    原理

    简单理解如下:

    +
      +
    • 正常请求

      +

      客户端 ——> 服务器

      +
    • +
    +
      +
    • Charles 抓包

      +

      客户端 ——> 代理 (Charles)——> 服务器

      +
    • +
    +

    安装

    Charles官网

    +

    破解版本请自行百度

    +

    重要说明安装之前请暂时关闭电脑的VPN

    +

    重要说明安装之前请暂时关闭电脑的VPN

    +

    重要说明安装之前请暂时关闭电脑的VPN

    +

    启动Charles

    安装Charles证书

    +
    如果安装失败,请检查以前是否已经安装过,如果已经安装过并且已经失效,请删除失效证书后再安装
    +

    打开钥匙串–> 选择Charles CA -> 双击证书 –> 信任证书

    +

    设置 https

    - Proxy -> SSL Proxying Settings -> SSL Proxying -> Add
    +- Host: * 为需要过滤的域名地址,
    +- *: 表示不过滤Port, 固定为443 `*`表示任意端口
    +

    +

    Mac 抓包

      +
    • Proxy -> macOS Proxy (☑️)

      +

      +
    • +
    +

    真机抓包

    此时可以将 macOS Proxy 选项中的 ✅ 去掉,

    +

    Mac与iPhone连接必须同一网络

    设置代理端口号

    +

    手机安装证书

      +
    • 安装手机证书

      +

      +
    • +
    • 安装完成后弹窗

      +

      +
    • +
    +
      +
    • 查看mac 地址,为手机设置代理

      +
        +
      1. 根据安装手机证书后的弹窗显示IP配置

        +
      2. +
      3. Charles –> Help –> Loca IP Address

        +

        +
      4. +
      5. 打开电脑网络设置查看

        +

        +
      6. +
      +
    • +
    +
      +
    • 设置代理

      +

      第一步

      +

      第二步

      +

      第三步

      +
    • +
    • 根据弹窗提示,在手机浏览器下载并安装证书

      +

      +
    • +
    +
      +
    • 手机信任证书

      +
        +
      • 设置 –> 通用 –> 关于本机 –> 证书信任设置

        +

        +
      • +
      +
    • +
    +

    模拟器抓包

      +
    • 安装模拟器证书

      +

      +
    • +
    • 勾选 macOS Proxy 选项

      +

      +
    • +
    +

    如果失败,请先关闭模拟器,重新启动Charles 再打开模拟器

    +

    使用浏览器打开网页提示不是私密链接

    解决办法

    + + +
    + @@ -394,7 +394,7 @@

    @@ -450,7 +450,7 @@

    - Share + Share @@ -526,7 +526,7 @@

    - Share + Share @@ -636,7 +636,7 @@

    Runlo @@ -812,7 +812,7 @@

    - Share + Share @@ -886,7 +886,7 @@

    Socket @@ -1145,7 +1145,7 @@

    - Share + Share @@ -1207,23 +1207,23 @@

    Recent Posts

    diff --git a/page/6/index.html b/page/6/index.html index f4a9fe0..b36b30e 100644 --- a/page/6/index.html +++ b/page/6/index.html @@ -114,7 +114,7 @@

    - Share + Share @@ -166,7 +166,7 @@

    @@ -247,7 +247,7 @@

    - Share + Share @@ -301,7 +301,7 @@

    - Share + Share @@ -349,7 +349,7 @@

    @@ -406,7 +406,7 @@

    - Share + Share @@ -569,7 +569,7 @@

    - Share + Share @@ -676,7 +676,7 @@

    - Share + Share @@ -725,7 +725,7 @@

    - Share + Share @@ -836,7 +836,7 @@

    - Share + Share @@ -898,23 +898,23 @@

    Recent Posts

    diff --git a/page/7/index.html b/page/7/index.html index 2fbada4..4ed7809 100644 --- a/page/7/index.html +++ b/page/7/index.html @@ -127,7 +127,7 @@

    - Share + Share @@ -213,7 +213,7 @@

    - Share + Share @@ -263,7 +263,7 @@

    - Share + Share @@ -348,7 +348,7 @@

    - Share + Share @@ -476,7 +476,7 @@

    - Share + Share @@ -642,7 +642,7 @@

    - Share + Share @@ -694,7 +694,7 @@

    - Share + Share @@ -783,7 +783,7 @@

    - Share + Share @@ -872,7 +872,7 @@

    - Share + Share @@ -961,7 +961,7 @@

    - Share + Share @@ -1023,23 +1023,23 @@

    Recent Posts

    diff --git a/page/8/index.html b/page/8/index.html index 0f1c2c3..f448f2f 100644 --- a/page/8/index.html +++ b/page/8/index.html @@ -139,7 +139,7 @@

    - Share + Share @@ -240,7 +240,7 @@

    - Share + Share @@ -344,7 +344,7 @@

    分类

    原理

    由运行时来决议的,不同分类当中含有同名方法 谁最终生效取决于谁最终参与编译,最后参编译的同名分类方法会最终生效,假如分类方法中添加的方法和宿主类中的某一个方法名相同,分类中的方法会覆盖宿主类中的同名方法,覆盖是指在消息传递过程中优先查找数组靠前的元素,如果查找到了同名方法就直接调用,实际上宿主类的同名方法实现仍然是存在的,我们可可以通过一些手段调用到原有类的同名方法的实现

    使用场景

    • 申明私有方法,分类的 .m 文件中申明私有方法啊,对外不暴露
    • 对类中的代码进行抽取分解
    • 把Framework的私有方法公开化

    特点

    • 运行时决议,运行时通过runtime 才把分类中的方法添加到了宿主类上
    • 可以为系统类添加方法
    • 有多个分类中存在同名方法时,最后编译的方法会最先生效
    • 分类中有和宿主类同名方法时,底层中通过内存copy将分类方法“覆盖”宿主类方法
    • 名字相同的分类编译会报错

    分类中可以添加的内容

    • 实例方法
    • 类方法
    • 协议
    • 属性,实际上只是声明了 getter / setter 方法,并没有添加成员变量

    关联对象技术

    关联对象由 AssocicationsManager 管理并在 AssociationsHashMap 上存储
    所有对象的关联内容都在同一个全局容器中

    • 给分类添加成员变量,通过关联对象的方式

    扩展

    使用场景

    • 声明私有属性
    • 声明私有方法
    • 声明私有成员变量

    特点

    • 编译时决议
    • 只以声明的形式存在,多数情况下寄生在宿主类的.m中
    • 不能为系统添加扩展

    代理

    • 代理设计模式,传递方式是一对一
    • 使用weak 避免循环引用

    通知

    使用观察者模式来实现的用于跨层传递消息的机制

    KVO

    KVO 是系统对观察者模式的又一实现,使用isa 混写技术(isa - swizzling)来动态运行时为某一个类添加一个子类重写了它的setter 方法,同时将原有类的isa指针指向了新创建的类上

    • KVO 是OC对观察者模式的又一实现
    • apple 使用isa 混写技术(isa - swizzling)来实现KVO
      • 给我A类注册一个观察者时本质上是调用系统的 Observer for keyPath 方法,系统会创建一个NSKVONotifiying_A 的类
    • 使用 setter 方法设置值KVO才能生效
    • 使用setValue:forKey: 改变值KVO才能生效
    • 使用下划线的成员变量直接修改值需要手动添加KVO才能生效(增加 willChangeValueForKey 和 didChangeValueForKey 两个方法)

    KVC

    apple提供的键值编码技术

    • (nullable id)valueForKey:(NSString *)key;
    • (void)setValue:(nullable id)value forKey:(NSString *)key;

      以上两个方法中的key是没有任何限制的,只要知道对应的成员变量名称,就可以对私有的成员变量设置和操作,这违背了面向对象的编程思想

    (void)setValue:(nullable id)value forKey:(NSString *)key; 方法调用流程

    • 先判断是否有跟key 相关的setter 方法,如果有就直接调用,结束调用
    • 如果没有再判断是否存在实例变量,如果存在直接给赋值,结束调用
    • 如果说实例变量不存在,会去调用 - (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key; 抛出异常

    属性关键字

    读写权限

    • readonly

    • readwrite 系统默认

    原子性

    • atomic (系统默认)保证赋值和获取(对成员属性的直接获取和赋值)时线程安全,并不代表操作和访问,如果atomic修饰的一个数组,只能保证独具改数组的读取和赋值时线程安全,并不能保证对数据操作(增加/删除数组元素)时是安全的
    • nonatomic

    引用技术

    • assign
      • 修饰基本数据类型,
      • 修饰对象时不改变其引用计数,
      • 会产生悬垂指针
    • weak
      • 不改变被修饰对象的引用计数,
      • 所指对象被释放之后指针自动置为nil

    两者区别:

    • weak可以修饰对象,而assign 既可以修饰对象也可以修饰基本数据类型
    • assign 修饰的对象,对象释放后指针仍然指向原对象的内存地址,而weak 修饰的对象,释放后会自动置为nil

    问题1

    weak 修饰的对象为什么释放后会自动置为nil

    问题2

    @property (copy) NSMutableArray *array; 有什么问题

    • 被copy修饰后,如果赋值过来的是NSMutableArray,copy之后就是NSArray,如果赋值过来的是NSAarray,copy 之后仍然是NSArray,有可能会调用array的添加元素方法会导致crash

    copy 关键词

    • 浅拷贝:内存地址的复制,让目标对象指针和源对象指向同一块内存空间,
      • 会增加对象的引用计数
      • 并没有一个新的内存分配
    • 深拷贝:目标对象指针和源对象指针分别指向两块内容相同的内存空间
      • 不会增加源对象的引用计数
      • 有新对象的内存分配

    MRC 重写 retain修饰的变量的setter 方法

    1
    2
    3
    4
    5
    6
    7
    8

    - (void)setObj:(id)obj {
    if (_obj != obj) {
    [_obj release];
    }
    _obj = [obj retain];

    }

    判断是为了防止异常处理,如果不做 if 判断,当传入的 obj 对象正好是原来的_obj 对象,对原对象尽行releas 操作,实际上也会对传入的对象进行releas 操作进行释放,此时如果再通过obj指针访问废弃的对象时就会导致carsh

    ]]> @@ -18,10 +18,10 @@ - - /2019/06/18/%E7%AC%AC%E5%8D%81%E5%9B%9B%E7%AB%A0%E7%AC%AC%E4%B8%89%E6%96%B9%E5%BA%93%E9%9D%A2%E8%AF%95/ + + /2019/06/18/%E7%AC%AC%E5%8D%81%E4%B8%80%E7%AB%A0%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E9%9D%A2%E8%AF%95/ - 第十四章 第三方库

    AFNetworking

    框架图

    会话NSURSession

    主要类关系图

    AFURLSessionManager

    • 创建和管理 NSURLSession, NSURLSessionTask
    • 实现NSURLSessionDelegate 等协议的代理方法
    • 引入AFSecurityPolicy 保证请求安全,https 请求时 证书检验,公钥验证
    • 引入AFNetworkReachabilityManager 监控网络状态

    SDWebImage

    架构图

    加载图片流程

    Reactive Cocoa

    RACStream

    AsyncDisplayKit

    主要解决的问题

    基本原理

    ]]>
    + 第十一章 设计模式

    六大设计原则

    单一职责原则:一个类只负责一件事

    开闭原则: 对修改关闭,对扩展开放

    接口隔离原则:使用多个专门的协议,而不是一个庞大臃肿的协议,iOS系统的UITableView的数据和代理协议分开

    依赖倒置原则:抽象不应该依赖于具体实现,具体实现可以依赖于抽象

    里氏替换原则:父类可以被子类无缝替换,且原有功能不受任何影响,iOS系统 的 KVO 机制遵守了这一原则,当调用 addObserver 方法时,因为系统在动态运行时为我们创建了一个子类,直观感受是仍然使用的是父类,实际上系统已经替换为创建的子类,所以KVO 机制 不但使用了管擦这模式,并且遵从了 里氏替换原则

    迪米特法则: 一个对象应对其他对象有尽可能少的了解,平时所说的高内聚,低耦合

    常用设计模式

    责任链

    有三个业务分别为A/B/C,以前的调用顺序为 A -> B -> C ,现在顺序有调整,

    定义某一个类,有一个成员变量他的类型跟原有类类型一致,组成一个责任链,让处理具体业务的责任者去处理

    桥接模式

    业务解耦的问题:同一个列表和多套数据显示,多套数据通过后端来控制是共存,

    类构成

    抽象类A ——> class B

    适配器模式

    一个现有类需要适应变化的问题, 年代比较久远的类或者对象

    类构成

    • 对象适配
    • 类适配

    单例模式

    命令模式

    行为参数化

    降低代码重合度

    ]]>
    @@ -31,10 +31,10 @@ - - /2019/06/18/%E7%AC%AC%E5%8D%81%E4%BA%8C%E7%AB%A0%E6%9E%B6%E6%9E%84%E5%92%8C%E6%A1%86%E6%9E%B6%E9%9D%A2%E8%AF%95/ + + /2019/06/18/%E7%AC%AC%E5%8D%81%E4%B8%89%E7%AB%A0%E7%AE%97%E6%B3%95%E9%9D%A2%E8%AF%95/ - 第十二章 架构和框架

    目的

    • 实现模块化
    • 分层
    • 解耦

    图片缓存

    图片缓存框架设计方案?

    图片缓存时内存设计上需要考虑的问题

    • 内存存储的size

      • 10kb的图片开辟的内存空间 50 张
      • 100kb图片的 20 张
      • 100kb 以上的 10 张
        通过队列的方式存储,先进先出的方式淘汰
    • 淘汰策略

      • 以队列先进先出的方式淘汰
      • 已LRU 算法 (如 30 min 之内是否使用过)

    磁盘设计考虑的问题

    • 存储方式
    • 大小限制
    • 淘汰策略(超过 7 天)

    网络部分的设计需要考虑的问题

    • 图片请求最大并发量
    • 请求超时策略,一旦超时了,可以重试,如果重试2次失败,是否还下载
    • 请求优先级,下载或者缓存的图片是否紧急需要

    图片通过什么方式进行读写,过程是怎样的?

    • 以图片的url 的单向哈希值作为key 存储
    • 读取过程

    图片解码

    • 对于不同格式的图片,解码采用什么方式来做?

      • 应用策略模式对于不同图片格式进行解码
    • 在那个阶段做图片解码处理?

      • 再磁盘读取后未解码或者网络请求返回后,解码后再放到内存中,可以减轻主线程的压力,解码在主线程中

    线程处理

    阅读时长统计

    怎样设计一个时长统计框架?

    为何要有不同类型的记录器,处于什么考虑?

    基于不同分类场景提供的关于记录的封装、适配

    记录的数据由于程序杀死或者断电,关机等丢失,你是怎样处理的?

    • 定时写磁盘
    • 限定内存缓存条数,超过该条数就写如磁盘

    记录器上传 中延时上传的具体场景有哪些?上传时机如何把握?

    上传时机

    • 立刻上传
    • 延时上传
    • 定时上传

    延时上传场景

    统计结果立即上传会重复调用接口导致资源浪费

    • 前后台切换
    • 从无网到有网的变化时
    • 通过其他的接口捎带着上传

    复杂页面的架构

    MVVM 框架思想

    RN 的数据流思想

    任何一个子节点是没有权力做自己的变化更新的,它必须把这个消息传递给根结点,根结点通过自顶向下的遍历查找需要更新的节点

    系统UIView 更新机制的思想

    AsyncDisplayKit 关于预排版的设计思想

    客户端整体架构

    需要独立于APP 的通用层
    通用的业务层
    中间层
    业务层

    业务之间的解耦通信方式

    • OpenURL
    • 依赖注入方式, 在中间层通过代理 的方式实现业务A 和业务B之间的通信
    ]]>
    + 第十三章算法

    ]]>
    @@ -44,10 +44,10 @@ - - /2019/06/18/%E7%AC%AC%E5%8D%81%E7%AB%A0%E7%BD%91%E7%BB%9C%E7%9B%B8%E5%85%B3/ + + /2019/06/18/%E7%AC%AC%E5%8D%81%E4%BA%8C%E7%AB%A0%E6%9E%B6%E6%9E%84%E5%92%8C%E6%A1%86%E6%9E%B6%E9%9D%A2%E8%AF%95/ - 第十章 网络请求相关

    HTTP协议

    超文本传输协议

    请求/响应报文

    请求报文:

    • 请求行
      方法 URL 协议版本(1.1版本) CRLF(回车)

    • 首部字段区域
      首部字段名(key) :值(value )CRLF
      首部字段名(key) :值(value )CRLF

    • 实体主体
      get 请求没有
      post 有实体主体

    响应报文:

    • 响应行
      版本 状态码 状态码描述(短语) CRLF

    • 首部字段区

    • 响应实体主体

    连接建立流程

    三次握手和四次挥手

    三次握手

    • 客户端 发送 syn 同步报文到Server,
    • server 端收到syn报文后会,链接建立,server 端会返回一个 syn,ack 报文给客户端,
    • 客户端收到 server 端的ask 报文后,会再次回应一个确认ack 的报文

    数据请求

    • 接下来在已经建立的tcp 通道上进行http 的请求报文
    • server 端回复给客户端一个http响应报文

    四次挥手

    • 客户端发送 fin 终止报文到server 端
    • server 端收到报文后会回回复一个ack 报文到客户端
    • 客户端到server 端的tcp的链接已经断开,但是 server 端到客户端的链接还会传输数据
    • server 端向客户端发送一个fin ack 报文 ,然后客户端回应给server 一个ack 报文,释放链接

    HTTP 的特点

    • 无连接
      非持久链接 需要多次断开/建立链接,就会经历 三次握手,四次挥手

      HTTP 的持久链接: 多个http请求在同一个tcp 通道上, 一定时间内不会断开tcp通道,提高了网络请求的效率

      头部字段:

      coonection : keep-alive 客户端是否采用持久链接time :20 多长时间有效max:10 最多发生多少个http请求和响应对
    • 无状态
      同一个用户多次发送http请求,server 端是不知道是同一个用户的,server 端通过 Cookie/Session 规避这个问题

      • cookie
        cookie 主要是用来记录用户状态,区分用户,cookie由server端返回给客户端,cookie 状态保存在客户端

        保证cookie 的安全

        • cookie 加密处理
        • 只在https 上携带Cookie
        • 设置Cookie为 httpOnly,防止跨站脚本攻击
      • session 用来记录用户的状态,区分用户,状态存放在服务端

    HTTPS 与网络安全

    HTTPS 都使用了那些加密手段,为什么?

    • 建立链接过程使用非对称加密,因为耗时
    • 后续通信数据传输过程中使用对称性加密

    对称性加密

    TCP: 传输控制协议

    • 面向链接
      TCP 是全双工的所以数据传输之前需要三次握手,传输完毕需要四次挥手,
    • 可靠传输

      • 无差错
      • 按序到达
    • 面向字节流

      发送方发送数据时不管一次性提交给TCP的缓冲是多大的数据,TCP本身会根据实际情况来划分数据包再发送给接受数据方

    • 流量控制

      滑动窗口协议

    UDP : 用户数据协议

    • 无连接
      发送数据时不需要建立链接,传输完毕也不需要释放链接

    • 尽最大努力交付
      是不保证可靠传输的,

    • 面向报文
      既不合并,也不拆分

    问题

    GET 和post 请求的区别

    • GET 获取资源的

      • 安全的: 不因该引起Server 端的任何状态变化的
      • 幂等的:同一个请求方法执行多次和执行一次的效果完全相同
      • 可缓存的:请求是可以缓存的,代理服务器可以缓存
      • 请求参数以? 分割拼接到URL 后面
    • post 请求处理资源

      • 不安全的:
      • 非幂等的
      • 不可缓存的

    GET 请求参数有长度限制,是2048 个字符,post 一般没有限制

    HTTPS 链接建立流程

    客户端会发送服务端一个支持的加密算法列表
    包括 TLS 的版本号 + 随机数C,
    服务端再回给客户端一个证书
    包括 商定的加密算法 后续首先通过非对称加密进行对称性加密的密钥传输,http的网络就通过被非对称密钥保护的对称密钥访问和传输数据

    TCP 和 UDP的区别

    tcp是面向链接的,并且支持可靠的传输,支持面向字节流, tcp 提供了流浪上的控制和拥塞的控制
    UDP 提供了简单的复用/分用及差错检测的传输层的功能,

    状态吗有哪些

    • 1XX
    • 2XX:成功
    • 3XX:重定向
    • 4XX:客户端请求错误
    • 5XX: 服务器端请求错误

    为什么需要三次握手,两次行不行?

    解决同步请求链接建立时超时的情况,如果只有两次握手,

    持久链接怎么判断一个请求是否结束

    • 请求/响应报文的头部字段

    响应报文 content-length: 1024 头部字段是由server 端返回数据大小,客户端根据所接收的数据的字节数是否达到了头部字段 cotent-length 的大小判断

    • 通过post请求时server 端返回数据有可能需要多次响应才能返回,此时需要根据chunked 字段名 是否为空来判断

    Charels 抓包原理是什么?

    中间人攻击

    正常步骤是 客户端 —- server

    抓包:发送请求和响应请求都可以通过中间人修改再返回给某一端

    客户端 —中间人—- server

    DNS 解析

    域名到IP地址的映射,DNS解析请求采用UDP数据报,且明文

    NDS 解析存在的常见问题

    • DNS 劫持问题
      钓鱼网站
    • DNS 解析转发问题

    DNS 劫持和HTTP 的关系时怎么样的

    没有关系,

    • DNS 解析发在HTTP 建立连接之前
    • DNS 解析请求使用UDP数据报, 端口是53,跟HTTP没有任何关系

    解决DNS 劫持问题

    • httpDNS
    • 长连接
    ]]>
    + 第十二章 架构和框架

    目的

    • 实现模块化
    • 分层
    • 解耦

    图片缓存

    图片缓存框架设计方案?

    图片缓存时内存设计上需要考虑的问题

    • 内存存储的size

      • 10kb的图片开辟的内存空间 50 张
      • 100kb图片的 20 张
      • 100kb 以上的 10 张
        通过队列的方式存储,先进先出的方式淘汰
    • 淘汰策略

      • 以队列先进先出的方式淘汰
      • 已LRU 算法 (如 30 min 之内是否使用过)

    磁盘设计考虑的问题

    • 存储方式
    • 大小限制
    • 淘汰策略(超过 7 天)

    网络部分的设计需要考虑的问题

    • 图片请求最大并发量
    • 请求超时策略,一旦超时了,可以重试,如果重试2次失败,是否还下载
    • 请求优先级,下载或者缓存的图片是否紧急需要

    图片通过什么方式进行读写,过程是怎样的?

    • 以图片的url 的单向哈希值作为key 存储
    • 读取过程

    图片解码

    • 对于不同格式的图片,解码采用什么方式来做?

      • 应用策略模式对于不同图片格式进行解码
    • 在那个阶段做图片解码处理?

      • 再磁盘读取后未解码或者网络请求返回后,解码后再放到内存中,可以减轻主线程的压力,解码在主线程中

    线程处理

    阅读时长统计

    怎样设计一个时长统计框架?

    为何要有不同类型的记录器,处于什么考虑?

    基于不同分类场景提供的关于记录的封装、适配

    记录的数据由于程序杀死或者断电,关机等丢失,你是怎样处理的?

    • 定时写磁盘
    • 限定内存缓存条数,超过该条数就写如磁盘

    记录器上传 中延时上传的具体场景有哪些?上传时机如何把握?

    上传时机

    • 立刻上传
    • 延时上传
    • 定时上传

    延时上传场景

    统计结果立即上传会重复调用接口导致资源浪费

    • 前后台切换
    • 从无网到有网的变化时
    • 通过其他的接口捎带着上传

    复杂页面的架构

    MVVM 框架思想

    RN 的数据流思想

    任何一个子节点是没有权力做自己的变化更新的,它必须把这个消息传递给根结点,根结点通过自顶向下的遍历查找需要更新的节点

    系统UIView 更新机制的思想

    AsyncDisplayKit 关于预排版的设计思想

    客户端整体架构

    需要独立于APP 的通用层
    通用的业务层
    中间层
    业务层

    业务之间的解耦通信方式

    • OpenURL
    • 依赖注入方式, 在中间层通过代理 的方式实现业务A 和业务B之间的通信
    ]]>
    @@ -57,10 +57,10 @@ - - /2019/06/18/%E7%AC%AC%E5%9B%9B%E7%AB%A0OC%E8%AF%AD%E8%A8%80%E7%89%B9%E6%80%A7%E9%9D%A2%E8%AF%95/ + + /2019/06/18/%E7%AC%AC%E5%8D%81%E5%9B%9B%E7%AB%A0%E7%AC%AC%E4%B8%89%E6%96%B9%E5%BA%93%E9%9D%A2%E8%AF%95/ - OC 语言特性

    分类

    原理

    由运行时来决议的,不同分类当中含有同名方法 谁最终生效取决于谁最终参与编译,最后参编译的同名分类方法会最终生效,假如分类方法中添加的方法和宿主类中的某一个方法名相同,分类中的方法会覆盖宿主类中的同名方法,覆盖是指在消息传递过程中优先查找数组靠前的元素,如果查找到了同名方法就直接调用,实际上宿主类的同名方法实现仍然是存在的,我们可可以通过一些手段调用到原有类的同名方法的实现

    使用场景

    • 申明私有方法,分类的 .m 文件中申明私有方法啊,对外不暴露
    • 对类中的代码进行抽取分解
    • 把Framework的私有方法公开化

    特点

    • 运行时决议,运行时通过runtime 才把分类中的方法添加到了宿主类上
    • 可以为系统类添加方法
    • 有多个分类中存在同名方法时,最后编译的方法会最先生效
    • 分类中有和宿主类同名方法时,底层中通过内存copy将分类方法“覆盖”宿主类方法
    • 名字相同的分类编译会报错

    分类中可以添加的内容

    • 实例方法
    • 类方法
    • 协议
    • 属性,实际上只是声明了 getter / setter 方法,并没有添加成员变量

    关联对象技术

    关联对象由 AssocicationsManager 管理并在 AssociationsHashMap 上存储
    所有对象的关联内容都在同一个全局容器中

    • 给分类添加成员变量,通过关联对象的方式

    扩展

    使用场景

    • 声明私有属性
    • 声明私有方法
    • 声明私有成员变量

    特点

    • 编译时决议
    • 只以声明的形式存在,多数情况下寄生在宿主类的.m中
    • 不能为系统添加扩展

    代理

    • 代理设计模式,传递方式是一对一
    • 使用weak 避免循环引用

    通知

    使用观察者模式来实现的用于跨层传递消息的机制

    KVO

    KVO 是系统对观察者模式的又一实现,使用isa 混写技术(isa - swizzling)来动态运行时为某一个类添加一个子类重写了它的setter 方法,同时将原有类的isa指针指向了新创建的类上

    • KVO 是OC对观察者模式的又一实现
    • apple 使用isa 混写技术(isa - swizzling)来实现KVO
      • 给我A类注册一个观察者时本质上是调用系统的 Observer for keyPath 方法,系统会创建一个NSKVONotifiying_A 的类
    • 使用 setter 方法设置值KVO才能生效
    • 使用setValue:forKey: 改变值KVO才能生效
    • 使用下划线的成员变量直接修改值需要手动添加KVO才能生效(增加 willChangeValueForKey 和 didChangeValueForKey 两个方法)

    KVC

    apple提供的键值编码技术

    • (nullable id)valueForKey:(NSString *)key;
    • (void)setValue:(nullable id)value forKey:(NSString *)key;

      以上两个方法中的key是没有任何限制的,只要知道对应的成员变量名称,就可以对私有的成员变量设置和操作,这违背了面向对象的编程思想

    (void)setValue:(nullable id)value forKey:(NSString *)key; 方法调用流程

    • 先判断是否有跟key 相关的setter 方法,如果有就直接调用,结束调用
    • 如果没有再判断是否存在实例变量,如果存在直接给赋值,结束调用
    • 如果说实例变量不存在,会去调用 - (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key; 抛出异常

    属性关键字

    读写权限

    • readonly

    • readwrite 系统默认

    原子性

    • atomic (系统默认)保证赋值和获取(对成员属性的直接获取和赋值)时线程安全,并不代表操作和访问,如果atomic修饰的一个数组,只能保证独具改数组的读取和赋值时线程安全,并不能保证对数据操作(增加/删除数组元素)时是安全的
    • nonatomic

    引用技术

    • assign
      • 修饰基本数据类型,
      • 修饰对象时不改变其引用计数,
      • 会产生悬垂指针
    • weak
      • 不改变被修饰对象的引用计数,
      • 所指对象被释放之后指针自动置为nil

    两者区别:

    • weak可以修饰对象,而assign 既可以修饰对象也可以修饰基本数据类型
    • assign 修饰的对象,对象释放后指针仍然指向原对象的内存地址,而weak 修饰的对象,释放后会自动置为nil

    问题1

    weak 修饰的对象为什么释放后会自动置为nil

    问题2

    @property (copy) NSMutableArray *array; 有什么问题

    • 被copy修饰后,如果赋值过来的是NSMutableArray,copy之后就是NSArray,如果赋值过来的是NSAarray,copy 之后仍然是NSArray,有可能会调用array的添加元素方法会导致crash

    copy 关键词

    • 浅拷贝:内存地址的复制,让目标对象指针和源对象指向同一块内存空间,
      • 会增加对象的引用计数
      • 并没有一个新的内存分配
    • 深拷贝:目标对象指针和源对象指针分别指向两块内容相同的内存空间
      • 不会增加源对象的引用计数
      • 有新对象的内存分配

    MRC 重写 retain修饰的变量的setter 方法

    1
    2
    3
    4
    5
    6
    7
    8

    - (void)setObj:(id)obj {
    if (_obj != obj) {
    [_obj release];
    }
    _obj = [obj retain];

    }

    判断是为了防止异常处理,如果不做 if 判断,当传入的 obj 对象正好是原来的_obj 对象,对原对象尽行releas 操作,实际上也会对传入的对象进行releas 操作进行释放,此时如果再通过obj指针访问废弃的对象时就会导致carsh

    ]]> + 第十四章 第三方库

    AFNetworking

    框架图

    会话NSURSession

    主要类关系图

    AFURLSessionManager

    • 创建和管理 NSURLSession, NSURLSessionTask
    • 实现NSURLSessionDelegate 等协议的代理方法
    • 引入AFSecurityPolicy 保证请求安全,https 请求时 证书检验,公钥验证
    • 引入AFNetworkReachabilityManager 监控网络状态

    SDWebImage

    架构图

    加载图片流程

    Reactive Cocoa

    RACStream

    AsyncDisplayKit

    主要解决的问题

    基本原理

    ]]>
    @@ -70,10 +70,10 @@ - - /2019/06/18/%E7%AC%AC%E4%B8%89%E7%AB%A0UI%E8%A7%86%E5%9B%BE%E5%93%8D%E5%BA%94%E9%93%BE%E7%A6%BB%E5%B1%8F%E6%B8%B2%E6%9F%93%E9%9D%A2%E8%AF%95/ + + /2019/06/18/%E7%AC%AC%E5%8D%81%E7%AB%A0%E7%BD%91%E7%BB%9C%E7%9B%B8%E5%85%B3/ - UITableView 相关

    • 重用机制
    • 并发访问,数据拷贝问题:主线程删除了一条数据,子线程数据由删除之前的数据源copy而来,子线程网络请求,数据解析,预排版等操作后,回到主线程刷新时已经删除的数据又会重现

      • 主线程数据源删除时对数据进行记录,子线程的数据回来后再进行一次同步删除操作,然后再刷新数据

      • 使用GCD中的串行队列,将数据删除和数据解析在此队列中进行

    UIView 和CALayer 的关系

    • UIView 为 CALayer 提供内容,负责处理触摸事件,参与响应链
    • CALayer 负责显示内容,分工明确,UIView 的Layer 实际上就是CALayer, backgroundColor 等属性本质是对CALayer 做了一层包装

    事件传递

    • hitTest 方法是返回响应时间的试图
    • pointInside withEvent 方法判断点击位置是否在当前视图范围内
    • 倒序遍历最终响应事件的试图,最后添加的试图会最优先被遍历到

    需求案例

    只让正方形的view中圆形区域内可以响应事件,四个角的位置不响应事件

    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
    class CircularAreaResponseButton: UIButton {

    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
    if self.isHidden || !self.isUserInteractionEnabled || self.alpha <= 0.01 {
    return nil
    }

    var hitView: UIView?
    if self.point(inside: point, with: event) {
    // 遍历当前视图的子试图
    for subView in subviews {
    // 坐标转换
    let convertPoint = self.convert(point, to: subView)
    if let hitTestView = subView.hitTest(convertPoint, with: event) {
    // 找到了最终响应事件的试图
    hitView = hitTestView
    break;
    }
    }

    if hitView != nil {
    return hitView
    }
    return self
    }
    return nil
    }


    override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
    let x1 = point.x
    let y1 = point.y

    let x2 = self.frame.width / 2.0
    let y2 = self.frame.height / 2.0
    // 使用平面内两点见距离公式计算距离, 并且判断是否 <= 圆的半径
    return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)) <= x2
    }
    }

    视图响应

    UIView 都继承于 UIResponder,都有以下方法

    1
    2
    3
    4
    func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
    func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?)
    func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?)
    func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?)

    如果事件沿着视图响应链一直传递到 UIApplicationDelegate 仍然没有任何一个视图处理事件,程序将会发生什么?

    • 忽略掉了这个事件当作什么都没有发生

    图像显示原理

    CALayer 的 contents 是位图

    • CPU的工作

      Layout | Display | Prepare | Commite |
      ———|———–|————|———|
      UI布局 | 绘制 | 图片编解码 | 提交位图 |
      文本计算 |

    • GPU渲染管线

      定点着色 | 图元转配 | 光栅化 | 片段着色 | 片段处理 |
      ——–|——–|——-|———|———|

    提交到帧缓冲区(frameBuffer)

    UI卡顿掉帧的原因

    1/60 ms 时间内, 由cpu 和GPU 协同产生最终的数据,当 CPU UI布局,文本计算所用的时间较长时,留给GPU渲染,提交到帧缓冲区的时间就会变短,这样以来就会发生掉帧情况

    UIView 的绘制原理

    CPU优化方案

    • 对象的创建、调整、销毁放在子线程,节省cpu的一部分时间
    • 预排版(布局计算,文本计算)放在子线程中
    • 预渲染(文本等异步绘制,图片编解码)等

    GPU优化方案

    在屏渲染

    指GPU的渲染操作是在当前用于显示的屏幕缓冲区域中进行

    离屏渲染

    指GPU在当前屏幕缓冲区域外新开劈了一个缓冲区域进行渲染操作
    指定了UI视图图层的某些属性,标记为视图在未预合成之前不能用于在当前屏幕上直接显示的时候就会发生离屏渲染,例如图层的圆角设置和蒙层设置

    • 离屏渲染何时触发

      • 设置layer 的圆角并且和 maskToBounds 一起使用时
      • 图层蒙版
      • 阴影
      • 光栅化
    • 为何要避免离屏渲染

      • 离屏渲染,会增加 GPU 的工作量,GPU工作量的增加就很有可能导致CPU和GPU的总耗时超过1/60s,导致UI卡顿和掉帧
    • 避免离屏渲染

    ###异步绘制

    ]]> + 第十章 网络请求相关

    HTTP协议

    超文本传输协议

    请求/响应报文

    请求报文:

    • 请求行
      方法 URL 协议版本(1.1版本) CRLF(回车)

    • 首部字段区域
      首部字段名(key) :值(value )CRLF
      首部字段名(key) :值(value )CRLF

    • 实体主体
      get 请求没有
      post 有实体主体

    响应报文:

    • 响应行
      版本 状态码 状态码描述(短语) CRLF

    • 首部字段区

    • 响应实体主体

    连接建立流程

    三次握手和四次挥手

    三次握手

    • 客户端 发送 syn 同步报文到Server,
    • server 端收到syn报文后会,链接建立,server 端会返回一个 syn,ack 报文给客户端,
    • 客户端收到 server 端的ask 报文后,会再次回应一个确认ack 的报文

    数据请求

    • 接下来在已经建立的tcp 通道上进行http 的请求报文
    • server 端回复给客户端一个http响应报文

    四次挥手

    • 客户端发送 fin 终止报文到server 端
    • server 端收到报文后会回回复一个ack 报文到客户端
    • 客户端到server 端的tcp的链接已经断开,但是 server 端到客户端的链接还会传输数据
    • server 端向客户端发送一个fin ack 报文 ,然后客户端回应给server 一个ack 报文,释放链接

    HTTP 的特点

    • 无连接
      非持久链接 需要多次断开/建立链接,就会经历 三次握手,四次挥手

      HTTP 的持久链接: 多个http请求在同一个tcp 通道上, 一定时间内不会断开tcp通道,提高了网络请求的效率

      头部字段:

      coonection : keep-alive 客户端是否采用持久链接time :20 多长时间有效max:10 最多发生多少个http请求和响应对
    • 无状态
      同一个用户多次发送http请求,server 端是不知道是同一个用户的,server 端通过 Cookie/Session 规避这个问题

      • cookie
        cookie 主要是用来记录用户状态,区分用户,cookie由server端返回给客户端,cookie 状态保存在客户端

        保证cookie 的安全

        • cookie 加密处理
        • 只在https 上携带Cookie
        • 设置Cookie为 httpOnly,防止跨站脚本攻击
      • session 用来记录用户的状态,区分用户,状态存放在服务端

    HTTPS 与网络安全

    HTTPS 都使用了那些加密手段,为什么?

    • 建立链接过程使用非对称加密,因为耗时
    • 后续通信数据传输过程中使用对称性加密

    对称性加密

    TCP: 传输控制协议

    • 面向链接
      TCP 是全双工的所以数据传输之前需要三次握手,传输完毕需要四次挥手,
    • 可靠传输

      • 无差错
      • 按序到达
    • 面向字节流

      发送方发送数据时不管一次性提交给TCP的缓冲是多大的数据,TCP本身会根据实际情况来划分数据包再发送给接受数据方

    • 流量控制

      滑动窗口协议

    UDP : 用户数据协议

    • 无连接
      发送数据时不需要建立链接,传输完毕也不需要释放链接

    • 尽最大努力交付
      是不保证可靠传输的,

    • 面向报文
      既不合并,也不拆分

    问题

    GET 和post 请求的区别

    • GET 获取资源的

      • 安全的: 不因该引起Server 端的任何状态变化的
      • 幂等的:同一个请求方法执行多次和执行一次的效果完全相同
      • 可缓存的:请求是可以缓存的,代理服务器可以缓存
      • 请求参数以? 分割拼接到URL 后面
    • post 请求处理资源

      • 不安全的:
      • 非幂等的
      • 不可缓存的

    GET 请求参数有长度限制,是2048 个字符,post 一般没有限制

    HTTPS 链接建立流程

    客户端会发送服务端一个支持的加密算法列表
    包括 TLS 的版本号 + 随机数C,
    服务端再回给客户端一个证书
    包括 商定的加密算法 后续首先通过非对称加密进行对称性加密的密钥传输,http的网络就通过被非对称密钥保护的对称密钥访问和传输数据

    TCP 和 UDP的区别

    tcp是面向链接的,并且支持可靠的传输,支持面向字节流, tcp 提供了流浪上的控制和拥塞的控制
    UDP 提供了简单的复用/分用及差错检测的传输层的功能,

    状态吗有哪些

    • 1XX
    • 2XX:成功
    • 3XX:重定向
    • 4XX:客户端请求错误
    • 5XX: 服务器端请求错误

    为什么需要三次握手,两次行不行?

    解决同步请求链接建立时超时的情况,如果只有两次握手,

    持久链接怎么判断一个请求是否结束

    • 请求/响应报文的头部字段

    响应报文 content-length: 1024 头部字段是由server 端返回数据大小,客户端根据所接收的数据的字节数是否达到了头部字段 cotent-length 的大小判断

    • 通过post请求时server 端返回数据有可能需要多次响应才能返回,此时需要根据chunked 字段名 是否为空来判断

    Charels 抓包原理是什么?

    中间人攻击

    正常步骤是 客户端 —- server

    抓包:发送请求和响应请求都可以通过中间人修改再返回给某一端

    客户端 —中间人—- server

    DNS 解析

    域名到IP地址的映射,DNS解析请求采用UDP数据报,且明文

    NDS 解析存在的常见问题

    • DNS 劫持问题
      钓鱼网站
    • DNS 解析转发问题

    DNS 劫持和HTTP 的关系时怎么样的

    没有关系,

    • DNS 解析发在HTTP 建立连接之前
    • DNS 解析请求使用UDP数据报, 端口是53,跟HTTP没有任何关系

    解决DNS 劫持问题

    • httpDNS
    • 长连接
    ]]>
    @@ -109,10 +109,10 @@ - - /2019/06/18/%E7%AC%AC%E5%85%AD%E7%AB%A0%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86%E9%9D%A2%E8%AF%95/ + + /2019/06/18/%E7%AC%AC%E5%85%AB%E7%AB%A0%E5%A4%9A%E7%BA%BF%E7%A8%8B%E9%9D%A2%E8%AF%95/ - 内存管理

    内存布局

    • stack 栈是从高地址向低地址扩展,所以栈是向下增长的,对象和block copy 后都会放在堆上
    • heap 堆区向上增长
    • 散列表方式

      • 自旋锁
        • 自旋锁是“忙等” 的锁
      • 引用计数表
      • 弱应用表

    内存管理方案

    • 小对象使用 TaggedPointer
    • 64 位架构下面使用的是 NONPONINT_ISA 内存管理方案,非指针型的 isa,内部存储了一些
    • 散列表结构 (Side Tables() 结构)
      • 自旋锁
      • 引用计数表
      • 弱引用表
      • Side Tables() 结构

    为什么不是一个SideTable,而是多个组成了Side Tables

    如果所有对象的存储都放在一张大表当中,因为每个对象是在不同的线程中创建的,要操作其中一个对象时,需要将表加锁处理保证数据安全,要等锁释放之后才能操作下一个对象,此时存在效率问题,引入分离锁技术方案

    • 分离锁:把引用计数表分成多张表进行,
    • Side Tables 本质是一张Hash 表

    自旋锁

    是“忙等”的锁,如果当前锁已被其他线程获取,当前线程会不断探测是否被释放,会第一时间获取,而其他锁比如信号量当它获取不到锁时,会把自己的线程阻塞休眠,等到其他线程释放这个锁时再唤醒这个锁

    • 适用于轻量访问

    引用计数表

    使用过Hash表来实现,hash查找提高了查找高效率,插入和获取是通过Hash 算法来是实现的,避免了for循环

    alloc 实现

    经过一系列调用,最终调用了C函数 calloc, 并没有引用计数+1

    retain 实现

    底层经过了2次Hash表查找

    release 实现

    和 retain 实现相反

    dealloc 实现原理

    objc_destructInstance() 实现

    clearDeallocating() 实现

    MRC

    手动引用计数

    ARC

    • 编译器在对应位置自动插入retain 和 release 操作,并和runtime协作达到自动引用计数管理内存的效果

    • ARC 中禁止手动调用 retain/release

    • ARC 中新增 weeak、strong 等关键字

    弱引用管理

    被声明为__weak 的对象指针经过编译后会进过调用以下方法,在最终的weak_register_no_lock() 方法中进行弱引用变量的添加。添加的位置是通过Hash算法查找的

    weak 修饰的对象,释放时置为nil 如何实现的

    当一个对象被 dealloc 之后,在dealloc内部实现当中会调用弱引用清除的相关函数,在相关函数当中会根据当前对象指针查找弱引用表,把当前对象下对应的弱应用都取出,遍历弱应用指针并置为nil。

    自动释放池

    是以栈为结点通过双向链表的形式组合而成,和线程一一对应

    Autoreleasepool

    循环引用

    自循环引用

    一个对象中有拥有一个 强持有他的 obj,如果给 obj 赋值为原对象就会造成自循环引用

    相互循环引用

    • 代理
    • Block
    • NSTimer
    • 大环多循环

    多循环引用

    解决循环引用的方案

    __weak

    解决相互循环引用

    __block

    __unsafe_unretained

    一般不建议使用

    ]]> + 第三章多线程面试

    ]]>
    @@ -122,10 +122,10 @@ - - /2019/06/18/%E7%AC%AC%E5%85%AB%E7%AB%A0%E5%A4%9A%E7%BA%BF%E7%A8%8B%E9%9D%A2%E8%AF%95/ + + /2019/06/18/%E7%AC%AC%E5%85%AD%E7%AB%A0%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86%E9%9D%A2%E8%AF%95/ - 第三章多线程面试

    ]]>
    + 内存管理

    内存布局

    • stack 栈是从高地址向低地址扩展,所以栈是向下增长的,对象和block copy 后都会放在堆上
    • heap 堆区向上增长
    • 散列表方式

      • 自旋锁
        • 自旋锁是“忙等” 的锁
      • 引用计数表
      • 弱应用表

    内存管理方案

    • 小对象使用 TaggedPointer
    • 64 位架构下面使用的是 NONPONINT_ISA 内存管理方案,非指针型的 isa,内部存储了一些
    • 散列表结构 (Side Tables() 结构)
      • 自旋锁
      • 引用计数表
      • 弱引用表
      • Side Tables() 结构

    为什么不是一个SideTable,而是多个组成了Side Tables

    如果所有对象的存储都放在一张大表当中,因为每个对象是在不同的线程中创建的,要操作其中一个对象时,需要将表加锁处理保证数据安全,要等锁释放之后才能操作下一个对象,此时存在效率问题,引入分离锁技术方案

    • 分离锁:把引用计数表分成多张表进行,
    • Side Tables 本质是一张Hash 表

    自旋锁

    是“忙等”的锁,如果当前锁已被其他线程获取,当前线程会不断探测是否被释放,会第一时间获取,而其他锁比如信号量当它获取不到锁时,会把自己的线程阻塞休眠,等到其他线程释放这个锁时再唤醒这个锁

    • 适用于轻量访问

    引用计数表

    使用过Hash表来实现,hash查找提高了查找高效率,插入和获取是通过Hash 算法来是实现的,避免了for循环

    alloc 实现

    经过一系列调用,最终调用了C函数 calloc, 并没有引用计数+1

    retain 实现

    底层经过了2次Hash表查找

    release 实现

    和 retain 实现相反

    dealloc 实现原理

    objc_destructInstance() 实现

    clearDeallocating() 实现

    MRC

    手动引用计数

    ARC

    • 编译器在对应位置自动插入retain 和 release 操作,并和runtime协作达到自动引用计数管理内存的效果

    • ARC 中禁止手动调用 retain/release

    • ARC 中新增 weeak、strong 等关键字

    弱引用管理

    被声明为__weak 的对象指针经过编译后会进过调用以下方法,在最终的weak_register_no_lock() 方法中进行弱引用变量的添加。添加的位置是通过Hash算法查找的

    weak 修饰的对象,释放时置为nil 如何实现的

    当一个对象被 dealloc 之后,在dealloc内部实现当中会调用弱引用清除的相关函数,在相关函数当中会根据当前对象指针查找弱引用表,把当前对象下对应的弱应用都取出,遍历弱应用指针并置为nil。

    自动释放池

    是以栈为结点通过双向链表的形式组合而成,和线程一一对应

    Autoreleasepool

    循环引用

    自循环引用

    一个对象中有拥有一个 强持有他的 obj,如果给 obj 赋值为原对象就会造成自循环引用

    相互循环引用

    • 代理
    • Block
    • NSTimer
    • 大环多循环

    多循环引用

    解决循环引用的方案

    __weak

    解决相互循环引用

    __block

    __unsafe_unretained

    一般不建议使用

    ]]> @@ -135,10 +135,10 @@ - - /2019/06/18/%E7%AC%AC%E5%8D%81%E4%B8%80%E7%AB%A0%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E9%9D%A2%E8%AF%95/ + + /2019/06/18/%E7%AC%AC%E4%B8%83%E7%AB%A0Blocks%E9%9D%A2%E8%AF%95/ - 第十一章 设计模式

    六大设计原则

    单一职责原则:一个类只负责一件事

    开闭原则: 对修改关闭,对扩展开放

    接口隔离原则:使用多个专门的协议,而不是一个庞大臃肿的协议,iOS系统的UITableView的数据和代理协议分开

    依赖倒置原则:抽象不应该依赖于具体实现,具体实现可以依赖于抽象

    里氏替换原则:父类可以被子类无缝替换,且原有功能不受任何影响,iOS系统 的 KVO 机制遵守了这一原则,当调用 addObserver 方法时,因为系统在动态运行时为我们创建了一个子类,直观感受是仍然使用的是父类,实际上系统已经替换为创建的子类,所以KVO 机制 不但使用了管擦这模式,并且遵从了 里氏替换原则

    迪米特法则: 一个对象应对其他对象有尽可能少的了解,平时所说的高内聚,低耦合

    常用设计模式

    责任链

    有三个业务分别为A/B/C,以前的调用顺序为 A -> B -> C ,现在顺序有调整,

    定义某一个类,有一个成员变量他的类型跟原有类类型一致,组成一个责任链,让处理具体业务的责任者去处理

    桥接模式

    业务解耦的问题:同一个列表和多套数据显示,多套数据通过后端来控制是共存,

    类构成

    抽象类A ——> class B

    适配器模式

    一个现有类需要适应变化的问题, 年代比较久远的类或者对象

    类构成

    • 对象适配
    • 类适配

    单例模式

    命令模式

    行为参数化

    降低代码重合度

    ]]>
    + Blocks

    Block 是什么?

    Blocks 是 C 语言的扩充功能,将函数及其执行上下文封装起来的对象,带有自动变量(局部变量)的匿名函数

    • 因为block 底层C++ 结构体中存在着 isa 指针,所以是一个对象
    • 因为block 底层C++ 结构体中存在着一个函数指针 FuncPtr,所以是一个函数

    什么是Block 调用

    Block 的调用就是函数的调用
    在终端使用 clang 编译之后查看文件内容可得出以下结论:

    • 对其进行一个强制转换,转换之后取出成员变量 FuncPtr

    截获变量

    以下代码运行结果是 12

    原因:block 对基本数据类型的局部变量会截获其值,在定义 block 时以值的方式传到了block 对应的结构体当中,而调用block 时直接使用的是block对应结构体当中已经传过来的那个值,所以值为12
    • 局部变量截获

      • 基本数据类型的局部变量截获其值
      • 对象类型的局部变量连同所有权修饰符一起截获
    • 局部静态变量: 已指针形式截获

    • 全局变量:不截获

    • 静态全局变量: 不截获

    以下代码运行结果是 8

    原因:block 对局部静态变量会以指针形式截获,所以值为8

    __block 修饰符

    • __block 修饰后的变量最后变成了对象,底层c++ 结构体中存在isa指针
    • 一般情况下,对被截获变量进行赋值操作需要添加block 修饰符,而操作是不需要添加block 修饰符的
    • 不需要__block 修饰符,全局变量和静态全局变量 block 是不截获的,所以不需要修饰,静态局部变量是通过指针截获的,修改外部的变量,也不需要添加
      • 静态局部变量
      • 全局变量
      • 静态全局变量

    不需要添加__Block 修饰符

    1
    2
    3
    4
    5
    6
    7
    8
    - (void)test {
    NSMutableArray *array = [NSMutableArray array];
    void(^block)(void) = ^{
    [array addObject:@1234];
    };

    block();
    }

    需要添加__Block 修饰符

    1
    2
    3
    4
    5
    6
    7
    8
    - (void)test {
    __block NSMutableArray *tempArray = nil;
    void(^block)(void) = ^{
    tempArray = [NSMutableArray array];
    };

    block();
    }

    以下代码运行结果是 8

    1
    2
    3
    4
    5
    6
    7
    8
    - (void)test__Block3 {
    __block int multiplier = 6;
    int(^Block)(int) = ^int(int num){
    return multiplier * num;
    };
    multiplier = 4;
    NSLog(@"__block--test__Block3---result is %d", Block(2));
    }

    原因

    栈上Block 的block 变量中的 forwarding 指针指向的是自身

    Block 的内存管理

    Block 的 内存分布

    Block 的 Copy 操作

    • 全局block
    • 堆block
    • 栈block

    Block 循环引用

    • 如果当前block对当前对象的某一变量进行截获的话,block 会对对应变量有一个强引用,当前block因为当前的对象对其有个强引用,产生了自循环引用,通过声明为__weak变量来解决
    • 如果定义了__block 修饰符的话也会产生循环引用,在ARC 下会产生循环引用,而在MRC 下不会,在ARC 下通过打破环来消除,但是存在弊端时当block 没有机会执行时,

    循环引用1

    解决方法:

    大环循环引用2

    以上代码,在MRC 下,不会产生循环引用,在ARC 下,会产生循环应用,引起内存泄露

    解决方法

    如果block 没有机会执行,那么循环引用将不会被打破

    ]]> @@ -148,10 +148,10 @@ - - /2019/06/18/%E7%AC%AC%E4%B8%83%E7%AB%A0Blocks%E9%9D%A2%E8%AF%95/ + + /2019/06/18/%E7%AC%AC%E4%B8%89%E7%AB%A0UI%E8%A7%86%E5%9B%BE%E5%93%8D%E5%BA%94%E9%93%BE%E7%A6%BB%E5%B1%8F%E6%B8%B2%E6%9F%93%E9%9D%A2%E8%AF%95/ - Blocks

    Block 是什么?

    Blocks 是 C 语言的扩充功能,将函数及其执行上下文封装起来的对象,带有自动变量(局部变量)的匿名函数

    • 因为block 底层C++ 结构体中存在着 isa 指针,所以是一个对象
    • 因为block 底层C++ 结构体中存在着一个函数指针 FuncPtr,所以是一个函数

    什么是Block 调用

    Block 的调用就是函数的调用
    在终端使用 clang 编译之后查看文件内容可得出以下结论:

    • 对其进行一个强制转换,转换之后取出成员变量 FuncPtr

    截获变量

    以下代码运行结果是 12

    原因:block 对基本数据类型的局部变量会截获其值,在定义 block 时以值的方式传到了block 对应的结构体当中,而调用block 时直接使用的是block对应结构体当中已经传过来的那个值,所以值为12
    • 局部变量截获

      • 基本数据类型的局部变量截获其值
      • 对象类型的局部变量连同所有权修饰符一起截获
    • 局部静态变量: 已指针形式截获

    • 全局变量:不截获

    • 静态全局变量: 不截获

    以下代码运行结果是 8

    原因:block 对局部静态变量会以指针形式截获,所以值为8

    __block 修饰符

    • __block 修饰后的变量最后变成了对象,底层c++ 结构体中存在isa指针
    • 一般情况下,对被截获变量进行赋值操作需要添加block 修饰符,而操作是不需要添加block 修饰符的
    • 不需要__block 修饰符,全局变量和静态全局变量 block 是不截获的,所以不需要修饰,静态局部变量是通过指针截获的,修改外部的变量,也不需要添加
      • 静态局部变量
      • 全局变量
      • 静态全局变量

    不需要添加__Block 修饰符

    1
    2
    3
    4
    5
    6
    7
    8
    - (void)test {
    NSMutableArray *array = [NSMutableArray array];
    void(^block)(void) = ^{
    [array addObject:@1234];
    };

    block();
    }

    需要添加__Block 修饰符

    1
    2
    3
    4
    5
    6
    7
    8
    - (void)test {
    __block NSMutableArray *tempArray = nil;
    void(^block)(void) = ^{
    tempArray = [NSMutableArray array];
    };

    block();
    }

    以下代码运行结果是 8

    1
    2
    3
    4
    5
    6
    7
    8
    - (void)test__Block3 {
    __block int multiplier = 6;
    int(^Block)(int) = ^int(int num){
    return multiplier * num;
    };
    multiplier = 4;
    NSLog(@"__block--test__Block3---result is %d", Block(2));
    }

    原因

    栈上Block 的block 变量中的 forwarding 指针指向的是自身

    Block 的内存管理

    Block 的 内存分布

    Block 的 Copy 操作

    • 全局block
    • 堆block
    • 栈block

    Block 循环引用

    • 如果当前block对当前对象的某一变量进行截获的话,block 会对对应变量有一个强引用,当前block因为当前的对象对其有个强引用,产生了自循环引用,通过声明为__weak变量来解决
    • 如果定义了__block 修饰符的话也会产生循环引用,在ARC 下会产生循环引用,而在MRC 下不会,在ARC 下通过打破环来消除,但是存在弊端时当block 没有机会执行时,

    循环引用1

    解决方法:

    大环循环引用2

    以上代码,在MRC 下,不会产生循环引用,在ARC 下,会产生循环应用,引起内存泄露

    解决方法

    如果block 没有机会执行,那么循环引用将不会被打破

    ]]> + UITableView 相关

    • 重用机制
    • 并发访问,数据拷贝问题:主线程删除了一条数据,子线程数据由删除之前的数据源copy而来,子线程网络请求,数据解析,预排版等操作后,回到主线程刷新时已经删除的数据又会重现

      • 主线程数据源删除时对数据进行记录,子线程的数据回来后再进行一次同步删除操作,然后再刷新数据

      • 使用GCD中的串行队列,将数据删除和数据解析在此队列中进行

    UIView 和CALayer 的关系

    • UIView 为 CALayer 提供内容,负责处理触摸事件,参与响应链
    • CALayer 负责显示内容,分工明确,UIView 的Layer 实际上就是CALayer, backgroundColor 等属性本质是对CALayer 做了一层包装

    事件传递

    • hitTest 方法是返回响应时间的试图
    • pointInside withEvent 方法判断点击位置是否在当前视图范围内
    • 倒序遍历最终响应事件的试图,最后添加的试图会最优先被遍历到

    需求案例

    只让正方形的view中圆形区域内可以响应事件,四个角的位置不响应事件

    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
    class CircularAreaResponseButton: UIButton {

    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
    if self.isHidden || !self.isUserInteractionEnabled || self.alpha <= 0.01 {
    return nil
    }

    var hitView: UIView?
    if self.point(inside: point, with: event) {
    // 遍历当前视图的子试图
    for subView in subviews {
    // 坐标转换
    let convertPoint = self.convert(point, to: subView)
    if let hitTestView = subView.hitTest(convertPoint, with: event) {
    // 找到了最终响应事件的试图
    hitView = hitTestView
    break;
    }
    }

    if hitView != nil {
    return hitView
    }
    return self
    }
    return nil
    }


    override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
    let x1 = point.x
    let y1 = point.y

    let x2 = self.frame.width / 2.0
    let y2 = self.frame.height / 2.0
    // 使用平面内两点见距离公式计算距离, 并且判断是否 <= 圆的半径
    return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)) <= x2
    }
    }

    视图响应

    UIView 都继承于 UIResponder,都有以下方法

    1
    2
    3
    4
    func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
    func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?)
    func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?)
    func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?)

    如果事件沿着视图响应链一直传递到 UIApplicationDelegate 仍然没有任何一个视图处理事件,程序将会发生什么?

    • 忽略掉了这个事件当作什么都没有发生

    图像显示原理

    CALayer 的 contents 是位图

    • CPU的工作

      Layout | Display | Prepare | Commite |
      ———|———–|————|———|
      UI布局 | 绘制 | 图片编解码 | 提交位图 |
      文本计算 |

    • GPU渲染管线

      定点着色 | 图元转配 | 光栅化 | 片段着色 | 片段处理 |
      ——–|——–|——-|———|———|

    提交到帧缓冲区(frameBuffer)

    UI卡顿掉帧的原因

    1/60 ms 时间内, 由cpu 和GPU 协同产生最终的数据,当 CPU UI布局,文本计算所用的时间较长时,留给GPU渲染,提交到帧缓冲区的时间就会变短,这样以来就会发生掉帧情况

    UIView 的绘制原理

    CPU优化方案

    • 对象的创建、调整、销毁放在子线程,节省cpu的一部分时间
    • 预排版(布局计算,文本计算)放在子线程中
    • 预渲染(文本等异步绘制,图片编解码)等

    GPU优化方案

    在屏渲染

    指GPU的渲染操作是在当前用于显示的屏幕缓冲区域中进行

    离屏渲染

    指GPU在当前屏幕缓冲区域外新开劈了一个缓冲区域进行渲染操作
    指定了UI视图图层的某些属性,标记为视图在未预合成之前不能用于在当前屏幕上直接显示的时候就会发生离屏渲染,例如图层的圆角设置和蒙层设置

    • 离屏渲染何时触发

      • 设置layer 的圆角并且和 maskToBounds 一起使用时
      • 图层蒙版
      • 阴影
      • 光栅化
    • 为何要避免离屏渲染

      • 离屏渲染,会增加 GPU 的工作量,GPU工作量的增加就很有可能导致CPU和GPU的总耗时超过1/60s,导致UI卡顿和掉帧
    • 避免离屏渲染

    ###异步绘制

    ]]> @@ -161,10 +161,10 @@ - - /2019/06/18/%E4%B8%80%E8%87%AA%E5%8A%A8%E5%BC%95%E7%94%A8%E8%AE%A1%E6%95%B0/ + + /2019/06/18/typescripe%20%E5%AE%89%E8%A3%85%E5%8F%8A%E8%AF%AD%E6%B3%95/ - 自动引用计数

    什么是自动引用计数

    内存管理中对引用采取自动计数的技术

    内存管理

    • 自己生成的对象,自己所持有
      • alloc
      • new
      • copy
      • mutableCopy
    • 非自己生成的对象,自己也能持有
      • retain
    • 不再需要自己持有的对象时释放
      • release
    • 非自己持有的对象无法释放

    • 废弃对象

      • dealloc

    __strong

    __strong 修饰符表示对对象的“强引用”

    __unsafe_unretained 修饰符

    __unsafe_unretained 是不安全的所有权修饰符, 被它修饰的变量不属于编译器的内存管理对象

    1
    id __unsafe_unretained objc = [[NSObject alloc] init];

    Xcode 提示:Assigning retained object to unsafe_unretained variable; object will be released after assignment

    将保留对象赋给unsafe_unretain变量;对象将在赋值后释放

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    id __unsafe_unretained obj1 = nil;

    {
    id __unsafe_unretained obj = [[NSObject alloc] init];

    // 自己生成并持有对象
    // 因为obj0 变量是__strong,强引用,所以自己持有对象
    id __strong obj0 = [[NSObject alloc] init];

    // obj0 变量赋值给你obj1
    // ojb1 变量不持有对象的强应用,也不持有弱引用
    obj1 = obj0;


    // 输出 obj1 变量表示的对象
    NSLog(@"A: %@", obj1);
    } // obj0变量 超出了其作用域,强引用失效自动释放自己持有的对象,除此之外没有其他持有者,所以废弃该对象




    // 输出 obj1 变量表示的对象
    // obj1 变量表示的对象因为无持有者已经销毁,坏的内存访问
    NSLog(@"B: %@", obj1);
    ]]>
    + ReactNative 中使用 TypeScript

    从RN0.57版本开始已经可以直接支持typescript,无需任何配置。在保留根入口文件index.js的前提下,其他文件都可以直接使用.ts或.tsx后缀。但这一由babel进行的转码过程并不进行实际的类型检查,因此仍然需要编辑器和插件来共同进行类型检查

    TypeScript和JavaScript对比

    两者深度对比

    最大的区别:

    • TypeScript是强类型语⾔和静态类型语⾔(需要指定类型)
    • JavaScript是弱类型语⾔和动态类型语⾔(不需要指定类型)

    TypeScript

    由微软开发的自由和开源的编程语言,是JavaScript的一个严格超集,并添加了可选的静态类型和基于类的面向对象编程

    TypeScript定义

    安装及语法

    教程

    安装
    1
    npm install -g typescript
    安装 typescript 依赖管理器 typings
    1
    npm install -g typings
    安装 typings 依赖
    1
    2
    typings install npm~react --save
    typings install dt~react-native --globals --save
    使用 yarn 安装

    如果已经安装了 yarn 安装如下:

    1
    2
    yarn add tslib @types/react @types/react-native
    yarn add --dev react-native-typescript-transformer typescript
    创建 tsconfig.json 配置文件
    • cd 到 ReactNative 项目根文件夹下

      1
      tsc --init
    • 修改 tsconfig.json 文件

      如果一个目录下存在一个tsconfig.json文件,那么它意味着这个目录是TypeScript项目的根目录。 tsconfig.json文件中指定了用来编译这个项目的根文件和编译选项

      tsconfig.json文件配置详解

      TypeScript配置文件tsconfig简析

      务必设置”jsx”:”react”
      注意多余的注释可能会不兼容,需要移除

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      {
      "compilerOptions": {
      "target": "es6",
      "allowJs": true,
      "jsx": "react",
      "outDir": "./dist",
      "sourceMap": true,
      "noImplicitAny": false
      },

      // "files"属性 :指定一个包含相对或绝对文件路径的列表。 "include"和"exclude"属性指定一个文件glob匹配模式列表

      "include": [
      "typings/**/*.d.ts",
      "src/**/*.ts",
      "src/**/*.tsx"
      ],
      "exclude": [
      "node_modules"
      ]
      }
    • cd 到项目根目录中新建存放 typescripe 源代码的文件夹

    建立 rn-cli.config.js 文件,使用支持typescript的transfomer
    1
    2
    3
    4
    5
    6
    7
    8
    module.exports = {
    getTransformModulePath() {
    return require.resolve('react-native-typescript-transformer');
    },
    getSourceExts() {
    return ['ts', 'tsx'];
    }
    }
    修改 .babelrc 文件
    1
    2
    3
    4
    {
    "presets": ["react-native"],
    "plugins": ["transform-decorators-legacy"]
    }
    修改项目中文件导入
    • 入口index.jsApp.js的文件名请不要修改,项目根目录下新建src文件夹
    • src文件夹下新建 .tsx 后缀的文件
    • .tsx 后缀的文件引用react的写法有所区别

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      import * as React from 'react'
      import { Text, View } from 'react-native';
      import { Component } from 'react';

      export default class HelloWorld extends Component{
      render() {
      return (
      <View>
      <Text>Hello!</Text>
      <Text>Typescript</Text>
      </View>
      );
      }
      }
    • App.js中使用

      • App.js 导入方式修改为如下方式

        1
        2
        3
        4
        import './src';
        ```

        - 导入 .tsx 类型的组建

        import HelloWorld from “./src/HelloWorld”;

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11

        ### 配置jsconfig

        `jsconfig.json`目录中存在文件表明该目录是JavaScript项目的根目录。该jsconfig.json文件指定根文件和JavaScript语言服务提供的功能选项

        [jsconfig详解](https://code.visualstudio.com/docs/languages/jsconfig)

        [jsconfig.json](https://jeasonstudio.gitbooks.io/vscode-cn-doc/content/md/%E8%AF%AD%E8%A8%80/javascript.html?q=)


        ##### 新建 `jsconfig.json` 文件

    touch jsconfig.json

    1
    2

    ##### 编辑 `jsconfig.json` 文件

    {
    “compilerOptions”: {
    “allowJs”: true,
    “allowSyntheticDefaultImports”: true,
    “emitDecoratorMetadata”: true
    },
    “exclude”: [
    “node_modules”,
    “rnApp/Tools” // 项目根目录下新建的存放RN中所用的一些工具类
    ]
    }

    1
    2
    3


    ### package.json 文件配置

    {
    “name”: “RNProject”,
    “version”: “0.0.1”,
    “private”: true,
    “scripts”: { // 通过设置这个可以使npm调用一些命令脚本,封装一些功能
    “start”: “node node_modules/react-native/local-cli/cli.js start”,
    “test”: “jest”
    },
    “dependencies”: { // 发布后依赖的包
    “@types/react”: “^16.4.16”,
    “@types/react-native”: “^0.57.4”,
    “react”: “16.5.0”,
    “react-native”: “0.57.2”,
    “react-transform-hmr”: “^1.0.4”,
    “tslib”: “^1.9.3”
    },

    “devDependencies”: { // 开发中依赖的其它包
    “babel-jest”: “23.6.0”,
    “jest”: “23.6.0”,
    “metro-react-native-babel-preset”: “0.48.0”,
    “react-native-typescript-transformer”: “^1.2.10”,
    “react-test-renderer”: “16.5.0”,
    “typescript”: “^3.1.2”
    },

    “jest”: { // JavaScript 的单元测试框架
    “preset”: “react-native”
    }
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9

    ##### `–save` 和 `-–save-dev` 区别

    - --save参数表示将该模块写入dependencies属性,
    - --save-dev表示将该模块写入devDependencies属性。

    ##### 添加开发依赖库

    - 类型检查

    npm install @types/jest --save-devnpm install @types/react --save-devnpm install @types/react-native --save-dev 
    1
    - 装饰器转化核心插件
    npm install babel-plugin-transform-decorators-legacy --save-dev
    1
    2

    - 测试框架
    npm install react-addons-test-utils --save-devnpm install react-native-mock --save-dev
    1
    2

    ##### 删除依赖库

    npm uninstall @types/jest –save

    1
    2


    npm uninstall @types/jest –save-dev

    1
    2

    ##### 彻底删除,-d 表示 devDependencies, -O 表示 optionalDependencies 中

    npm uninstall -s -D -O @types/jest

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    ### 报错汇总

    ##### 错误1 `Cannot find module 'react-transform-hmr/lib/index.js'`

    [解决1](https://stackoverflow.com/questions/50142561/cannot-find-module-react-transform-hmr-lib-index-js?noredirect=1&lq=1)

    [解决2](https://github.com/facebook/react-native/issues/21530)

    - 安装 `react-transform-hmr`
    npm install react-transform-hmr --save
    1
    2

    - 清除缓存重新启动服务
    npm start --reset-cache```
    ]]> @@ -174,10 +174,10 @@ - - /2019/06/18/is%20%E5%85%B3%E9%94%AE%E5%AD%97%E7%B1%BB%E5%9E%8B%E6%A3%80%E6%9F%A5/ + + /2019/06/18/swift%E4%B8%AD%E5%BC%BA%E5%BC%95%E7%94%A8%E5%BE%AA%E7%8E%AF%E5%92%8Cweak%E5%85%B3%E9%94%AE%E5%AD%97/ - swift类型检查和类型转换

    is 关键字类型检查

    判断父类和子类之间的关系

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    func typeCheck() {
    let programmer = Programmer(name: "老张-php")
    if dev is iOSDeveloper {
    print("iOS 开发者")
    } else {
    print("其他开发者")
    }

    if programmer is Coder {
    print("老张-php是一农个码农")
    } else {
    print("")
    }
    }

    as 关键字类型转换

    1
    2
    let ios = iosDev as? Programmer
    let ios2 = iosDev as! Coder

    判断是否遵守了协议

    swift 中协议可以当作是一个类型使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    et ios = iosDev as? Programmer
    let ios2 = iosDev as Coder

    // 是否遵守了 Person 协议
    if iosDev is Person {
    print("遵守了Person协议")
    }

    let devs = [dev, iosDev, programmer] as [Any]

    for dev in devs {
    if let dev = dev as? Person {
    print(dev)
    print("遵守Person协议")
    } else {
    print("未遵守Person协议")
    }
    }

    AnyObject Any

    1
    2
    3
    4
    var anyObjectArray: [AnyObject] = [CGFloat(0.5) as AnyObject,
    1 as AnyObject,
    "string" as AnyObject,
    iOSODev]

    swift是面向函数编程, 函数是一等公民,数组中可以存入一个函数,函数表达的是一个过程,不是一个名词或者物体,所以函数不是一个对象,需要使用 any 关键字

    1
    2
    3
    4
    var anyArray: [Any] = [CGFloat(0.5),
    1,
    "string",
    iOSODev]

    放入函数

    1
    anyArray.append({ (a: Int) -> (Int) in return a * a })
    ]]>
    + Swift中内存管理

    强引用循环和 weak 关键字

    房东

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class Landlord {
    var name: String
    var house: House?

    init(name: String) {
    self.name = name
    }

    deinit {
    print("Landlord内存释放")
    }
    }

    房子

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class House {
    var name: String
    var landlord: Landlord?

    init(person: String) {
    self.name = person
    }

    deinit {
    print("House内存释放")
    }
    }

    循环引用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    func strongRetain() {
    var landlord: Landlord? = Landlord(name: "老李")
    var house: House? = House(person: "望京府邸")
    // 房东有个房子
    landlord?.house = house
    // 房子有个主人
    house?.landlord = landlord

    landlord = nil
    house = nil
    }
    • 虽然 landlord = nil 和 house = nil,但是 Landlord 和 House 内存空间并没有释放 (并未执行 deinit 函数)

    • 原因:因为两个内存空间相互引用,引用计数不为0,形成了强循环引用

    • 解决办法:使用weak 关键字来修饰两者中其中一个变量

      • 使用weak修饰的变量必须是可选类型,可以赋值为nil
      • 使用weak修饰必须是var 类型变量
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class House {
    var name: String
    weak var landlord: Landlord?

    init(person: String) {
    self.name = person
    }

    deinit {
    print("House内存释放")
    }
    }

    unowned 关键字的使用

    相互循环引用的两个变量有一个是可选的

    有个身份证的类,每个身份证肯定对应一个人

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    class ID {
    var number: String
    let person: Landlord

    init(person: Landlord, number: String) {
    self.person = person
    self.number = number
    }

    deinit {
    print("ID内存释放")
    }
    }

    ```

    房东

    class Landlord {
    var name: String
    var house: House?
    /// 身份证可以为nil,
    var id: ID?

    init(name: String) {    self.name = name}deinit {    print("Landlord内存释放")}

    }

    1
    2

    循环引用

    func unownedTest() {
    var person: Landlord? = Landlord(name: “老王”)
    var id: ID? = ID(person: person!, number: “10010”)

    person?.id = idperson = nilid = nil

    }

    1
    2
    3
    4
    5
    6
    7
    8

    - person 和 id 之间有强引用关系
    - 每个id必须有一个对应的人并且是固定的只能使用let 修饰,不能为nil, 而id可以为空
    - unowned和 weak 作用相同都是弱引用,只是unowned 修改的变量不能是nil (可选型的)
    - unowned只能使用在 class 类型上,不能修饰函数类型,例如闭包存在循环引用时不能使用unowned修饰
    - 解决方法:
    - 此时可以通过使用 weak 修饰 id 的方式,因为 id 可以为nil
    - let 修饰并且不能为nil 这种情况可以使用 unowned 修饰解决
    class ID {    var number: String    unowned let person: Landlord    init(person: Landlord, number: String) {        self.person = person        self.number = number    }    deinit {        print("ID内存释放")    }}
    1
    2
    3
    4

    **unowned有一定的危险性**

    因为unowned 修饰的对象不能赋值为nil,所以执行下面的代码会crash,

    func unownedTest() {
    var person: Landlord? = Landlord(name: “老王”)
    var id: ID? = ID(person: person!, number: “10010”)

    person?.id = id// 提前释放 person 内存person = nil// 获取身份证对应的人print(id?.person) // 此处奔溃,let owner = id?.personid = nil

    }

    1
    2

    正确的写法因该是先将🆔设置为nil,再将 Person 设置为nil,再调用 print(id?.person) 就不会再报错

    func unownedTest() {
    var person: Landlord? = Landlord(name: “老王”)
    var id: ID? = ID(person: person!, number: “10010”)

    person?.id = idid = nil// 提前释放 person 内存person = nil// 获取身份证对应的人print(id?.person) // 此处奔溃,let owner = id?.person

    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

    #### 相互循环引用的两个变量都不是可选的

    使用隐式可选类型

    ### 闭包中的强引用循环

    - 并不是所有的闭包都会产生循环引用,只有相互引用的两个对象会产生循环引用

    - 使用swift闭包捕获列表,在闭包的声明参数之前使用 [unowned self], 不需要解包

    - 使用 unowned表示这个对象不能为空,可能存在风险,如果对象为空就会crash


    使用 unowned 对象不能为空,可能存在风险

    class Closure {
    var closure: ((Int) -> Void)?

    init() {    closure = { [unowned self] index in        print(self)        print(index)    }}

    }

    1
    2

    使用 weak, 此时需要解包

    closure = { [weak self] index in
    // 解包
    if let self = self {
    print(self)
    print(index)
    }
    }

    1
    2

    强制解包

    closure = { [weak self] index in
    // 强制解包
    print(self!)
    print(index)
    }
    `

    ]]>
    @@ -187,10 +187,10 @@ - - /2019/06/18/npm%E5%92%8Ccnpm/ + + /2019/06/18/%E3%80%8A52%E4%B8%AA%E6%96%B9%E6%B3%95%E7%AC%AC%E4%B8%80%E7%AB%A0%E3%80%8B/ - ReactNative配置.npm 和cnpm 相关

    npm

    Nodejs的包管理工具

    npm 常用命令

    • 更新或卸载

      1
      npm update/uninstall moduleName
    • 查看当前目录下已安装的包

      1
      npm list
    • 查看全局安装的包的路径

      1
      npm root -g
    • 全部命令

      1
      npm help

    cnpm

    因为npm安装插件是从国外服务器下载,受网络影响大,可能出现异常,cnpm 淘宝镜像

    安装

    1
    npm install -g cnpm --registry=https://registry.npm.taobao.org

    设置镜像源

    1
    2
    cnpm set registry http://registry.cnpm.taobao.com
    npm set registry http://registry.cnpm.taobao.com

    配置 .npmrc 文件

    前端项目开发离不开安装各种npm依赖包,可以选择远程的仓库也可以选择本地的仓库,但更改仓库地址需要在安装时控制台打命令,比较麻烦, 而npmrc可以很方便地解决上面问题

    .npmrc 原理

    当安装项目的依赖包时,会查找并读取项目根目录下的.npmrc文件的配置,自动指定仓库地址

    • 打开 .npmrc

      1
      open .npmrc
    • 编辑文件, 指定特定的库的镜像源, 比如以@test开头的库镜像为:http://registry.cnpm.test.com/ 而其他的正常

      1
      2
      @test:registry=http://registry.cnpm.lietou.com/
      registry=https://registry.npm.taobao.org/
    ]]> + 《52个方法-第一章》

    一、OC起源

    • OC使用的是“消息结构”而不是“函数调用”
    • 两者区别:
      • 消息结构:在运行时所执行的代码由运行环境来决定,无论是否多态总会在运行时才会去查找所要执行的方法
        1
        2
        NSObject *objc = [NSObject new];
        [objc performWith: parameter1 and: parameter2];
    - 函数调用:由编译器决定,如果函数多态,编译时就要查处到底应该执行那个函数实现
    1
    2
    Object *objc = new Object;
    objc -> perform(parameter1, parameter2);
    • 消息结构中编译器不会关心接收消息的对象是何种类型,接收消息的对象问题也要在运行时处理,此过程叫做“动态绑定”

    要点:

    1. OC语言使用的动态绑定的消息结构,在运行时所执行的代码由运行环境来决定,无论是否多态总会在运行时才会去查找所要执行的方法,
    2. OC 中对象总是分配在“堆空间”,而绝不会分配在”栈“上,不带 “*”的变量或者结构体等可能会使用“栈空间”

    二、类头文件尽量少导入其他头文件

    要点:

    1. 除非必要,否则不要引入头文件,使用向前声明降低类之间的耦合
    2. 有时无法使用向前声明,应该把该类遵守协议的声明放到“class-continuation分类”(匿名分类)中。
    3. 如果“class-continuation分类”也不行的话,就把协议单独放在一个文件中,再将其引入### 三、多用字面量语法,少用与之等价的方法

    三、多用字面量语法,少用与之等价的方法

    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
    - (void)tempMethond {
    // OC1.0 起,支持简单方式创建基本类型对象

    // - 使用字符串字面量:简介易读
    NSString *someString = @"Effective Objective-C 2.0";
    // 传统方式
    NSString *someStrongOld = [NSString stringWithFormat:@"Effective Objective-C 2.0"];


    // - 字面数值
    NSNumber *someNumber = @1;
    NSNumber *floatNumber = @2.5f;
    NSNumber *doubleNumber = @3.1415926;
    NSNumber *boolNumber = @YES;
    NSNumber *charNumber = @'a';
    // 字面量语法用于表达式
    int x = 5;
    float y = 6.23f;
    NSNumber *expressionNumber = @(x * y);


    // - 字面量数组
    NSArray *animals = @[@"cat", @"dog", @"mouse", @"badger"];
    NSString *dog = animals[1];

    // arrayWithObjects 和 字面量 的区别:
    // arrayWithObjects 方法依次处理各个参数,直到发现nil为止
    // 字面量数组遇到nil直接抛出异常
    NSArray *animalsOld = [NSArray arrayWithObjects:@"cat", @"dog", @"mouse", @"badger", nil];
    NSString *dogOld = [animalsOld objectAtIndex:1];


    // - 字面量字典
    // 如果有nil直接抛出异常
    NSDictionary *personData = @{@"firtName": @"Matt",
    @"lastName": @"Galloway",
    @"age": @28};
    NSString *lastName = personData[@"lastName"];

    // dictionaryWithObjectsAndKeys 方法依次处理各个参数,直到发现nil为止
    NSDictionary *personDataOld = [NSDictionary dictionaryWithObjectsAndKeys:@"Matt",@"firtName",
    @"Galloway", @"lastName",
    [NSNumber numberWithInteger:28], @"age", nil];

    NSString *lastNameOld = [personData objectForKey:@"lastName"];


    // - 可变数组和字典
    NSMutableArray *mutableArray = animals.mutableCopy;
    mutableArray[1] = @"dog";

    NSMutableDictionary *mutableDict = [personData mutableCopy];
    mutableDict[@"lastName"] = @"Galloway";
    }

    要点:

    1. 使用字面量语法创建字符串,数值,数组,字典简明扼要
    2. 通过下标操作获取数组,字典对应的元素
    3. 使用字面量创建数组,字典时若有nil ,会抛出异常,确保值里不含nil### 五、用枚举表示状态、选项、状态码

    四、多用类型常量,少用#define预处理指令

    宏定义
    • 缺点:
      • 可以把所有相同的字符串值都替换为定义的值,
        • 定义出来的常量没有具体的类型信息
        • 如果有人重新定义了宏定义,不会有警告或者提醒,导致程序中值不一样
    类型常量
    • 命名规则:

      • 如果只是在实现文件.m 中使用,则在前面加字母k
      • 如果此常量在类之外也可见,则以类名为前缀,(第19条详细了解命名习惯)
    • 声明位置:

      • 总是喜欢在头文件中声明宏定义,可能和常量名冲突,
      • 所有引入这个头文件的类都会出现宏定义或者常量,相当于声明了一个名叫kAnimationDuration的全局变量
      • 应该加上前缀,表明其所属的具体类,例如:EOCViewClassAnimationDuration
    • 常量从右往左解读依次是: 具体类型,不可修改

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      // const 修饰的是常量不希望别人修改, NSString * 指向NSString对象
      extern NSString *const EOCStringConstant;


      //两者写法都可以
      extern const NSTimeInterval EOCAnimatedViewAnimationDuration;
      extern NSTimeInterval const EOCAnimatedViewAnimationDuration1;
      ```

      - 如果不打算公开常量,可以定义在 .m 实现文件里

      static const NSTimeInterval kAnimationDuration = 0.3;

      1
      2
      3

      - static关键字
      - static 修饰意味着该变量仅在定义此变量的编译单元(实现文件.m)中可见,作用域就是当前.m文件
      static const NSTimeInterval kAnimationDuration = 0.3;
      1
      2
      3
      - 如果需要声明一个外部可见的常值变量,需要在头文件中声明并使用 extern 关键词修饰 "extern 

      在 .h 文件中
      extern NSString *const EOCStringConstant;// 两者写法都可以extern const NSTimeInterval EOCAnimatedViewAnimationDuration;extern NSTimeInterval const EOCAnimatedViewAnimationDuration1;
      1
      2

      在 .m 文件中
      NSString *const EOCStringConstant = @"VALUE";const NSTimeInterval EOCAnimatedViewAnimationDuration = 0.3;NSTimeInterval const EOCAnimatedViewAnimationDuration1 = 0.3;
      1
      2
      3
      4
      5
      6
      7
      8
      9

      **要点:**

      1. 定义常量时static const定义编译单元(实现文件.m)中可见的常量,无需为其名称加前缀
      2. 在头文件中使用extern 声明全局常量,并在实现文件中定义其值,这种常量要出现在全局符号表中,所以加相关类作为前缀

      ### 五、用枚举表示状态、选项、状态码

      - 普通枚举

      enum EOCConnectionState {

      EOCConnectionStateDisconnected, // 断开连接EOCConnectionStateConnecting,   // 连接中...EOCConnectionStateConnected,    // 已连接

      };

      1
      普通枚举如果要使用如下:

      enum EOCConnectionState status = EOCConnectionStateConnected;

      1
      2

      如果每次都不需要输入enum关键字,只需要typedef关键字重新定义枚举类型

      EOCConnectionState status1 = EOCConnectionStateConnecting;

      1
      2

      - 设置枚举的初始值

      enum EOCConnectionStateConnectionState {

      EOCConnectionStateConnectionStateDisconnected = 1, // 断开连接EOCConnectionStateConnectionStateConnecting,   // 连接中...EOCConnectionStateConnectionStateConnected,    // 已连接

      };

      1
      2
      3
      4
      5

      - 可选枚举

      - << 位操作符(左移), 表示往左移动N位,使用 位运算符 | (或)来组合使用
      - 使用位运算符 &(与) 判断是否开启了某个选项

      enum UIViewAutoresizing {

      UIViewAutoresizingNone                 = 0,UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,UIViewAutoresizingFlexibleWidth        = 1 << 1,UIViewAutoresizingFlexibleRightMargin  = 1 << 2,UIViewAutoresizingFlexibleTopMargin    = 1 << 3,UIViewAutoresizingFlexibleHeight       = 1 << 4,UIViewAutoresizingFlexibleBottomMargin = 1 << 5

      }

      1
      2
      3
      4

      - 使用宏定义枚举

      普通枚举类型(老式语法定义枚举)

      typedef enum : NSUInteger {

      EOCConnectionState2DisConnected,EOCConnectionState2DisConnecting,EOCConnectionState2Connected,

      } EOCConnectionState2;

      1
      2

      NS_ENUM 宏定义的枚举展开后:

      typedef enum EOCConnectionState_3 : NSUInteger EOCConnectionState_3;
      enum EOCConnectionState_3 : NSUInteger {

      EOCConnectionState_3DisConnected,EOCConnectionState_3DisConnecting,EOCConnectionState_3Connected,

      };

      1
      2

      - 定义新特性枚举

      typedef NS_ENUM(NSUInteger, EOCConnectionState3) {

      EOCConnectionState3DisConnected,EOCConnectionState3DisConnecting,EOCConnectionState3Connected,

      };

      1
      2
      3
      4

      - 枚举使用

      .h文件

      #import <UIKit/UIKit.h>

      typedef enum EOCConnectionState EOCConnectionState;

      typedef NS_ENUM(NSUInteger, EOCConnectionState3) {

      EOCConnectionState3DisConnected,EOCConnectionState3DisConnecting,EOCConnectionState3Connected,

      };

    @interface EOCEnum : NSObject@property (nonatomic, assign) enum EOCConnectionState state;@property (nonatomic, assign) EOCConnectionState3 state3;@end
    1
    2

    .m 文件中
    #import "EOCEnum.h"@implementation EOCEnum- (void)testEnum { // 未使用typedef 关键字 enum EOCConnectionState status = EOCConnectionStateConnected; NSLog(@"%u", status); // 使用typedef 关键字 EOCConnectionState status1 = EOCConnectionStateConnecting; NSLog(@"%u", status1); UIViewAutoresizing resizing = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; if (resizing & UIViewAutoresizingFlexibleWidth) { // 设置了 UIViewAutoresizingFlexibleWidth 约束 }}// UIKit 试图所支持的设置显示方向- (UIInterfaceOrientationMask)supportedInterfaceOrientations { return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft;}// 枚举使用- (void)userEnum { switch (_state3) { case EOCConnectionState3DisConnected: // ... break; case EOCConnectionState3DisConnecting: // ... break; case EOCConnectionState3Connected: // ... break; default: // 最好不用,防止以后增加一种状态机之后,编译器不发出警告 break; }}@end```

    要点:

    1. 使用枚举表示状态机的状态、传递给方法的选项以及状态码等值,给枚举值起名时要注重易懂
    2. 如果一个类型可以使用状态机的状态来表示,并且多个选项可同时使用,那么就定义为可选的枚举类型,枚举各值定义为2的幂,以便通过“按位或”操作组合
    3. 使用NS_ENUM 和 NS_OPTIONS 宏来定义枚举,并指明底层的数据类型。这样可以确保枚举是用开发者所选的底层数据类型实现出来的,而不会采用编译器所选的类型
    4. switch语句处理枚举类型时不要实现default 分支,如果以后加入枚举的新状态之后,编译器会发出警告
    ]]>
    @@ -200,10 +200,10 @@ - - /2019/06/18/swift%E4%B8%AD%E5%BC%BA%E5%BC%95%E7%94%A8%E5%BE%AA%E7%8E%AF%E5%92%8Cweak%E5%85%B3%E9%94%AE%E5%AD%97/ + + /2019/06/18/%E4%B8%80%E8%87%AA%E5%8A%A8%E5%BC%95%E7%94%A8%E8%AE%A1%E6%95%B0/ - Swift中内存管理

    强引用循环和 weak 关键字

    房东

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class Landlord {
    var name: String
    var house: House?

    init(name: String) {
    self.name = name
    }

    deinit {
    print("Landlord内存释放")
    }
    }

    房子

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class House {
    var name: String
    var landlord: Landlord?

    init(person: String) {
    self.name = person
    }

    deinit {
    print("House内存释放")
    }
    }

    循环引用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    func strongRetain() {
    var landlord: Landlord? = Landlord(name: "老李")
    var house: House? = House(person: "望京府邸")
    // 房东有个房子
    landlord?.house = house
    // 房子有个主人
    house?.landlord = landlord

    landlord = nil
    house = nil
    }
    • 虽然 landlord = nil 和 house = nil,但是 Landlord 和 House 内存空间并没有释放 (并未执行 deinit 函数)

    • 原因:因为两个内存空间相互引用,引用计数不为0,形成了强循环引用

    • 解决办法:使用weak 关键字来修饰两者中其中一个变量

      • 使用weak修饰的变量必须是可选类型,可以赋值为nil
      • 使用weak修饰必须是var 类型变量
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class House {
    var name: String
    weak var landlord: Landlord?

    init(person: String) {
    self.name = person
    }

    deinit {
    print("House内存释放")
    }
    }

    unowned 关键字的使用

    相互循环引用的两个变量有一个是可选的

    有个身份证的类,每个身份证肯定对应一个人

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    class ID {
    var number: String
    let person: Landlord

    init(person: Landlord, number: String) {
    self.person = person
    self.number = number
    }

    deinit {
    print("ID内存释放")
    }
    }

    ```

    房东

    class Landlord {
    var name: String
    var house: House?
    /// 身份证可以为nil,
    var id: ID?

    init(name: String) {    self.name = name}deinit {    print("Landlord内存释放")}

    }

    1
    2

    循环引用

    func unownedTest() {
    var person: Landlord? = Landlord(name: “老王”)
    var id: ID? = ID(person: person!, number: “10010”)

    person?.id = idperson = nilid = nil

    }

    1
    2
    3
    4
    5
    6
    7
    8

    - person 和 id 之间有强引用关系
    - 每个id必须有一个对应的人并且是固定的只能使用let 修饰,不能为nil, 而id可以为空
    - unowned和 weak 作用相同都是弱引用,只是unowned 修改的变量不能是nil (可选型的)
    - unowned只能使用在 class 类型上,不能修饰函数类型,例如闭包存在循环引用时不能使用unowned修饰
    - 解决方法:
    - 此时可以通过使用 weak 修饰 id 的方式,因为 id 可以为nil
    - let 修饰并且不能为nil 这种情况可以使用 unowned 修饰解决
    class ID {    var number: String    unowned let person: Landlord    init(person: Landlord, number: String) {        self.person = person        self.number = number    }    deinit {        print("ID内存释放")    }}
    1
    2
    3
    4

    **unowned有一定的危险性**

    因为unowned 修饰的对象不能赋值为nil,所以执行下面的代码会crash,

    func unownedTest() {
    var person: Landlord? = Landlord(name: “老王”)
    var id: ID? = ID(person: person!, number: “10010”)

    person?.id = id// 提前释放 person 内存person = nil// 获取身份证对应的人print(id?.person) // 此处奔溃,let owner = id?.personid = nil

    }

    1
    2

    正确的写法因该是先将🆔设置为nil,再将 Person 设置为nil,再调用 print(id?.person) 就不会再报错

    func unownedTest() {
    var person: Landlord? = Landlord(name: “老王”)
    var id: ID? = ID(person: person!, number: “10010”)

    person?.id = idid = nil// 提前释放 person 内存person = nil// 获取身份证对应的人print(id?.person) // 此处奔溃,let owner = id?.person

    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

    #### 相互循环引用的两个变量都不是可选的

    使用隐式可选类型

    ### 闭包中的强引用循环

    - 并不是所有的闭包都会产生循环引用,只有相互引用的两个对象会产生循环引用

    - 使用swift闭包捕获列表,在闭包的声明参数之前使用 [unowned self], 不需要解包

    - 使用 unowned表示这个对象不能为空,可能存在风险,如果对象为空就会crash


    使用 unowned 对象不能为空,可能存在风险

    class Closure {
    var closure: ((Int) -> Void)?

    init() {    closure = { [unowned self] index in        print(self)        print(index)    }}

    }

    1
    2

    使用 weak, 此时需要解包

    closure = { [weak self] index in
    // 解包
    if let self = self {
    print(self)
    print(index)
    }
    }

    1
    2

    强制解包

    closure = { [weak self] index in
    // 强制解包
    print(self!)
    print(index)
    }
    `

    ]]>
    + 自动引用计数

    什么是自动引用计数

    内存管理中对引用采取自动计数的技术

    内存管理

    • 自己生成的对象,自己所持有
      • alloc
      • new
      • copy
      • mutableCopy
    • 非自己生成的对象,自己也能持有
      • retain
    • 不再需要自己持有的对象时释放
      • release
    • 非自己持有的对象无法释放

    • 废弃对象

      • dealloc

    __strong

    __strong 修饰符表示对对象的“强引用”

    __unsafe_unretained 修饰符

    __unsafe_unretained 是不安全的所有权修饰符, 被它修饰的变量不属于编译器的内存管理对象

    1
    id __unsafe_unretained objc = [[NSObject alloc] init];

    Xcode 提示:Assigning retained object to unsafe_unretained variable; object will be released after assignment

    将保留对象赋给unsafe_unretain变量;对象将在赋值后释放

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    id __unsafe_unretained obj1 = nil;

    {
    id __unsafe_unretained obj = [[NSObject alloc] init];

    // 自己生成并持有对象
    // 因为obj0 变量是__strong,强引用,所以自己持有对象
    id __strong obj0 = [[NSObject alloc] init];

    // obj0 变量赋值给你obj1
    // ojb1 变量不持有对象的强应用,也不持有弱引用
    obj1 = obj0;


    // 输出 obj1 变量表示的对象
    NSLog(@"A: %@", obj1);
    } // obj0变量 超出了其作用域,强引用失效自动释放自己持有的对象,除此之外没有其他持有者,所以废弃该对象




    // 输出 obj1 变量表示的对象
    // obj1 变量表示的对象因为无持有者已经销毁,坏的内存访问
    NSLog(@"B: %@", obj1);
    ]]>
    @@ -213,10 +213,10 @@ - - /2019/06/18/typescripe%20%E5%AE%89%E8%A3%85%E5%8F%8A%E8%AF%AD%E6%B3%95/ + + /2019/06/18/iOS%E9%9D%A2%E8%AF%95OC%E8%AF%AD%E8%A8%80%E7%89%B9%E6%80%A7/ - ReactNative 中使用 TypeScript

    从RN0.57版本开始已经可以直接支持typescript,无需任何配置。在保留根入口文件index.js的前提下,其他文件都可以直接使用.ts或.tsx后缀。但这一由babel进行的转码过程并不进行实际的类型检查,因此仍然需要编辑器和插件来共同进行类型检查

    TypeScript和JavaScript对比

    两者深度对比

    最大的区别:

    • TypeScript是强类型语⾔和静态类型语⾔(需要指定类型)
    • JavaScript是弱类型语⾔和动态类型语⾔(不需要指定类型)

    TypeScript

    由微软开发的自由和开源的编程语言,是JavaScript的一个严格超集,并添加了可选的静态类型和基于类的面向对象编程

    TypeScript定义

    安装及语法

    教程

    安装
    1
    npm install -g typescript
    安装 typescript 依赖管理器 typings
    1
    npm install -g typings
    安装 typings 依赖
    1
    2
    typings install npm~react --save
    typings install dt~react-native --globals --save
    使用 yarn 安装

    如果已经安装了 yarn 安装如下:

    1
    2
    yarn add tslib @types/react @types/react-native
    yarn add --dev react-native-typescript-transformer typescript
    创建 tsconfig.json 配置文件
    • cd 到 ReactNative 项目根文件夹下

      1
      tsc --init
    • 修改 tsconfig.json 文件

      如果一个目录下存在一个tsconfig.json文件,那么它意味着这个目录是TypeScript项目的根目录。 tsconfig.json文件中指定了用来编译这个项目的根文件和编译选项

      tsconfig.json文件配置详解

      TypeScript配置文件tsconfig简析

      务必设置”jsx”:”react”
      注意多余的注释可能会不兼容,需要移除

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      {
      "compilerOptions": {
      "target": "es6",
      "allowJs": true,
      "jsx": "react",
      "outDir": "./dist",
      "sourceMap": true,
      "noImplicitAny": false
      },

      // "files"属性 :指定一个包含相对或绝对文件路径的列表。 "include"和"exclude"属性指定一个文件glob匹配模式列表

      "include": [
      "typings/**/*.d.ts",
      "src/**/*.ts",
      "src/**/*.tsx"
      ],
      "exclude": [
      "node_modules"
      ]
      }
    • cd 到项目根目录中新建存放 typescripe 源代码的文件夹

    建立 rn-cli.config.js 文件,使用支持typescript的transfomer
    1
    2
    3
    4
    5
    6
    7
    8
    module.exports = {
    getTransformModulePath() {
    return require.resolve('react-native-typescript-transformer');
    },
    getSourceExts() {
    return ['ts', 'tsx'];
    }
    }
    修改 .babelrc 文件
    1
    2
    3
    4
    {
    "presets": ["react-native"],
    "plugins": ["transform-decorators-legacy"]
    }
    修改项目中文件导入
    • 入口index.jsApp.js的文件名请不要修改,项目根目录下新建src文件夹
    • src文件夹下新建 .tsx 后缀的文件
    • .tsx 后缀的文件引用react的写法有所区别

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      import * as React from 'react'
      import { Text, View } from 'react-native';
      import { Component } from 'react';

      export default class HelloWorld extends Component{
      render() {
      return (
      <View>
      <Text>Hello!</Text>
      <Text>Typescript</Text>
      </View>
      );
      }
      }
    • App.js中使用

      • App.js 导入方式修改为如下方式

        1
        2
        3
        4
        import './src';
        ```

        - 导入 .tsx 类型的组建

        import HelloWorld from “./src/HelloWorld”;

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11

        ### 配置jsconfig

        `jsconfig.json`目录中存在文件表明该目录是JavaScript项目的根目录。该jsconfig.json文件指定根文件和JavaScript语言服务提供的功能选项

        [jsconfig详解](https://code.visualstudio.com/docs/languages/jsconfig)

        [jsconfig.json](https://jeasonstudio.gitbooks.io/vscode-cn-doc/content/md/%E8%AF%AD%E8%A8%80/javascript.html?q=)


        ##### 新建 `jsconfig.json` 文件

    touch jsconfig.json

    1
    2

    ##### 编辑 `jsconfig.json` 文件

    {
    “compilerOptions”: {
    “allowJs”: true,
    “allowSyntheticDefaultImports”: true,
    “emitDecoratorMetadata”: true
    },
    “exclude”: [
    “node_modules”,
    “rnApp/Tools” // 项目根目录下新建的存放RN中所用的一些工具类
    ]
    }

    1
    2
    3


    ### package.json 文件配置

    {
    “name”: “RNProject”,
    “version”: “0.0.1”,
    “private”: true,
    “scripts”: { // 通过设置这个可以使npm调用一些命令脚本,封装一些功能
    “start”: “node node_modules/react-native/local-cli/cli.js start”,
    “test”: “jest”
    },
    “dependencies”: { // 发布后依赖的包
    “@types/react”: “^16.4.16”,
    “@types/react-native”: “^0.57.4”,
    “react”: “16.5.0”,
    “react-native”: “0.57.2”,
    “react-transform-hmr”: “^1.0.4”,
    “tslib”: “^1.9.3”
    },

    “devDependencies”: { // 开发中依赖的其它包
    “babel-jest”: “23.6.0”,
    “jest”: “23.6.0”,
    “metro-react-native-babel-preset”: “0.48.0”,
    “react-native-typescript-transformer”: “^1.2.10”,
    “react-test-renderer”: “16.5.0”,
    “typescript”: “^3.1.2”
    },

    “jest”: { // JavaScript 的单元测试框架
    “preset”: “react-native”
    }
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9

    ##### `–save` 和 `-–save-dev` 区别

    - --save参数表示将该模块写入dependencies属性,
    - --save-dev表示将该模块写入devDependencies属性。

    ##### 添加开发依赖库

    - 类型检查

    npm install @types/jest --save-devnpm install @types/react --save-devnpm install @types/react-native --save-dev 
    1
    - 装饰器转化核心插件
    npm install babel-plugin-transform-decorators-legacy --save-dev
    1
    2

    - 测试框架
    npm install react-addons-test-utils --save-devnpm install react-native-mock --save-dev
    1
    2

    ##### 删除依赖库

    npm uninstall @types/jest –save

    1
    2


    npm uninstall @types/jest –save-dev

    1
    2

    ##### 彻底删除,-d 表示 devDependencies, -O 表示 optionalDependencies 中

    npm uninstall -s -D -O @types/jest

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    ### 报错汇总

    ##### 错误1 `Cannot find module 'react-transform-hmr/lib/index.js'`

    [解决1](https://stackoverflow.com/questions/50142561/cannot-find-module-react-transform-hmr-lib-index-js?noredirect=1&lq=1)

    [解决2](https://github.com/facebook/react-native/issues/21530)

    - 安装 `react-transform-hmr`
    npm install react-transform-hmr --save
    1
    2

    - 清除缓存重新启动服务
    npm start --reset-cache```
    ]]> + OC 语言特性

    分类

    原理

    由运行时来决议的,不同分类当中含有同名方法 谁最终生效取决于谁最终参与编译,最后参编译的同名分类方法会最终生效,假如分类方法中添加的方法和宿主类中的某一个方法名相同,分类中的方法会覆盖宿主类中的同名方法,覆盖是指在消息传递过程中优先查找数组靠前的元素,如果查找到了同名方法就直接调用,实际上宿主类的同名方法实现仍然是存在的,我们可可以通过一些手段调用到原有类的同名方法的实现

    使用场景

    • 申明私有方法,分类的 .m 文件中申明私有方法啊,对外不暴露
    • 对类中的代码进行抽取分解
    • 把Framework的私有方法公开化

    特点

    • 运行时决议,运行时通过runtime 才把分类中的方法添加到了宿主类上
    • 可以为系统类添加方法
    • 有多个分类中存在同名方法时,最后编译的方法会最先生效
    • 分类中有和宿主类同名方法时,底层中通过内存copy将分类方法“覆盖”宿主类方法
    • 名字相同的分类编译会报错

    分类中可以添加的内容

    • 实例方法
    • 类方法
    • 协议
    • 属性,实际上只是声明了 getter / setter 方法,并没有添加成员变量

    关联对象技术

    关联对象由 AssocicationsManager 管理并在 AssociationsHashMap 上存储
    所有对象的关联内容都在同一个全局容器中

    • 给分类添加成员变量,通过关联对象的方式

    扩展

    使用场景

    • 声明私有属性
    • 声明私有方法
    • 声明私有成员变量

    特点

    • 编译时决议
    • 只以声明的形式存在,多数情况下寄生在宿主类的.m中
    • 不能为系统添加扩展

    代理

    • 代理设计模式,传递方式是一对一
    • 使用weak 避免循环引用

    通知

    使用观察者模式来实现的用于跨层传递消息的机制

    KVO

    KVO 是系统对观察者模式的又一实现,使用isa 混写技术(isa - swizzling)来动态运行时为某一个类添加一个子类重写了它的setter 方法,同时将原有类的isa指针指向了新创建的类上

    • KVO 是OC对观察者模式的又一实现
    • apple 使用isa 混写技术(isa - swizzling)来实现KVO
      • 给我A类注册一个观察者时本质上是调用系统的 Observer for keyPath 方法,系统会创建一个NSKVONotifiying_A 的类
    • 使用 setter 方法设置值KVO才能生效
    • 使用setValue:forKey: 改变值KVO才能生效
    • 使用下划线的成员变量直接修改值需要手动添加KVO才能生效(增加 willChangeValueForKey 和 didChangeValueForKey 两个方法)

    KVC

    apple提供的键值编码技术

    • (nullable id)valueForKey:(NSString *)key;
    • (void)setValue:(nullable id)value forKey:(NSString *)key;

      以上两个方法中的key是没有任何限制的,只要知道对应的成员变量名称,就可以对私有的成员变量设置和操作,这违背了面向对象的编程思想

    (void)setValue:(nullable id)value forKey:(NSString *)key; 方法调用流程

    • 先判断是否有跟key 相关的setter 方法,如果有就直接调用,结束调用
    • 如果没有再判断是否存在实例变量,如果存在直接给赋值,结束调用
    • 如果说实例变量不存在,会去调用 - (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key; 抛出异常

    属性关键字

    读写权限

    • readonly

    • readwrite 系统默认

    原子性

    • atomic (系统默认)保证赋值和获取(对成员属性的直接获取和赋值)时线程安全,并不代表操作和访问,如果atomic修饰的一个数组,只能保证独具改数组的读取和赋值时线程安全,并不能保证对数据操作(增加/删除数组元素)时是安全的
    • nonatomic

    引用技术

    • assign
      • 修饰基本数据类型,
      • 修饰对象时不改变其引用计数,
      • 会产生悬垂指针
    • weak
      • 不改变被修饰对象的引用计数,
      • 所指对象被释放之后指针自动置为nil

    两者区别:

    • weak可以修饰对象,而assign 既可以修饰对象也可以修饰基本数据类型
    • assign 修饰的对象,对象释放后指针仍然指向原对象的内存地址,而weak 修饰的对象,释放后会自动置为nil

    问题1

    weak 修饰的对象为什么释放后会自动置为nil

    问题2

    @property (copy) NSMutableArray *array; 有什么问题

    • 被copy修饰后,如果赋值过来的是NSMutableArray,copy之后就是NSArray,如果赋值过来的是NSAarray,copy 之后仍然是NSArray,有可能会调用array的添加元素方法会导致crash

    copy 关键词

    • 浅拷贝:内存地址的复制,让目标对象指针和源对象指向同一块内存空间,
      • 会增加对象的引用计数
      • 并没有一个新的内存分配
    • 深拷贝:目标对象指针和源对象指针分别指向两块内容相同的内存空间
      • 不会增加源对象的引用计数
      • 有新对象的内存分配

    MRC 重写 retain修饰的变量的setter 方法

    1
    2
    3
    4
    5
    6
    7
    8

    - (void)setObj:(id)obj {
    if (_obj != obj) {
    [_obj release];
    }
    _obj = [obj retain];

    }

    判断是为了防止异常处理,如果不做 if 判断,当传入的 obj 对象正好是原来的_obj 对象,对原对象尽行releas 操作,实际上也会对传入的对象进行releas 操作进行释放,此时如果再通过obj指针访问废弃的对象时就会导致carsh

    ]]>
    @@ -226,10 +226,10 @@ - - /2019/06/18/%E3%80%8A52%E4%B8%AA%E6%96%B9%E6%B3%95%E7%AC%AC%E4%B8%80%E7%AB%A0%E3%80%8B/ + + /2019/06/18/is%20%E5%85%B3%E9%94%AE%E5%AD%97%E7%B1%BB%E5%9E%8B%E6%A3%80%E6%9F%A5/ - 《52个方法-第一章》

    一、OC起源

    • OC使用的是“消息结构”而不是“函数调用”
    • 两者区别:
      • 消息结构:在运行时所执行的代码由运行环境来决定,无论是否多态总会在运行时才会去查找所要执行的方法
        1
        2
        NSObject *objc = [NSObject new];
        [objc performWith: parameter1 and: parameter2];
    - 函数调用:由编译器决定,如果函数多态,编译时就要查处到底应该执行那个函数实现
    1
    2
    Object *objc = new Object;
    objc -> perform(parameter1, parameter2);
    • 消息结构中编译器不会关心接收消息的对象是何种类型,接收消息的对象问题也要在运行时处理,此过程叫做“动态绑定”

    要点:

    1. OC语言使用的动态绑定的消息结构,在运行时所执行的代码由运行环境来决定,无论是否多态总会在运行时才会去查找所要执行的方法,
    2. OC 中对象总是分配在“堆空间”,而绝不会分配在”栈“上,不带 “*”的变量或者结构体等可能会使用“栈空间”

    二、类头文件尽量少导入其他头文件

    要点:

    1. 除非必要,否则不要引入头文件,使用向前声明降低类之间的耦合
    2. 有时无法使用向前声明,应该把该类遵守协议的声明放到“class-continuation分类”(匿名分类)中。
    3. 如果“class-continuation分类”也不行的话,就把协议单独放在一个文件中,再将其引入### 三、多用字面量语法,少用与之等价的方法

    三、多用字面量语法,少用与之等价的方法

    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
    - (void)tempMethond {
    // OC1.0 起,支持简单方式创建基本类型对象

    // - 使用字符串字面量:简介易读
    NSString *someString = @"Effective Objective-C 2.0";
    // 传统方式
    NSString *someStrongOld = [NSString stringWithFormat:@"Effective Objective-C 2.0"];


    // - 字面数值
    NSNumber *someNumber = @1;
    NSNumber *floatNumber = @2.5f;
    NSNumber *doubleNumber = @3.1415926;
    NSNumber *boolNumber = @YES;
    NSNumber *charNumber = @'a';
    // 字面量语法用于表达式
    int x = 5;
    float y = 6.23f;
    NSNumber *expressionNumber = @(x * y);


    // - 字面量数组
    NSArray *animals = @[@"cat", @"dog", @"mouse", @"badger"];
    NSString *dog = animals[1];

    // arrayWithObjects 和 字面量 的区别:
    // arrayWithObjects 方法依次处理各个参数,直到发现nil为止
    // 字面量数组遇到nil直接抛出异常
    NSArray *animalsOld = [NSArray arrayWithObjects:@"cat", @"dog", @"mouse", @"badger", nil];
    NSString *dogOld = [animalsOld objectAtIndex:1];


    // - 字面量字典
    // 如果有nil直接抛出异常
    NSDictionary *personData = @{@"firtName": @"Matt",
    @"lastName": @"Galloway",
    @"age": @28};
    NSString *lastName = personData[@"lastName"];

    // dictionaryWithObjectsAndKeys 方法依次处理各个参数,直到发现nil为止
    NSDictionary *personDataOld = [NSDictionary dictionaryWithObjectsAndKeys:@"Matt",@"firtName",
    @"Galloway", @"lastName",
    [NSNumber numberWithInteger:28], @"age", nil];

    NSString *lastNameOld = [personData objectForKey:@"lastName"];


    // - 可变数组和字典
    NSMutableArray *mutableArray = animals.mutableCopy;
    mutableArray[1] = @"dog";

    NSMutableDictionary *mutableDict = [personData mutableCopy];
    mutableDict[@"lastName"] = @"Galloway";
    }

    要点:

    1. 使用字面量语法创建字符串,数值,数组,字典简明扼要
    2. 通过下标操作获取数组,字典对应的元素
    3. 使用字面量创建数组,字典时若有nil ,会抛出异常,确保值里不含nil### 五、用枚举表示状态、选项、状态码

    四、多用类型常量,少用#define预处理指令

    宏定义
    • 缺点:
      • 可以把所有相同的字符串值都替换为定义的值,
        • 定义出来的常量没有具体的类型信息
        • 如果有人重新定义了宏定义,不会有警告或者提醒,导致程序中值不一样
    类型常量
    • 命名规则:

      • 如果只是在实现文件.m 中使用,则在前面加字母k
      • 如果此常量在类之外也可见,则以类名为前缀,(第19条详细了解命名习惯)
    • 声明位置:

      • 总是喜欢在头文件中声明宏定义,可能和常量名冲突,
      • 所有引入这个头文件的类都会出现宏定义或者常量,相当于声明了一个名叫kAnimationDuration的全局变量
      • 应该加上前缀,表明其所属的具体类,例如:EOCViewClassAnimationDuration
    • 常量从右往左解读依次是: 具体类型,不可修改

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      // const 修饰的是常量不希望别人修改, NSString * 指向NSString对象
      extern NSString *const EOCStringConstant;


      //两者写法都可以
      extern const NSTimeInterval EOCAnimatedViewAnimationDuration;
      extern NSTimeInterval const EOCAnimatedViewAnimationDuration1;
      ```

      - 如果不打算公开常量,可以定义在 .m 实现文件里

      static const NSTimeInterval kAnimationDuration = 0.3;

      1
      2
      3

      - static关键字
      - static 修饰意味着该变量仅在定义此变量的编译单元(实现文件.m)中可见,作用域就是当前.m文件
      static const NSTimeInterval kAnimationDuration = 0.3;
      1
      2
      3
      - 如果需要声明一个外部可见的常值变量,需要在头文件中声明并使用 extern 关键词修饰 "extern 

      在 .h 文件中
      extern NSString *const EOCStringConstant;// 两者写法都可以extern const NSTimeInterval EOCAnimatedViewAnimationDuration;extern NSTimeInterval const EOCAnimatedViewAnimationDuration1;
      1
      2

      在 .m 文件中
      NSString *const EOCStringConstant = @"VALUE";const NSTimeInterval EOCAnimatedViewAnimationDuration = 0.3;NSTimeInterval const EOCAnimatedViewAnimationDuration1 = 0.3;
      1
      2
      3
      4
      5
      6
      7
      8
      9

      **要点:**

      1. 定义常量时static const定义编译单元(实现文件.m)中可见的常量,无需为其名称加前缀
      2. 在头文件中使用extern 声明全局常量,并在实现文件中定义其值,这种常量要出现在全局符号表中,所以加相关类作为前缀

      ### 五、用枚举表示状态、选项、状态码

      - 普通枚举

      enum EOCConnectionState {

      EOCConnectionStateDisconnected, // 断开连接EOCConnectionStateConnecting,   // 连接中...EOCConnectionStateConnected,    // 已连接

      };

      1
      普通枚举如果要使用如下:

      enum EOCConnectionState status = EOCConnectionStateConnected;

      1
      2

      如果每次都不需要输入enum关键字,只需要typedef关键字重新定义枚举类型

      EOCConnectionState status1 = EOCConnectionStateConnecting;

      1
      2

      - 设置枚举的初始值

      enum EOCConnectionStateConnectionState {

      EOCConnectionStateConnectionStateDisconnected = 1, // 断开连接EOCConnectionStateConnectionStateConnecting,   // 连接中...EOCConnectionStateConnectionStateConnected,    // 已连接

      };

      1
      2
      3
      4
      5

      - 可选枚举

      - << 位操作符(左移), 表示往左移动N位,使用 位运算符 | (或)来组合使用
      - 使用位运算符 &(与) 判断是否开启了某个选项

      enum UIViewAutoresizing {

      UIViewAutoresizingNone                 = 0,UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,UIViewAutoresizingFlexibleWidth        = 1 << 1,UIViewAutoresizingFlexibleRightMargin  = 1 << 2,UIViewAutoresizingFlexibleTopMargin    = 1 << 3,UIViewAutoresizingFlexibleHeight       = 1 << 4,UIViewAutoresizingFlexibleBottomMargin = 1 << 5

      }

      1
      2
      3
      4

      - 使用宏定义枚举

      普通枚举类型(老式语法定义枚举)

      typedef enum : NSUInteger {

      EOCConnectionState2DisConnected,EOCConnectionState2DisConnecting,EOCConnectionState2Connected,

      } EOCConnectionState2;

      1
      2

      NS_ENUM 宏定义的枚举展开后:

      typedef enum EOCConnectionState_3 : NSUInteger EOCConnectionState_3;
      enum EOCConnectionState_3 : NSUInteger {

      EOCConnectionState_3DisConnected,EOCConnectionState_3DisConnecting,EOCConnectionState_3Connected,

      };

      1
      2

      - 定义新特性枚举

      typedef NS_ENUM(NSUInteger, EOCConnectionState3) {

      EOCConnectionState3DisConnected,EOCConnectionState3DisConnecting,EOCConnectionState3Connected,

      };

      1
      2
      3
      4

      - 枚举使用

      .h文件

      #import <UIKit/UIKit.h>

      typedef enum EOCConnectionState EOCConnectionState;

      typedef NS_ENUM(NSUInteger, EOCConnectionState3) {

      EOCConnectionState3DisConnected,EOCConnectionState3DisConnecting,EOCConnectionState3Connected,

      };

    @interface EOCEnum : NSObject@property (nonatomic, assign) enum EOCConnectionState state;@property (nonatomic, assign) EOCConnectionState3 state3;@end
    1
    2

    .m 文件中
    #import "EOCEnum.h"@implementation EOCEnum- (void)testEnum { // 未使用typedef 关键字 enum EOCConnectionState status = EOCConnectionStateConnected; NSLog(@"%u", status); // 使用typedef 关键字 EOCConnectionState status1 = EOCConnectionStateConnecting; NSLog(@"%u", status1); UIViewAutoresizing resizing = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; if (resizing & UIViewAutoresizingFlexibleWidth) { // 设置了 UIViewAutoresizingFlexibleWidth 约束 }}// UIKit 试图所支持的设置显示方向- (UIInterfaceOrientationMask)supportedInterfaceOrientations { return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft;}// 枚举使用- (void)userEnum { switch (_state3) { case EOCConnectionState3DisConnected: // ... break; case EOCConnectionState3DisConnecting: // ... break; case EOCConnectionState3Connected: // ... break; default: // 最好不用,防止以后增加一种状态机之后,编译器不发出警告 break; }}@end```

    要点:

    1. 使用枚举表示状态机的状态、传递给方法的选项以及状态码等值,给枚举值起名时要注重易懂
    2. 如果一个类型可以使用状态机的状态来表示,并且多个选项可同时使用,那么就定义为可选的枚举类型,枚举各值定义为2的幂,以便通过“按位或”操作组合
    3. 使用NS_ENUM 和 NS_OPTIONS 宏来定义枚举,并指明底层的数据类型。这样可以确保枚举是用开发者所选的底层数据类型实现出来的,而不会采用编译器所选的类型
    4. switch语句处理枚举类型时不要实现default 分支,如果以后加入枚举的新状态之后,编译器会发出警告
    ]]>
    + swift类型检查和类型转换

    is 关键字类型检查

    判断父类和子类之间的关系

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    func typeCheck() {
    let programmer = Programmer(name: "老张-php")
    if dev is iOSDeveloper {
    print("iOS 开发者")
    } else {
    print("其他开发者")
    }

    if programmer is Coder {
    print("老张-php是一农个码农")
    } else {
    print("")
    }
    }

    as 关键字类型转换

    1
    2
    let ios = iosDev as? Programmer
    let ios2 = iosDev as! Coder

    判断是否遵守了协议

    swift 中协议可以当作是一个类型使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    et ios = iosDev as? Programmer
    let ios2 = iosDev as Coder

    // 是否遵守了 Person 协议
    if iosDev is Person {
    print("遵守了Person协议")
    }

    let devs = [dev, iosDev, programmer] as [Any]

    for dev in devs {
    if let dev = dev as? Person {
    print(dev)
    print("遵守Person协议")
    } else {
    print("未遵守Person协议")
    }
    }

    AnyObject Any

    1
    2
    3
    4
    var anyObjectArray: [AnyObject] = [CGFloat(0.5) as AnyObject,
    1 as AnyObject,
    "string" as AnyObject,
    iOSODev]

    swift是面向函数编程, 函数是一等公民,数组中可以存入一个函数,函数表达的是一个过程,不是一个名词或者物体,所以函数不是一个对象,需要使用 any 关键字

    1
    2
    3
    4
    var anyArray: [Any] = [CGFloat(0.5),
    1,
    "string",
    iOSODev]

    放入函数

    1
    anyArray.append({ (a: Int) -> (Int) in return a * a })
    ]]>
    @@ -239,10 +239,10 @@ - - /2019/06/18/iOS%E9%80%9A%E8%BF%87URL%E8%8E%B7%E5%8F%96HTML%E4%B8%AD%E6%A0%87%E9%A2%98/ + + /2019/06/18/npm%E5%92%8Ccnpm/ - iOS通过URL获取HTML中标题

    获取标题

    String扩展一个方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    private let patternTitle = "(?<=title>).*(?=</title)"
    extension String {
    static func parseHTMLTitle(_ urlString: String) -> String? {
    let url = URL(string: urlString)!
    do {
    let html = try String(contentsOf: url, encoding: .utf8)
    let range = html.range(of: patternTitle, options: String.CompareOptions.regularExpression, range: html.startIndex..<html.endIndex, locale: nil)
    guard let tempRange = range else { return "" }
    let titleStr = html[tempRange]
    return String(titleStr)
    } catch {
    return ""
    }
    }
    }
    iOS 中简单正则表达式

    iOS 中 正则表达式中的\ 需要使用 \\ 来表示

    // 获取let testStr = "我到底是[iOS]开发还是[产品]经理"// 获取 "[" 和 "]" 之间的所有内容, 左边[ 起一直到 右边 ] 结束的所有内容 let pattern1 = "\\[.*\\]" // [iOS]开发还是[产品]// 获取 "[" 和 "]" 之间的所有内容,包括[]let pattern2 = "\\[.*?\\]"    // [iOS]// 获取第一个 "[" 和 "]" 之间的所有内容 不包括[] let pattern3 = "(?<=\\[).*?(?=\\])" // iOS// 获取html中 title 标签中内容let pattern5 = "(?<=title>).*(?=</title)" 
    获取范围截取子串
    let testRange1 = testStr.range(of: pattern1, options: String.CompareOptions.regularExpression, range: testStr.startIndex..<testStr.endIndex, locale: nil)    let result1 = String(testStr[testRange1!])let testRange2 = testStr.range(of: pattern2, options: String.CompareOptions.regularExpression, range: testStr.startIndex..<testStr.endIndex, locale: nil)let result2 = String(testStr[testRange2!])let testRange3 = testStr.range(of: pattern3, options: String.CompareOptions.regularExpression, range: testStr.startIndex..<testStr.endIndex, locale: nil)let result3 = String(testStr[testRange3!])print(result1) print(result2) print(result3) 
    正则表达式

    通过URL解析html中的标题

    • 获取html字符串
    • 正则匹配
    • 生成字符串Range
    • 截取字符串

    匹配一次直接返回结果

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // 生成表达式
    let regular = try NSRegularExpression(pattern: pattern5, options: .caseInsensitive)


    // 开始匹配
    let matchResult1 = regular.firstMatch(in: html, options: NSRegularExpression.MatchingOptions.reportProgress, range: NSMakeRange(0, html.count))

    // 获取匹配结果范围
    let matchRange = matchResult1?.range
    let range = Range(matchRange!)!

    // 生成String.Index 类型范围
    let startIndex = html.index(html.startIndex, offsetBy: range.lowerBound)
    let endIndex = html.index(html.startIndex, offsetBy: range.upperBound)
    let range3 = Range(uncheckedBounds: (lower: startIndex, upper: endIndex))

    // 截取字符串
    let result1 = String(html[range3])
    print(result1)

    全部匹配后返回结果时一个集合

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    let matchResult2 = regular.matches(in: html, options: .reportProgress, range: NSMakeRange(0, html.count))
    matchResult2.forEach { (result) in
    let range = Range(result.range)!

    let startIndex = html.index(html.startIndex, offsetBy: range.lowerBound)
    let endIndex = html.index(html.startIndex, offsetBy: range.upperBound)

    let subStr2 = html[Range(uncheckedBounds: (startIndex, endIndex))]
    print(String(subStr2))
    }

    将匹配到的内容提还

    1
    let str3 = regular.stringByReplacingMatches(in: html, options: .reportProgress, range: NSMakeRange(0, html.count), withTemplate: "====")
    ]]>
    + ReactNative配置.npm 和cnpm 相关

    npm

    Nodejs的包管理工具

    npm 常用命令

    • 更新或卸载

      1
      npm update/uninstall moduleName
    • 查看当前目录下已安装的包

      1
      npm list
    • 查看全局安装的包的路径

      1
      npm root -g
    • 全部命令

      1
      npm help

    cnpm

    因为npm安装插件是从国外服务器下载,受网络影响大,可能出现异常,cnpm 淘宝镜像

    安装

    1
    npm install -g cnpm --registry=https://registry.npm.taobao.org

    设置镜像源

    1
    2
    cnpm set registry http://registry.cnpm.taobao.com
    npm set registry http://registry.cnpm.taobao.com

    配置 .npmrc 文件

    前端项目开发离不开安装各种npm依赖包,可以选择远程的仓库也可以选择本地的仓库,但更改仓库地址需要在安装时控制台打命令,比较麻烦, 而npmrc可以很方便地解决上面问题

    .npmrc 原理

    当安装项目的依赖包时,会查找并读取项目根目录下的.npmrc文件的配置,自动指定仓库地址

    • 打开 .npmrc

      1
      open .npmrc
    • 编辑文件, 指定特定的库的镜像源, 比如以@test开头的库镜像为:http://registry.cnpm.test.com/ 而其他的正常

      1
      2
      @test:registry=http://registry.cnpm.lietou.com/
      registry=https://registry.npm.taobao.org/
    ]]>
    @@ -252,10 +252,10 @@ - - /2019/06/18/iOS%E9%9D%A2%E8%AF%95OC%E8%AF%AD%E8%A8%80%E7%89%B9%E6%80%A7/ + + /2019/06/18/iOS%E9%80%9A%E8%BF%87URL%E8%8E%B7%E5%8F%96HTML%E4%B8%AD%E6%A0%87%E9%A2%98/ - OC 语言特性

    分类

    原理

    由运行时来决议的,不同分类当中含有同名方法 谁最终生效取决于谁最终参与编译,最后参编译的同名分类方法会最终生效,假如分类方法中添加的方法和宿主类中的某一个方法名相同,分类中的方法会覆盖宿主类中的同名方法,覆盖是指在消息传递过程中优先查找数组靠前的元素,如果查找到了同名方法就直接调用,实际上宿主类的同名方法实现仍然是存在的,我们可可以通过一些手段调用到原有类的同名方法的实现

    使用场景

    • 申明私有方法,分类的 .m 文件中申明私有方法啊,对外不暴露
    • 对类中的代码进行抽取分解
    • 把Framework的私有方法公开化

    特点

    • 运行时决议,运行时通过runtime 才把分类中的方法添加到了宿主类上
    • 可以为系统类添加方法
    • 有多个分类中存在同名方法时,最后编译的方法会最先生效
    • 分类中有和宿主类同名方法时,底层中通过内存copy将分类方法“覆盖”宿主类方法
    • 名字相同的分类编译会报错

    分类中可以添加的内容

    • 实例方法
    • 类方法
    • 协议
    • 属性,实际上只是声明了 getter / setter 方法,并没有添加成员变量

    关联对象技术

    关联对象由 AssocicationsManager 管理并在 AssociationsHashMap 上存储
    所有对象的关联内容都在同一个全局容器中

    • 给分类添加成员变量,通过关联对象的方式

    扩展

    使用场景

    • 声明私有属性
    • 声明私有方法
    • 声明私有成员变量

    特点

    • 编译时决议
    • 只以声明的形式存在,多数情况下寄生在宿主类的.m中
    • 不能为系统添加扩展

    代理

    • 代理设计模式,传递方式是一对一
    • 使用weak 避免循环引用

    通知

    使用观察者模式来实现的用于跨层传递消息的机制

    KVO

    KVO 是系统对观察者模式的又一实现,使用isa 混写技术(isa - swizzling)来动态运行时为某一个类添加一个子类重写了它的setter 方法,同时将原有类的isa指针指向了新创建的类上

    • KVO 是OC对观察者模式的又一实现
    • apple 使用isa 混写技术(isa - swizzling)来实现KVO
      • 给我A类注册一个观察者时本质上是调用系统的 Observer for keyPath 方法,系统会创建一个NSKVONotifiying_A 的类
    • 使用 setter 方法设置值KVO才能生效
    • 使用setValue:forKey: 改变值KVO才能生效
    • 使用下划线的成员变量直接修改值需要手动添加KVO才能生效(增加 willChangeValueForKey 和 didChangeValueForKey 两个方法)

    KVC

    apple提供的键值编码技术

    • (nullable id)valueForKey:(NSString *)key;
    • (void)setValue:(nullable id)value forKey:(NSString *)key;

      以上两个方法中的key是没有任何限制的,只要知道对应的成员变量名称,就可以对私有的成员变量设置和操作,这违背了面向对象的编程思想

    (void)setValue:(nullable id)value forKey:(NSString *)key; 方法调用流程

    • 先判断是否有跟key 相关的setter 方法,如果有就直接调用,结束调用
    • 如果没有再判断是否存在实例变量,如果存在直接给赋值,结束调用
    • 如果说实例变量不存在,会去调用 - (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key; 抛出异常

    属性关键字

    读写权限

    • readonly

    • readwrite 系统默认

    原子性

    • atomic (系统默认)保证赋值和获取(对成员属性的直接获取和赋值)时线程安全,并不代表操作和访问,如果atomic修饰的一个数组,只能保证独具改数组的读取和赋值时线程安全,并不能保证对数据操作(增加/删除数组元素)时是安全的
    • nonatomic

    引用技术

    • assign
      • 修饰基本数据类型,
      • 修饰对象时不改变其引用计数,
      • 会产生悬垂指针
    • weak
      • 不改变被修饰对象的引用计数,
      • 所指对象被释放之后指针自动置为nil

    两者区别:

    • weak可以修饰对象,而assign 既可以修饰对象也可以修饰基本数据类型
    • assign 修饰的对象,对象释放后指针仍然指向原对象的内存地址,而weak 修饰的对象,释放后会自动置为nil

    问题1

    weak 修饰的对象为什么释放后会自动置为nil

    问题2

    @property (copy) NSMutableArray *array; 有什么问题

    • 被copy修饰后,如果赋值过来的是NSMutableArray,copy之后就是NSArray,如果赋值过来的是NSAarray,copy 之后仍然是NSArray,有可能会调用array的添加元素方法会导致crash

    copy 关键词

    • 浅拷贝:内存地址的复制,让目标对象指针和源对象指向同一块内存空间,
      • 会增加对象的引用计数
      • 并没有一个新的内存分配
    • 深拷贝:目标对象指针和源对象指针分别指向两块内容相同的内存空间
      • 不会增加源对象的引用计数
      • 有新对象的内存分配

    MRC 重写 retain修饰的变量的setter 方法

    1
    2
    3
    4
    5
    6
    7
    8

    - (void)setObj:(id)obj {
    if (_obj != obj) {
    [_obj release];
    }
    _obj = [obj retain];

    }

    判断是为了防止异常处理,如果不做 if 判断,当传入的 obj 对象正好是原来的_obj 对象,对原对象尽行releas 操作,实际上也会对传入的对象进行releas 操作进行释放,此时如果再通过obj指针访问废弃的对象时就会导致carsh

    ]]>
    + iOS通过URL获取HTML中标题

    获取标题

    String扩展一个方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    private let patternTitle = "(?<=title>).*(?=</title)"
    extension String {
    static func parseHTMLTitle(_ urlString: String) -> String? {
    let url = URL(string: urlString)!
    do {
    let html = try String(contentsOf: url, encoding: .utf8)
    let range = html.range(of: patternTitle, options: String.CompareOptions.regularExpression, range: html.startIndex..<html.endIndex, locale: nil)
    guard let tempRange = range else { return "" }
    let titleStr = html[tempRange]
    return String(titleStr)
    } catch {
    return ""
    }
    }
    }
    iOS 中简单正则表达式

    iOS 中 正则表达式中的\ 需要使用 \\ 来表示

    // 获取let testStr = "我到底是[iOS]开发还是[产品]经理"// 获取 "[" 和 "]" 之间的所有内容, 左边[ 起一直到 右边 ] 结束的所有内容 let pattern1 = "\\[.*\\]" // [iOS]开发还是[产品]// 获取 "[" 和 "]" 之间的所有内容,包括[]let pattern2 = "\\[.*?\\]"    // [iOS]// 获取第一个 "[" 和 "]" 之间的所有内容 不包括[] let pattern3 = "(?<=\\[).*?(?=\\])" // iOS// 获取html中 title 标签中内容let pattern5 = "(?<=title>).*(?=</title)" 
    获取范围截取子串
    let testRange1 = testStr.range(of: pattern1, options: String.CompareOptions.regularExpression, range: testStr.startIndex..<testStr.endIndex, locale: nil)    let result1 = String(testStr[testRange1!])let testRange2 = testStr.range(of: pattern2, options: String.CompareOptions.regularExpression, range: testStr.startIndex..<testStr.endIndex, locale: nil)let result2 = String(testStr[testRange2!])let testRange3 = testStr.range(of: pattern3, options: String.CompareOptions.regularExpression, range: testStr.startIndex..<testStr.endIndex, locale: nil)let result3 = String(testStr[testRange3!])print(result1) print(result2) print(result3) 
    正则表达式

    通过URL解析html中的标题

    • 获取html字符串
    • 正则匹配
    • 生成字符串Range
    • 截取字符串

    匹配一次直接返回结果

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // 生成表达式
    let regular = try NSRegularExpression(pattern: pattern5, options: .caseInsensitive)


    // 开始匹配
    let matchResult1 = regular.firstMatch(in: html, options: NSRegularExpression.MatchingOptions.reportProgress, range: NSMakeRange(0, html.count))

    // 获取匹配结果范围
    let matchRange = matchResult1?.range
    let range = Range(matchRange!)!

    // 生成String.Index 类型范围
    let startIndex = html.index(html.startIndex, offsetBy: range.lowerBound)
    let endIndex = html.index(html.startIndex, offsetBy: range.upperBound)
    let range3 = Range(uncheckedBounds: (lower: startIndex, upper: endIndex))

    // 截取字符串
    let result1 = String(html[range3])
    print(result1)

    全部匹配后返回结果时一个集合

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    let matchResult2 = regular.matches(in: html, options: .reportProgress, range: NSMakeRange(0, html.count))
    matchResult2.forEach { (result) in
    let range = Range(result.range)!

    let startIndex = html.index(html.startIndex, offsetBy: range.lowerBound)
    let endIndex = html.index(html.startIndex, offsetBy: range.upperBound)

    let subStr2 = html[Range(uncheckedBounds: (startIndex, endIndex))]
    print(String(subStr2))
    }

    将匹配到的内容提还

    1
    let str3 = regular.stringByReplacingMatches(in: html, options: .reportProgress, range: NSMakeRange(0, html.count), withTemplate: "====")
    ]]>
    @@ -289,6 +289,19 @@ + + + + /2019/06/18/Flutter%E5%B8%B8%E7%94%A8%E7%BB%84%E4%BB%B6/ + + flutter 常用组件

    组件

    • StatefulWidget
      • 具有可变状态的窗口部件,使用应用时可随时变化,例如进度条
    • StatelessWidget
      • 不可变窗口部件

    Text 组件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    //  Text 组件
    class MyText extends StatelessWidget{
    @override
    Widget build(BuildContext context){
    return Text('reacctNative、 flutter 学习之路是非常坚信不疑的,作为一个程序员,因该对于技术有着追求,不断的提高自己才是王道,可是道理都懂依然过不好这一生',
    maxLines: 2,
    overflow: TextOverflow.fade,
    textAlign: TextAlign.center,
    style: TextStyle(
    fontSize: 20.0,
    color: Colors.deepOrange,
    decoration: TextDecoration.underline,
    decorationColor: Colors.redAccent,
    decorationStyle: TextDecorationStyle.dotted
    ),
    );
    }
    }

    COntainer 组件

    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

    // Container容器组件
    class MyContainer extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
    return Container(
    child: Text('Container 容器中有文字',
    style: TextStyle(
    backgroundColor: Colors.cyan,
    ),
    ),
    alignment: Alignment.center, // 容器中内容对其方式
    width: 300.0,
    height: 200.0,
    padding: const EdgeInsets.all(10), // 容器内边距
    margin: const EdgeInsets.fromLTRB(10, 5, 10, 5), // container 和外部控件的边距
    decoration: new BoxDecoration(
    gradient: const LinearGradient(
    colors: [Colors.lightBlue, Colors.greenAccent, Colors.yellow]
    ),

    // 设置 border 的宽度
    border: Border.all(width: 5, color: Colors.black)

    ),
    );
    }
    }

    Image 组件

    fit属性

    • BoxFit.fill:全图显示,图片会被拉伸,并充满父容器。
    • BoxFit.contain:全图显示,显示原比例,可能会有空隙。
    • BoxFit.cover:显示可能拉伸,可能裁切,充满(图片要充满整个容器,还不变形)。
    • BoxFit.fitWidth:宽度充满(横向充满),显示可能拉伸,可能裁切。
    • BoxFit.fitHeight :高度充满(竖向充满),显示可能拉伸,可能裁切。
    • BoxFit.scaleDown:效果和contain差不多,但是此属性不允许显示超过源图片大小,可小不可大。

    图片的混合模式

    • color:是要混合的颜色,如果你只设置color是没有意义的。
    • colorBlendMode: 是混合模式。

    repeat 图片填充模式

    • ImageRepeat.repeat : 横向和纵向都进行重复,直到铺满整个画布。
    • ImageRepeat.repeatX: 横向重复,纵向不重复。
    • ImageRepeat.repeatY:纵向重复,横向不重复
    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
    class MyImage extends StatelessWidget {
    @override
    Widget build(BuildContext context){
    return Container(
    child: new Image.network(
    // 'https://images.xiaozhuanlan.com/photo/2019/28ed76123023e46483fcf0ae5c2ba11b.png',
    'https://p.ampmake.com/mall/product/9666f00e-f02a-46ce-9b7c-090bf6aba9aa.png',

    // 1. 设置填充模式
    // fit: BoxFit.scaleDown,
    // fit: BoxFit.none,
    // fit: BoxFit.fill,
    // fit: BoxFit.fitHeight,
    // fit: BoxFit.fitWidth,

    // 保持原图比例
    // fit: BoxFit.contain,

    // 充满整个容器,图片被裁切
    // fit: BoxFit.cover,
    // scale: 1.0,

    // 2. 图片混合
    color: Colors.red,
    colorBlendMode: BlendMode.overlay,

    // 3. reap
    repeat: ImageRepeat.repeat,
    ),
    width: 300.0,
    height: 200.0,
    color: Colors.lightBlue,
    );
    }
    }

    ListView 列表组件

    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
    class MyListView extends StatelessWidget{
    @override
    Widget build(BuildContext contex){

    return ListView(
    children: <Widget>[
    new ListTile(
    leading: new Icon(Icons.camera),
    title: Text('相册'),
    ),

    new ListTile(
    leading: new Icon(Icons.camera_alt),
    title: Text('相机'),
    ),

    new ListTile(
    leading: new Icon(Icons.camera_enhance),
    title: Text('相机1'),
    ),

    new ListTile(
    leading: new Icon(Icons.camera_front),
    title: Text('相机2'),
    ),

    new ListTile(
    leading: new Icon(Icons.camera_rear),
    title: Text('相机3'),
    ),
    ],
    );
    }
    }

    ListView.builder 适合列表项比较多(或者无限)的情况

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class MyListViewBuilder extends StatelessWidget {

    @override
    Widget build(BuildContext context){
    return ListView.builder(
    itemCount: 100, // 数量
    itemExtent: 40, // Tile 高度
    itemBuilder: (BuildContext context, int index){
    return ListTile(title: Text("$index"));
    }
    );
    }
    }

    横向列表

    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
    class MyHorizontalListView extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
    return Container(
    height: 100,
    child: ListView(
    // 设置滚动方式
    scrollDirection: Axis.horizontal,
    children: <Widget>[
    new Container(
    width: 180.0,
    color: Colors.green,
    ),
    new Container(
    width: 180.0,
    color: Colors.yellow,
    ),
    new Container(
    width: 180.0,
    color: Colors.blue,
    ),
    new Container(
    width: 180.0,
    color: Colors.cyan,
    ),
    new Container(
    width: 180.0,
    color: Colors.purple,
    ),
    ],
    ),
    );
    }
    }

    动态列表和构造方法的创建

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    class MyDynamicListView extends StatelessWidget {
    // 声明参数
    final List<String> items;
    // 构造方法
    MyDynamicListView({Key key, @required this.items}):super(key:key);
    @override
    Widget build(BuildContext context) {
    return Container(
    child: new ListView.builder(
    itemCount: items.length,
    itemBuilder: (context, index){
    return ListTile(
    title: new Text('通过构造方法创建的动态列表${items[index]}'),
    );
    },
    ),
    );
    }
    }

    GridView 网格列表的使用

    ]]>
    + + + +
    + + + @@ -304,8 +317,8 @@ - - /2019/06/18/docs/Flutter%E5%85%A5%E9%97%A8/ + + /2019/06/18/Flutter%E5%85%A5%E9%97%A8/ flutter 安装

    flutter 项目代码

    fluter 中一切皆是组件

    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
    // 导入头文件
    import 'package:flutter/material.dart';

    // 程序的入口,main 函数
    void main() => (MyApp());

    // 定义类, 继承

    class MyApp extends StatelessWidget {
    // 重写
    @override

    Widget build(BuildContext context) {

    // 返回一窗口
    return MaterialApp(
    title:'Flutter',
    home: Scaffold(
    appBar: AppBar(
    title: Text('flutter 初体验')
    ),

    body: Center(
    child: Text('flutter 学习之路'),
    ),
    ),
    );
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112

    import 'package:flutter/material.dart';

    void main() => runApp(MyApp());

    class MyApp extends StatelessWidget {
    // This widget is the root of your application.
    @override
    Widget build(BuildContext context) {
    return MaterialApp(
    title: 'Flutter Demo',
    theme: ThemeData(
    // This is the theme of your application.
    //
    // Try running your application with "flutter run". You'll see the
    // application has a blue toolbar. Then, without quitting the app, try
    // changing the primarySwatch below to Colors.green and then invoke
    // "hot reload" (press "r" in the console where you ran "flutter run",
    // or simply save your changes to "hot reload" in a Flutter IDE).
    // Notice that the counter didn't reset back to zero; the application
    // is not restarted.
    primarySwatch: Colors.blue,
    ),
    home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
    }
    }

    class MyHomePage extends StatefulWidget {
    MyHomePage({Key key, this.title}) : super(key: key);

    // This widget is the home page of your application. It is stateful, meaning
    // that it has a State object (defined below) that contains fields that affect
    // how it looks.

    // This class is the configuration for the state. It holds the values (in this
    // case the title) provided by the parent (in this case the App widget) and
    // used by the build method of the State. Fields in a Widget subclass are
    // always marked "final".

    final String title;

    @override
    _MyHomePageState createState() => _MyHomePageState();
    }

    class _MyHomePageState extends State<MyHomePage> {
    int _counter = 0;

    void _incrementCounter() {
    setState(() {
    // This call to setState tells the Flutter framework that something has
    // changed in this State, which causes it to rerun the build method below
    // so that the display can reflect the updated values. If we changed
    // _counter without calling setState(), then the build method would not be
    // called again, and so nothing would appear to happen.
    _counter++;
    });
    }

    @override
    Widget build(BuildContext context) {
    // This method is rerun every time setState is called, for instance as done
    // by the _incrementCounter method above.
    //
    // The Flutter framework has been optimized to make rerunning build methods
    // fast, so that you can just rebuild anything that needs updating rather
    // than having to individually change instances of widgets.
    return Scaffold(
    appBar: AppBar(
    // Here we take the value from the MyHomePage object that was created by
    // the App.build method, and use it to set our appbar title.
    title: Text(widget.title),
    ),
    body: Center(
    // Center is a layout widget. It takes a single child and positions it
    // in the middle of the parent.
    child: Column(
    // Column is also layout widget. It takes a list of children and
    // arranges them vertically. By default, it sizes itself to fit its
    // children horizontally, and tries to be as tall as its parent.
    //
    // Invoke "debug painting" (press "p" in the console, choose the
    // "Toggle Debug Paint" action from the Flutter Inspector in Android
    // Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
    // to see the wireframe for each widget.
    //
    // Column has various properties to control how it sizes itself and
    // how it positions its children. Here we use mainAxisAlignment to
    // center the children vertically; the main axis here is the vertical
    // axis because Columns are vertical (the cross axis would be
    // horizontal).
    mainAxisAlignment: MainAxisAlignment.center,
    children: <Widget>[
    Text(
    '你可以点击按钮来修改文字,我是热重载的结果',
    ),
    Text(
    '$_counter',
    style: Theme.of(context).textTheme.display1,
    ),
    ],
    ),
    ),
    floatingActionButton: FloatingActionButton(
    onPressed: _incrementCounter,
    tooltip: 'Increment',
    child: Icon(Icons.add),
    ), // This trailing comma makes auto-formatting nicer for build methods.
    );
    }
    }
    ]]>
    @@ -317,10 +330,10 @@ - - /2019/06/18/docs/Flutter%E5%B8%B8%E7%94%A8%E7%BB%84%E4%BB%B6/ + + /2019/06/18/Xcode%E4%B8%AD%E4%BD%BF%E7%94%A8Debug%20Memory%20Graph%E6%A3%80%E6%9F%A5%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F/ - flutter 常用组件

    组件

    • StatefulWidget
      • 具有可变状态的窗口部件,使用应用时可随时变化,例如进度条
    • StatelessWidget
      • 不可变窗口部件

    Text 组件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    //  Text 组件
    class MyText extends StatelessWidget{
    @override
    Widget build(BuildContext context){
    return Text('reacctNative、 flutter 学习之路是非常坚信不疑的,作为一个程序员,因该对于技术有着追求,不断的提高自己才是王道,可是道理都懂依然过不好这一生',
    maxLines: 2,
    overflow: TextOverflow.fade,
    textAlign: TextAlign.center,
    style: TextStyle(
    fontSize: 20.0,
    color: Colors.deepOrange,
    decoration: TextDecoration.underline,
    decorationColor: Colors.redAccent,
    decorationStyle: TextDecorationStyle.dotted
    ),
    );
    }
    }

    COntainer 组件

    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

    // Container容器组件
    class MyContainer extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
    return Container(
    child: Text('Container 容器中有文字',
    style: TextStyle(
    backgroundColor: Colors.cyan,
    ),
    ),
    alignment: Alignment.center, // 容器中内容对其方式
    width: 300.0,
    height: 200.0,
    padding: const EdgeInsets.all(10), // 容器内边距
    margin: const EdgeInsets.fromLTRB(10, 5, 10, 5), // container 和外部控件的边距
    decoration: new BoxDecoration(
    gradient: const LinearGradient(
    colors: [Colors.lightBlue, Colors.greenAccent, Colors.yellow]
    ),

    // 设置 border 的宽度
    border: Border.all(width: 5, color: Colors.black)

    ),
    );
    }
    }

    Image 组件

    fit属性

    • BoxFit.fill:全图显示,图片会被拉伸,并充满父容器。
    • BoxFit.contain:全图显示,显示原比例,可能会有空隙。
    • BoxFit.cover:显示可能拉伸,可能裁切,充满(图片要充满整个容器,还不变形)。
    • BoxFit.fitWidth:宽度充满(横向充满),显示可能拉伸,可能裁切。
    • BoxFit.fitHeight :高度充满(竖向充满),显示可能拉伸,可能裁切。
    • BoxFit.scaleDown:效果和contain差不多,但是此属性不允许显示超过源图片大小,可小不可大。

    图片的混合模式

    • color:是要混合的颜色,如果你只设置color是没有意义的。
    • colorBlendMode: 是混合模式。

    repeat 图片填充模式

    • ImageRepeat.repeat : 横向和纵向都进行重复,直到铺满整个画布。
    • ImageRepeat.repeatX: 横向重复,纵向不重复。
    • ImageRepeat.repeatY:纵向重复,横向不重复
    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
    class MyImage extends StatelessWidget {
    @override
    Widget build(BuildContext context){
    return Container(
    child: new Image.network(
    // 'https://images.xiaozhuanlan.com/photo/2019/28ed76123023e46483fcf0ae5c2ba11b.png',
    'https://p.ampmake.com/mall/product/9666f00e-f02a-46ce-9b7c-090bf6aba9aa.png',

    // 1. 设置填充模式
    // fit: BoxFit.scaleDown,
    // fit: BoxFit.none,
    // fit: BoxFit.fill,
    // fit: BoxFit.fitHeight,
    // fit: BoxFit.fitWidth,

    // 保持原图比例
    // fit: BoxFit.contain,

    // 充满整个容器,图片被裁切
    // fit: BoxFit.cover,
    // scale: 1.0,

    // 2. 图片混合
    color: Colors.red,
    colorBlendMode: BlendMode.overlay,

    // 3. reap
    repeat: ImageRepeat.repeat,
    ),
    width: 300.0,
    height: 200.0,
    color: Colors.lightBlue,
    );
    }
    }

    ListView 列表组件

    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
    class MyListView extends StatelessWidget{
    @override
    Widget build(BuildContext contex){

    return ListView(
    children: <Widget>[
    new ListTile(
    leading: new Icon(Icons.camera),
    title: Text('相册'),
    ),

    new ListTile(
    leading: new Icon(Icons.camera_alt),
    title: Text('相机'),
    ),

    new ListTile(
    leading: new Icon(Icons.camera_enhance),
    title: Text('相机1'),
    ),

    new ListTile(
    leading: new Icon(Icons.camera_front),
    title: Text('相机2'),
    ),

    new ListTile(
    leading: new Icon(Icons.camera_rear),
    title: Text('相机3'),
    ),
    ],
    );
    }
    }

    ListView.builder 适合列表项比较多(或者无限)的情况

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class MyListViewBuilder extends StatelessWidget {

    @override
    Widget build(BuildContext context){
    return ListView.builder(
    itemCount: 100, // 数量
    itemExtent: 40, // Tile 高度
    itemBuilder: (BuildContext context, int index){
    return ListTile(title: Text("$index"));
    }
    );
    }
    }

    横向列表

    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
    class MyHorizontalListView extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
    return Container(
    height: 100,
    child: ListView(
    // 设置滚动方式
    scrollDirection: Axis.horizontal,
    children: <Widget>[
    new Container(
    width: 180.0,
    color: Colors.green,
    ),
    new Container(
    width: 180.0,
    color: Colors.yellow,
    ),
    new Container(
    width: 180.0,
    color: Colors.blue,
    ),
    new Container(
    width: 180.0,
    color: Colors.cyan,
    ),
    new Container(
    width: 180.0,
    color: Colors.purple,
    ),
    ],
    ),
    );
    }
    }

    动态列表和构造方法的创建

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    class MyDynamicListView extends StatelessWidget {
    // 声明参数
    final List<String> items;
    // 构造方法
    MyDynamicListView({Key key, @required this.items}):super(key:key);
    @override
    Widget build(BuildContext context) {
    return Container(
    child: new ListView.builder(
    itemCount: items.length,
    itemBuilder: (context, index){
    return ListTile(
    title: new Text('通过构造方法创建的动态列表${items[index]}'),
    );
    },
    ),
    );
    }
    }

    GridView 网格列表的使用

    ]]>
    + Xcode中使用Debug Memory Graph检查内存泄漏

    勾选如下图所示选项

    运行程序后点击

    内存泄露定位

    ]]>
    @@ -330,10 +343,10 @@ - - /2019/06/18/Swift%E9%A1%B9%E7%9B%AE%E4%B8%AD%E9%9B%86%E6%88%90RN/ + + /2019/06/18/Swift%E9%94%99%E8%AF%AF%E5%A4%84%E7%90%86/ - Swift已有项目中集成ReactNative

    本教程针对于已经有的Swift项目集成ReactNative

    创建新的空目录

    为了不影响原有的swift项目目录结构,建议先创建一个空的文件夹,例如SwiftRN

    创建package.json文件

    终端进入创建的SwiftRN文件目录下,创建package.json文件

    1
    touch package.json
    修改package.json文件,指定各个版本
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    {
    "name": "SwiftRN",
    "version": "0.0.1",
    "private": true,
    "scripts": {
    "start": "node node_modules/react-native/local-cli/cli.js start",
    "test": "jest"
    },
    "dependencies": {
    "react": "16.3.1",
    "react-native": "^0.56.0"
    }
    }
    执行npm install

    在创建package.json所在的文件目录下执行,在此过程中可以遇到的错误可能较多

    1
    npm install
    添加index.js文件
    1
    touch index.js
    修改文件内容
    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
    import {
    AppRegistry,
    Text,
    View,
    } from 'react-native';

    import React, { Component } from 'react';

    // 用于最后在项目中测试
    class SwiftRNHomeView extends Component {
    render() {
    return(
    <View style={{backgroundColor:'white'}}>
    <Text style={{fontSize: 20, marginTop: 200}}>Hellow world</Text>
    </View>
    );
    }
    }


    class MyApp extends Component {
    render() {
    return <SwiftRNHomeView></SwiftRNHomeView>;
    }
    }

    AppRegistry.registerComponent('SwiftRN', () => MyApp);

    配置swift项目

    将新创建的Swift文件目录下的所有文件拷贝到Swift项目所在的根目录下

    使用Pod集成RN

    swift项目采用了pod管理第三方库,在Podfile文件中加入下面代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    pod 'React', :path => './node_modules/react-native', :subspecs => [
    'Core',
    'RCTText',
    'RCTNetwork',
    'RCTWebSocket', # 这个模块是用于调试功能的
    'CxxBridge', # Include this for RN >= 0.47,不引入的话会报错误或者警告
    'DevSupport', # Include this to enable In-App Devmenu if RN >= 0.43,不引入的话会报错误或者警告
    'RCTAnimation', 不引入的话会报错误或者警告
    ]


    # 如果你的RN版本 >= 0.42.0,请加入下面这行,不引入的话会报错误或者警告
    pod "yoga", :path => "./node_modules/react-native/ReactCommon/yoga"
    pod 'DoubleConversion', :podspec => './node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
    pod 'glog', :podspec => './node_modules/react-native/third-party-podspecs/glog.podspec'
    pod 'Folly', :podspec => './node_modules/react-native/third-party-podspecs/Folly.podspec'

    创建桥接文件

    • 如果原项目中有SwiftDemo-Bridging-Header.h类似这个文件可以跳过。
    • 如果从来没有创建过桥接文件,创建一个OC的类会自动弹出是否需要创建桥接文件,直接创建即可,最后把无用的OC文件删掉

    引用RN头文件

    在桥接文件中导入头文件

    1
    2
    #import <React/RCTRootView.h>
    #import <React/RCTBundleURLProvider.h>

    使用RN页面

    在模拟器中使用RN的页面, 确保在 info.plist 文件中添加了Allow Arbitrary Loads=YES

    • moduleName 对应的名字和index.js中 AppRegistry.registerComponent(‘SwiftRN’, () => MyApp)注册的名字必须相同
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    import UIKit

    class RNTestController: UIViewController {

    override func viewDidLoad() {
    super.viewDidLoad()
    let url = URL(string: "http://localhost:8081/index.bundle?platform=ios")!
    let rootView = RCTRootView(
    bundleURL: url,
    moduleName: "SwiftRN", //这里的名字必须和AppRegistry.registerComponent('MyApp', () => MyApp)中注册的名字一样
    initialProperties: nil,
    launchOptions: nil
    )
    view = rootView
    }
    }
    ]]>
    + Swift 中错误处理

    assert 断言

    只在debug 模式下程序会自动退出

    1
    public func assert(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)

    assertionFailure 断言错误

    只在debug 模式下程序会自动退出, 没有条件

    1
    public func assertionFailure(_ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)

    precondition

    releas 模式程序也会退出

    1
    public func precondition(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)

    throw

    ]]>
    @@ -343,10 +356,10 @@ - - /2019/06/18/Xcode%E4%B8%AD%E4%BD%BF%E7%94%A8Debug%20Memory%20Graph%E6%A3%80%E6%9F%A5%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F/ + + /2019/06/18/Swift%E9%A1%B9%E7%9B%AE%E4%B8%AD%E9%9B%86%E6%88%90RN/ - Xcode中使用Debug Memory Graph检查内存泄漏

    勾选如下图所示选项

    运行程序后点击

    内存泄露定位

    ]]>
    + Swift已有项目中集成ReactNative

    本教程针对于已经有的Swift项目集成ReactNative

    创建新的空目录

    为了不影响原有的swift项目目录结构,建议先创建一个空的文件夹,例如SwiftRN

    创建package.json文件

    终端进入创建的SwiftRN文件目录下,创建package.json文件

    1
    touch package.json
    修改package.json文件,指定各个版本
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    {
    "name": "SwiftRN",
    "version": "0.0.1",
    "private": true,
    "scripts": {
    "start": "node node_modules/react-native/local-cli/cli.js start",
    "test": "jest"
    },
    "dependencies": {
    "react": "16.3.1",
    "react-native": "^0.56.0"
    }
    }
    执行npm install

    在创建package.json所在的文件目录下执行,在此过程中可以遇到的错误可能较多

    1
    npm install
    添加index.js文件
    1
    touch index.js
    修改文件内容
    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
    import {
    AppRegistry,
    Text,
    View,
    } from 'react-native';

    import React, { Component } from 'react';

    // 用于最后在项目中测试
    class SwiftRNHomeView extends Component {
    render() {
    return(
    <View style={{backgroundColor:'white'}}>
    <Text style={{fontSize: 20, marginTop: 200}}>Hellow world</Text>
    </View>
    );
    }
    }


    class MyApp extends Component {
    render() {
    return <SwiftRNHomeView></SwiftRNHomeView>;
    }
    }

    AppRegistry.registerComponent('SwiftRN', () => MyApp);

    配置swift项目

    将新创建的Swift文件目录下的所有文件拷贝到Swift项目所在的根目录下

    使用Pod集成RN

    swift项目采用了pod管理第三方库,在Podfile文件中加入下面代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    pod 'React', :path => './node_modules/react-native', :subspecs => [
    'Core',
    'RCTText',
    'RCTNetwork',
    'RCTWebSocket', # 这个模块是用于调试功能的
    'CxxBridge', # Include this for RN >= 0.47,不引入的话会报错误或者警告
    'DevSupport', # Include this to enable In-App Devmenu if RN >= 0.43,不引入的话会报错误或者警告
    'RCTAnimation', 不引入的话会报错误或者警告
    ]


    # 如果你的RN版本 >= 0.42.0,请加入下面这行,不引入的话会报错误或者警告
    pod "yoga", :path => "./node_modules/react-native/ReactCommon/yoga"
    pod 'DoubleConversion', :podspec => './node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
    pod 'glog', :podspec => './node_modules/react-native/third-party-podspecs/glog.podspec'
    pod 'Folly', :podspec => './node_modules/react-native/third-party-podspecs/Folly.podspec'

    创建桥接文件

    • 如果原项目中有SwiftDemo-Bridging-Header.h类似这个文件可以跳过。
    • 如果从来没有创建过桥接文件,创建一个OC的类会自动弹出是否需要创建桥接文件,直接创建即可,最后把无用的OC文件删掉

    引用RN头文件

    在桥接文件中导入头文件

    1
    2
    #import <React/RCTRootView.h>
    #import <React/RCTBundleURLProvider.h>

    使用RN页面

    在模拟器中使用RN的页面, 确保在 info.plist 文件中添加了Allow Arbitrary Loads=YES

    • moduleName 对应的名字和index.js中 AppRegistry.registerComponent(‘SwiftRN’, () => MyApp)注册的名字必须相同
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    import UIKit

    class RNTestController: UIViewController {

    override func viewDidLoad() {
    super.viewDidLoad()
    let url = URL(string: "http://localhost:8081/index.bundle?platform=ios")!
    let rootView = RCTRootView(
    bundleURL: url,
    moduleName: "SwiftRN", //这里的名字必须和AppRegistry.registerComponent('MyApp', () => MyApp)中注册的名字一样
    initialProperties: nil,
    launchOptions: nil
    )
    view = rootView
    }
    }
    ]]>
    @@ -356,10 +369,10 @@ - - /2019/06/18/Swift%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95/ + + /2019/06/18/Swift%E5%8D%8F%E8%AE%AE%E5%88%AB%E5%90%8D/ - Swift协议扩展

    定义结构体

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    struct Point {
    var x = 0.0
    var y = 0.0

    }

    struct Size {
    var height = 0.0
    var width = 0.0
    }

    struct Rectangle {
    var origin = Point()
    var size = Size()

    init(origin: Point, size: Size) {
    self.origin = origin
    self.size = size
    }
    }

    扩展

    • 扩展方法
    1
    2
    3
    4
    5
    6
    extension Rectangle {
    mutating func translate(x: Double, y: Double) {
    self.origin.x += x
    self.origin.y += y
    }
    }
    • 只能扩展计算型的属性,不能扩展存储型属性, 存储型属性需要在定义类或者结构体时声明
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    extension Rectangle {
    var center: Point {
    get {
    let center_x = origin.x + size.width / 2.0
    let center_y = origin.y + size.height / 2.0
    return Point(x: center_x, y: center_y)
    }

    set {
    origin.x = newValue.x - size.width / 2.0
    origin.y = newValue.y - size.height / 2.0
    }
    }
    }
    • 扩展构造方法

      • 类中不能扩展指定构造方法,只能在结构体中扩展
      • 结构体中不能扩展便利构造方法m,只能在类中扩展
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17

    extension Rectangle {
    init(center: Point, size: Size) {
    let origin_x = center.x - size.width / 2.0
    let origin_y = center.y - size.height / 2.0
    self.origin = Point(x: origin_x, y: origin_y)
    self.size = size
    }

    // 结构体中不能扩展便利构造函数 Delegating initializers in structs are not marked with 'convenience'
    // convenience init(center: Point, size: Size) {
    // let origin_x = center.x - size.width / 2.0
    // let origin_y = center.y - size.height / 2.0
    // self.origin = Point(x: origin_x, y: origin_y)
    // self.size = size
    // }
    }
    • 扩展嵌套类型
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    extension Rectangle {
    enum Vertex: Int {
    case left_top
    case left_bottom
    case right_bottom
    case right_top
    }

    // 获取某一个顶点坐标
    func point(of vertex: Vertex) -> Point {
    switch vertex {
    case .left_top:
    return origin
    case .left_bottom:
    return Point(x: origin.x, y: origin.y + size.height)
    case .right_bottom:
    return Point(x: origin.x + size.width, y: origin.y + size.height)
    case .right_top:
    return Point(x: origin.x + size.width, y: origin.y)
    }
    }
    }
    • 扩展下标,根据传入的索引获取对应顶点坐标
    1
    2
    3
    4
    5
    6
    extension Rectangle {
    subscript(index: Int) -> Point? {
    assert(0 <= index && index < 4, "传入值非法")
    return point(of: Vertex(rawValue: index)!)
    }
    }

    扩展系统方法

    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
    extension Int {
    /// 平方
    var square: Int {
    return self * self
    }

    /// 立方
    var cube: Int {
    return self * self * self
    }

    /// 判断数组是否在某一个范围内
    func inRange(clousedLeft left: Int, openRight right: Int) -> Bool {
    return self >= left && self > right
    }

    /// 重复执行操作
    func repeatitions(task: () -> ()) {
    for _ in 0..<self {
    task()
    }
    }

    /// 变长函数
    func stride(from: Int, to: Int, by: Int, task: () -> ()) {
    // 系统变长函数
    for i in Swift.stride(from: 0, to: 21, by: 3) {
    print(i)
    }

    for _ in Swift.stride(from: from, to: to, by: by) {
    task()
    }
    }
    }

    扩展后使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    print(2.square)
    print(3.cube)
    print(4.inRange(clousedLeft: 0, openRight: 4))
    4.repeatitions {
    print("extension")
    }


    // 使用变长函数
    4.stride(from: 0, to: 8, by: 4) {
    print("stride")
    }
    ]]>
    + Swift协议和别名

    协议

    某些属性的集合,可以有多个协议组成,具有的特点

    1. 可以声明函数,但是不能实现,由实现这协议的具体结构或者类来实现
    2. 声明的函数如果有参数也不能设置参数的默认值, 默认参数被认为也是函的一种实现
    3. 可以声明属性,必须是计算型属性 只能使用var 修饰,不能是存储型属,必须在 {} 中明确指出属性的读取类型 例如 {get set}
    4. 遵守协议的类可选择存储型属性或者计算型属性来实现协议中定义的属性
    5. protocol Person: class { } 表示此协议只能被类来遵守,结构体能遵守
    6. 协议可以看着是一种类型,如果协议中的属性是只读的,如果是协议类型只能访问get 方法, 但是遵守协议的具体类或者结构可以设置属性的读和写

    7. 协议中的构造函数

      - 如果父类中遵守了协议中的构造函数,子类中必须重写父类中的构造函数并且使用require修饰构造函数,表示继承此类的子类也必须实现该构造函数- 如果class 使用了final 修饰,表示不能被继承,此时不需要 require 关键词修饰

    类型别名

    • 定义别名 typealias, 供扩展使用

      1
      2
      3
      4
      5
      6
      7
      8
      typealias Length = Double
      extension Double {
      var km: Length {return self * 1_000.0}
      var m: Length {return self * 100.0}
      var cm: Length {return self / 10.0}
      /// 英尺
      var ft: Length { return self / 3.28084}
      }
    使用
    1
    let distance: Length = 10.5.km
    • 定义别名防止硬编码

      1
      2
      // 音频采样率
      typealias AudioSample = UInt64

    协议中 typealias

    协议中定义一个属性,该属性的类型根据不同的情况返回不同的类型(我理解为类似泛型),具体的类型由遵守此协议的类或者结构指定

    • 定义协议中别名
    1
    2
    3
    4
    5
    protocol WeightCalculable {
    // 属性重量根据不同体积返回的可能是Int,可以可能是double类型,所以使用别名, 协议中使用associatedtype关键词,
    associatedtype WeightType
    var weight: WeightType { get }
    }

    扩展 Int 中的吨单位

    1
    2
    3
    4
    extension Int {
    typealias Weight = Int
    var t: Weight {return self * 1_000}
    }
    • 使用协议中别名
    • 一部手机的时使用该协议WeightCalculable
    1
    2
    3
    4
    5
    6
    class iPhone: WeightCalculable {
    typealias WeightType = Double
    var weight: WeightType {
    return 0.5
    }
    }
    • 一辆大卡时使用该协议WeightCalculable
    1
    2
    3
    4
    5
    6
    7
    8
    class Car: WeightCalculable {
    typealias WeightType = Int
    let weight: WeightType

    init(weight: Int) {
    self.weight = weight
    }
    }
    1
    let bigCar = Car(weight: 8_000.t) // 8 吨

    面向协议编程

    • 定义比赛协议,具有 赢/输/总场次/胜率 等特性
    1
    2
    3
    4
    5
    6
    protocol Recordable: CustomStringConvertible {
    var wins: Int { get }
    var losses: Int { get }

    func winningPerent() -> Double
    }
    • 协议中方法或者属性的默认实现
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    extension Recordable {
    // CustomStringConvertible 协议默认实现
    var description: String {
    return String(format: "WINS: %d---LOSSES: %d", [wins, losses])
    }

    // 默认实现总场数计算型属性
    var totalGames: Int {
    return wins + losses
    }

    // 默认实现的方法
    func shoutWins() {
    print("come on", wins, "times!")
    }
    }

    篮球比赛:

    • Equatable 协议 - 相等比较 需要重载 ==
    • Comparable 协议 - 是否可比较 ,重载 < 可直接调用sort()函数
    • CustomStringConvertible 协议 - 打印信息,需重写 description
    • Recordable协议 - 比赛协议,没有平局
    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
    struct BasketballRecord:Equatable, Comparable, CustomStringConvertible, Recordable {
    var wins: Int
    var losses: Int


    /// 在具体定义的类型中实现 协议中的属性或者方法就会覆盖协议中的
    var totalGames: Int = 200

    var description: String {
    return "本次比赛胜: " + String(wins) + "负" + "\(losses)"
    }

    // 胜率
    func winningPerent() -> Double {
    return (Double(wins) / Double(totalGames))
    }

    // swift4.1 后,不需要再重写此方法,swift 会自动合成,如果不想让某一个属性参与比较,就需要重写该方法
    static func == (lhs: BasketballRecord, rhs: BasketballRecord) -> Bool {
    return lhs.wins == rhs.wins && lhs.losses == rhs.losses
    }

    // Comparable
    static func < (lhs: BasketballRecord, rhs: BasketballRecord) -> Bool {
    if lhs.wins != rhs.wins {
    return lhs.wins < rhs.wins
    }
    return lhs.losses > rhs.losses
    }
    }
    ```

    #### 足球比赛:

    有平局的情况,原来的协议不能满足,需要增加一个平局的协议

    - 定义平局协议

    protocol Tieable {
    /// 平局,可读写
    var ties: Int { get set }

    }

    1
    2
    3
    4

    - 足球比赛需要遵守的协议 `Recordable` 和 `Tieable`

    - 总场数需要加上平局的场数,需要实现totalGames的get方法

    struct FootableRecord: Recordable, Tieable {
    var wins: Int
    var losses: Int
    // 平局数
    var ties: Int

    // 总场数var totalGames: Int {    return wins + losses + ties}var description: String {    return "本次比赛胜: " + String(wins) + "负" + "\(losses)"}func winningPerent() -> Double {    return (Double(wins) / Double(totalGames))}

    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    - 足球比赛中以上写法存在的问题

    - 所有有平局的比赛计算总场次的逻辑都是一样,每次都要写一次实现
    - 单一修改 Recordable 协议不能解决问题,
    - 存在平局和没有平局两种情况 totalGames 的计算逻辑不相同

    - 解决办法:
    - 扩展遵守了`Recordable`协议的类型,前提条件是:这个类型遵守了 Tieable 同时也遵守了 `Recordable ` 协议,可以理解为如果该类型遵守了 `Tieable `协议之后,`Recordable` 协议中的 totalGames 属性实现是另一种方式了,不在是以前的 `totalGames` 中直接返回 `wins + losses` 的值


    定义协议

    protocol Recordable: CustomStringConvertible {
    var wins: Int { get }
    var losses: Int { get }

    func winningPerent() -> Double

    }

    1
    2

    协议默认实现

    extension Recordable {
    var description: String {
    return String(format: “WINS: %d—LOSSES: %d”, [wins, losses])
    }

    // 默认实现总场数计算型属性var totalGames: Int {    return wins + losses}// 默认实现胜率func winningPerent() -> Double {    return (Double(wins) / Double(totalGames))}// 默认实现的方法func shoutWins() {    print("come on", wins, "times!")}

    }

    1
    2

    扩展遵守了Tieable 的 类型的 Recordable 协议

    extension Recordable where Self: Tieable {
    var totalGames: Int {
    return wins + losses + ties
    }
    }

    1
    2
    3
    4
    5
    6

    ### 协议聚合

    如果对某一类型需要限制他必须遵守某些协议,可以使用协议聚合来定义

    比如: 有个奖赏协议

    protocol Prizable {
    func isPrizable() -> Bool
    }

    1
    2

    篮球比赛遵守此协议

    // 篮球比赛奖赏
    extension BasketballRecord: Prizable {
    func isPrizable() -> Bool {
    return totalGames > 10 && winningPerent() > 0.5
    }
    }

    1
    2

    足球比赛奖赏遵守此协议

    extension FootableRecord: Prizable {
    func isPrizable() -> Bool {
    return wins > 1
    }
    }

    1
    2

    现在有某一个学生也遵守了此协议

    struct Student: Prizable {
    var name: String
    var score: Int

    func isPrizable() -> Bool {    return score >= 60}

    }

    1
    2

    定义奖赏的方法,参数类型必须是遵守了此协议的结构或者类型

    private func award(_ one: Prizable) {
    if one.isPrizable() {
    print(one)
    print(“恭喜获得奖励”)
    } else {
    print(one)
    print(“很遗憾”)
    }
    }

    1
    2

    如果说 BasketballRecord 这个类还遵守了其他的协议,例如遵守了 `Recordable` 协议, 并且这个协议也遵守了 `CustomStringConvertible` 并且默认实现了`description` 的`get` 方法

    // MARK: - 比赛协议,具有 赢。输。总场次。胜率 特性
    protocol Recordable: CustomStringConvertible {
    var wins: Int { get }
    var losses: Int { get }

    func winningPerent() -> Double

    }

    1
    2

    `Recordable` 默认实现

    extension Recordable {
    var description: String {
    return String(format: “WINS: %d—LOSSES: %d”, [wins, losses])
    }

    // 默认实现总场数计算型属性var totalGames: Int {    return wins + losses}// 默认实现胜率func winningPerent() -> Double {    return (Double(wins) / Double(totalGames))}// 默认实现的方法func shoutWins() {    print("come on", wins, "times!")}

    }

    1
    2
    3
    4
    5

    此时如果 `Student` 类还是调用 `award` 方法的话,print(one) 打印的信息将是`Recordable `中默认实现的内容,因此需要约束`award`函数的参数必须遵守两个协议让`Student` 也重写自己的`description `属性的`get`方法,不能再让 `Prizable` 扩展 默认实现

    - swift 3 写法: protocol<A, B>
    - swift 4 写法: A & B

    private func award2(_ one: Prizable & CustomStringConvertible) {
    if one.isPrizable() {
    print(one)
    print(“恭喜获得奖励”)
    } else {
    print(one)
    print(“很遗憾”)
    }
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    ### 泛型约束


    - 定义一个函数,找出一个学生数组中分数最大的
    - 参数:一个学生数组,都遵守了 Comparable 的类型
    - 返回值:某个遵守了 Comparable 的类型实例
    - 此时函数报错 `Protocol 'Comparable' can only be used as a generic constraint because it has Self or associated type requirements`,

    因为 Comparable 协议中定义的方法 public static func < (lhs: Self, rhs: Self) -> Bool 的参数类型是Self,是具体的某个类型

    func maxScore(seq: [Comparable]) -> Comparable { }

    1
    2
    3
    4
    5


    - 如果需要定义一个函数实现在一个数组中找出需要奖励的人的名字该如何实现呢
    - 参数:遵守两个协议 Comparable 和 Prizable 协议, 并且使用泛型
    - 返回值:返回值是可选值,有可能没有任何奖励的对象

    func topPrizable<T: Comparable & Prizable>(seq: [T]) -> T? {
    return seq.reduce(nil) { (tempTop: T?, condender: T) in
    guard condender.isPrizable() else { return tempTop }
    // 解包 condender 失败, 上一层验证了他必须是奖励的那个
    guard let tempTop = tempTop else { return condender }
    return max(tempTop, condender)
    }
    }

    `

    ]]>
    @@ -369,10 +382,10 @@ - - /2019/06/18/Swift%E6%B3%9B%E5%9E%8B/ + + /2019/06/18/Swift%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95/ - Swift 泛型

    泛型定义–wikipedia

    1. 在程序编码中一些包含类型参数的类型,也就是说泛型的参数只可以代表类,不能代表个别对象。(这是当今较常见的定义)
    2. 在程序编码中一些包含参数的类。其参数可以代表类或对象等等。(现在人们大多把这称作模板)

    泛型的用处

    泛型可以实现传入一个指定类型后做一些操作然后返回后还是原来指定的类型,你可以指定特定的类型(特定是指约束)

    泛型和 Any 的区别

    区别:Any能代表任何类型,但是不能返回某个具体的类型,都需要使用 as 去转化,而泛型正好相反

    • Any 可以代表任何类型,除了class之外还可以代表struct,enum,
    • AnyObject 可以代码任何 class 的类型, swift 中基本类型, ArrayDictionary 都是 struct 类型,都不能使用 AnyObject 来接受

    Swift中Any 和 AnyObject用法

    泛型函数和泛型参数

    swapTwoValues是一个泛型函数,可以接收或者返回任何类型

    1
    2
    3
    4
    5
    func swapTwoValues<T>(inout a: T, inout b: T) {
    let temporaryA = a
    a = b
    b = temporaryA
    }
    • swapTwoValues 函数名后面的 <T> 表示这个 T 类型是函数所定义的一个类型, T 只是一个类型的占位符,不用关心具体类型

    • (inout a: T, inout b: T) 中a和b两个参数类型T是泛型参数

    类型约束

    两个泛型参数的泛型函数

    1
    2
    3
    func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
    // function body goes here
    }
    • 泛型参数T约束条件:必须是SomeClass类的子类
    • 泛型参数U约束条件:必须遵守SomeProtocol协议
    • 使用where指定约束
    ]]>
    + Swift协议扩展

    定义结构体

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    struct Point {
    var x = 0.0
    var y = 0.0

    }

    struct Size {
    var height = 0.0
    var width = 0.0
    }

    struct Rectangle {
    var origin = Point()
    var size = Size()

    init(origin: Point, size: Size) {
    self.origin = origin
    self.size = size
    }
    }

    扩展

    • 扩展方法
    1
    2
    3
    4
    5
    6
    extension Rectangle {
    mutating func translate(x: Double, y: Double) {
    self.origin.x += x
    self.origin.y += y
    }
    }
    • 只能扩展计算型的属性,不能扩展存储型属性, 存储型属性需要在定义类或者结构体时声明
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    extension Rectangle {
    var center: Point {
    get {
    let center_x = origin.x + size.width / 2.0
    let center_y = origin.y + size.height / 2.0
    return Point(x: center_x, y: center_y)
    }

    set {
    origin.x = newValue.x - size.width / 2.0
    origin.y = newValue.y - size.height / 2.0
    }
    }
    }
    • 扩展构造方法

      • 类中不能扩展指定构造方法,只能在结构体中扩展
      • 结构体中不能扩展便利构造方法m,只能在类中扩展
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17

    extension Rectangle {
    init(center: Point, size: Size) {
    let origin_x = center.x - size.width / 2.0
    let origin_y = center.y - size.height / 2.0
    self.origin = Point(x: origin_x, y: origin_y)
    self.size = size
    }

    // 结构体中不能扩展便利构造函数 Delegating initializers in structs are not marked with 'convenience'
    // convenience init(center: Point, size: Size) {
    // let origin_x = center.x - size.width / 2.0
    // let origin_y = center.y - size.height / 2.0
    // self.origin = Point(x: origin_x, y: origin_y)
    // self.size = size
    // }
    }
    • 扩展嵌套类型
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    extension Rectangle {
    enum Vertex: Int {
    case left_top
    case left_bottom
    case right_bottom
    case right_top
    }

    // 获取某一个顶点坐标
    func point(of vertex: Vertex) -> Point {
    switch vertex {
    case .left_top:
    return origin
    case .left_bottom:
    return Point(x: origin.x, y: origin.y + size.height)
    case .right_bottom:
    return Point(x: origin.x + size.width, y: origin.y + size.height)
    case .right_top:
    return Point(x: origin.x + size.width, y: origin.y)
    }
    }
    }
    • 扩展下标,根据传入的索引获取对应顶点坐标
    1
    2
    3
    4
    5
    6
    extension Rectangle {
    subscript(index: Int) -> Point? {
    assert(0 <= index && index < 4, "传入值非法")
    return point(of: Vertex(rawValue: index)!)
    }
    }

    扩展系统方法

    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
    extension Int {
    /// 平方
    var square: Int {
    return self * self
    }

    /// 立方
    var cube: Int {
    return self * self * self
    }

    /// 判断数组是否在某一个范围内
    func inRange(clousedLeft left: Int, openRight right: Int) -> Bool {
    return self >= left && self > right
    }

    /// 重复执行操作
    func repeatitions(task: () -> ()) {
    for _ in 0..<self {
    task()
    }
    }

    /// 变长函数
    func stride(from: Int, to: Int, by: Int, task: () -> ()) {
    // 系统变长函数
    for i in Swift.stride(from: 0, to: 21, by: 3) {
    print(i)
    }

    for _ in Swift.stride(from: from, to: to, by: by) {
    task()
    }
    }
    }

    扩展后使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    print(2.square)
    print(3.cube)
    print(4.inRange(clousedLeft: 0, openRight: 4))
    4.repeatitions {
    print("extension")
    }


    // 使用变长函数
    4.stride(from: 0, to: 8, by: 4) {
    print("stride")
    }
    ]]>
    @@ -382,10 +395,10 @@ - - /2019/06/18/Swift%E9%94%99%E8%AF%AF%E5%A4%84%E7%90%86/ + + /2019/06/18/Swift%E6%B3%9B%E5%9E%8B/ - Swift 中错误处理

    assert 断言

    只在debug 模式下程序会自动退出

    1
    public func assert(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)

    assertionFailure 断言错误

    只在debug 模式下程序会自动退出, 没有条件

    1
    public func assertionFailure(_ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)

    precondition

    releas 模式程序也会退出

    1
    public func precondition(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)

    throw

    ]]>
    + Swift 泛型

    泛型定义–wikipedia

    1. 在程序编码中一些包含类型参数的类型,也就是说泛型的参数只可以代表类,不能代表个别对象。(这是当今较常见的定义)
    2. 在程序编码中一些包含参数的类。其参数可以代表类或对象等等。(现在人们大多把这称作模板)

    泛型的用处

    泛型可以实现传入一个指定类型后做一些操作然后返回后还是原来指定的类型,你可以指定特定的类型(特定是指约束)

    泛型和 Any 的区别

    区别:Any能代表任何类型,但是不能返回某个具体的类型,都需要使用 as 去转化,而泛型正好相反

    • Any 可以代表任何类型,除了class之外还可以代表struct,enum,
    • AnyObject 可以代码任何 class 的类型, swift 中基本类型, ArrayDictionary 都是 struct 类型,都不能使用 AnyObject 来接受

    Swift中Any 和 AnyObject用法

    泛型函数和泛型参数

    swapTwoValues是一个泛型函数,可以接收或者返回任何类型

    1
    2
    3
    4
    5
    func swapTwoValues<T>(inout a: T, inout b: T) {
    let temporaryA = a
    a = b
    b = temporaryA
    }
    • swapTwoValues 函数名后面的 <T> 表示这个 T 类型是函数所定义的一个类型, T 只是一个类型的占位符,不用关心具体类型

    • (inout a: T, inout b: T) 中a和b两个参数类型T是泛型参数

    类型约束

    两个泛型参数的泛型函数

    1
    2
    3
    func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
    // function body goes here
    }
    • 泛型参数T约束条件:必须是SomeClass类的子类
    • 泛型参数U约束条件:必须遵守SomeProtocol协议
    • 使用where指定约束
    ]]>
    @@ -421,10 +434,10 @@ - - /2019/06/18/Swift%E5%8D%8F%E8%AE%AE%E5%88%AB%E5%90%8D/ + + /2019/06/18/Swift%20%E5%8D%8F%E8%AE%AE%E4%BD%BF%E7%94%A8/ - Swift协议和别名

    协议

    某些属性的集合,可以有多个协议组成,具有的特点

    1. 可以声明函数,但是不能实现,由实现这协议的具体结构或者类来实现
    2. 声明的函数如果有参数也不能设置参数的默认值, 默认参数被认为也是函的一种实现
    3. 可以声明属性,必须是计算型属性 只能使用var 修饰,不能是存储型属,必须在 {} 中明确指出属性的读取类型 例如 {get set}
    4. 遵守协议的类可选择存储型属性或者计算型属性来实现协议中定义的属性
    5. protocol Person: class { } 表示此协议只能被类来遵守,结构体能遵守
    6. 协议可以看着是一种类型,如果协议中的属性是只读的,如果是协议类型只能访问get 方法, 但是遵守协议的具体类或者结构可以设置属性的读和写

    7. 协议中的构造函数

      - 如果父类中遵守了协议中的构造函数,子类中必须重写父类中的构造函数并且使用require修饰构造函数,表示继承此类的子类也必须实现该构造函数- 如果class 使用了final 修饰,表示不能被继承,此时不需要 require 关键词修饰

    类型别名

    • 定义别名 typealias, 供扩展使用

      1
      2
      3
      4
      5
      6
      7
      8
      typealias Length = Double
      extension Double {
      var km: Length {return self * 1_000.0}
      var m: Length {return self * 100.0}
      var cm: Length {return self / 10.0}
      /// 英尺
      var ft: Length { return self / 3.28084}
      }
    使用
    1
    let distance: Length = 10.5.km
    • 定义别名防止硬编码

      1
      2
      // 音频采样率
      typealias AudioSample = UInt64

    协议中 typealias

    协议中定义一个属性,该属性的类型根据不同的情况返回不同的类型(我理解为类似泛型),具体的类型由遵守此协议的类或者结构指定

    • 定义协议中别名
    1
    2
    3
    4
    5
    protocol WeightCalculable {
    // 属性重量根据不同体积返回的可能是Int,可以可能是double类型,所以使用别名, 协议中使用associatedtype关键词,
    associatedtype WeightType
    var weight: WeightType { get }
    }

    扩展 Int 中的吨单位

    1
    2
    3
    4
    extension Int {
    typealias Weight = Int
    var t: Weight {return self * 1_000}
    }
    • 使用协议中别名
    • 一部手机的时使用该协议WeightCalculable
    1
    2
    3
    4
    5
    6
    class iPhone: WeightCalculable {
    typealias WeightType = Double
    var weight: WeightType {
    return 0.5
    }
    }
    • 一辆大卡时使用该协议WeightCalculable
    1
    2
    3
    4
    5
    6
    7
    8
    class Car: WeightCalculable {
    typealias WeightType = Int
    let weight: WeightType

    init(weight: Int) {
    self.weight = weight
    }
    }
    1
    let bigCar = Car(weight: 8_000.t) // 8 吨

    面向协议编程

    • 定义比赛协议,具有 赢/输/总场次/胜率 等特性
    1
    2
    3
    4
    5
    6
    protocol Recordable: CustomStringConvertible {
    var wins: Int { get }
    var losses: Int { get }

    func winningPerent() -> Double
    }
    • 协议中方法或者属性的默认实现
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    extension Recordable {
    // CustomStringConvertible 协议默认实现
    var description: String {
    return String(format: "WINS: %d---LOSSES: %d", [wins, losses])
    }

    // 默认实现总场数计算型属性
    var totalGames: Int {
    return wins + losses
    }

    // 默认实现的方法
    func shoutWins() {
    print("come on", wins, "times!")
    }
    }

    篮球比赛:

    • Equatable 协议 - 相等比较 需要重载 ==
    • Comparable 协议 - 是否可比较 ,重载 < 可直接调用sort()函数
    • CustomStringConvertible 协议 - 打印信息,需重写 description
    • Recordable协议 - 比赛协议,没有平局
    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
    struct BasketballRecord:Equatable, Comparable, CustomStringConvertible, Recordable {
    var wins: Int
    var losses: Int


    /// 在具体定义的类型中实现 协议中的属性或者方法就会覆盖协议中的
    var totalGames: Int = 200

    var description: String {
    return "本次比赛胜: " + String(wins) + "负" + "\(losses)"
    }

    // 胜率
    func winningPerent() -> Double {
    return (Double(wins) / Double(totalGames))
    }

    // swift4.1 后,不需要再重写此方法,swift 会自动合成,如果不想让某一个属性参与比较,就需要重写该方法
    static func == (lhs: BasketballRecord, rhs: BasketballRecord) -> Bool {
    return lhs.wins == rhs.wins && lhs.losses == rhs.losses
    }

    // Comparable
    static func < (lhs: BasketballRecord, rhs: BasketballRecord) -> Bool {
    if lhs.wins != rhs.wins {
    return lhs.wins < rhs.wins
    }
    return lhs.losses > rhs.losses
    }
    }
    ```

    #### 足球比赛:

    有平局的情况,原来的协议不能满足,需要增加一个平局的协议

    - 定义平局协议

    protocol Tieable {
    /// 平局,可读写
    var ties: Int { get set }

    }

    1
    2
    3
    4

    - 足球比赛需要遵守的协议 `Recordable` 和 `Tieable`

    - 总场数需要加上平局的场数,需要实现totalGames的get方法

    struct FootableRecord: Recordable, Tieable {
    var wins: Int
    var losses: Int
    // 平局数
    var ties: Int

    // 总场数var totalGames: Int {    return wins + losses + ties}var description: String {    return "本次比赛胜: " + String(wins) + "负" + "\(losses)"}func winningPerent() -> Double {    return (Double(wins) / Double(totalGames))}

    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    - 足球比赛中以上写法存在的问题

    - 所有有平局的比赛计算总场次的逻辑都是一样,每次都要写一次实现
    - 单一修改 Recordable 协议不能解决问题,
    - 存在平局和没有平局两种情况 totalGames 的计算逻辑不相同

    - 解决办法:
    - 扩展遵守了`Recordable`协议的类型,前提条件是:这个类型遵守了 Tieable 同时也遵守了 `Recordable ` 协议,可以理解为如果该类型遵守了 `Tieable `协议之后,`Recordable` 协议中的 totalGames 属性实现是另一种方式了,不在是以前的 `totalGames` 中直接返回 `wins + losses` 的值


    定义协议

    protocol Recordable: CustomStringConvertible {
    var wins: Int { get }
    var losses: Int { get }

    func winningPerent() -> Double

    }

    1
    2

    协议默认实现

    extension Recordable {
    var description: String {
    return String(format: “WINS: %d—LOSSES: %d”, [wins, losses])
    }

    // 默认实现总场数计算型属性var totalGames: Int {    return wins + losses}// 默认实现胜率func winningPerent() -> Double {    return (Double(wins) / Double(totalGames))}// 默认实现的方法func shoutWins() {    print("come on", wins, "times!")}

    }

    1
    2

    扩展遵守了Tieable 的 类型的 Recordable 协议

    extension Recordable where Self: Tieable {
    var totalGames: Int {
    return wins + losses + ties
    }
    }

    1
    2
    3
    4
    5
    6

    ### 协议聚合

    如果对某一类型需要限制他必须遵守某些协议,可以使用协议聚合来定义

    比如: 有个奖赏协议

    protocol Prizable {
    func isPrizable() -> Bool
    }

    1
    2

    篮球比赛遵守此协议

    // 篮球比赛奖赏
    extension BasketballRecord: Prizable {
    func isPrizable() -> Bool {
    return totalGames > 10 && winningPerent() > 0.5
    }
    }

    1
    2

    足球比赛奖赏遵守此协议

    extension FootableRecord: Prizable {
    func isPrizable() -> Bool {
    return wins > 1
    }
    }

    1
    2

    现在有某一个学生也遵守了此协议

    struct Student: Prizable {
    var name: String
    var score: Int

    func isPrizable() -> Bool {    return score >= 60}

    }

    1
    2

    定义奖赏的方法,参数类型必须是遵守了此协议的结构或者类型

    private func award(_ one: Prizable) {
    if one.isPrizable() {
    print(one)
    print(“恭喜获得奖励”)
    } else {
    print(one)
    print(“很遗憾”)
    }
    }

    1
    2

    如果说 BasketballRecord 这个类还遵守了其他的协议,例如遵守了 `Recordable` 协议, 并且这个协议也遵守了 `CustomStringConvertible` 并且默认实现了`description` 的`get` 方法

    // MARK: - 比赛协议,具有 赢。输。总场次。胜率 特性
    protocol Recordable: CustomStringConvertible {
    var wins: Int { get }
    var losses: Int { get }

    func winningPerent() -> Double

    }

    1
    2

    `Recordable` 默认实现

    extension Recordable {
    var description: String {
    return String(format: “WINS: %d—LOSSES: %d”, [wins, losses])
    }

    // 默认实现总场数计算型属性var totalGames: Int {    return wins + losses}// 默认实现胜率func winningPerent() -> Double {    return (Double(wins) / Double(totalGames))}// 默认实现的方法func shoutWins() {    print("come on", wins, "times!")}

    }

    1
    2
    3
    4
    5

    此时如果 `Student` 类还是调用 `award` 方法的话,print(one) 打印的信息将是`Recordable `中默认实现的内容,因此需要约束`award`函数的参数必须遵守两个协议让`Student` 也重写自己的`description `属性的`get`方法,不能再让 `Prizable` 扩展 默认实现

    - swift 3 写法: protocol<A, B>
    - swift 4 写法: A & B

    private func award2(_ one: Prizable & CustomStringConvertible) {
    if one.isPrizable() {
    print(one)
    print(“恭喜获得奖励”)
    } else {
    print(one)
    print(“很遗憾”)
    }
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    ### 泛型约束


    - 定义一个函数,找出一个学生数组中分数最大的
    - 参数:一个学生数组,都遵守了 Comparable 的类型
    - 返回值:某个遵守了 Comparable 的类型实例
    - 此时函数报错 `Protocol 'Comparable' can only be used as a generic constraint because it has Self or associated type requirements`,

    因为 Comparable 协议中定义的方法 public static func < (lhs: Self, rhs: Self) -> Bool 的参数类型是Self,是具体的某个类型

    func maxScore(seq: [Comparable]) -> Comparable { }

    1
    2
    3
    4
    5


    - 如果需要定义一个函数实现在一个数组中找出需要奖励的人的名字该如何实现呢
    - 参数:遵守两个协议 Comparable 和 Prizable 协议, 并且使用泛型
    - 返回值:返回值是可选值,有可能没有任何奖励的对象

    func topPrizable<T: Comparable & Prizable>(seq: [T]) -> T? {
    return seq.reduce(nil) { (tempTop: T?, condender: T) in
    guard condender.isPrizable() else { return tempTop }
    // 解包 condender 失败, 上一层验证了他必须是奖励的那个
    guard let tempTop = tempTop else { return condender }
    return max(tempTop, condender)
    }
    }

    `

    ]]>
    + Swift 协议使用

    对UIView 扩展

    按钮/文本框抖动动画

    • 声明协议

      • 协议中定义属性:遵循该协议的类型都具有此属性

        • 必须明确规定该属性是可读的 {get} 或者可写的 {set},或是可读可写的 {get set}
        • 使用static修饰声明一个类类型属性
      • 协议中定义方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    protocol Shakable {}
    extension Shakable where Self: UIView {
    func shakeAnimation() {
    let animation = CABasicAnimation(keyPath: "position")
    animation.duration = 0.08
    animation.repeatCount = 5
    animation.autoreverses = true
    animation.fromValue = NSValue(cgPoint: CGPoint(x: self.center.x - 4, y: self.center.y))
    animation.toValue = NSValue(cgPoint: CGPoint(x: self.center.x + 4, y: self.center.y))
    layer.add(animation, forKey: "position")
    }
    }
    • 自定义UI控件并遵守协议
    1
    2
    3
    class MyButton: UIButton, Shakable {  }
    class MySwitch: UISwitch, Shakable { }
    class MyTextField: UITextField, Shakable { }
    • 使用
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    let mySwitch = MySwitch()
    mySwitch.isOn = true
    self.mySwitch = mySwitch
    view.addSubview(mySwitch)
    mySwitch.translatesAutoresizingMaskIntoConstraints = false
    mySwitch.topAnchor.constraint(equalTo: view.bottomAnchor, constant: 10).isActive = true
    mySwitch.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10).isActive = true

    // 调用
    mySwitch.shakeAnimation()

    UITableViewCell使用

    定义协议

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    protocol ReusableView: class {}

    extension ReusableView where Self: UIView {
    static var reuseIdentifier: String {
    return String(describing: self)
    }
    }

    ```

    ### 对tableView扩展

    extension UITableView {
    /// 注册cell
    func register<T: UITableViewCell>(T: T.Type) where T: ReusableView {
    print(T.reuseIdentifier)
    self.register(T.self, forCellReuseIdentifier: T.reuseIdentifier)
    }

    /// 调用cellfunc dequeueReusableCell<T: UITableViewCell>(_ indexPath: IndexPath) -> T where T: ReusableView {    print(T.reuseIdentifier)    return self.dequeueReusableCell(withIdentifier: T.reuseIdentifier, for: indexPath) as! T}

    }

    1
    2
    3
    4

    ### 用法

    - 创建自定义的cell,需要遵守协议

    class TableProtocolCell: UITableViewCell, ReusableView {
    override var reuseIdentifier: String? {
    return “cell”
    }
    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
    super.init(style: style, reuseIdentifier: reuseIdentifier)
    self.backgroundColor = .cyan
    self.textLabel?.text = “通过协议和泛型创建的cell”
    self.textLabel?.textColor = .blue
    }
    required init?(coder aDecoder: NSCoder) {
    fatalError(“init(coder:) has not been implemented”)
    }
    }

    class TableProtocolCell2: UITableViewCell, ReusableView {
    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
    super.init(style: style, reuseIdentifier: reuseIdentifier)
    self.backgroundColor = .red
    self.textLabel?.text = “cell1 - 通过协议和泛型创建的”
    self.textLabel?.textAlignment = .right
    }
    required init?(coder aDecoder: NSCoder) {
    fatalError(“init(coder:) has not been implemented”)
    }
    }

    1
    2

    - 注册和使用

    class TableViewProtocolController: UITableViewController {
    override func viewDidLoad() {
    super.viewDidLoad()
    tableView.register(T: TableProtocolCell.self)
    tableView.register(T: TableProtocolCell2.self)
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {    return 10}override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {    var cell = UITableViewCell()    if indexPath.row % 2 == 0 {        cell = tableView.dequeueReusableCell(indexPath) as TableProtocolCell    } else {        cell = tableView.dequeueReusableCell(indexPath) as TableProtocolCell2    }    return cell}

    }

    `

    ]]>
    @@ -434,10 +447,10 @@ - - /2019/06/18/ReactNative%E4%B8%ADref%E4%BD%BF%E7%94%A8/ + + /2019/06/18/Swift%20%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/ - Reat Native ref 使用

    State 状态的使用

    在父控件中指定,整个生命周期讲不再改变
    需求:一段闪烁的文字, Props 属性显示文字内容创建时初始化,State控制随时间变化是否显示,在父控件中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    class Blink extends Component{
    // 构造
    constructor(props) {
    super(props);
    // 初始状态
    this.state = {showWithText:true};
    this.state = {}
    setInterval(() => {
    this.setState(previousState => {
    return {showWithText: !previousState.showWithText}
    });
    }, 100);
    }
    render() {
    let display= this.state.showWithText ? this.props.text : ' ';
    return(
    <Text>{display}</Text>
    );
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    export default class App extends Component<Props> {
    render() {
    return(
    <View style={styles.container}>
    <Blink
    text='I love to blink'
    />

    <Blink
    text='猜猜猜'
    />

    <Blink
    text='Blink is great'
    />
    </View>
    );
    }
    }

    Props 属性的使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 定义一个组件
    class Greeting extends Component {
    render() {
    return(
    <Text> Hello {this.props.name}!
    </Text>
    );
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    export default class App extends Component<Props> {
    render() {
    return(
    <View style={styles.container}>
    <Image source={{uri:'https://upload.wikimedia.org/wikipedia/commons/d/de/Bananavarieties.jpg'}}
    style={{width: 190, height: 110}}
    />
    <Text style={styles.instructions}>
    使用Props属性
    </Text>
    <View>
    <Greeting name='React-Native'
    />
    <Greeting name='iOS'
    />
    <Greeting name='Swift'
    />
    </View>

    </View>
    );
    }
    }

    ref的使用

    • 可以理解为组件渲染后指向组件的一个应用,可以通过ref获取到真实的组件(类似指针?)

    定义

    1
    ref='scrollView'

    获取

    1
    2
    let scrollView = this.refs.scrollView
    let scrollView = this.refs['scrollView']
    • 可以通过ref访问组件的属性和方法

    ES6 定时器

    • 开启定时器
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    let scrollView = this.refs.scrollView;
    // 2.4timer 定时器的写法
    this.timer = setInterval(
    ()=>{
    var tempPage = 0;
    // 修改banner索引
    if ((this.state.currentPage+1) >= ImageData.data.length) {
    tempPage = 0;
    } else {
    tempPage = this.state.currentPage+1;
    }

    // 更新状态
    this.setState({
    currentPage: tempPage
    })

    let offSet_x = width * tempPage
    scrollView.scrollResponderScrollTo({x:offSet_x, y:0, animated: true})
    },
    this.state.duration
    );
    • 暂停定时器
    1
    this.timer && clearInterval(this.timer)
    ]]>
    + Swift 学习

    枚举

    1. 定义 rawValue 为 Int 类型,初始值为1,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    enum Month: Int {
    case January = 1
    case February
    case March
    case April
    case May
    case June
    case July
    case August
    case September
    case October
    case November
    case December
    }

    2. rawValue 的整型值可以不连续

    1
    2
    3
    4
    5
    6
    enum Season: Int {
    case Spring = 1
    case Summer = 5
    case Autumn = 10
    case Winter = 40
    }

    3. 枚举可以是字符串,字符串是变量名rawValue 的值相等

    1
    2
    3
    4
    5
    6
    7
    enum ProgrammerLanguae: String {
    case Swift
    case OC = "Objective-C"
    case C = "语言"
    case RN = "React-Native"
    case Java
    }

    使用rawValue

    1
    2
    3
    func residueNewYear(month: Month) -> Int {
    return 12 - month.rawValue
    }

    调用枚举的Moth的构造函数生成一个Month

    1
    2
    3
    4
    let month = Month(rawValue: 5)
    let swift = ProgrammerLanguae.Swift.rawValue
    let oc = ProgrammerLanguae.OC
    let RN = ProgrammerLanguae.RN.rawValue

    4. 枚举关联值 associate value

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    enum Status {
    case success(Int)
    case fail(String)
    case null // 未关联值
    }

    func associateValueTest(isSuccess: Bool) -> Status {
    if isSuccess {
    return .success(200)
    }
    return .fail("失败")
    }

    5. 枚举associateValue多个值

    • 本质是关联了一个元祖(value0, value1, value2…)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    enum Shape {
    case Square(width: Double)
    case Reactangle(width: Double, height: Double)
    case Circle(x: Double, y: Double, radius: Double)
    case Point
    }

    let square = Shape.Square(width: 20)
    let reactangle = Shape.Reactangle(width: 10, height: 20)
    let circle = Shape.Circle(x: 10, y: 10, radius: 20)
    let point = Shape.Point

    private func area(shape: Shape) -> Double {
    switch shape {
    case let .Square(width):
    return width * width
    case let .Reactangle(width, height):
    return width * height
    case let .Circle(_, _, radius):
    return radius * radius * Double.pi
    case .Point:
    return 0
    }
    }

    6. 递归枚举

    • 定义一个递归算术表达式
    • 使用 indirect 来修饰
    1
    2
    3
    4
    5
    6
    7
    8
    indirect enum ArithmeticalExpression {
    case Number(Int)
    case Addition(ArithmeticalExpression, ArithmeticalExpression) // + 时两边也是一个表达式
    case Multiplication(ArithmeticalExpression, ArithmeticalExpression) // * 时两边也是一个表达式

    // indirect case Addition(ArithmeticalExpression, ArithmeticalExpression) // + 时两边也是一个表达式
    // indirect case Multiplication(ArithmeticalExpression, ArithmeticalExpression) // * 时两边也是一个表达式
    }
    • 递归表达式使用 (2+3) * 4
    1
    2
    3
    4
    let two = ArithmeticalExpression.Number(2)
    let one = ArithmeticalExpression.Number(3)
    let sum = ArithmeticalExpression.Addition(two, one)
    let indirectEnumResult = ArithmeticalExpression.Multiplication(sum, ArithmeticalExpression.Number(4))
    • 计算表达式值的函数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    private func calculate(expression: ArithmeticalExpression) -> Int {
    switch expression {
    case let .Number(value):
    return value
    case let .Addition(left, right):
    return calculate(expression: left) + calculate(expression: right)
    case let .Multiplication(left, right):
    return calculate(expression: left) * calculate(expression: right)
    }
    }

    结构体

    • 结构体中的属性值没有初始化时必须使用构造函数来初始化,否则报错
    1
    2
    3
    4
    5
    6
    7
    struct Location {
    var latitude: Double
    var longitude: Double
    var placeName: String?
    }

    let location1 = Location(latitude: 37.3230, longitude: -122.0322, placeName: "测试")
    • 结构体中属性如果都赋了初始值就可以直接初始化
    1
    2
    3
    4
    5
    6
    struct Location2 {
    var latitude: Double = 0
    var longitude: Double = 0
    }

    let location2 = Location2()
    • 如果未指定初始值,swift 就不会默认初始化为(不初始化)
    • 当属性值为可选值时此时允许值为nil,那么就允许不通过构造函数来初始化赋值
    • let 只有一次赋值机会
    • 不管是类还是结构体都应该提供一个全参数的构造函数 init(latitude: Double, longitude: Double, placeName: String?)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    struct Location3 {
    let latitude: Double
    let longtitude: Double
    var placeName: String?

    init(coordinateString: String) {
    let index = coordinateString.index(of: ",")
    let index1 = coordinateString.index(after: index!)
    let test_1 = coordinateString.prefix(upTo: index!)
    let test_2 = coordinateString.suffix(from: index1)

    latitude = Double(test_1)!
    longtitude = Double(test_2)!
    }

    init(latitude: Double, longitude: Double, placeName: String?) {
    self.latitude = latitude
    self.longtitude = longitude
    self.placeName = placeName
    }
    }

    let test3 = Location3(coordinateString: "12,45")

    属性和方法

    计算型属性

    类型属性

    类型方法

    属性管擦器

    延迟属性

    访问控制

    单利模式

    继承和构造函数

    swift 中继承

    多态性

    属性和函数重载

    子类两段式构造

    • 子类构造函数分为两段,第一段是构造自己,第二段是构造父类

      • 父类中构造函数
      • 子类要设置一个自己的构造函数
      • 子类构造函数中需要先初始化自己的属性
      • 然后在构造函数中调用父类初始化父类构造函数中的相关属性

        父类

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        class Father {
        var name: String
        var sex: Int = 0
        var old: Int = 32
        var desc: String {
        return "我是\(name)"
        }
        init(name: String) {
        self.name = name
        }

        func run() {
        print("Father can Running")
        }
        }
    子类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    class Son: Father {

    var isSwimming = true

    var computer: String

    override var desc: String {
    return "Son is \(name)"
    }

    init(name: String, computer: String) {
    self.computer = computer
    super.init(name: name)
    }
    }
    • 子类两段构造都完成后子类的初始化才完成,可以使用self调用属性或者方法

      1
      2
      3
      4
      5
      init(name: String, computer: String) {
      self.computer = computer
      self.getToys() // 此代码报错,子类初始化未完成
      super.init(name: name)
      }

    便利构造函数和指定构造函数

    • 便利构造函数只能调用自己的指定构造函数
    • 指定构造函数通过一系列的调用都会调用super.init()
    • 便利构造函数无法调用super.init()
    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
    class Father {
    var name: String
    var sex: Int = 0
    var old: Int = 32
    var desc: String {
    return "我是\(name)"
    }
    init(name: String) {
    self.name = name
    }

    init(name: String, old: Int) {
    self.name = name
    self.old = old
    }

    func run() {
    print("Father can Running")
    }
    }

    class Son: Father {
    var isSwimming = true
    var computer: String

    override var desc: String {
    return "Son is \(name)"
    }

    // 子类指定构造函数,调用了父类的指定构造函数
    init(name: String, computer: String) {
    self.computer = computer
    super.init(name: name)
    }

    // 子类便利构造函数,调用了指定构造函数
    convenience override init(name: String) {
    let computer = "iMac"
    self.init(name: name, computer: computer)
    }
    }

    构造函数继承

    • 子类有可能会继承父类的构造函数
    • 子类没有实现父类的任何指定构造函数;则自动继承父类的所有指定构造函数, 因为继承了指定构造函数所以同时便利构造函数也被继承

    父类

    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
    class Father {
    var name: String
    var sex: Int = 0
    var old: Int = 32
    var desc: String {
    return "我是\(name)"
    }

    /// Father指定构造函数-1
    init(name: String) {
    self.name = name
    }

    /// Father指定构造函数-2
    init(name: String, old: Int) {
    self.name = name
    self.old = old
    }

    /// Father便利构造函数
    convenience init(old: Int) {
    self.init(name: "Father", old: old)
    }

    func run() {
    print("Father can Running")
    }
    }

    子类

    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
    class Son: Father {
    var isSwimming = true
    var computer: String
    var job: String

    override var desc: String {
    return "Son is \(name)"
    }

    /// 子类重载的指定构造函数-1
    convenience override init(name: String) {
    self.init(name: name, computer: "Dell")
    }

    /// 子类重载的指定构造函数-2
    override convenience init(name: String, old: Int) {
    self.init(name: name, computer: "acer")
    }


    /// 子类自己的指定构造函数,调用了父类的指定构造函数
    init(name: String, computer: String) {
    self.computer = computer
    self.job = "C#"
    super.init(name: name)
    }

    // 子类便利构造函数,调用了自己指定构造函数
    convenience init(computer: String) {
    let name = "小张"
    self.init(name: name, computer: computer)
    }
    }

    未实现父类任何的指定构造函数

    1
    2
    3
    class Grandson: Son {
    var toy: String = "dog toys"
    }

    子类可以调用的构造函数

    1
    2
    3
    4
    5
    let grandSon0 = Grandson(old: 4)
    let grandSon1 = Grandson(computer: "Mi")
    let grandSon2 = Grandson(name: "小王")
    let grandSon3 = Grandson(name: "小虎", computer: "👽")
    let grandSon4 = Grandson(name: "小李", old: 8)
    • 子类实现了父类所有的指定构造函数,则自动继承父类的所有便利构造函数
    1
    2
    3
    4
    5
    let son0 = Son(old: 30)
    let son1 = Son(computer: "Mi")
    let son2 = Son(name: "小王")
    let son3 = Son(name: "小虎", computer: "👽")
    let son4 = Son(name: "小李", old: 8)

    required 构造函数

    • 父类中有被 required 关键词修饰的构造函数子类必须实现此构造函数
    • 子类实现的此构造函数不需要再使用 override 关键词修饰,需要 required 修饰

    子类

    1
    2
    3
    4
    5
    6
    7
    8
    class Father {
    var name: String
    /// Father指定构造函数-1
    // required 修饰的指定构造函数子类中必须实现
    required init(name: String) {
    self.name = name
    }
    }

    父类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    class Son: Father {
    var isSwimming = true
    var computer: String

    /// 子类重载的指定构造函数-1
    convenience required init(name: String) {
    self.init(name: name, computer: "Dell")
    }

    /// 子类自己的指定构造函数,调用了父类的指定构造函数
    init(name: String, computer: String) {
    self.computer = computer
    self.job = "C#"
    super.init(name: name)
    }
    }
    • 子类如果实现了自己的指定构造函数,那么 required 修饰指定构造函数就初始化失败,因为自己实现了指定构造函数所以不能继承父类中的构造函数,此时可以自己实现被required 修饰的便利构造函数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    class Son1: Son {
    var sport: String

    // 指定构造函数,需要调用父类的指定构造函数
    init(name: String, sport: String) {
    self.sport = sport
    Son.init(name: name)
    }

    // 父类中有被 required 关键词修饰的必须实现的构造函数
    convenience required init(name: String) {
    // fatalError("init(name:) has not been implemented")
    // 调用自己的指定构造函数,
    self.init(name: name, sport: "")
    }
    }
    ]]>
    @@ -447,10 +460,10 @@ - - /2019/06/18/Swift%20%E5%8D%8F%E8%AE%AE%E4%BD%BF%E7%94%A8/ + + /2019/06/18/ReactNative%E4%B8%ADref%E4%BD%BF%E7%94%A8/ - Swift 协议使用

    对UIView 扩展

    按钮/文本框抖动动画

    • 声明协议

      • 协议中定义属性:遵循该协议的类型都具有此属性

        • 必须明确规定该属性是可读的 {get} 或者可写的 {set},或是可读可写的 {get set}
        • 使用static修饰声明一个类类型属性
      • 协议中定义方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    protocol Shakable {}
    extension Shakable where Self: UIView {
    func shakeAnimation() {
    let animation = CABasicAnimation(keyPath: "position")
    animation.duration = 0.08
    animation.repeatCount = 5
    animation.autoreverses = true
    animation.fromValue = NSValue(cgPoint: CGPoint(x: self.center.x - 4, y: self.center.y))
    animation.toValue = NSValue(cgPoint: CGPoint(x: self.center.x + 4, y: self.center.y))
    layer.add(animation, forKey: "position")
    }
    }
    • 自定义UI控件并遵守协议
    1
    2
    3
    class MyButton: UIButton, Shakable {  }
    class MySwitch: UISwitch, Shakable { }
    class MyTextField: UITextField, Shakable { }
    • 使用
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    let mySwitch = MySwitch()
    mySwitch.isOn = true
    self.mySwitch = mySwitch
    view.addSubview(mySwitch)
    mySwitch.translatesAutoresizingMaskIntoConstraints = false
    mySwitch.topAnchor.constraint(equalTo: view.bottomAnchor, constant: 10).isActive = true
    mySwitch.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10).isActive = true

    // 调用
    mySwitch.shakeAnimation()

    UITableViewCell使用

    定义协议

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    protocol ReusableView: class {}

    extension ReusableView where Self: UIView {
    static var reuseIdentifier: String {
    return String(describing: self)
    }
    }

    ```

    ### 对tableView扩展

    extension UITableView {
    /// 注册cell
    func register<T: UITableViewCell>(T: T.Type) where T: ReusableView {
    print(T.reuseIdentifier)
    self.register(T.self, forCellReuseIdentifier: T.reuseIdentifier)
    }

    /// 调用cellfunc dequeueReusableCell<T: UITableViewCell>(_ indexPath: IndexPath) -> T where T: ReusableView {    print(T.reuseIdentifier)    return self.dequeueReusableCell(withIdentifier: T.reuseIdentifier, for: indexPath) as! T}

    }

    1
    2
    3
    4

    ### 用法

    - 创建自定义的cell,需要遵守协议

    class TableProtocolCell: UITableViewCell, ReusableView {
    override var reuseIdentifier: String? {
    return “cell”
    }
    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
    super.init(style: style, reuseIdentifier: reuseIdentifier)
    self.backgroundColor = .cyan
    self.textLabel?.text = “通过协议和泛型创建的cell”
    self.textLabel?.textColor = .blue
    }
    required init?(coder aDecoder: NSCoder) {
    fatalError(“init(coder:) has not been implemented”)
    }
    }

    class TableProtocolCell2: UITableViewCell, ReusableView {
    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
    super.init(style: style, reuseIdentifier: reuseIdentifier)
    self.backgroundColor = .red
    self.textLabel?.text = “cell1 - 通过协议和泛型创建的”
    self.textLabel?.textAlignment = .right
    }
    required init?(coder aDecoder: NSCoder) {
    fatalError(“init(coder:) has not been implemented”)
    }
    }

    1
    2

    - 注册和使用

    class TableViewProtocolController: UITableViewController {
    override func viewDidLoad() {
    super.viewDidLoad()
    tableView.register(T: TableProtocolCell.self)
    tableView.register(T: TableProtocolCell2.self)
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {    return 10}override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {    var cell = UITableViewCell()    if indexPath.row % 2 == 0 {        cell = tableView.dequeueReusableCell(indexPath) as TableProtocolCell    } else {        cell = tableView.dequeueReusableCell(indexPath) as TableProtocolCell2    }    return cell}

    }

    `

    ]]>
    + Reat Native ref 使用

    State 状态的使用

    在父控件中指定,整个生命周期讲不再改变
    需求:一段闪烁的文字, Props 属性显示文字内容创建时初始化,State控制随时间变化是否显示,在父控件中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    class Blink extends Component{
    // 构造
    constructor(props) {
    super(props);
    // 初始状态
    this.state = {showWithText:true};
    this.state = {}
    setInterval(() => {
    this.setState(previousState => {
    return {showWithText: !previousState.showWithText}
    });
    }, 100);
    }
    render() {
    let display= this.state.showWithText ? this.props.text : ' ';
    return(
    <Text>{display}</Text>
    );
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    export default class App extends Component<Props> {
    render() {
    return(
    <View style={styles.container}>
    <Blink
    text='I love to blink'
    />

    <Blink
    text='猜猜猜'
    />

    <Blink
    text='Blink is great'
    />
    </View>
    );
    }
    }

    Props 属性的使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 定义一个组件
    class Greeting extends Component {
    render() {
    return(
    <Text> Hello {this.props.name}!
    </Text>
    );
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    export default class App extends Component<Props> {
    render() {
    return(
    <View style={styles.container}>
    <Image source={{uri:'https://upload.wikimedia.org/wikipedia/commons/d/de/Bananavarieties.jpg'}}
    style={{width: 190, height: 110}}
    />
    <Text style={styles.instructions}>
    使用Props属性
    </Text>
    <View>
    <Greeting name='React-Native'
    />
    <Greeting name='iOS'
    />
    <Greeting name='Swift'
    />
    </View>

    </View>
    );
    }
    }

    ref的使用

    • 可以理解为组件渲染后指向组件的一个应用,可以通过ref获取到真实的组件(类似指针?)

    定义

    1
    ref='scrollView'

    获取

    1
    2
    let scrollView = this.refs.scrollView
    let scrollView = this.refs['scrollView']
    • 可以通过ref访问组件的属性和方法

    ES6 定时器

    • 开启定时器
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    let scrollView = this.refs.scrollView;
    // 2.4timer 定时器的写法
    this.timer = setInterval(
    ()=>{
    var tempPage = 0;
    // 修改banner索引
    if ((this.state.currentPage+1) >= ImageData.data.length) {
    tempPage = 0;
    } else {
    tempPage = this.state.currentPage+1;
    }

    // 更新状态
    this.setState({
    currentPage: tempPage
    })

    let offSet_x = width * tempPage
    scrollView.scrollResponderScrollTo({x:offSet_x, y:0, animated: true})
    },
    this.state.duration
    );
    • 暂停定时器
    1
    this.timer && clearInterval(this.timer)
    ]]>
    @@ -460,10 +473,10 @@ - - /2019/06/18/Swift%20%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/ + + /2019/06/18/React-Native%E5%BC%80%E5%8F%91iOS%E6%89%93%E5%8C%85/ - Swift 学习

    枚举

    1. 定义 rawValue 为 Int 类型,初始值为1,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    enum Month: Int {
    case January = 1
    case February
    case March
    case April
    case May
    case June
    case July
    case August
    case September
    case October
    case November
    case December
    }

    2. rawValue 的整型值可以不连续

    1
    2
    3
    4
    5
    6
    enum Season: Int {
    case Spring = 1
    case Summer = 5
    case Autumn = 10
    case Winter = 40
    }

    3. 枚举可以是字符串,字符串是变量名rawValue 的值相等

    1
    2
    3
    4
    5
    6
    7
    enum ProgrammerLanguae: String {
    case Swift
    case OC = "Objective-C"
    case C = "语言"
    case RN = "React-Native"
    case Java
    }

    使用rawValue

    1
    2
    3
    func residueNewYear(month: Month) -> Int {
    return 12 - month.rawValue
    }

    调用枚举的Moth的构造函数生成一个Month

    1
    2
    3
    4
    let month = Month(rawValue: 5)
    let swift = ProgrammerLanguae.Swift.rawValue
    let oc = ProgrammerLanguae.OC
    let RN = ProgrammerLanguae.RN.rawValue

    4. 枚举关联值 associate value

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    enum Status {
    case success(Int)
    case fail(String)
    case null // 未关联值
    }

    func associateValueTest(isSuccess: Bool) -> Status {
    if isSuccess {
    return .success(200)
    }
    return .fail("失败")
    }

    5. 枚举associateValue多个值

    • 本质是关联了一个元祖(value0, value1, value2…)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    enum Shape {
    case Square(width: Double)
    case Reactangle(width: Double, height: Double)
    case Circle(x: Double, y: Double, radius: Double)
    case Point
    }

    let square = Shape.Square(width: 20)
    let reactangle = Shape.Reactangle(width: 10, height: 20)
    let circle = Shape.Circle(x: 10, y: 10, radius: 20)
    let point = Shape.Point

    private func area(shape: Shape) -> Double {
    switch shape {
    case let .Square(width):
    return width * width
    case let .Reactangle(width, height):
    return width * height
    case let .Circle(_, _, radius):
    return radius * radius * Double.pi
    case .Point:
    return 0
    }
    }

    6. 递归枚举

    • 定义一个递归算术表达式
    • 使用 indirect 来修饰
    1
    2
    3
    4
    5
    6
    7
    8
    indirect enum ArithmeticalExpression {
    case Number(Int)
    case Addition(ArithmeticalExpression, ArithmeticalExpression) // + 时两边也是一个表达式
    case Multiplication(ArithmeticalExpression, ArithmeticalExpression) // * 时两边也是一个表达式

    // indirect case Addition(ArithmeticalExpression, ArithmeticalExpression) // + 时两边也是一个表达式
    // indirect case Multiplication(ArithmeticalExpression, ArithmeticalExpression) // * 时两边也是一个表达式
    }
    • 递归表达式使用 (2+3) * 4
    1
    2
    3
    4
    let two = ArithmeticalExpression.Number(2)
    let one = ArithmeticalExpression.Number(3)
    let sum = ArithmeticalExpression.Addition(two, one)
    let indirectEnumResult = ArithmeticalExpression.Multiplication(sum, ArithmeticalExpression.Number(4))
    • 计算表达式值的函数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    private func calculate(expression: ArithmeticalExpression) -> Int {
    switch expression {
    case let .Number(value):
    return value
    case let .Addition(left, right):
    return calculate(expression: left) + calculate(expression: right)
    case let .Multiplication(left, right):
    return calculate(expression: left) * calculate(expression: right)
    }
    }

    结构体

    • 结构体中的属性值没有初始化时必须使用构造函数来初始化,否则报错
    1
    2
    3
    4
    5
    6
    7
    struct Location {
    var latitude: Double
    var longitude: Double
    var placeName: String?
    }

    let location1 = Location(latitude: 37.3230, longitude: -122.0322, placeName: "测试")
    • 结构体中属性如果都赋了初始值就可以直接初始化
    1
    2
    3
    4
    5
    6
    struct Location2 {
    var latitude: Double = 0
    var longitude: Double = 0
    }

    let location2 = Location2()
    • 如果未指定初始值,swift 就不会默认初始化为(不初始化)
    • 当属性值为可选值时此时允许值为nil,那么就允许不通过构造函数来初始化赋值
    • let 只有一次赋值机会
    • 不管是类还是结构体都应该提供一个全参数的构造函数 init(latitude: Double, longitude: Double, placeName: String?)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    struct Location3 {
    let latitude: Double
    let longtitude: Double
    var placeName: String?

    init(coordinateString: String) {
    let index = coordinateString.index(of: ",")
    let index1 = coordinateString.index(after: index!)
    let test_1 = coordinateString.prefix(upTo: index!)
    let test_2 = coordinateString.suffix(from: index1)

    latitude = Double(test_1)!
    longtitude = Double(test_2)!
    }

    init(latitude: Double, longitude: Double, placeName: String?) {
    self.latitude = latitude
    self.longtitude = longitude
    self.placeName = placeName
    }
    }

    let test3 = Location3(coordinateString: "12,45")

    属性和方法

    计算型属性

    类型属性

    类型方法

    属性管擦器

    延迟属性

    访问控制

    单利模式

    继承和构造函数

    swift 中继承

    多态性

    属性和函数重载

    子类两段式构造

    • 子类构造函数分为两段,第一段是构造自己,第二段是构造父类

      • 父类中构造函数
      • 子类要设置一个自己的构造函数
      • 子类构造函数中需要先初始化自己的属性
      • 然后在构造函数中调用父类初始化父类构造函数中的相关属性

        父类

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        class Father {
        var name: String
        var sex: Int = 0
        var old: Int = 32
        var desc: String {
        return "我是\(name)"
        }
        init(name: String) {
        self.name = name
        }

        func run() {
        print("Father can Running")
        }
        }
    子类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    class Son: Father {

    var isSwimming = true

    var computer: String

    override var desc: String {
    return "Son is \(name)"
    }

    init(name: String, computer: String) {
    self.computer = computer
    super.init(name: name)
    }
    }
    • 子类两段构造都完成后子类的初始化才完成,可以使用self调用属性或者方法

      1
      2
      3
      4
      5
      init(name: String, computer: String) {
      self.computer = computer
      self.getToys() // 此代码报错,子类初始化未完成
      super.init(name: name)
      }

    便利构造函数和指定构造函数

    • 便利构造函数只能调用自己的指定构造函数
    • 指定构造函数通过一系列的调用都会调用super.init()
    • 便利构造函数无法调用super.init()
    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
    class Father {
    var name: String
    var sex: Int = 0
    var old: Int = 32
    var desc: String {
    return "我是\(name)"
    }
    init(name: String) {
    self.name = name
    }

    init(name: String, old: Int) {
    self.name = name
    self.old = old
    }

    func run() {
    print("Father can Running")
    }
    }

    class Son: Father {
    var isSwimming = true
    var computer: String

    override var desc: String {
    return "Son is \(name)"
    }

    // 子类指定构造函数,调用了父类的指定构造函数
    init(name: String, computer: String) {
    self.computer = computer
    super.init(name: name)
    }

    // 子类便利构造函数,调用了指定构造函数
    convenience override init(name: String) {
    let computer = "iMac"
    self.init(name: name, computer: computer)
    }
    }

    构造函数继承

    • 子类有可能会继承父类的构造函数
    • 子类没有实现父类的任何指定构造函数;则自动继承父类的所有指定构造函数, 因为继承了指定构造函数所以同时便利构造函数也被继承

    父类

    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
    class Father {
    var name: String
    var sex: Int = 0
    var old: Int = 32
    var desc: String {
    return "我是\(name)"
    }

    /// Father指定构造函数-1
    init(name: String) {
    self.name = name
    }

    /// Father指定构造函数-2
    init(name: String, old: Int) {
    self.name = name
    self.old = old
    }

    /// Father便利构造函数
    convenience init(old: Int) {
    self.init(name: "Father", old: old)
    }

    func run() {
    print("Father can Running")
    }
    }

    子类

    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
    class Son: Father {
    var isSwimming = true
    var computer: String
    var job: String

    override var desc: String {
    return "Son is \(name)"
    }

    /// 子类重载的指定构造函数-1
    convenience override init(name: String) {
    self.init(name: name, computer: "Dell")
    }

    /// 子类重载的指定构造函数-2
    override convenience init(name: String, old: Int) {
    self.init(name: name, computer: "acer")
    }


    /// 子类自己的指定构造函数,调用了父类的指定构造函数
    init(name: String, computer: String) {
    self.computer = computer
    self.job = "C#"
    super.init(name: name)
    }

    // 子类便利构造函数,调用了自己指定构造函数
    convenience init(computer: String) {
    let name = "小张"
    self.init(name: name, computer: computer)
    }
    }

    未实现父类任何的指定构造函数

    1
    2
    3
    class Grandson: Son {
    var toy: String = "dog toys"
    }

    子类可以调用的构造函数

    1
    2
    3
    4
    5
    let grandSon0 = Grandson(old: 4)
    let grandSon1 = Grandson(computer: "Mi")
    let grandSon2 = Grandson(name: "小王")
    let grandSon3 = Grandson(name: "小虎", computer: "👽")
    let grandSon4 = Grandson(name: "小李", old: 8)
    • 子类实现了父类所有的指定构造函数,则自动继承父类的所有便利构造函数
    1
    2
    3
    4
    5
    let son0 = Son(old: 30)
    let son1 = Son(computer: "Mi")
    let son2 = Son(name: "小王")
    let son3 = Son(name: "小虎", computer: "👽")
    let son4 = Son(name: "小李", old: 8)

    required 构造函数

    • 父类中有被 required 关键词修饰的构造函数子类必须实现此构造函数
    • 子类实现的此构造函数不需要再使用 override 关键词修饰,需要 required 修饰

    子类

    1
    2
    3
    4
    5
    6
    7
    8
    class Father {
    var name: String
    /// Father指定构造函数-1
    // required 修饰的指定构造函数子类中必须实现
    required init(name: String) {
    self.name = name
    }
    }

    父类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    class Son: Father {
    var isSwimming = true
    var computer: String

    /// 子类重载的指定构造函数-1
    convenience required init(name: String) {
    self.init(name: name, computer: "Dell")
    }

    /// 子类自己的指定构造函数,调用了父类的指定构造函数
    init(name: String, computer: String) {
    self.computer = computer
    self.job = "C#"
    super.init(name: name)
    }
    }
    • 子类如果实现了自己的指定构造函数,那么 required 修饰指定构造函数就初始化失败,因为自己实现了指定构造函数所以不能继承父类中的构造函数,此时可以自己实现被required 修饰的便利构造函数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    class Son1: Son {
    var sport: String

    // 指定构造函数,需要调用父类的指定构造函数
    init(name: String, sport: String) {
    self.sport = sport
    Son.init(name: name)
    }

    // 父类中有被 required 关键词修饰的必须实现的构造函数
    convenience required init(name: String) {
    // fatalError("init(name:) has not been implemented")
    // 调用自己的指定构造函数,
    self.init(name: name, sport: "")
    }
    }
    ]]>
    + React-Native开发iOS打包

    打jsbudnle包

    创建打包后存放文件目录

    • cd 到 React-Native 项目根目录下 并且 进入 ios 文件目录下
    • 创建 bundle 文件夹

      1
      mkdir bundle

    配置 React-Native 打包命令

    • 打包命令

      1
      react-native bundle
    • 在React Native项目的根目录下执行命令

      1
      react-native bundle --entry-file index.js --platform ios --dev false --bundle-output ./ios/bundle/index.jsbundle --assets-dest ./ios/bundle
    - `--entry-file`: ios或者android入口的js名称,比如 `index.js`- `--platform`: 平台名称(ios或者android)- `--dev`: 设置为`false`的时候将会对`JavaScript`代码进行优化处理。- `--bundle-output`: 生成的jsbundle文件的名称和路径,比如 `./ios/bundle/index.jsbundle`- `--assets-dest`: 图片以及其他资源存放的目录

    使用打包命令

    • 将打包命令添加到 packger.json

      1
      2
      3
      4
      5
      "scripts": {
      "start": "node node_modules/react-native/local-cli/cli.js start",
      "test": "jest",
      "bundle-ios": "node node_modules/react-native/local-cli/cli.js bundle --entry-file index.js --platform ios --dev false --bundle-output ./ios/bundle/index.jsbundle --assets-dest ./ios/bundle"
      },
    • 再次打包只需要 React Native项目的根目录下执行命令,不用再次输入类似上面的命令

      1
      npm run bundle-ios

    集成到Xcode(iOS原生项目中)

    • 添加已经打好的.jsbundle 离线包

    • iOS 项目代码配置

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      private func loadReactNativeTest() {
      // debug 测试加载本地文件
      let url = URL(string: "http://localhost:8081/index.bundle?platform=ios")!

      // 加载已经加入的离线包
      let url2 = Bundle.main.url(forResource: "index", withExtension: "jsbundle")

      let rootView = RCTRootView(
      bundleURL: url,
      moduleName: "MyApp", //这里的名字要和index.js中相同
      initialProperties: nil,
      launchOptions: nil
      )
      view = rootView
      }
    ]]>
    @@ -486,10 +499,10 @@ - - /2019/06/18/React-Native%E5%BC%80%E5%8F%91iOS%E6%89%93%E5%8C%85/ + + /2019/06/18/MasonrySnapkit%E4%BD%BF%E7%94%A8%E6%80%BB%E7%BB%93/ - React-Native开发iOS打包

    打jsbudnle包

    创建打包后存放文件目录

    • cd 到 React-Native 项目根目录下 并且 进入 ios 文件目录下
    • 创建 bundle 文件夹

      1
      mkdir bundle

    配置 React-Native 打包命令

    • 打包命令

      1
      react-native bundle
    • 在React Native项目的根目录下执行命令

      1
      react-native bundle --entry-file index.js --platform ios --dev false --bundle-output ./ios/bundle/index.jsbundle --assets-dest ./ios/bundle
    - `--entry-file`: ios或者android入口的js名称,比如 `index.js`- `--platform`: 平台名称(ios或者android)- `--dev`: 设置为`false`的时候将会对`JavaScript`代码进行优化处理。- `--bundle-output`: 生成的jsbundle文件的名称和路径,比如 `./ios/bundle/index.jsbundle`- `--assets-dest`: 图片以及其他资源存放的目录

    使用打包命令

    • 将打包命令添加到 packger.json

      1
      2
      3
      4
      5
      "scripts": {
      "start": "node node_modules/react-native/local-cli/cli.js start",
      "test": "jest",
      "bundle-ios": "node node_modules/react-native/local-cli/cli.js bundle --entry-file index.js --platform ios --dev false --bundle-output ./ios/bundle/index.jsbundle --assets-dest ./ios/bundle"
      },
    • 再次打包只需要 React Native项目的根目录下执行命令,不用再次输入类似上面的命令

      1
      npm run bundle-ios

    集成到Xcode(iOS原生项目中)

    • 添加已经打好的.jsbundle 离线包

    • iOS 项目代码配置

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      private func loadReactNativeTest() {
      // debug 测试加载本地文件
      let url = URL(string: "http://localhost:8081/index.bundle?platform=ios")!

      // 加载已经加入的离线包
      let url2 = Bundle.main.url(forResource: "index", withExtension: "jsbundle")

      let rootView = RCTRootView(
      bundleURL: url,
      moduleName: "MyApp", //这里的名字要和index.js中相同
      initialProperties: nil,
      launchOptions: nil
      )
      view = rootView
      }
    ]]>
    + Masonry使用

    基本使用官网Demo非常详细,以下总结的是常见问题和技巧

    安装遇到的问题

    • pod 安装后出现⚠️,项目运行报错

      1
      [UIView mas_makeConstraints:]: unrecognized selector sent to instance

      github

      解决方法

    • 使用lastBaseline属性crash

      解决办法

    使用时注意点

    • edgesForExtendedLayout 方法

      重写此方法的目的是为了确定布局位置从什么位置开始什么位置结束,

      1
      2
      3
      - (UIRectEdge)edgesForExtendedLayout {
      return UIRectEdgeNone;
      }
    **重写后**![](https://ws1.sinaimg.cn/large/006tNc79ly1fsgcn3fmo0j30s61iyalk.jpg)**重写前**![](https://ws4.sinaimg.cn/large/006tNc79ly1fsgcokvpdwj30s61iy7eq.jpg)
    • translucent 设置UINavigationBar是否半透明, 如果设置了导航栏的styleUIBarStyleBlackTranslucent 就为true

      设置为false不透明效果

    • extendedLayoutIncludesOpaqueBars 导航栏不透明条件下是否可以扩展,默认是NO不可以扩展

      1
      2
      self.navigationController?.navigationBar.isTranslucent = false
      self.extendedLayoutIncludesOpaqueBars = true
    **设置不透明,设置可以扩展此时从坐标的(0,0)点开始布局**![](https://ws2.sinaimg.cn/large/006tNc79ly1fsggem13mjj30ow0j83zy.jpg)    
    • inset 的使用,当约束控件的属性相同时不用再考虑正负值的问题,例如要约束一个控件左边距离另一控件的右边位置时还需要使用负值情况

      1
      2
      3
      4
      5
      6
      7
      [yellowdView makeConstraints:^(MASConstraintMaker *make) {
      make.height.equalTo(@150);
      make.top.equalTo(self).inset(220);
      make.leading.equalTo(self).inset(10);
      make.trailing.equalTo(greenView.leading).inset(10);
      make.width.equalTo(greenView);
      }];
    • edges的使用,约束边距更方便

      1
      2
      3
      [edgeView remakeConstraints:^(MASConstraintMaker *make) {
      make.edges.equalTo(lasView).insets(UIEdgeInsetsMake(10, 5, 5, 5));
      }];
    • Priority 设置约束的优先级

      • priorityHigh
      • priorityMedium
      • priorityLow
    • makeConstraintsupdateConstraintsremakeConstraints三者区别

      • makeConstraints 添加约束
      • updateConstraints 更新约束,更新之前会查找一边控件已经存在的约束,没有就添加,有就更新到最新的约束
      • remakeConstraints删除控件以前的所有约束重新添加约束
    • 设置控件或者某一约束的’key’ 方便冲突时⚠️查找

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      greenView.mas_key = @"greenView";
      [greenView makeConstraints:^(MASConstraintMaker *make) {
      make.height.equalTo(yellowdView).key(@"greenView-height");
      make.top.equalTo(yellowdView).key(@"greenView-top");
      make.width.equalTo(yellowdView).key(@"greenView-width");
      make.trailing.equalTo(self).inset(10).key(@"greenView-trailing");
      // 冲突约束
      // make.leading.equalTo(greenView.trailing).inset(10).key(@"greenView-leading");
      }];
      ```

      - 使用 `MASAttachKeys`批量给`view`设置key

      MASAttachKeys(greenView, yellowdView);

      1
      2
      3
      4

      ![](https://ws3.sinaimg.cn/large/006tNc79ly1fsgalvd4l6j31a20modnp.jpg)

      - `dividedBy`和 `multipliedBy` 约束宽高比

      [topInnerView makeConstraints:^(MASConstraintMaker *make) {

      // 设置自己的宽高比是3:1make.width.equalTo( topInnerView.height).multipliedBy(3); // 乘因数// 设置宽高并且设置他们的优先级最低make.width.height.lessThanOrEqualTo(topView);make.width.height.equalTo(topView).priorityLow();// 设置位置make.top.equalTo(topView);

      }];

      1
      - 多个控件批量约束

      NSValue sizeValue = [NSValue valueWithCGSize:CGSizeMake(100, 100)];
      [@[blueView, redView, yellowView] makeConstraints:^(MASConstraintMaker
      make) {

      make.size.equalTo(sizeValue);

      }];

      [@[blueView, redView, yellowView] mas_makeConstraints:^(MASConstraintMaker *make) {

      make.top.equalTo(self).inset(20);

      }];

      [blueView makeConstraints:^(MASConstraintMaker *make) {

      make.left.equalTo(self).inset(20);

      }];

      [redView makeConstraints:^(MASConstraintMaker *make) {

      make.left.equalTo(blueView.right).inset(10);

      }];

      [yellowView makeConstraints:^(MASConstraintMaker *make) {

      make.left.equalTo(redView.right).inset(10);

      }];

      1
      2
      3
      4

      ![批量约束](https://ws1.sinaimg.cn/large/006tNc79ly1fsgaz670hvj30s61iyn2l.jpg)

      **以上代码也可以简化为**

      [redView makeConstraints:^(MASConstraintMaker *make) {

      make.left.equalTo(blueView.right).inset(10);

      }];

      [yellowView makeConstraints:^(MASConstraintMaker *make) {

      make.left.equalTo(redView.right).inset(10);

      }];

    [blueView makeConstraints:^(MASConstraintMaker *make) {    make.top.left.equalTo(self);    make.size.equalTo(@[redView, yellowView, sizeValue]);}];
    1
    2
    3
    4
    5
    ![](https://ws2.sinaimg.cn/large/006tNc79ly1fsgbhz8lw5j30s61iyte5.jpg)

    ## Snapkit 使用

    - Swift 中`edgesForExtendedLayout:UIRectEdge` 扩展布局的边缘是一个属性,默认是`All`
    self.edgesForExtendedLayout = []
    1
    2
    3
    4

    在Swift中闭包参数可以使用$0、$1、$2 一次代表第一个,第二个,第三个参数

    - 使用$0 来代替闭包参数
    nullDataImageView.snp.makeConstraints { $0.top.equalTo(view).offset(44) $0.centerX.equalTo(view) $0.width.height.equalTo(200)}
    1
    2
    3
    4
    5
    6
    7

    - `deactivate` 和 `activate` 使用

    - `activate` 激活指定的约束
    - `deactivate` 移除指定的约束

    添加约束
    nullDataImageView.snp.makeConstraints { nullDataViewTopConstraint = $0.top.equalTo(view).offset(80).constraint nullDataViewLeftConstraint = $0.left.equalTo(view).inset(10).constraint $0.width.height.equalTo(200)}
    1
    2

    移除对应约束,设置约束再激活约束
    nullDataViewLeftConstraint?.deactivate()nullDataViewLeftConstraint?.update(inset: 100)nullDataViewLeftConstraint?.activate()UIView.animate(withDuration: 1.5) { self.view.layoutIfNeeded()}
    1
    2
    3
    4

    - 更新约束

    - 添加约束
    nullDataImageView.snp.makeConstraints { nullDataViewTopConstraint = $0.top.equalTo(view).offset(64).constraint $0.centerX.equalTo(view) $0.width.height.equalTo(200)}
    1
    2

    - 更新约束
    nullDataViewTopConstraint?.update(inset: 84)UIView.animate(withDuration: 0.5, delay: 0, options: .curveEaseOut, animations: { self.view.layoutIfNeeded()}) { (finished) in print("动画执行完毕")}
    1
    2

    - 添加`debug`模式中的`key`使用`labeled()`方法
    nullDataImageView.snp.makeConstraints { nullDataViewTopConstraint = $0.top.equalTo(view).offset(80).constraint nullDataViewLeftConstraint = $0.left.equalTo(view).inset(10).constraint $0.width.height.equalTo(200).labeled("width和height约束")}```
    • 其他注意点都和Masnory相同
    ]]>
    @@ -510,19 +523,6 @@ - - - - /2019/06/18/MasonrySnapkit%E4%BD%BF%E7%94%A8%E6%80%BB%E7%BB%93/ - - Masonry使用

    基本使用官网Demo非常详细,以下总结的是常见问题和技巧

    安装遇到的问题

    • pod 安装后出现⚠️,项目运行报错

      1
      [UIView mas_makeConstraints:]: unrecognized selector sent to instance

      github

      解决方法

    • 使用lastBaseline属性crash

      解决办法

    使用时注意点

    • edgesForExtendedLayout 方法

      重写此方法的目的是为了确定布局位置从什么位置开始什么位置结束,

      1
      2
      3
      - (UIRectEdge)edgesForExtendedLayout {
      return UIRectEdgeNone;
      }
    **重写后**![](https://ws1.sinaimg.cn/large/006tNc79ly1fsgcn3fmo0j30s61iyalk.jpg)**重写前**![](https://ws4.sinaimg.cn/large/006tNc79ly1fsgcokvpdwj30s61iy7eq.jpg)
    • translucent 设置UINavigationBar是否半透明, 如果设置了导航栏的styleUIBarStyleBlackTranslucent 就为true

      设置为false不透明效果

    • extendedLayoutIncludesOpaqueBars 导航栏不透明条件下是否可以扩展,默认是NO不可以扩展

      1
      2
      self.navigationController?.navigationBar.isTranslucent = false
      self.extendedLayoutIncludesOpaqueBars = true
    **设置不透明,设置可以扩展此时从坐标的(0,0)点开始布局**![](https://ws2.sinaimg.cn/large/006tNc79ly1fsggem13mjj30ow0j83zy.jpg)    
    • inset 的使用,当约束控件的属性相同时不用再考虑正负值的问题,例如要约束一个控件左边距离另一控件的右边位置时还需要使用负值情况

      1
      2
      3
      4
      5
      6
      7
      [yellowdView makeConstraints:^(MASConstraintMaker *make) {
      make.height.equalTo(@150);
      make.top.equalTo(self).inset(220);
      make.leading.equalTo(self).inset(10);
      make.trailing.equalTo(greenView.leading).inset(10);
      make.width.equalTo(greenView);
      }];
    • edges的使用,约束边距更方便

      1
      2
      3
      [edgeView remakeConstraints:^(MASConstraintMaker *make) {
      make.edges.equalTo(lasView).insets(UIEdgeInsetsMake(10, 5, 5, 5));
      }];
    • Priority 设置约束的优先级

      • priorityHigh
      • priorityMedium
      • priorityLow
    • makeConstraintsupdateConstraintsremakeConstraints三者区别

      • makeConstraints 添加约束
      • updateConstraints 更新约束,更新之前会查找一边控件已经存在的约束,没有就添加,有就更新到最新的约束
      • remakeConstraints删除控件以前的所有约束重新添加约束
    • 设置控件或者某一约束的’key’ 方便冲突时⚠️查找

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      greenView.mas_key = @"greenView";
      [greenView makeConstraints:^(MASConstraintMaker *make) {
      make.height.equalTo(yellowdView).key(@"greenView-height");
      make.top.equalTo(yellowdView).key(@"greenView-top");
      make.width.equalTo(yellowdView).key(@"greenView-width");
      make.trailing.equalTo(self).inset(10).key(@"greenView-trailing");
      // 冲突约束
      // make.leading.equalTo(greenView.trailing).inset(10).key(@"greenView-leading");
      }];
      ```

      - 使用 `MASAttachKeys`批量给`view`设置key

      MASAttachKeys(greenView, yellowdView);

      1
      2
      3
      4

      ![](https://ws3.sinaimg.cn/large/006tNc79ly1fsgalvd4l6j31a20modnp.jpg)

      - `dividedBy`和 `multipliedBy` 约束宽高比

      [topInnerView makeConstraints:^(MASConstraintMaker *make) {

      // 设置自己的宽高比是3:1make.width.equalTo( topInnerView.height).multipliedBy(3); // 乘因数// 设置宽高并且设置他们的优先级最低make.width.height.lessThanOrEqualTo(topView);make.width.height.equalTo(topView).priorityLow();// 设置位置make.top.equalTo(topView);

      }];

      1
      - 多个控件批量约束

      NSValue sizeValue = [NSValue valueWithCGSize:CGSizeMake(100, 100)];
      [@[blueView, redView, yellowView] makeConstraints:^(MASConstraintMaker
      make) {

      make.size.equalTo(sizeValue);

      }];

      [@[blueView, redView, yellowView] mas_makeConstraints:^(MASConstraintMaker *make) {

      make.top.equalTo(self).inset(20);

      }];

      [blueView makeConstraints:^(MASConstraintMaker *make) {

      make.left.equalTo(self).inset(20);

      }];

      [redView makeConstraints:^(MASConstraintMaker *make) {

      make.left.equalTo(blueView.right).inset(10);

      }];

      [yellowView makeConstraints:^(MASConstraintMaker *make) {

      make.left.equalTo(redView.right).inset(10);

      }];

      1
      2
      3
      4

      ![批量约束](https://ws1.sinaimg.cn/large/006tNc79ly1fsgaz670hvj30s61iyn2l.jpg)

      **以上代码也可以简化为**

      [redView makeConstraints:^(MASConstraintMaker *make) {

      make.left.equalTo(blueView.right).inset(10);

      }];

      [yellowView makeConstraints:^(MASConstraintMaker *make) {

      make.left.equalTo(redView.right).inset(10);

      }];

    [blueView makeConstraints:^(MASConstraintMaker *make) {    make.top.left.equalTo(self);    make.size.equalTo(@[redView, yellowView, sizeValue]);}];
    1
    2
    3
    4
    5
    ![](https://ws2.sinaimg.cn/large/006tNc79ly1fsgbhz8lw5j30s61iyte5.jpg)

    ## Snapkit 使用

    - Swift 中`edgesForExtendedLayout:UIRectEdge` 扩展布局的边缘是一个属性,默认是`All`
    self.edgesForExtendedLayout = []
    1
    2
    3
    4

    在Swift中闭包参数可以使用$0、$1、$2 一次代表第一个,第二个,第三个参数

    - 使用$0 来代替闭包参数
    nullDataImageView.snp.makeConstraints { $0.top.equalTo(view).offset(44) $0.centerX.equalTo(view) $0.width.height.equalTo(200)}
    1
    2
    3
    4
    5
    6
    7

    - `deactivate` 和 `activate` 使用

    - `activate` 激活指定的约束
    - `deactivate` 移除指定的约束

    添加约束
    nullDataImageView.snp.makeConstraints { nullDataViewTopConstraint = $0.top.equalTo(view).offset(80).constraint nullDataViewLeftConstraint = $0.left.equalTo(view).inset(10).constraint $0.width.height.equalTo(200)}
    1
    2

    移除对应约束,设置约束再激活约束
    nullDataViewLeftConstraint?.deactivate()nullDataViewLeftConstraint?.update(inset: 100)nullDataViewLeftConstraint?.activate()UIView.animate(withDuration: 1.5) { self.view.layoutIfNeeded()}
    1
    2
    3
    4

    - 更新约束

    - 添加约束
    nullDataImageView.snp.makeConstraints { nullDataViewTopConstraint = $0.top.equalTo(view).offset(64).constraint $0.centerX.equalTo(view) $0.width.height.equalTo(200)}
    1
    2

    - 更新约束
    nullDataViewTopConstraint?.update(inset: 84)UIView.animate(withDuration: 0.5, delay: 0, options: .curveEaseOut, animations: { self.view.layoutIfNeeded()}) { (finished) in print("动画执行完毕")}
    1
    2

    - 添加`debug`模式中的`key`使用`labeled()`方法
    nullDataImageView.snp.makeConstraints { nullDataViewTopConstraint = $0.top.equalTo(view).offset(80).constraint nullDataViewLeftConstraint = $0.left.equalTo(view).inset(10).constraint $0.width.height.equalTo(200).labeled("width和height约束")}```
    • 其他注意点都和Masnory相同
    ]]>
    - - - -
    - - - @@ -538,10 +538,10 @@ - - /2019/06/18/Charles%20%E6%8A%93%E5%8C%85/ + + /2019/06/18/ES6%E8%AF%AD%E6%B3%95/ - Charles 抓包

    原理

    简单理解如下:

    • 正常请求

      客户端 ——> 服务器

    • Charles 抓包

      客户端 ——> 代理 (Charles)——> 服务器

    安装

    Charles官网

    破解版本请自行百度

    重要说明安装之前请暂时关闭电脑的VPN

    重要说明安装之前请暂时关闭电脑的VPN

    重要说明安装之前请暂时关闭电脑的VPN

    启动Charles

    安装Charles证书

    如果安装失败,请检查以前是否已经安装过,如果已经安装过并且已经失效,请删除失效证书后再安装

    打开钥匙串–> 选择Charles CA -> 双击证书 –> 信任证书

    设置 https

    - Proxy -> SSL Proxying Settings -> SSL Proxying -> Add- Host: * 为需要过滤的域名地址,- *: 表示不过滤Port, 固定为443 `*`表示任意端口

    Mac 抓包

    • Proxy -> macOS Proxy (☑️)

    真机抓包

    此时可以将 macOS Proxy 选项中的 ✅ 去掉,

    Mac与iPhone连接必须同一网络

    设置代理端口号

    手机安装证书

    • 安装手机证书

    • 安装完成后弹窗

    • 查看mac 地址,为手机设置代理

      1. 根据安装手机证书后的弹窗显示IP配置

      2. Charles –> Help –> Loca IP Address

      3. 打开电脑网络设置查看

    • 设置代理

      第一步

      第二步

      第三步

    • 根据弹窗提示,在手机浏览器下载并安装证书

    • 手机信任证书

      • 设置 –> 通用 –> 关于本机 –> 证书信任设置

    模拟器抓包

    • 安装模拟器证书

    • 勾选 macOS Proxy 选项

    如果失败,请先关闭模拟器,重新启动Charles 再打开模拟器

    使用浏览器打开网页提示不是私密链接

    解决办法

    ]]>
    + JS ES6 语法

    1. let var 变量,作用域

    var a = 10;if (a > 10) {    let b = 5; // let 只能在代码块内被访问    console.log(b);}// 访问 b 报错console.log(b);console.log(a);

    2. const 只能分配一次值

    {
    const const_a = 1;
    console.log(const_a)

    // 报错const_a = 2;// 声明一个常量数组const const_array = [];const_array.push(1);const_array.push(2);// 再次赋值时会报错const_array = [3];

    }

    3. 解构语法

    {
    // 1.解构数组
    function getArray() {
    return [10, 20, 30];
    }

    let [a,b,c] = getArray()console.log(a, b, c)// 函数返回一个对象function getObj() {    return {dessert: '面包', drink: '水', play: 'Game'};}// 2.解构对象let {des: dessert,  dri: drink, pla: play} = getObj()console.log(des, dri, pla);

    }

    4. 模版字符串

    {
    let name = ‘姓名:’, password = ‘密码:’
    let str_4 = name + ‘李老师’ + ‘—-‘ + password + ‘北京太热。。。’
    console.log(str_4)

    // 使用 `${}` 包裹会生成字符串let str_41 = `${name}李老师----${password}北京太热`console.log(str_41)// 多行显示时直接回车换行let str_42 = `${name}李老师----${password}北京太热`console.log(str_42)

    }

    5. 带标签的模版字符串

    {
    let name = ‘姓名:’, password = ‘密码:’
    // 多行显示时直接回车换行
    let str_5 = strTest${name}李老师 ---- ${password}北京太热
    console.log(str_5)

    // strings 字符中包含的被插值分割的子字符串已数组形式展现// values 字符串中的插值数组function strTest(strings, ...values) {    console.log(strings)    console.log(values)}function strTest1(strings, ...values) {    let result = '';    for (let i = 0; i < values.length; i++) {        result += strings[i]        result += values[i]    }    return result}

    }

    6. 判断字符串是否包含其他字符

    {
    let name = ‘姓名:’, password = ‘密码:’
    let str_6 = ${name}李老师---${password}北京真热
    console.log(str_6.startsWith(${name}))
    console.log(str_6.endsWith(${name}))
    console.log(str_6.includes(‘李老师’))
    }

    7. 函数默认参数

    {
    function funcDefaultParame(param1 = ‘a’, param2 = ‘b’, param3 = ‘c’) {
    return ${param1}--${param2}--${param3}
    }
    console.log(funcDefaultParame(1, 2,3))
    }

    8. “…”展开操作符

    {
    let a = [1,2,3];
    console.log(a);
    console.log(…a)

    let b = [...a, 4, 5];console.log(a);console.log(b)

    }

    9. ‘… ‘剩余操作符, 一般用在函数参数, 可以看作是一个可以展开的参数

    //  ... param3表示函数可以接收除了param1, param2,两个参数外,多个参数,都会放在param3中function operator(param1, param2, ...param3) {    console.log(param1, param2, param3)}operator('a','b','c','d','e','f','g')

    10. 解构对象解构函数

    // 当函数参数是对象是可以按照对象解构function getObjcValue(param1, param2, {value1, value2} = {}) {    console.log(param1, param2, value1, value2)}getObjcValue(1,2,{value1: -3,value2: -4})

    11. 获取函数名称

    function getObjcValue(param1, param2, {value1, value2} = {}) {    console.log(param1, param2, value1, value2)}console.log(getObjcValue.name)let funName = function getfunctionName() {  }console.log(funName.name)

    12. 箭头函数

    // 原来普通写法var funType = function arrowfunction(param) {    return param}// 函数参数 => 函数返回值// '=>' 的左边表示箭头函数的参数,多个时表示为 (param1, param2, param3)// '=>' 的右边表示箭头函数的返回值,如果返回值是一个对象时用 { } 表示let arrowFunction = (param1, param2) => {    return `${param1}${param2}`

    }

    13

    ]]>
    @@ -551,10 +551,10 @@ - - /2019/06/18/ES6%E8%AF%AD%E6%B3%95/ + + /2019/06/18/Charles%20%E6%8A%93%E5%8C%85/ - JS ES6 语法

    1. let var 变量,作用域

    var a = 10;if (a > 10) {    let b = 5; // let 只能在代码块内被访问    console.log(b);}// 访问 b 报错console.log(b);console.log(a);

    2. const 只能分配一次值

    {
    const const_a = 1;
    console.log(const_a)

    // 报错const_a = 2;// 声明一个常量数组const const_array = [];const_array.push(1);const_array.push(2);// 再次赋值时会报错const_array = [3];

    }

    3. 解构语法

    {
    // 1.解构数组
    function getArray() {
    return [10, 20, 30];
    }

    let [a,b,c] = getArray()console.log(a, b, c)// 函数返回一个对象function getObj() {    return {dessert: '面包', drink: '水', play: 'Game'};}// 2.解构对象let {des: dessert,  dri: drink, pla: play} = getObj()console.log(des, dri, pla);

    }

    4. 模版字符串

    {
    let name = ‘姓名:’, password = ‘密码:’
    let str_4 = name + ‘李老师’ + ‘—-‘ + password + ‘北京太热。。。’
    console.log(str_4)

    // 使用 `${}` 包裹会生成字符串let str_41 = `${name}李老师----${password}北京太热`console.log(str_41)// 多行显示时直接回车换行let str_42 = `${name}李老师----${password}北京太热`console.log(str_42)

    }

    5. 带标签的模版字符串

    {
    let name = ‘姓名:’, password = ‘密码:’
    // 多行显示时直接回车换行
    let str_5 = strTest${name}李老师 ---- ${password}北京太热
    console.log(str_5)

    // strings 字符中包含的被插值分割的子字符串已数组形式展现// values 字符串中的插值数组function strTest(strings, ...values) {    console.log(strings)    console.log(values)}function strTest1(strings, ...values) {    let result = '';    for (let i = 0; i < values.length; i++) {        result += strings[i]        result += values[i]    }    return result}

    }

    6. 判断字符串是否包含其他字符

    {
    let name = ‘姓名:’, password = ‘密码:’
    let str_6 = ${name}李老师---${password}北京真热
    console.log(str_6.startsWith(${name}))
    console.log(str_6.endsWith(${name}))
    console.log(str_6.includes(‘李老师’))
    }

    7. 函数默认参数

    {
    function funcDefaultParame(param1 = ‘a’, param2 = ‘b’, param3 = ‘c’) {
    return ${param1}--${param2}--${param3}
    }
    console.log(funcDefaultParame(1, 2,3))
    }

    8. “…”展开操作符

    {
    let a = [1,2,3];
    console.log(a);
    console.log(…a)

    let b = [...a, 4, 5];console.log(a);console.log(b)

    }

    9. ‘… ‘剩余操作符, 一般用在函数参数, 可以看作是一个可以展开的参数

    //  ... param3表示函数可以接收除了param1, param2,两个参数外,多个参数,都会放在param3中function operator(param1, param2, ...param3) {    console.log(param1, param2, param3)}operator('a','b','c','d','e','f','g')

    10. 解构对象解构函数

    // 当函数参数是对象是可以按照对象解构function getObjcValue(param1, param2, {value1, value2} = {}) {    console.log(param1, param2, value1, value2)}getObjcValue(1,2,{value1: -3,value2: -4})

    11. 获取函数名称

    function getObjcValue(param1, param2, {value1, value2} = {}) {    console.log(param1, param2, value1, value2)}console.log(getObjcValue.name)let funName = function getfunctionName() {  }console.log(funName.name)

    12. 箭头函数

    // 原来普通写法var funType = function arrowfunction(param) {    return param}// 函数参数 => 函数返回值// '=>' 的左边表示箭头函数的参数,多个时表示为 (param1, param2, param3)// '=>' 的右边表示箭头函数的返回值,如果返回值是一个对象时用 { } 表示let arrowFunction = (param1, param2) => {    return `${param1}${param2}`

    }

    13

    ]]>
    + Charles 抓包

    原理

    简单理解如下:

    • 正常请求

      客户端 ——> 服务器

    • Charles 抓包

      客户端 ——> 代理 (Charles)——> 服务器

    安装

    Charles官网

    破解版本请自行百度

    重要说明安装之前请暂时关闭电脑的VPN

    重要说明安装之前请暂时关闭电脑的VPN

    重要说明安装之前请暂时关闭电脑的VPN

    启动Charles

    安装Charles证书

    如果安装失败,请检查以前是否已经安装过,如果已经安装过并且已经失效,请删除失效证书后再安装

    打开钥匙串–> 选择Charles CA -> 双击证书 –> 信任证书

    设置 https

    - Proxy -> SSL Proxying Settings -> SSL Proxying -> Add- Host: * 为需要过滤的域名地址,- *: 表示不过滤Port, 固定为443 `*`表示任意端口

    Mac 抓包

    • Proxy -> macOS Proxy (☑️)

    真机抓包

    此时可以将 macOS Proxy 选项中的 ✅ 去掉,

    Mac与iPhone连接必须同一网络

    设置代理端口号

    手机安装证书

    • 安装手机证书

    • 安装完成后弹窗

    • 查看mac 地址,为手机设置代理

      1. 根据安装手机证书后的弹窗显示IP配置

      2. Charles –> Help –> Loca IP Address

      3. 打开电脑网络设置查看

    • 设置代理

      第一步

      第二步

      第三步

    • 根据弹窗提示,在手机浏览器下载并安装证书

    • 手机信任证书

      • 设置 –> 通用 –> 关于本机 –> 证书信任设置

    模拟器抓包

    • 安装模拟器证书

    • 勾选 macOS Proxy 选项

    如果失败,请先关闭模拟器,重新启动Charles 再打开模拟器

    使用浏览器打开网页提示不是私密链接

    解决办法

    ]]>
    diff --git a/tags/AES-Base64-MD5/index.html b/tags/AES-Base64-MD5/index.html index c5ccc22..aff55d5 100644 --- a/tags/AES-Base64-MD5/index.html +++ b/tags/AES-Base64-MD5/index.html @@ -138,23 +138,23 @@

    Recent Posts

    diff --git a/tags/Alamofire/index.html b/tags/Alamofire/index.html index 915fa3a..ab00161 100644 --- a/tags/Alamofire/index.html +++ b/tags/Alamofire/index.html @@ -138,23 +138,23 @@

    Recent Posts

    diff --git a/tags/AlertView/index.html b/tags/AlertView/index.html index 62959cf..c09e8aa 100644 --- a/tags/AlertView/index.html +++ b/tags/AlertView/index.html @@ -138,23 +138,23 @@

    Recent Posts

    diff --git a/tags/GCD/index.html b/tags/GCD/index.html index 50d132e..89b2132 100644 --- a/tags/GCD/index.html +++ b/tags/GCD/index.html @@ -138,23 +138,23 @@

    Recent Posts

    diff --git a/tags/Hexo/index.html b/tags/Hexo/index.html index 9dcaf01..f65424f 100644 --- a/tags/Hexo/index.html +++ b/tags/Hexo/index.html @@ -138,23 +138,23 @@

    Recent Posts

    diff --git a/tags/OC-JS/index.html b/tags/OC-JS/index.html index 4fbb65f..3481ec9 100644 --- a/tags/OC-JS/index.html +++ b/tags/OC-JS/index.html @@ -138,23 +138,23 @@

    Recent Posts

    diff --git a/tags/Quartz-2D/index.html b/tags/Quartz-2D/index.html index cf476ce..fd482b3 100644 --- a/tags/Quartz-2D/index.html +++ b/tags/Quartz-2D/index.html @@ -138,23 +138,23 @@

    Recent Posts

    diff --git a/tags/Quartz2D/index.html b/tags/Quartz2D/index.html index 3194d8d..718006b 100644 --- a/tags/Quartz2D/index.html +++ b/tags/Quartz2D/index.html @@ -157,23 +157,23 @@

    Recent Posts

    diff --git a/tags/RunTime/index.html b/tags/RunTime/index.html index 0222f65..d2e2827 100644 --- a/tags/RunTime/index.html +++ b/tags/RunTime/index.html @@ -138,23 +138,23 @@

    Recent Posts

    diff --git a/tags/SDWebImage/index.html b/tags/SDWebImage/index.html index a52c885..e67dbd3 100644 --- a/tags/SDWebImage/index.html +++ b/tags/SDWebImage/index.html @@ -138,23 +138,23 @@

    Recent Posts

    diff --git a/tags/SQL/index.html b/tags/SQL/index.html index 73f77ac..0556277 100644 --- a/tags/SQL/index.html +++ b/tags/SQL/index.html @@ -138,23 +138,23 @@

    Recent Posts

    diff --git a/tags/Swift/index.html b/tags/Swift/index.html index c538aa0..283bc81 100644 --- a/tags/Swift/index.html +++ b/tags/Swift/index.html @@ -5,11 +5,11 @@ - Tag: swift | aTreey's Blog + Tag: Swift | aTreey's Blog - + @@ -76,32 +76,13 @@

    - - - -
    -
    -
    - - - -

    - Swift3.0学习(二) + Swift 3.0 学习

    @@ -157,23 +138,23 @@

    Recent Posts

    @@ -203,6 +184,33 @@

    Recent Posts

    + + + + + + + + + + + +
    + +o.io/" target="_blank">Hexo + + + + + + + diff --git a/tags/TableView/index.html b/tags/TableView/index.html index e07b0b3..3940f60 100644 --- a/tags/TableView/index.html +++ b/tags/TableView/index.html @@ -138,23 +138,23 @@

    Recent Posts

    diff --git a/tags/WKWebView/index.html b/tags/WKWebView/index.html index 47fcff6..dd92ada 100644 --- a/tags/WKWebView/index.html +++ b/tags/WKWebView/index.html @@ -224,23 +224,23 @@

    Recent Posts

    diff --git a/tags/git/index.html b/tags/git/index.html index 2a1c709..58fb8fb 100644 --- a/tags/git/index.html +++ b/tags/git/index.html @@ -138,23 +138,23 @@

    Recent Posts

    diff --git "a/tags/\345\274\202\346\255\245/index.html" "b/tags/\345\274\202\346\255\245/index.html" index 2b44402..923dfb4 100644 --- "a/tags/\345\274\202\346\255\245/index.html" +++ "b/tags/\345\274\202\346\255\245/index.html" @@ -138,23 +138,23 @@

    Recent Posts

    diff --git "a/tags/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" "b/tags/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" index 64d7bee..85eff32 100644 --- "a/tags/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" +++ "b/tags/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" @@ -138,23 +138,23 @@

    Recent Posts

    From 843bbd1e939008367f19fd41f6edcebbd0d20f3d Mon Sep 17 00:00:00 2001 From: YuHongpeng <480814177@qq.com> Date: Tue, 18 Jun 2019 22:51:47 +0800 Subject: [PATCH 06/18] Site updated: 2019-06-18 22:51:47 --- .../index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 8 +- .../ES6\350\257\255\346\263\225/index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 10 +- .../index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 8 +- .../18/RN\346\212\245\351\224\231/index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 10 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 8 +- .../index.html" | 10 +- .../index.html" | 8 +- .../index.html" | 8 +- .../Swift\346\263\233\345\236\213/index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 10 +- .../index.html" | 8 +- .../index.html" | 8 +- "2019/06/18/npm\345\222\214cnpm/index.html" | 8 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 10 +- .../index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 10 +- .../index.html" | 10 +- .../index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 10 +- archives/2016/10/index.html | 6 +- archives/2016/11/index.html | 6 +- archives/2016/12/index.html | 6 +- archives/2016/index.html | 6 +- archives/2016/page/2/index.html | 6 +- archives/2017/01/index.html | 6 +- archives/2017/02/index.html | 6 +- archives/2017/03/index.html | 6 +- archives/2017/04/index.html | 6 +- archives/2017/11/index.html | 6 +- archives/2017/12/index.html | 6 +- archives/2017/index.html | 6 +- archives/2017/page/2/index.html | 6 +- archives/2018/01/index.html | 6 +- archives/2018/02/index.html | 6 +- archives/2018/index.html | 6 +- archives/2019/06/index.html | 14 +- archives/2019/06/page/2/index.html | 10 +- archives/2019/06/page/3/index.html | 10 +- archives/2019/06/page/4/index.html | 10 +- archives/2019/06/page/5/index.html | 6 +- archives/2019/index.html | 14 +- archives/2019/page/2/index.html | 10 +- archives/2019/page/3/index.html | 10 +- archives/2019/page/4/index.html | 10 +- archives/2019/page/5/index.html | 6 +- archives/index.html | 14 +- archives/page/2/index.html | 10 +- archives/page/3/index.html | 10 +- archives/page/4/index.html | 10 +- archives/page/5/index.html | 6 +- archives/page/6/index.html | 6 +- archives/page/7/index.html | 6 +- archives/page/8/index.html | 6 +- index.html | 166 ++++++------- page/2/index.html | 228 +++++++++--------- page/3/index.html | 108 ++++----- page/4/index.html | 174 ++++++------- page/5/index.html | 26 +- page/6/index.html | 26 +- page/7/index.html | 26 +- page/8/index.html | 20 +- search.xml | 60 ++--- tags/AES-Base64-MD5/index.html | 6 +- tags/Alamofire/index.html | 6 +- tags/AlertView/index.html | 6 +- tags/GCD/index.html | 6 +- tags/Hexo/index.html | 6 +- tags/OC-JS/index.html | 6 +- tags/Quartz-2D/index.html | 6 +- tags/Quartz2D/index.html | 6 +- tags/RunTime/index.html | 6 +- tags/SDWebImage/index.html | 6 +- tags/SQL/index.html | 6 +- tags/Swift/index.html | 62 +++-- tags/TableView/index.html | 6 +- tags/WKWebView/index.html | 6 +- tags/git/index.html | 6 +- "tags/\345\274\202\346\255\245/index.html" | 6 +- .../index.html" | 6 +- 137 files changed, 962 insertions(+), 970 deletions(-) diff --git "a/2016/10/13/SQlite\344\275\277\347\224\250\345\255\246\344\271\240/index.html" "b/2016/10/13/SQlite\344\275\277\347\224\250\345\255\246\344\271\240/index.html" index 8b89a5c..3d5f720 100644 --- "a/2016/10/13/SQlite\344\275\277\347\224\250\345\255\246\344\271\240/index.html" +++ "b/2016/10/13/SQlite\344\275\277\347\224\250\345\255\246\344\271\240/index.html" @@ -200,7 +200,7 @@

    - Share + Share @@ -273,11 +273,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -285,7 +285,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2016/10/20/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" "b/2016/10/20/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" index f91e33d..47e4a94 100644 --- "a/2016/10/20/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" +++ "b/2016/10/20/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" @@ -94,7 +94,7 @@

    - Share + Share @@ -172,11 +172,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -184,7 +184,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2016/10/27/iOS\345\274\271\346\241\206\345\274\217\345\233\276/index.html" "b/2016/10/27/iOS\345\274\271\346\241\206\345\274\217\345\233\276/index.html" index 3452bfa..3d8227b 100644 --- "a/2016/10/27/iOS\345\274\271\346\241\206\345\274\217\345\233\276/index.html" +++ "b/2016/10/27/iOS\345\274\271\346\241\206\345\274\217\345\233\276/index.html" @@ -112,7 +112,7 @@

    - Share + Share @@ -190,11 +190,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -202,7 +202,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2016/11/05/Hexo\344\270\252\344\272\272\345\215\232\345\256\242\346\220\255\345\273\272/index.html" "b/2016/11/05/Hexo\344\270\252\344\272\272\345\215\232\345\256\242\346\220\255\345\273\272/index.html" index 15bc7d0..7d576d2 100644 --- "a/2016/11/05/Hexo\344\270\252\344\272\272\345\215\232\345\256\242\346\220\255\345\273\272/index.html" +++ "b/2016/11/05/Hexo\344\270\252\344\272\272\345\215\232\345\256\242\346\220\255\345\273\272/index.html" @@ -170,7 +170,7 @@

    - Share + Share @@ -248,11 +248,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -260,7 +260,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2016/11/05/TableView\344\275\277\347\224\250\346\200\273\347\273\223/index.html" "b/2016/11/05/TableView\344\275\277\347\224\250\346\200\273\347\273\223/index.html" index 86e35fc..09b14c1 100644 --- "a/2016/11/05/TableView\344\275\277\347\224\250\346\200\273\347\273\223/index.html" +++ "b/2016/11/05/TableView\344\275\277\347\224\250\346\200\273\347\273\223/index.html" @@ -157,7 +157,7 @@

    - Share + Share @@ -235,11 +235,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -247,7 +247,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2016/11/20/git\345\255\246\344\271\240\346\200\273\347\273\223/index.html" "b/2016/11/20/git\345\255\246\344\271\240\346\200\273\347\273\223/index.html" index 03b3830..3535a88 100644 --- "a/2016/11/20/git\345\255\246\344\271\240\346\200\273\347\273\223/index.html" +++ "b/2016/11/20/git\345\255\246\344\271\240\346\200\273\347\273\223/index.html" @@ -154,7 +154,7 @@

    - Share + Share @@ -232,11 +232,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -244,7 +244,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2016/11/22/OC RunTime\345\255\246\344\271\240/index.html" "b/2016/11/22/OC RunTime\345\255\246\344\271\240/index.html" index 49353d9..3a9bd8f 100644 --- "a/2016/11/22/OC RunTime\345\255\246\344\271\240/index.html" +++ "b/2016/11/22/OC RunTime\345\255\246\344\271\240/index.html" @@ -105,7 +105,7 @@

    - Share + Share @@ -183,11 +183,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -195,7 +195,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2016/11/22/Quartz2D\347\273\230\345\210\266\347\272\277\346\235\241/index.html" "b/2016/11/22/Quartz2D\347\273\230\345\210\266\347\272\277\346\235\241/index.html" index a5fb7df..129a12c 100644 --- "a/2016/11/22/Quartz2D\347\273\230\345\210\266\347\272\277\346\235\241/index.html" +++ "b/2016/11/22/Quartz2D\347\273\230\345\210\266\347\272\277\346\235\241/index.html" @@ -142,7 +142,7 @@

    - Share + Share @@ -220,11 +220,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -232,7 +232,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2016/11/22/Swift3.0\345\255\246\344\271\240(\344\272\224)/index.html" "b/2016/11/22/Swift3.0\345\255\246\344\271\240(\344\272\224)/index.html" index 03ce9c4..a8124f8 100644 --- "a/2016/11/22/Swift3.0\345\255\246\344\271\240(\344\272\224)/index.html" +++ "b/2016/11/22/Swift3.0\345\255\246\344\271\240(\344\272\224)/index.html" @@ -142,7 +142,7 @@

    - Share + Share @@ -220,11 +220,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -232,7 +232,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2016/11/22/Swift3.0\345\255\246\344\271\240(\345\233\233)/index.html" "b/2016/11/22/Swift3.0\345\255\246\344\271\240(\345\233\233)/index.html" index 16d7f7f..48d3253 100644 --- "a/2016/11/22/Swift3.0\345\255\246\344\271\240(\345\233\233)/index.html" +++ "b/2016/11/22/Swift3.0\345\255\246\344\271\240(\345\233\233)/index.html" @@ -142,7 +142,7 @@

    - Share + Share @@ -220,11 +220,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -232,7 +232,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2016/11/22/WKWebiView\344\275\277\347\224\250/index.html" "b/2016/11/22/WKWebiView\344\275\277\347\224\250/index.html" index 41ad429..a2eefc0 100644 --- "a/2016/11/22/WKWebiView\344\275\277\347\224\250/index.html" +++ "b/2016/11/22/WKWebiView\344\275\277\347\224\250/index.html" @@ -142,7 +142,7 @@

    - Share + Share @@ -220,11 +220,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -232,7 +232,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2016/12/04/Quartz2D\345\210\235\350\257\206/index.html" "b/2016/12/04/Quartz2D\345\210\235\350\257\206/index.html" index c2adba2..7999b32 100644 --- "a/2016/12/04/Quartz2D\345\210\235\350\257\206/index.html" +++ "b/2016/12/04/Quartz2D\345\210\235\350\257\206/index.html" @@ -219,7 +219,7 @@

    - Share + Share @@ -297,11 +297,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -309,7 +309,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2016/12/04/Quartz2D\347\273\230\345\210\266\346\233\262\347\272\277/index.html" "b/2016/12/04/Quartz2D\347\273\230\345\210\266\346\233\262\347\272\277/index.html" index e7c257b..e59f71e 100644 --- "a/2016/12/04/Quartz2D\347\273\230\345\210\266\346\233\262\347\272\277/index.html" +++ "b/2016/12/04/Quartz2D\347\273\230\345\210\266\346\233\262\347\272\277/index.html" @@ -181,7 +181,7 @@

    - Share + Share @@ -259,11 +259,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -271,7 +271,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2016/12/05/GCD\345\256\236\347\216\260\346\214\211\351\222\256\345\256\232\346\227\266\344\272\256\350\265\267/index.html" "b/2016/12/05/GCD\345\256\236\347\216\260\346\214\211\351\222\256\345\256\232\346\227\266\344\272\256\350\265\267/index.html" index ac6e3cd..65cb0fe 100644 --- "a/2016/12/05/GCD\345\256\236\347\216\260\346\214\211\351\222\256\345\256\232\346\227\266\344\272\256\350\265\267/index.html" +++ "b/2016/12/05/GCD\345\256\236\347\216\260\346\214\211\351\222\256\345\256\232\346\227\266\344\272\256\350\265\267/index.html" @@ -138,7 +138,7 @@

    - Share + Share @@ -216,11 +216,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -228,7 +228,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2016/12/10/Quartz2D\347\273\230\345\210\266\345\275\242\347\212\266/index.html" "b/2016/12/10/Quartz2D\347\273\230\345\210\266\345\275\242\347\212\266/index.html" index 326b91a..de3a743 100644 --- "a/2016/12/10/Quartz2D\347\273\230\345\210\266\345\275\242\347\212\266/index.html" +++ "b/2016/12/10/Quartz2D\347\273\230\345\210\266\345\275\242\347\212\266/index.html" @@ -103,7 +103,7 @@

    - Share + Share @@ -181,11 +181,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -193,7 +193,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2017/01/18/\345\274\202\346\255\245\344\270\213\350\275\275\345\233\276\347\211\207/index.html" "b/2017/01/18/\345\274\202\346\255\245\344\270\213\350\275\275\345\233\276\347\211\207/index.html" index 618c0d5..7871430 100644 --- "a/2017/01/18/\345\274\202\346\255\245\344\270\213\350\275\275\345\233\276\347\211\207/index.html" +++ "b/2017/01/18/\345\274\202\346\255\245\344\270\213\350\275\275\345\233\276\347\211\207/index.html" @@ -139,7 +139,7 @@

    - Share + Share @@ -217,11 +217,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -229,7 +229,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2017/02/02/iOS\351\235\231\346\200\201\345\272\223\345\212\250\346\200\201\345\272\223/index.html" "b/2017/02/02/iOS\351\235\231\346\200\201\345\272\223\345\212\250\346\200\201\345\272\223/index.html" index 9749ac7..e28fb00 100644 --- "a/2017/02/02/iOS\351\235\231\346\200\201\345\272\223\345\212\250\346\200\201\345\272\223/index.html" +++ "b/2017/02/02/iOS\351\235\231\346\200\201\345\272\223\345\212\250\346\200\201\345\272\223/index.html" @@ -130,7 +130,7 @@

    - Share + Share @@ -208,11 +208,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -220,7 +220,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2017/02/08/SDWebImage\345\255\246\344\271\240/index.html" "b/2017/02/08/SDWebImage\345\255\246\344\271\240/index.html" index fef013d..cbc423c 100644 --- "a/2017/02/08/SDWebImage\345\255\246\344\271\240/index.html" +++ "b/2017/02/08/SDWebImage\345\255\246\344\271\240/index.html" @@ -164,7 +164,7 @@

    - Share + Share @@ -242,11 +242,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -254,7 +254,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2017/02/20/OC\344\270\216js\344\272\244\344\272\222\345\255\246\344\271\240/index.html" "b/2017/02/20/OC\344\270\216js\344\272\244\344\272\222\345\255\246\344\271\240/index.html" index a456806..c3bbbb8 100644 --- "a/2017/02/20/OC\344\270\216js\344\272\244\344\272\222\345\255\246\344\271\240/index.html" +++ "b/2017/02/20/OC\344\270\216js\344\272\244\344\272\222\345\255\246\344\271\240/index.html" @@ -102,7 +102,7 @@

    - Share + Share @@ -180,11 +180,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -192,7 +192,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2017/03/01/Swift3.0\345\255\246\344\271\240(\344\270\200)/index.html" "b/2017/03/01/Swift3.0\345\255\246\344\271\240(\344\270\200)/index.html" index cde88c6..3d052f4 100644 --- "a/2017/03/01/Swift3.0\345\255\246\344\271\240(\344\270\200)/index.html" +++ "b/2017/03/01/Swift3.0\345\255\246\344\271\240(\344\270\200)/index.html" @@ -160,7 +160,7 @@

    - Share + Share @@ -238,11 +238,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -250,7 +250,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2017/03/05/Swift3.0\345\255\246\344\271\240(\344\272\214)/index.html" "b/2017/03/05/Swift3.0\345\255\246\344\271\240(\344\272\214)/index.html" index 27795bb..80d48c4 100644 --- "a/2017/03/05/Swift3.0\345\255\246\344\271\240(\344\272\214)/index.html" +++ "b/2017/03/05/Swift3.0\345\255\246\344\271\240(\344\272\214)/index.html" @@ -216,7 +216,7 @@

    - Share + Share @@ -294,11 +294,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -306,7 +306,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2017/03/08/Swift3.0\345\255\246\344\271\240(\344\270\211)/index.html" "b/2017/03/08/Swift3.0\345\255\246\344\271\240(\344\270\211)/index.html" index aae1814..152fcf3 100644 --- "a/2017/03/08/Swift3.0\345\255\246\344\271\240(\344\270\211)/index.html" +++ "b/2017/03/08/Swift3.0\345\255\246\344\271\240(\344\270\211)/index.html" @@ -112,7 +112,7 @@

    - Share + Share @@ -190,11 +190,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -202,7 +202,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2017/03/25/\347\237\245\350\257\206\347\202\271\346\200\273\347\273\223/index.html" "b/2017/03/25/\347\237\245\350\257\206\347\202\271\346\200\273\347\273\223/index.html" index 1f13475..4cd1b3a 100644 --- "a/2017/03/25/\347\237\245\350\257\206\347\202\271\346\200\273\347\273\223/index.html" +++ "b/2017/03/25/\347\237\245\350\257\206\347\202\271\346\200\273\347\273\223/index.html" @@ -100,7 +100,7 @@

    @@ -176,11 +176,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -188,7 +188,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2017/04/10/Alamofire\346\272\220\347\240\201\345\255\246\344\271\240/index.html" "b/2017/04/10/Alamofire\346\272\220\347\240\201\345\255\246\344\271\240/index.html" index e452a2b..481fa77 100644 --- "a/2017/04/10/Alamofire\346\272\220\347\240\201\345\255\246\344\271\240/index.html" +++ "b/2017/04/10/Alamofire\346\272\220\347\240\201\345\255\246\344\271\240/index.html" @@ -109,7 +109,7 @@

    - Share + Share @@ -187,11 +187,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -199,7 +199,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2017/11/29/CocoaPods\345\210\266\344\275\234\347\247\201\346\234\211pod/index.html" "b/2017/11/29/CocoaPods\345\210\266\344\275\234\347\247\201\346\234\211pod/index.html" index 9ba7416..081f111 100644 --- "a/2017/11/29/CocoaPods\345\210\266\344\275\234\347\247\201\346\234\211pod/index.html" +++ "b/2017/11/29/CocoaPods\345\210\266\344\275\234\347\247\201\346\234\211pod/index.html" @@ -135,7 +135,7 @@

    - Share + Share

    @@ -211,11 +211,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -223,7 +223,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2017/12/02/\346\234\254\345\234\260\345\267\262\346\234\211\351\241\271\347\233\256\346\267\273\345\212\240Pod/index.html" "b/2017/12/02/\346\234\254\345\234\260\345\267\262\346\234\211\351\241\271\347\233\256\346\267\273\345\212\240Pod/index.html" index 29ae924..734c458 100644 --- "a/2017/12/02/\346\234\254\345\234\260\345\267\262\346\234\211\351\241\271\347\233\256\346\267\273\345\212\240Pod/index.html" +++ "b/2017/12/02/\346\234\254\345\234\260\345\267\262\346\234\211\351\241\271\347\233\256\346\267\273\345\212\240Pod/index.html" @@ -106,7 +106,7 @@

    @@ -182,11 +182,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -194,7 +194,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2017/12/14/Mac-\345\256\211\350\243\205MySQL/index.html" "b/2017/12/14/Mac-\345\256\211\350\243\205MySQL/index.html" index 720bd80..33d1246 100644 --- "a/2017/12/14/Mac-\345\256\211\350\243\205MySQL/index.html" +++ "b/2017/12/14/Mac-\345\256\211\350\243\205MySQL/index.html" @@ -116,7 +116,7 @@

    - Share + Share

    @@ -192,11 +192,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -204,7 +204,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2018/01/08/Swift\345\212\240\345\257\206\347\233\270\345\205\263/index.html" "b/2018/01/08/Swift\345\212\240\345\257\206\347\233\270\345\205\263/index.html" index 88ae974..d7c9a23 100644 --- "a/2018/01/08/Swift\345\212\240\345\257\206\347\233\270\345\205\263/index.html" +++ "b/2018/01/08/Swift\345\212\240\345\257\206\347\233\270\345\205\263/index.html" @@ -314,7 +314,7 @@

    - Share + Share @@ -392,11 +392,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -404,7 +404,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2018/01/10/\347\275\221\347\273\234\344\270\203\345\261\202\345\215\217\350\256\256/index.html" "b/2018/01/10/\347\275\221\347\273\234\344\270\203\345\261\202\345\215\217\350\256\256/index.html" index 884e360..ada661b 100644 --- "a/2018/01/10/\347\275\221\347\273\234\344\270\203\345\261\202\345\215\217\350\256\256/index.html" +++ "b/2018/01/10/\347\275\221\347\273\234\344\270\203\345\261\202\345\215\217\350\256\256/index.html" @@ -128,7 +128,7 @@

    Socket @@ -204,11 +204,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -216,7 +216,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2018/01/12/iOS\345\215\263\346\227\266\351\200\232\350\256\257\345\256\236\347\216\260\344\272\214/index.html" "b/2018/01/12/iOS\345\215\263\346\227\266\351\200\232\350\256\257\345\256\236\347\216\260\344\272\214/index.html" index 6dd27ed..5715ae2 100644 --- "a/2018/01/12/iOS\345\215\263\346\227\266\351\200\232\350\256\257\345\256\236\347\216\260\344\272\214/index.html" +++ "b/2018/01/12/iOS\345\215\263\346\227\266\351\200\232\350\256\257\345\256\236\347\216\260\344\272\214/index.html" @@ -232,7 +232,7 @@

    - Share + Share

    @@ -308,11 +308,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -320,7 +320,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2018/01/17/\351\235\242\350\257\225\344\270\255\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223/index.html" "b/2018/01/17/\351\235\242\350\257\225\344\270\255\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223/index.html" index 4cc5d64..489533d 100644 --- "a/2018/01/17/\351\235\242\350\257\225\344\270\255\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223/index.html" +++ "b/2018/01/17/\351\235\242\350\257\225\344\270\255\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223/index.html" @@ -164,7 +164,7 @@

    Runlo @@ -240,11 +240,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -252,7 +252,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2018/01/18/\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223-\344\272\214/index.html" "b/2018/01/18/\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223-\344\272\214/index.html" index 97e9424..fa54203 100644 --- "a/2018/01/18/\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223-\344\272\214/index.html" +++ "b/2018/01/18/\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223-\344\272\214/index.html" @@ -130,7 +130,7 @@

    - Share + Share

    @@ -206,11 +206,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -218,7 +218,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2018/01/19/Block\345\255\246\344\271\240\346\216\242\347\251\266/index.html" "b/2018/01/19/Block\345\255\246\344\271\240\346\216\242\347\251\266/index.html" index ad5c10d..2d02a92 100644 --- "a/2018/01/19/Block\345\255\246\344\271\240\346\216\242\347\251\266/index.html" +++ "b/2018/01/19/Block\345\255\246\344\271\240\346\216\242\347\251\266/index.html" @@ -110,7 +110,7 @@

    - Share + Share

    @@ -186,11 +186,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -198,7 +198,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2018/02/03/Mac\345\256\211\350\243\205VirtualBox\350\277\236\346\216\245U\347\233\230/index.html" "b/2018/02/03/Mac\345\256\211\350\243\205VirtualBox\350\277\236\346\216\245U\347\233\230/index.html" index ce146f6..a9c5905 100644 --- "a/2018/02/03/Mac\345\256\211\350\243\205VirtualBox\350\277\236\346\216\245U\347\233\230/index.html" +++ "b/2018/02/03/Mac\345\256\211\350\243\205VirtualBox\350\277\236\346\216\245U\347\233\230/index.html" @@ -94,7 +94,7 @@

    @@ -170,11 +170,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -182,7 +182,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2019/06/18/Charles \346\212\223\345\214\205/index.html" "b/2019/06/18/Charles \346\212\223\345\214\205/index.html" index 4ceef3a..15bc07f 100644 --- "a/2019/06/18/Charles \346\212\223\345\214\205/index.html" +++ "b/2019/06/18/Charles \346\212\223\345\214\205/index.html" @@ -179,7 +179,7 @@

    - Share + Share

    @@ -255,11 +255,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -267,7 +267,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2019/06/18/ES6\350\257\255\346\263\225/index.html" "b/2019/06/18/ES6\350\257\255\346\263\225/index.html" index 07a5036..30bd656 100644 --- "a/2019/06/18/ES6\350\257\255\346\263\225/index.html" +++ "b/2019/06/18/ES6\350\257\255\346\263\225/index.html" @@ -180,7 +180,7 @@

    13

    @@ -256,11 +256,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -268,7 +268,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2019/06/18/Flutter\345\205\245\351\227\250/index.html" "b/2019/06/18/Flutter\345\205\245\351\227\250/index.html" index 380dcea..13100e9 100644 --- "a/2019/06/18/Flutter\345\205\245\351\227\250/index.html" +++ "b/2019/06/18/Flutter\345\205\245\351\227\250/index.html" @@ -82,7 +82,7 @@

    - Share + Share

    @@ -158,11 +158,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -170,7 +170,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2019/06/18/Flutter\345\270\270\347\224\250\347\273\204\344\273\266/index.html" "b/2019/06/18/Flutter\345\270\270\347\224\250\347\273\204\344\273\266/index.html" index 20d4151..fc4f823 100644 --- "a/2019/06/18/Flutter\345\270\270\347\224\250\347\273\204\344\273\266/index.html" +++ "b/2019/06/18/Flutter\345\270\270\347\224\250\347\273\204\344\273\266/index.html" @@ -114,7 +114,7 @@

    - Share + Share

    @@ -123,7 +123,7 @@

    - + Newer
    @@ -190,11 +190,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -202,7 +202,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2019/06/18/GCD \344\277\241\345\217\267\351\207\217/index.html" "b/2019/06/18/GCD \344\277\241\345\217\267\351\207\217/index.html" index c2fefaa..a125167 100644 --- "a/2019/06/18/GCD \344\277\241\345\217\267\351\207\217/index.html" +++ "b/2019/06/18/GCD \344\277\241\345\217\267\351\207\217/index.html" @@ -122,7 +122,7 @@

    - Share + Share

    @@ -198,11 +198,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -210,7 +210,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2019/06/18/JavaScript\345\237\272\347\241\200/index.html" "b/2019/06/18/JavaScript\345\237\272\347\241\200/index.html" index 031e4ae..049558d 100644 --- "a/2019/06/18/JavaScript\345\237\272\347\241\200/index.html" +++ "b/2019/06/18/JavaScript\345\237\272\347\241\200/index.html" @@ -130,7 +130,7 @@

    数组 @@ -206,11 +206,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -218,7 +218,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2019/06/18/MasonrySnapkit\344\275\277\347\224\250\346\200\273\347\273\223/index.html" "b/2019/06/18/MasonrySnapkit\344\275\277\347\224\250\346\200\273\347\273\223/index.html" index 9070704..09710e5 100644 --- "a/2019/06/18/MasonrySnapkit\344\275\277\347\224\250\346\200\273\347\273\223/index.html" +++ "b/2019/06/18/MasonrySnapkit\344\275\277\347\224\250\346\200\273\347\273\223/index.html" @@ -235,7 +235,7 @@

    @@ -311,11 +311,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -323,7 +323,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2019/06/18/RN\346\212\245\351\224\231/index.html" "b/2019/06/18/RN\346\212\245\351\224\231/index.html" index 764aa8c..e902e5a 100644 --- "a/2019/06/18/RN\346\212\245\351\224\231/index.html" +++ "b/2019/06/18/RN\346\212\245\351\224\231/index.html" @@ -144,7 +144,7 @@

    - Share + Share @@ -220,11 +220,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -232,7 +232,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2019/06/18/React-Native\345\274\200\345\217\221iOS\346\211\223\345\214\205/index.html" "b/2019/06/18/React-Native\345\274\200\345\217\221iOS\346\211\223\345\214\205/index.html" index 8527379..1ecaafa 100644 --- "a/2019/06/18/React-Native\345\274\200\345\217\221iOS\346\211\223\345\214\205/index.html" +++ "b/2019/06/18/React-Native\345\274\200\345\217\221iOS\346\211\223\345\214\205/index.html" @@ -125,7 +125,7 @@

    - Share + Share @@ -201,11 +201,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -213,7 +213,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2019/06/18/ReactNative\344\270\255ref\344\275\277\347\224\250/index.html" "b/2019/06/18/ReactNative\344\270\255ref\344\275\277\347\224\250/index.html" index 57bb509..6afeb1b 100644 --- "a/2019/06/18/ReactNative\344\270\255ref\344\275\277\347\224\250/index.html" +++ "b/2019/06/18/ReactNative\344\270\255ref\344\275\277\347\224\250/index.html" @@ -104,7 +104,7 @@

    - Share + Share @@ -113,7 +113,7 @@

    - + Newer
    @@ -180,11 +180,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -192,7 +192,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2019/06/18/Swift \345\215\217\350\256\256\344\275\277\347\224\250/index.html" "b/2019/06/18/Swift \345\215\217\350\256\256\344\275\277\347\224\250/index.html" index d4691f9..b5643bb 100644 --- "a/2019/06/18/Swift \345\215\217\350\256\256\344\275\277\347\224\250/index.html" +++ "b/2019/06/18/Swift \345\215\217\350\256\256\344\275\277\347\224\250/index.html" @@ -129,7 +129,7 @@

    - Share + Share @@ -138,7 +138,7 @@

    - + Newer
    @@ -148,7 +148,7 @@

    + Older
    @@ -205,11 +205,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -217,7 +217,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2019/06/18/Swift \345\237\272\347\241\200\347\237\245\350\257\206/index.html" "b/2019/06/18/Swift \345\237\272\347\241\200\347\237\245\350\257\206/index.html" index aa70bbb..d3ff38b 100644 --- "a/2019/06/18/Swift \345\237\272\347\241\200\347\237\245\350\257\206/index.html" +++ "b/2019/06/18/Swift \345\237\272\347\241\200\347\237\245\350\257\206/index.html" @@ -176,7 +176,7 @@

    - Share + Share @@ -185,7 +185,7 @@

    - + Newer
    @@ -195,7 +195,7 @@

    + Older
    @@ -252,11 +252,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -264,7 +264,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2019/06/18/SwiftLint\344\275\277\347\224\250/index.html" "b/2019/06/18/SwiftLint\344\275\277\347\224\250/index.html" index e360810..feb1811 100644 --- "a/2019/06/18/SwiftLint\344\275\277\347\224\250/index.html" +++ "b/2019/06/18/SwiftLint\344\275\277\347\224\250/index.html" @@ -82,7 +82,7 @@

    @@ -158,11 +158,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -170,7 +170,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2019/06/18/Swift\344\270\213\346\240\207\345\222\214\350\277\220\347\256\227\347\254\246\351\207\215\350\275\275/index.html" "b/2019/06/18/Swift\344\270\213\346\240\207\345\222\214\350\277\220\347\256\227\347\254\246\351\207\215\350\275\275/index.html" index ce860a0..d731d35 100644 --- "a/2019/06/18/Swift\344\270\213\346\240\207\345\222\214\350\277\220\347\256\227\347\254\246\351\207\215\350\275\275/index.html" +++ "b/2019/06/18/Swift\344\270\213\346\240\207\345\222\214\350\277\220\347\256\227\347\254\246\351\207\215\350\275\275/index.html" @@ -132,7 +132,7 @@

    @@ -151,7 +151,7 @@

    - + Older
    @@ -208,11 +208,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -220,7 +220,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2019/06/18/Swift\345\215\217\350\256\256\345\210\253\345\220\215/index.html" "b/2019/06/18/Swift\345\215\217\350\256\256\345\210\253\345\220\215/index.html" index cd3d818..929f2d0 100644 --- "a/2019/06/18/Swift\345\215\217\350\256\256\345\210\253\345\220\215/index.html" +++ "b/2019/06/18/Swift\345\215\217\350\256\256\345\210\253\345\220\215/index.html" @@ -216,7 +216,7 @@

    - Share + Share @@ -292,11 +292,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -304,7 +304,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2019/06/18/Swift\345\215\217\350\256\256\346\211\251\345\261\225/index.html" "b/2019/06/18/Swift\345\215\217\350\256\256\346\211\251\345\261\225/index.html" index 6041d1e..f6d00df 100644 --- "a/2019/06/18/Swift\345\215\217\350\256\256\346\211\251\345\261\225/index.html" +++ "b/2019/06/18/Swift\345\215\217\350\256\256\346\211\251\345\261\225/index.html" @@ -110,7 +110,7 @@

    @@ -186,11 +186,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -198,7 +198,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2019/06/18/Swift\346\263\233\345\236\213/index.html" "b/2019/06/18/Swift\346\263\233\345\236\213/index.html" index ec1588c..08e0d53 100644 --- "a/2019/06/18/Swift\346\263\233\345\236\213/index.html" +++ "b/2019/06/18/Swift\346\263\233\345\236\213/index.html" @@ -106,7 +106,7 @@

    - Share + Share @@ -182,11 +182,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -194,7 +194,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2019/06/18/Swift\351\224\231\350\257\257\345\244\204\347\220\206/index.html" "b/2019/06/18/Swift\351\224\231\350\257\257\345\244\204\347\220\206/index.html" index a7955b5..6acb5bf 100644 --- "a/2019/06/18/Swift\351\224\231\350\257\257\345\244\204\347\220\206/index.html" +++ "b/2019/06/18/Swift\351\224\231\350\257\257\345\244\204\347\220\206/index.html" @@ -87,7 +87,7 @@

    throw

    @@ -163,11 +163,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -175,7 +175,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2019/06/18/Swift\351\241\271\347\233\256\344\270\255\351\233\206\346\210\220RN/index.html" "b/2019/06/18/Swift\351\241\271\347\233\256\344\270\255\351\233\206\346\210\220RN/index.html" index 7d71e38..32f8f4c 100644 --- "a/2019/06/18/Swift\351\241\271\347\233\256\344\270\255\351\233\206\346\210\220RN/index.html" +++ "b/2019/06/18/Swift\351\241\271\347\233\256\344\270\255\351\233\206\346\210\220RN/index.html" @@ -109,7 +109,7 @@

    - Share + Share @@ -185,11 +185,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -197,7 +197,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2019/06/18/Xcode\344\270\255\344\275\277\347\224\250Debug Memory Graph\346\243\200\346\237\245\345\206\205\345\255\230\346\263\204\346\274\217/index.html" "b/2019/06/18/Xcode\344\270\255\344\275\277\347\224\250Debug Memory Graph\346\243\200\346\237\245\345\206\205\345\255\230\346\263\204\346\274\217/index.html" index 4b25eff..81a9d55 100644 --- "a/2019/06/18/Xcode\344\270\255\344\275\277\347\224\250Debug Memory Graph\346\243\200\346\237\245\345\206\205\345\255\230\346\263\204\346\274\217/index.html" +++ "b/2019/06/18/Xcode\344\270\255\344\275\277\347\224\250Debug Memory Graph\346\243\200\346\237\245\345\206\205\345\255\230\346\263\204\346\274\217/index.html" @@ -89,7 +89,7 @@

    @@ -165,11 +165,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -177,7 +177,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2019/06/18/flutter \345\256\211\350\243\205/index.html" "b/2019/06/18/flutter \345\256\211\350\243\205/index.html" index 27e3841..ca0afbf 100644 --- "a/2019/06/18/flutter \345\256\211\350\243\205/index.html" +++ "b/2019/06/18/flutter \345\256\211\350\243\205/index.html" @@ -81,7 +81,7 @@

    @@ -157,11 +157,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -169,7 +169,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2019/06/18/iOS \345\212\250\346\200\201\350\256\276\347\275\256TableHeaderView\351\253\230\345\272\246/index.html" "b/2019/06/18/iOS \345\212\250\346\200\201\350\256\276\347\275\256TableHeaderView\351\253\230\345\272\246/index.html" index dd5cfc8..bc2aa5d 100644 --- "a/2019/06/18/iOS \345\212\250\346\200\201\350\256\276\347\275\256TableHeaderView\351\253\230\345\272\246/index.html" +++ "b/2019/06/18/iOS \345\212\250\346\200\201\350\256\276\347\275\256TableHeaderView\351\253\230\345\272\246/index.html" @@ -96,7 +96,7 @@

    - Share + Share @@ -105,7 +105,7 @@

    - + Newer
    @@ -115,7 +115,7 @@

    + Older
    @@ -172,11 +172,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -184,7 +184,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2019/06/18/iOS\345\274\200\345\217\221_\344\272\216\351\270\277\351\271\217/index.html" "b/2019/06/18/iOS\345\274\200\345\217\221_\344\272\216\351\270\277\351\271\217/index.html" index 976fe0d..3d40606 100644 --- "a/2019/06/18/iOS\345\274\200\345\217\221_\344\272\216\351\270\277\351\271\217/index.html" +++ "b/2019/06/18/iOS\345\274\200\345\217\221_\344\272\216\351\270\277\351\271\217/index.html" @@ -130,7 +130,7 @@

    - Share + Share @@ -139,7 +139,7 @@

    - + Newer
    @@ -149,7 +149,7 @@

    + Older
    @@ -206,11 +206,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -218,7 +218,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2019/06/18/iOS\351\200\232\350\277\207URL\350\216\267\345\217\226HTML\344\270\255\346\240\207\351\242\230/index.html" "b/2019/06/18/iOS\351\200\232\350\277\207URL\350\216\267\345\217\226HTML\344\270\255\346\240\207\351\242\230/index.html" index 87869e1..c1c5ea6 100644 --- "a/2019/06/18/iOS\351\200\232\350\277\207URL\350\216\267\345\217\226HTML\344\270\255\346\240\207\351\242\230/index.html" +++ "b/2019/06/18/iOS\351\200\232\350\277\207URL\350\216\267\345\217\226HTML\344\270\255\346\240\207\351\242\230/index.html" @@ -124,7 +124,7 @@
    @@ -143,7 +143,7 @@
    - + Older
    @@ -200,11 +200,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -212,7 +212,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2019/06/18/iOS\351\235\242\350\257\225OC\350\257\255\350\250\200\347\211\271\346\200\247/index.html" "b/2019/06/18/iOS\351\235\242\350\257\225OC\350\257\255\350\250\200\347\211\271\346\200\247/index.html" index 81549b3..0942afd 100644 --- "a/2019/06/18/iOS\351\235\242\350\257\225OC\350\257\255\350\250\200\347\211\271\346\200\247/index.html" +++ "b/2019/06/18/iOS\351\235\242\350\257\225OC\350\257\255\350\250\200\347\211\271\346\200\247/index.html" @@ -196,7 +196,7 @@

    - Share + Share @@ -272,11 +272,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -284,7 +284,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2019/06/18/is \345\205\263\351\224\256\345\255\227\347\261\273\345\236\213\346\243\200\346\237\245/index.html" "b/2019/06/18/is \345\205\263\351\224\256\345\255\227\347\261\273\345\236\213\346\243\200\346\237\245/index.html" index f1f1bb8..e93ab8c 100644 --- "a/2019/06/18/is \345\205\263\351\224\256\345\255\227\347\261\273\345\236\213\346\243\200\346\237\245/index.html" +++ "b/2019/06/18/is \345\205\263\351\224\256\345\255\227\347\261\273\345\236\213\346\243\200\346\237\245/index.html" @@ -91,7 +91,7 @@

    - Share + Share @@ -167,11 +167,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -179,7 +179,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2019/06/18/npm\345\222\214cnpm/index.html" "b/2019/06/18/npm\345\222\214cnpm/index.html" index 6e675f4..948bdfb 100644 --- "a/2019/06/18/npm\345\222\214cnpm/index.html" +++ "b/2019/06/18/npm\345\222\214cnpm/index.html" @@ -110,7 +110,7 @@

    - Share + Share @@ -186,11 +186,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -198,7 +198,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2019/06/18/swift\344\270\255\345\274\272\345\274\225\347\224\250\345\276\252\347\216\257\345\222\214weak\345\205\263\351\224\256\345\255\227/index.html" "b/2019/06/18/swift\344\270\255\345\274\272\345\274\225\347\224\250\345\276\252\347\216\257\345\222\214weak\345\205\263\351\224\256\345\255\227/index.html" index f4e6bf9..12445f8 100644 --- "a/2019/06/18/swift\344\270\255\345\274\272\345\274\225\347\224\250\345\276\252\347\216\257\345\222\214weak\345\205\263\351\224\256\345\255\227/index.html" +++ "b/2019/06/18/swift\344\270\255\345\274\272\345\274\225\347\224\250\345\276\252\347\216\257\345\222\214weak\345\205\263\351\224\256\345\255\227/index.html" @@ -165,7 +165,7 @@

    @@ -174,7 +174,7 @@

    - + Newer
    @@ -184,7 +184,7 @@

    - + Older
    @@ -241,11 +241,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -253,7 +253,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2019/06/18/typescripe \345\256\211\350\243\205\345\217\212\350\257\255\346\263\225/index.html" "b/2019/06/18/typescripe \345\256\211\350\243\205\345\217\212\350\257\255\346\263\225/index.html" index 336b2d3..615154d 100644 --- "a/2019/06/18/typescripe \345\256\211\350\243\205\345\217\212\350\257\255\346\263\225/index.html" +++ "b/2019/06/18/typescripe \345\256\211\350\243\205\345\217\212\350\257\255\346\263\225/index.html" @@ -156,7 +156,7 @@
    @@ -165,7 +165,7 @@
    - + Newer
    @@ -175,7 +175,7 @@
    - + Older
    @@ -232,11 +232,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -244,7 +244,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2019/06/18/\343\200\21252\344\270\252\346\226\271\346\263\225\347\254\254\344\270\200\347\253\240\343\200\213/index.html" "b/2019/06/18/\343\200\21252\344\270\252\346\226\271\346\263\225\347\254\254\344\270\200\347\253\240\343\200\213/index.html" index c194e61..6986bb1 100644 --- "a/2019/06/18/\343\200\21252\344\270\252\346\226\271\346\263\225\347\254\254\344\270\200\347\253\240\343\200\213/index.html" +++ "b/2019/06/18/\343\200\21252\344\270\252\346\226\271\346\263\225\347\254\254\344\270\200\347\253\240\343\200\213/index.html" @@ -268,7 +268,7 @@
    - Share + Share @@ -277,7 +277,7 @@
    - + Newer
    @@ -344,11 +344,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -356,7 +356,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2019/06/18/\344\270\200\350\207\252\345\212\250\345\274\225\347\224\250\350\256\241\346\225\260/index.html" "b/2019/06/18/\344\270\200\350\207\252\345\212\250\345\274\225\347\224\250\350\256\241\346\225\260/index.html" index f9eebee..a740e8c 100644 --- "a/2019/06/18/\344\270\200\350\207\252\345\212\250\345\274\225\347\224\250\350\256\241\346\225\260/index.html" +++ "b/2019/06/18/\344\270\200\350\207\252\345\212\250\345\274\225\347\224\250\350\256\241\346\225\260/index.html" @@ -112,7 +112,7 @@

    @@ -188,11 +188,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -200,7 +200,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2019/06/18/\347\254\254\344\270\203\347\253\240Blocks\351\235\242\350\257\225/index.html" "b/2019/06/18/\347\254\254\344\270\203\347\253\240Blocks\351\235\242\350\257\225/index.html" index 2a0535f..92453ec 100644 --- "a/2019/06/18/\347\254\254\344\270\203\347\253\240Blocks\351\235\242\350\257\225/index.html" +++ "b/2019/06/18/\347\254\254\344\270\203\347\253\240Blocks\351\235\242\350\257\225/index.html" @@ -162,7 +162,7 @@

    @@ -238,11 +238,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -250,7 +250,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2019/06/18/\347\254\254\344\270\211\347\253\240UI\350\247\206\345\233\276\345\223\215\345\272\224\351\223\276\347\246\273\345\261\217\346\270\262\346\237\223\351\235\242\350\257\225/index.html" "b/2019/06/18/\347\254\254\344\270\211\347\253\240UI\350\247\206\345\233\276\345\223\215\345\272\224\351\223\276\347\246\273\345\261\217\346\270\262\346\237\223\351\235\242\350\257\225/index.html" index 1ac482d..c86c67b 100644 --- "a/2019/06/18/\347\254\254\344\270\211\347\253\240UI\350\247\206\345\233\276\345\223\215\345\272\224\351\223\276\347\246\273\345\261\217\346\270\262\346\237\223\351\235\242\350\257\225/index.html" +++ "b/2019/06/18/\347\254\254\344\270\211\347\253\240UI\350\247\206\345\233\276\345\223\215\345\272\224\351\223\276\347\246\273\345\261\217\346\270\262\346\237\223\351\235\242\350\257\225/index.html" @@ -155,7 +155,7 @@

    - Share + Share @@ -174,7 +174,7 @@

    + Older
    @@ -231,11 +231,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -243,7 +243,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/2019/06/18/\347\254\254\344\271\235\347\253\240Runloop\351\235\242\350\257\225/index.html" "b/2019/06/18/\347\254\254\344\271\235\347\253\240Runloop\351\235\242\350\257\225/index.html" index 99431b9..669580b 100644 --- "a/2019/06/18/\347\254\254\344\271\235\347\253\240Runloop\351\235\242\350\257\225/index.html" +++ "b/2019/06/18/\347\254\254\344\271\235\347\253\240Runloop\351\235\242\350\257\225/index.html" @@ -84,7 +84,7 @@

    @@ -93,7 +93,7 @@

    - -o.io/" target="_blank">Hexo -
    -

    - -

    - - - diff --git a/tags/TableView/index.html b/tags/TableView/index.html index 3940f60..c24dcf3 100644 --- a/tags/TableView/index.html +++ b/tags/TableView/index.html @@ -142,11 +142,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -154,7 +154,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git a/tags/WKWebView/index.html b/tags/WKWebView/index.html index dd92ada..68628c6 100644 --- a/tags/WKWebView/index.html +++ b/tags/WKWebView/index.html @@ -228,11 +228,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -240,7 +240,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git a/tags/git/index.html b/tags/git/index.html index 58fb8fb..5c99520 100644 --- a/tags/git/index.html +++ b/tags/git/index.html @@ -142,11 +142,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -154,7 +154,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/tags/\345\274\202\346\255\245/index.html" "b/tags/\345\274\202\346\255\245/index.html" index 923dfb4..03ae7d0 100644 --- "a/tags/\345\274\202\346\255\245/index.html" +++ "b/tags/\345\274\202\346\255\245/index.html" @@ -142,11 +142,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -154,7 +154,7 @@

    Recent Posts

  • - (no title) + (no title)
  • diff --git "a/tags/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" "b/tags/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" index 85eff32..c151811 100644 --- "a/tags/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" +++ "b/tags/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" @@ -142,11 +142,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • @@ -154,7 +154,7 @@

    Recent Posts

  • - (no title) + (no title)
  • From 03caa114791618875bc51dac36859a2fca053579 Mon Sep 17 00:00:00 2001 From: YuHongpeng <480814177@qq.com> Date: Tue, 18 Jun 2019 23:18:12 +0800 Subject: [PATCH 07/18] Site updated: 2019-06-18 23:18:12 --- .../index.html" | 10 +- .../index.html" | 10 +- .../index.html" | 10 +- .../index.html" | 10 +- .../index.html" | 10 +- .../index.html" | 10 +- .../index.html" | 10 +- .../index.html" | 10 +- .../index.html" | 10 +- .../index.html" | 10 +- .../index.html" | 10 +- .../index.html" | 10 +- .../index.html" | 10 +- .../index.html" | 10 +- .../index.html" | 10 +- .../index.html" | 10 +- .../index.html" | 10 +- .../index.html" | 10 +- .../index.html" | 10 +- .../index.html" | 10 +- .../index.html" | 10 +- .../index.html" | 10 +- .../index.html" | 10 +- .../index.html" | 10 +- .../index.html" | 10 +- .../index.html" | 10 +- .../index.html" | 10 +- .../index.html" | 10 +- .../index.html" | 10 +- .../index.html" | 10 +- .../index.html" | 10 +- .../index.html" | 10 +- .../index.html" | 10 +- .../index.html" | 10 +- .../index.html" | 10 +- .../ES6\350\257\255\346\263\225/index.html" | 10 +- .../index.html" | 23 +- .../index.html" | 25 +- .../index.html" | 10 +- .../index.html" | 10 +- .../index.html" | 10 +- .../18/RN\346\212\245\351\224\231/index.html" | 10 +- .../index.html" | 10 +- .../index.html" | 12 +- .../index.html" | 14 +- .../index.html" | 14 +- .../index.html" | 10 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 10 +- .../Swift\346\263\233\345\236\213/index.html" | 10 +- .../index.html" | 14 +- .../index.html" | 14 +- .../index.html" | 12 +- 2019/06/18/docs/15608702427054/index.html | 206 ++++++ .../index.html" | 216 ++++++ .../index.html" | 14 +- .../index.html" | 14 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 10 +- "2019/06/18/npm\345\222\214cnpm/index.html" | 10 +- .../index.html" | 14 +- .../index.html" | 14 +- .../index.html" | 14 +- .../index.html" | 14 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 14 +- .../index.html" | 14 +- .../index.html" | 14 +- .../index.html" | 14 +- .../index.html" | 14 +- .../index.html" | 14 +- .../index.html" | 14 +- .../index.html" | 14 +- .../index.html" | 14 +- .../index.html" | 21 +- archives/2016/10/index.html | 8 +- archives/2016/11/index.html | 8 +- archives/2016/12/index.html | 8 +- archives/2016/index.html | 8 +- archives/2016/page/2/index.html | 8 +- archives/2017/01/index.html | 8 +- archives/2017/02/index.html | 8 +- archives/2017/03/index.html | 8 +- archives/2017/04/index.html | 8 +- archives/2017/11/index.html | 8 +- archives/2017/12/index.html | 8 +- archives/2017/index.html | 8 +- archives/2017/page/2/index.html | 8 +- archives/2018/01/index.html | 8 +- archives/2018/02/index.html | 8 +- archives/2018/index.html | 8 +- archives/2019/06/index.html | 32 +- archives/2019/06/page/2/index.html | 34 +- archives/2019/06/page/3/index.html | 36 +- archives/2019/06/page/4/index.html | 38 +- archives/2019/06/page/5/index.html | 21 +- archives/2019/index.html | 32 +- archives/2019/page/2/index.html | 34 +- archives/2019/page/3/index.html | 36 +- archives/2019/page/4/index.html | 38 +- archives/2019/page/5/index.html | 21 +- archives/index.html | 32 +- archives/page/2/index.html | 34 +- archives/page/3/index.html | 36 +- archives/page/4/index.html | 38 +- archives/page/5/index.html | 40 +- archives/page/6/index.html | 56 +- archives/page/7/index.html | 46 +- archives/page/8/index.html | 27 +- index.html | 412 +++++------- page/2/index.html | 627 ++++++++++-------- page/3/index.html | 311 +++++---- page/4/index.html | 306 ++++----- page/5/index.html | 363 +++------- page/6/index.html | 398 +++++++---- page/7/index.html | 226 ++++--- page/8/index.html | 111 +++- search.xml | 125 ++-- tags/AES-Base64-MD5/index.html | 8 +- tags/Alamofire/index.html | 8 +- tags/AlertView/index.html | 8 +- tags/GCD/index.html | 8 +- tags/Hexo/index.html | 8 +- tags/OC-JS/index.html | 8 +- tags/Quartz-2D/index.html | 8 +- tags/Quartz2D/index.html | 8 +- tags/RunTime/index.html | 8 +- tags/SDWebImage/index.html | 8 +- tags/SQL/index.html | 8 +- tags/Swift/index.html | 64 +- tags/TableView/index.html | 8 +- tags/WKWebView/index.html | 8 +- tags/git/index.html | 8 +- "tags/\345\274\202\346\255\245/index.html" | 8 +- .../index.html" | 8 +- 138 files changed, 2838 insertions(+), 2305 deletions(-) create mode 100644 2019/06/18/docs/15608702427054/index.html create mode 100644 "2019/06/18/flutter\345\256\211\350\243\205/index.html" diff --git "a/2016/10/13/SQlite\344\275\277\347\224\250\345\255\246\344\271\240/index.html" "b/2016/10/13/SQlite\344\275\277\347\224\250\345\255\246\344\271\240/index.html" index 3d5f720..766dca3 100644 --- "a/2016/10/13/SQlite\344\275\277\347\224\250\345\255\246\344\271\240/index.html" +++ "b/2016/10/13/SQlite\344\275\277\347\224\250\345\255\246\344\271\240/index.html" @@ -200,7 +200,7 @@

    - Share + Share @@ -269,19 +269,19 @@

    Recent Posts

    • - (no title) + (no title)
    • - (no title) + (no title)
    • - (no title) + (no title)
    • - (no title) + (no title)
    • diff --git "a/2016/10/20/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" "b/2016/10/20/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" index 47e4a94..a1b8e9d 100644 --- "a/2016/10/20/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" +++ "b/2016/10/20/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" @@ -94,7 +94,7 @@

      - Share + Share @@ -168,19 +168,19 @@

      Recent Posts

      • - (no title) + (no title)
      • - (no title) + (no title)
      • - (no title) + (no title)
      • - (no title) + (no title)
      • diff --git "a/2016/10/27/iOS\345\274\271\346\241\206\345\274\217\345\233\276/index.html" "b/2016/10/27/iOS\345\274\271\346\241\206\345\274\217\345\233\276/index.html" index 3d8227b..0532c3f 100644 --- "a/2016/10/27/iOS\345\274\271\346\241\206\345\274\217\345\233\276/index.html" +++ "b/2016/10/27/iOS\345\274\271\346\241\206\345\274\217\345\233\276/index.html" @@ -112,7 +112,7 @@

        - Share + Share @@ -186,19 +186,19 @@

        Recent Posts

        • - (no title) + (no title)
        • - (no title) + (no title)
        • - (no title) + (no title)
        • - (no title) + (no title)
        • diff --git "a/2016/11/05/Hexo\344\270\252\344\272\272\345\215\232\345\256\242\346\220\255\345\273\272/index.html" "b/2016/11/05/Hexo\344\270\252\344\272\272\345\215\232\345\256\242\346\220\255\345\273\272/index.html" index 7d576d2..f8496db 100644 --- "a/2016/11/05/Hexo\344\270\252\344\272\272\345\215\232\345\256\242\346\220\255\345\273\272/index.html" +++ "b/2016/11/05/Hexo\344\270\252\344\272\272\345\215\232\345\256\242\346\220\255\345\273\272/index.html" @@ -170,7 +170,7 @@

          - Share + Share @@ -244,19 +244,19 @@

          Recent Posts

          • - (no title) + (no title)
          • - (no title) + (no title)
          • - (no title) + (no title)
          • - (no title) + (no title)
          • diff --git "a/2016/11/05/TableView\344\275\277\347\224\250\346\200\273\347\273\223/index.html" "b/2016/11/05/TableView\344\275\277\347\224\250\346\200\273\347\273\223/index.html" index 09b14c1..98e74df 100644 --- "a/2016/11/05/TableView\344\275\277\347\224\250\346\200\273\347\273\223/index.html" +++ "b/2016/11/05/TableView\344\275\277\347\224\250\346\200\273\347\273\223/index.html" @@ -157,7 +157,7 @@

            - Share + Share @@ -231,19 +231,19 @@

            Recent Posts

            • - (no title) + (no title)
            • - (no title) + (no title)
            • - (no title) + (no title)
            • - (no title) + (no title)
            • diff --git "a/2016/11/20/git\345\255\246\344\271\240\346\200\273\347\273\223/index.html" "b/2016/11/20/git\345\255\246\344\271\240\346\200\273\347\273\223/index.html" index 3535a88..155409a 100644 --- "a/2016/11/20/git\345\255\246\344\271\240\346\200\273\347\273\223/index.html" +++ "b/2016/11/20/git\345\255\246\344\271\240\346\200\273\347\273\223/index.html" @@ -154,7 +154,7 @@

              - Share + Share @@ -228,19 +228,19 @@

              Recent Posts

              • - (no title) + (no title)
              • - (no title) + (no title)
              • - (no title) + (no title)
              • - (no title) + (no title)
              • diff --git "a/2016/11/22/OC RunTime\345\255\246\344\271\240/index.html" "b/2016/11/22/OC RunTime\345\255\246\344\271\240/index.html" index 3a9bd8f..394b1ff 100644 --- "a/2016/11/22/OC RunTime\345\255\246\344\271\240/index.html" +++ "b/2016/11/22/OC RunTime\345\255\246\344\271\240/index.html" @@ -105,7 +105,7 @@

                - Share + Share @@ -179,19 +179,19 @@

                Recent Posts

                • - (no title) + (no title)
                • - (no title) + (no title)
                • - (no title) + (no title)
                • - (no title) + (no title)
                • diff --git "a/2016/11/22/Quartz2D\347\273\230\345\210\266\347\272\277\346\235\241/index.html" "b/2016/11/22/Quartz2D\347\273\230\345\210\266\347\272\277\346\235\241/index.html" index 129a12c..d58db68 100644 --- "a/2016/11/22/Quartz2D\347\273\230\345\210\266\347\272\277\346\235\241/index.html" +++ "b/2016/11/22/Quartz2D\347\273\230\345\210\266\347\272\277\346\235\241/index.html" @@ -142,7 +142,7 @@

                  - Share + Share @@ -216,19 +216,19 @@

                  Recent Posts

                  • - (no title) + (no title)
                  • - (no title) + (no title)
                  • - (no title) + (no title)
                  • - (no title) + (no title)
                  • diff --git "a/2016/11/22/Swift3.0\345\255\246\344\271\240(\344\272\224)/index.html" "b/2016/11/22/Swift3.0\345\255\246\344\271\240(\344\272\224)/index.html" index a8124f8..55db94d 100644 --- "a/2016/11/22/Swift3.0\345\255\246\344\271\240(\344\272\224)/index.html" +++ "b/2016/11/22/Swift3.0\345\255\246\344\271\240(\344\272\224)/index.html" @@ -142,7 +142,7 @@

                    - Share + Share @@ -216,19 +216,19 @@

                    Recent Posts

                    • - (no title) + (no title)
                    • - (no title) + (no title)
                    • - (no title) + (no title)
                    • - (no title) + (no title)
                    • diff --git "a/2016/11/22/Swift3.0\345\255\246\344\271\240(\345\233\233)/index.html" "b/2016/11/22/Swift3.0\345\255\246\344\271\240(\345\233\233)/index.html" index 48d3253..7bef75d 100644 --- "a/2016/11/22/Swift3.0\345\255\246\344\271\240(\345\233\233)/index.html" +++ "b/2016/11/22/Swift3.0\345\255\246\344\271\240(\345\233\233)/index.html" @@ -142,7 +142,7 @@

                      - Share + Share @@ -216,19 +216,19 @@

                      Recent Posts

                      • - (no title) + (no title)
                      • - (no title) + (no title)
                      • - (no title) + (no title)
                      • - (no title) + (no title)
                      • diff --git "a/2016/11/22/WKWebiView\344\275\277\347\224\250/index.html" "b/2016/11/22/WKWebiView\344\275\277\347\224\250/index.html" index a2eefc0..6867522 100644 --- "a/2016/11/22/WKWebiView\344\275\277\347\224\250/index.html" +++ "b/2016/11/22/WKWebiView\344\275\277\347\224\250/index.html" @@ -142,7 +142,7 @@

                        - Share + Share @@ -216,19 +216,19 @@

                        Recent Posts

                        • - (no title) + (no title)
                        • - (no title) + (no title)
                        • - (no title) + (no title)
                        • - (no title) + (no title)
                        • diff --git "a/2016/12/04/Quartz2D\345\210\235\350\257\206/index.html" "b/2016/12/04/Quartz2D\345\210\235\350\257\206/index.html" index 7999b32..f7be3c4 100644 --- "a/2016/12/04/Quartz2D\345\210\235\350\257\206/index.html" +++ "b/2016/12/04/Quartz2D\345\210\235\350\257\206/index.html" @@ -219,7 +219,7 @@

                          - Share + Share @@ -293,19 +293,19 @@

                          Recent Posts

                          • - (no title) + (no title)
                          • - (no title) + (no title)
                          • - (no title) + (no title)
                          • - (no title) + (no title)
                          • diff --git "a/2016/12/04/Quartz2D\347\273\230\345\210\266\346\233\262\347\272\277/index.html" "b/2016/12/04/Quartz2D\347\273\230\345\210\266\346\233\262\347\272\277/index.html" index e59f71e..ae86ed2 100644 --- "a/2016/12/04/Quartz2D\347\273\230\345\210\266\346\233\262\347\272\277/index.html" +++ "b/2016/12/04/Quartz2D\347\273\230\345\210\266\346\233\262\347\272\277/index.html" @@ -181,7 +181,7 @@

                            - Share + Share @@ -255,19 +255,19 @@

                            Recent Posts

                            • - (no title) + (no title)
                            • - (no title) + (no title)
                            • - (no title) + (no title)
                            • - (no title) + (no title)
                            • diff --git "a/2016/12/05/GCD\345\256\236\347\216\260\346\214\211\351\222\256\345\256\232\346\227\266\344\272\256\350\265\267/index.html" "b/2016/12/05/GCD\345\256\236\347\216\260\346\214\211\351\222\256\345\256\232\346\227\266\344\272\256\350\265\267/index.html" index 65cb0fe..2a146e4 100644 --- "a/2016/12/05/GCD\345\256\236\347\216\260\346\214\211\351\222\256\345\256\232\346\227\266\344\272\256\350\265\267/index.html" +++ "b/2016/12/05/GCD\345\256\236\347\216\260\346\214\211\351\222\256\345\256\232\346\227\266\344\272\256\350\265\267/index.html" @@ -138,7 +138,7 @@

                              - Share + Share @@ -212,19 +212,19 @@

                              Recent Posts

                              • - (no title) + (no title)
                              • - (no title) + (no title)
                              • - (no title) + (no title)
                              • - (no title) + (no title)
                              • diff --git "a/2016/12/10/Quartz2D\347\273\230\345\210\266\345\275\242\347\212\266/index.html" "b/2016/12/10/Quartz2D\347\273\230\345\210\266\345\275\242\347\212\266/index.html" index de3a743..3e79c50 100644 --- "a/2016/12/10/Quartz2D\347\273\230\345\210\266\345\275\242\347\212\266/index.html" +++ "b/2016/12/10/Quartz2D\347\273\230\345\210\266\345\275\242\347\212\266/index.html" @@ -103,7 +103,7 @@

                                - Share + Share @@ -177,19 +177,19 @@

                                Recent Posts

                                • - (no title) + (no title)
                                • - (no title) + (no title)
                                • - (no title) + (no title)
                                • - (no title) + (no title)
                                • diff --git "a/2017/01/18/\345\274\202\346\255\245\344\270\213\350\275\275\345\233\276\347\211\207/index.html" "b/2017/01/18/\345\274\202\346\255\245\344\270\213\350\275\275\345\233\276\347\211\207/index.html" index 7871430..e9bd89e 100644 --- "a/2017/01/18/\345\274\202\346\255\245\344\270\213\350\275\275\345\233\276\347\211\207/index.html" +++ "b/2017/01/18/\345\274\202\346\255\245\344\270\213\350\275\275\345\233\276\347\211\207/index.html" @@ -139,7 +139,7 @@

                                  - Share + Share @@ -213,19 +213,19 @@

                                  Recent Posts

                                  • - (no title) + (no title)
                                  • - (no title) + (no title)
                                  • - (no title) + (no title)
                                  • - (no title) + (no title)
                                  • diff --git "a/2017/02/02/iOS\351\235\231\346\200\201\345\272\223\345\212\250\346\200\201\345\272\223/index.html" "b/2017/02/02/iOS\351\235\231\346\200\201\345\272\223\345\212\250\346\200\201\345\272\223/index.html" index e28fb00..3da4e95 100644 --- "a/2017/02/02/iOS\351\235\231\346\200\201\345\272\223\345\212\250\346\200\201\345\272\223/index.html" +++ "b/2017/02/02/iOS\351\235\231\346\200\201\345\272\223\345\212\250\346\200\201\345\272\223/index.html" @@ -130,7 +130,7 @@

                                    - Share + Share @@ -204,19 +204,19 @@

                                    Recent Posts

                                    • - (no title) + (no title)
                                    • - (no title) + (no title)
                                    • - (no title) + (no title)
                                    • - (no title) + (no title)
                                    • diff --git "a/2017/02/08/SDWebImage\345\255\246\344\271\240/index.html" "b/2017/02/08/SDWebImage\345\255\246\344\271\240/index.html" index cbc423c..6141875 100644 --- "a/2017/02/08/SDWebImage\345\255\246\344\271\240/index.html" +++ "b/2017/02/08/SDWebImage\345\255\246\344\271\240/index.html" @@ -164,7 +164,7 @@

                                      - Share + Share @@ -238,19 +238,19 @@

                                      Recent Posts

                                      • - (no title) + (no title)
                                      • - (no title) + (no title)
                                      • - (no title) + (no title)
                                      • - (no title) + (no title)
                                      • diff --git "a/2017/02/20/OC\344\270\216js\344\272\244\344\272\222\345\255\246\344\271\240/index.html" "b/2017/02/20/OC\344\270\216js\344\272\244\344\272\222\345\255\246\344\271\240/index.html" index c3bbbb8..44d80e7 100644 --- "a/2017/02/20/OC\344\270\216js\344\272\244\344\272\222\345\255\246\344\271\240/index.html" +++ "b/2017/02/20/OC\344\270\216js\344\272\244\344\272\222\345\255\246\344\271\240/index.html" @@ -102,7 +102,7 @@

                                        - Share + Share @@ -176,19 +176,19 @@

                                        Recent Posts

                                        • - (no title) + (no title)
                                        • - (no title) + (no title)
                                        • - (no title) + (no title)
                                        • - (no title) + (no title)
                                        • diff --git "a/2017/03/01/Swift3.0\345\255\246\344\271\240(\344\270\200)/index.html" "b/2017/03/01/Swift3.0\345\255\246\344\271\240(\344\270\200)/index.html" index 3d052f4..781c9aa 100644 --- "a/2017/03/01/Swift3.0\345\255\246\344\271\240(\344\270\200)/index.html" +++ "b/2017/03/01/Swift3.0\345\255\246\344\271\240(\344\270\200)/index.html" @@ -160,7 +160,7 @@

                                          - Share + Share @@ -234,19 +234,19 @@

                                          Recent Posts

                                          • - (no title) + (no title)
                                          • - (no title) + (no title)
                                          • - (no title) + (no title)
                                          • - (no title) + (no title)
                                          • diff --git "a/2017/03/05/Swift3.0\345\255\246\344\271\240(\344\272\214)/index.html" "b/2017/03/05/Swift3.0\345\255\246\344\271\240(\344\272\214)/index.html" index 80d48c4..e53d7f7 100644 --- "a/2017/03/05/Swift3.0\345\255\246\344\271\240(\344\272\214)/index.html" +++ "b/2017/03/05/Swift3.0\345\255\246\344\271\240(\344\272\214)/index.html" @@ -216,7 +216,7 @@

                                            - Share + Share @@ -290,19 +290,19 @@

                                            Recent Posts

                                            • - (no title) + (no title)
                                            • - (no title) + (no title)
                                            • - (no title) + (no title)
                                            • - (no title) + (no title)
                                            • diff --git "a/2017/03/08/Swift3.0\345\255\246\344\271\240(\344\270\211)/index.html" "b/2017/03/08/Swift3.0\345\255\246\344\271\240(\344\270\211)/index.html" index 152fcf3..6b9c29a 100644 --- "a/2017/03/08/Swift3.0\345\255\246\344\271\240(\344\270\211)/index.html" +++ "b/2017/03/08/Swift3.0\345\255\246\344\271\240(\344\270\211)/index.html" @@ -112,7 +112,7 @@

                                              - Share + Share @@ -186,19 +186,19 @@

                                              Recent Posts

                                              • - (no title) + (no title)
                                              • - (no title) + (no title)
                                              • - (no title) + (no title)
                                              • - (no title) + (no title)
                                              • diff --git "a/2017/03/25/\347\237\245\350\257\206\347\202\271\346\200\273\347\273\223/index.html" "b/2017/03/25/\347\237\245\350\257\206\347\202\271\346\200\273\347\273\223/index.html" index 4cd1b3a..768257a 100644 --- "a/2017/03/25/\347\237\245\350\257\206\347\202\271\346\200\273\347\273\223/index.html" +++ "b/2017/03/25/\347\237\245\350\257\206\347\202\271\346\200\273\347\273\223/index.html" @@ -100,7 +100,7 @@

                                                @@ -172,19 +172,19 @@

                                                Recent Posts

                                                • - (no title) + (no title)
                                                • - (no title) + (no title)
                                                • - (no title) + (no title)
                                                • - (no title) + (no title)
                                                • diff --git "a/2017/04/10/Alamofire\346\272\220\347\240\201\345\255\246\344\271\240/index.html" "b/2017/04/10/Alamofire\346\272\220\347\240\201\345\255\246\344\271\240/index.html" index 481fa77..af46a80 100644 --- "a/2017/04/10/Alamofire\346\272\220\347\240\201\345\255\246\344\271\240/index.html" +++ "b/2017/04/10/Alamofire\346\272\220\347\240\201\345\255\246\344\271\240/index.html" @@ -109,7 +109,7 @@

                                                  - Share + Share @@ -183,19 +183,19 @@

                                                  Recent Posts

                                                  • - (no title) + (no title)
                                                  • - (no title) + (no title)
                                                  • - (no title) + (no title)
                                                  • - (no title) + (no title)
                                                  • diff --git "a/2017/11/29/CocoaPods\345\210\266\344\275\234\347\247\201\346\234\211pod/index.html" "b/2017/11/29/CocoaPods\345\210\266\344\275\234\347\247\201\346\234\211pod/index.html" index 081f111..3ee656a 100644 --- "a/2017/11/29/CocoaPods\345\210\266\344\275\234\347\247\201\346\234\211pod/index.html" +++ "b/2017/11/29/CocoaPods\345\210\266\344\275\234\347\247\201\346\234\211pod/index.html" @@ -135,7 +135,7 @@

                                                    - Share + Share

                                              @@ -207,19 +207,19 @@

                                              Recent Posts

                                              • - (no title) + (no title)
                                              • - (no title) + (no title)
                                              • - (no title) + (no title)
                                              • - (no title) + (no title)
                                              • diff --git "a/2017/12/02/\346\234\254\345\234\260\345\267\262\346\234\211\351\241\271\347\233\256\346\267\273\345\212\240Pod/index.html" "b/2017/12/02/\346\234\254\345\234\260\345\267\262\346\234\211\351\241\271\347\233\256\346\267\273\345\212\240Pod/index.html" index 734c458..024a246 100644 --- "a/2017/12/02/\346\234\254\345\234\260\345\267\262\346\234\211\351\241\271\347\233\256\346\267\273\345\212\240Pod/index.html" +++ "b/2017/12/02/\346\234\254\345\234\260\345\267\262\346\234\211\351\241\271\347\233\256\346\267\273\345\212\240Pod/index.html" @@ -106,7 +106,7 @@

                                                @@ -178,19 +178,19 @@

                                                Recent Posts

                                            @@ -188,19 +188,19 @@

                                            Recent Posts

                                            • - (no title) + (no title)
                                            • - (no title) + (no title)
                                            • - (no title) + (no title)
                                            • - (no title) + (no title)
                                            • diff --git "a/2018/01/08/Swift\345\212\240\345\257\206\347\233\270\345\205\263/index.html" "b/2018/01/08/Swift\345\212\240\345\257\206\347\233\270\345\205\263/index.html" index d7c9a23..151209a 100644 --- "a/2018/01/08/Swift\345\212\240\345\257\206\347\233\270\345\205\263/index.html" +++ "b/2018/01/08/Swift\345\212\240\345\257\206\347\233\270\345\205\263/index.html" @@ -314,7 +314,7 @@

                                              - Share + Share @@ -388,19 +388,19 @@

                                              Recent Posts

                                              • - (no title) + (no title)
                                              • - (no title) + (no title)
                                              • - (no title) + (no title)
                                              • - (no title) + (no title)
                                              • diff --git "a/2018/01/10/\347\275\221\347\273\234\344\270\203\345\261\202\345\215\217\350\256\256/index.html" "b/2018/01/10/\347\275\221\347\273\234\344\270\203\345\261\202\345\215\217\350\256\256/index.html" index ada661b..20d1234 100644 --- "a/2018/01/10/\347\275\221\347\273\234\344\270\203\345\261\202\345\215\217\350\256\256/index.html" +++ "b/2018/01/10/\347\275\221\347\273\234\344\270\203\345\261\202\345\215\217\350\256\256/index.html" @@ -128,7 +128,7 @@

                                                Socket @@ -200,19 +200,19 @@

                                                Recent Posts

                                                • - (no title) + (no title)
                                                • - (no title) + (no title)
                                                • - (no title) + (no title)
                                                • - (no title) + (no title)
                                                • diff --git "a/2018/01/12/iOS\345\215\263\346\227\266\351\200\232\350\256\257\345\256\236\347\216\260\344\272\214/index.html" "b/2018/01/12/iOS\345\215\263\346\227\266\351\200\232\350\256\257\345\256\236\347\216\260\344\272\214/index.html" index 5715ae2..d31f0ca 100644 --- "a/2018/01/12/iOS\345\215\263\346\227\266\351\200\232\350\256\257\345\256\236\347\216\260\344\272\214/index.html" +++ "b/2018/01/12/iOS\345\215\263\346\227\266\351\200\232\350\256\257\345\256\236\347\216\260\344\272\214/index.html" @@ -232,7 +232,7 @@

                                                  - Share + Share

                                        @@ -304,19 +304,19 @@

                                        Recent Posts

                                        • - (no title) + (no title)
                                        • - (no title) + (no title)
                                        • - (no title) + (no title)
                                        • - (no title) + (no title)
                                        • diff --git "a/2018/01/17/\351\235\242\350\257\225\344\270\255\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223/index.html" "b/2018/01/17/\351\235\242\350\257\225\344\270\255\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223/index.html" index 489533d..f78b3af 100644 --- "a/2018/01/17/\351\235\242\350\257\225\344\270\255\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223/index.html" +++ "b/2018/01/17/\351\235\242\350\257\225\344\270\255\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223/index.html" @@ -164,7 +164,7 @@

                                          Runlo @@ -236,19 +236,19 @@

                                          Recent Posts

                                          • - (no title) + (no title)
                                          • - (no title) + (no title)
                                          • - (no title) + (no title)
                                          • - (no title) + (no title)
                                          • diff --git "a/2018/01/18/\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223-\344\272\214/index.html" "b/2018/01/18/\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223-\344\272\214/index.html" index fa54203..42d7215 100644 --- "a/2018/01/18/\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223-\344\272\214/index.html" +++ "b/2018/01/18/\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223-\344\272\214/index.html" @@ -130,7 +130,7 @@

                                            - Share + Share

                                      @@ -202,19 +202,19 @@

                                      Recent Posts

                                      • - (no title) + (no title)
                                      • - (no title) + (no title)
                                      • - (no title) + (no title)
                                      • - (no title) + (no title)
                                      • diff --git "a/2018/01/19/Block\345\255\246\344\271\240\346\216\242\347\251\266/index.html" "b/2018/01/19/Block\345\255\246\344\271\240\346\216\242\347\251\266/index.html" index 2d02a92..e26bcf1 100644 --- "a/2018/01/19/Block\345\255\246\344\271\240\346\216\242\347\251\266/index.html" +++ "b/2018/01/19/Block\345\255\246\344\271\240\346\216\242\347\251\266/index.html" @@ -110,7 +110,7 @@

                                        - Share + Share

                                @@ -182,19 +182,19 @@

                                Recent Posts

                                • - (no title) + (no title)
                                • - (no title) + (no title)
                                • - (no title) + (no title)
                                • - (no title) + (no title)
                                • diff --git "a/2018/02/03/Mac\345\256\211\350\243\205VirtualBox\350\277\236\346\216\245U\347\233\230/index.html" "b/2018/02/03/Mac\345\256\211\350\243\205VirtualBox\350\277\236\346\216\245U\347\233\230/index.html" index a9c5905..9d73a16 100644 --- "a/2018/02/03/Mac\345\256\211\350\243\205VirtualBox\350\277\236\346\216\245U\347\233\230/index.html" +++ "b/2018/02/03/Mac\345\256\211\350\243\205VirtualBox\350\277\236\346\216\245U\347\233\230/index.html" @@ -94,7 +94,7 @@

                                  @@ -166,19 +166,19 @@

                                  Recent Posts

                              @@ -251,19 +251,19 @@

                              Recent Posts

                              • - (no title) + (no title)
                              • - (no title) + (no title)
                              • - (no title) + (no title)
                              • - (no title) + (no title)
                              • diff --git "a/2019/06/18/ES6\350\257\255\346\263\225/index.html" "b/2019/06/18/ES6\350\257\255\346\263\225/index.html" index 30bd656..6be07f0 100644 --- "a/2019/06/18/ES6\350\257\255\346\263\225/index.html" +++ "b/2019/06/18/ES6\350\257\255\346\263\225/index.html" @@ -180,7 +180,7 @@

                                13

                                @@ -252,19 +252,19 @@

                                Recent Posts

                                • - (no title) + (no title)
                                • - (no title) + (no title)
                                • - (no title) + (no title)
                                • - (no title) + (no title)
                                • diff --git "a/2019/06/18/Flutter\345\205\245\351\227\250/index.html" "b/2019/06/18/Flutter\345\205\245\351\227\250/index.html" index 13100e9..fdd085c 100644 --- "a/2019/06/18/Flutter\345\205\245\351\227\250/index.html" +++ "b/2019/06/18/Flutter\345\205\245\351\227\250/index.html" @@ -7,17 +7,17 @@ aTreey's Blog - + - + - + - + @@ -76,13 +76,14 @@

                                  -

                                  flutter 安装

                                  flutter 项目代码

                                  fluter 中一切皆是组件

                                  +

                                  flutter入门

                                  +

                                  flutter 安装

                                  flutter 项目代码

                                  fluter 中一切皆是组件

                                  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
                                  // 导入头文件
                                  import 'package:flutter/material.dart';

                                  // 程序的入口,main 函数
                                  void main() => (MyApp());

                                  // 定义类, 继承

                                  class MyApp extends StatelessWidget {
                                  // 重写
                                  @override

                                  Widget build(BuildContext context) {

                                  // 返回一窗口
                                  return MaterialApp(
                                  title:'Flutter',
                                  home: Scaffold(
                                  appBar: AppBar(
                                  title: Text('flutter 初体验')
                                  ),

                                  body: Center(
                                  child: Text('flutter 学习之路'),
                                  ),
                                  ),
                                  );
                                  }
                                  }
                                  1
                                  2
                                  3
                                  4
                                  5
                                  6
                                  7
                                  8
                                  9
                                  10
                                  11
                                  12
                                  13
                                  14
                                  15
                                  16
                                  17
                                  18
                                  19
                                  20
                                  21
                                  22
                                  23
                                  24
                                  25
                                  26
                                  27
                                  28
                                  29
                                  30
                                  31
                                  32
                                  33
                                  34
                                  35
                                  36
                                  37
                                  38
                                  39
                                  40
                                  41
                                  42
                                  43
                                  44
                                  45
                                  46
                                  47
                                  48
                                  49
                                  50
                                  51
                                  52
                                  53
                                  54
                                  55
                                  56
                                  57
                                  58
                                  59
                                  60
                                  61
                                  62
                                  63
                                  64
                                  65
                                  66
                                  67
                                  68
                                  69
                                  70
                                  71
                                  72
                                  73
                                  74
                                  75
                                  76
                                  77
                                  78
                                  79
                                  80
                                  81
                                  82
                                  83
                                  84
                                  85
                                  86
                                  87
                                  88
                                  89
                                  90
                                  91
                                  92
                                  93
                                  94
                                  95
                                  96
                                  97
                                  98
                                  99
                                  100
                                  101
                                  102
                                  103
                                  104
                                  105
                                  106
                                  107
                                  108
                                  109
                                  110
                                  111
                                  112

                                  import 'package:flutter/material.dart';

                                  void main() => runApp(MyApp());

                                  class MyApp extends StatelessWidget {
                                  // This widget is the root of your application.
                                  @override
                                  Widget build(BuildContext context) {
                                  return MaterialApp(
                                  title: 'Flutter Demo',
                                  theme: ThemeData(
                                  // This is the theme of your application.
                                  //
                                  // Try running your application with "flutter run". You'll see the
                                  // application has a blue toolbar. Then, without quitting the app, try
                                  // changing the primarySwatch below to Colors.green and then invoke
                                  // "hot reload" (press "r" in the console where you ran "flutter run",
                                  // or simply save your changes to "hot reload" in a Flutter IDE).
                                  // Notice that the counter didn't reset back to zero; the application
                                  // is not restarted.
                                  primarySwatch: Colors.blue,
                                  ),
                                  home: MyHomePage(title: 'Flutter Demo Home Page'),
                                  );
                                  }
                                  }

                                  class MyHomePage extends StatefulWidget {
                                  MyHomePage({Key key, this.title}) : super(key: key);

                                  // This widget is the home page of your application. It is stateful, meaning
                                  // that it has a State object (defined below) that contains fields that affect
                                  // how it looks.

                                  // This class is the configuration for the state. It holds the values (in this
                                  // case the title) provided by the parent (in this case the App widget) and
                                  // used by the build method of the State. Fields in a Widget subclass are
                                  // always marked "final".

                                  final String title;

                                  @override
                                  _MyHomePageState createState() => _MyHomePageState();
                                  }

                                  class _MyHomePageState extends State<MyHomePage> {
                                  int _counter = 0;

                                  void _incrementCounter() {
                                  setState(() {
                                  // This call to setState tells the Flutter framework that something has
                                  // changed in this State, which causes it to rerun the build method below
                                  // so that the display can reflect the updated values. If we changed
                                  // _counter without calling setState(), then the build method would not be
                                  // called again, and so nothing would appear to happen.
                                  _counter++;
                                  });
                                  }

                                  @override
                                  Widget build(BuildContext context) {
                                  // This method is rerun every time setState is called, for instance as done
                                  // by the _incrementCounter method above.
                                  //
                                  // The Flutter framework has been optimized to make rerunning build methods
                                  // fast, so that you can just rebuild anything that needs updating rather
                                  // than having to individually change instances of widgets.
                                  return Scaffold(
                                  appBar: AppBar(
                                  // Here we take the value from the MyHomePage object that was created by
                                  // the App.build method, and use it to set our appbar title.
                                  title: Text(widget.title),
                                  ),
                                  body: Center(
                                  // Center is a layout widget. It takes a single child and positions it
                                  // in the middle of the parent.
                                  child: Column(
                                  // Column is also layout widget. It takes a list of children and
                                  // arranges them vertically. By default, it sizes itself to fit its
                                  // children horizontally, and tries to be as tall as its parent.
                                  //
                                  // Invoke "debug painting" (press "p" in the console, choose the
                                  // "Toggle Debug Paint" action from the Flutter Inspector in Android
                                  // Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
                                  // to see the wireframe for each widget.
                                  //
                                  // Column has various properties to control how it sizes itself and
                                  // how it positions its children. Here we use mainAxisAlignment to
                                  // center the children vertically; the main axis here is the vertical
                                  // axis because Columns are vertical (the cross axis would be
                                  // horizontal).
                                  mainAxisAlignment: MainAxisAlignment.center,
                                  children: <Widget>[
                                  Text(
                                  '你可以点击按钮来修改文字,我是热重载的结果',
                                  ),
                                  Text(
                                  '$_counter',
                                  style: Theme.of(context).textTheme.display1,
                                  ),
                                  ],
                                  ),
                                  ),
                                  floatingActionButton: FloatingActionButton(
                                  onPressed: _incrementCounter,
                                  tooltip: 'Increment',
                                  child: Icon(Icons.add),
                                  ), // This trailing comma makes auto-formatting nicer for build methods.
                                  );
                                  }
                                  }
                                  @@ -91,7 +92,7 @@

                                  - + Newer
                                  @@ -154,19 +155,19 @@

                                  Recent Posts

                                  • - (no title) + (no title)
                                  • - (no title) + (no title)
                                  • - (no title) + (no title)
                                  • - (no title) + (no title)
                                  • diff --git "a/2019/06/18/Flutter\345\270\270\347\224\250\347\273\204\344\273\266/index.html" "b/2019/06/18/Flutter\345\270\270\347\224\250\347\273\204\344\273\266/index.html" index fc4f823..a53f758 100644 --- "a/2019/06/18/Flutter\345\270\270\347\224\250\347\273\204\344\273\266/index.html" +++ "b/2019/06/18/Flutter\345\270\270\347\224\250\347\273\204\344\273\266/index.html" @@ -7,17 +7,17 @@ aTreey's Blog - + - + - + - + @@ -76,7 +76,8 @@

                                    -

                                    flutter 常用组件

                                    组件

                                      +

                                      flutter 常用组件

                                      +

                                      组件

                                      • StatefulWidget
                                        • 具有可变状态的窗口部件,使用应用时可随时变化,例如进度条
                                        @@ -114,7 +115,7 @@

                                        - Share + Share

                            @@ -123,7 +124,7 @@

                            - + Newer
                            @@ -133,7 +134,7 @@

                            + Older
                            @@ -186,19 +187,19 @@

                            Recent Posts

                          @@ -194,19 +194,19 @@

                          Recent Posts

                          • - (no title) + (no title)
                          • - (no title) + (no title)
                          • - (no title) + (no title)
                          • - (no title) + (no title)
                          • diff --git "a/2019/06/18/JavaScript\345\237\272\347\241\200/index.html" "b/2019/06/18/JavaScript\345\237\272\347\241\200/index.html" index 049558d..4817837 100644 --- "a/2019/06/18/JavaScript\345\237\272\347\241\200/index.html" +++ "b/2019/06/18/JavaScript\345\237\272\347\241\200/index.html" @@ -130,7 +130,7 @@

                            数组 @@ -202,19 +202,19 @@

                            Recent Posts

                            • - (no title) + (no title)
                            • - (no title) + (no title)
                            • - (no title) + (no title)
                            • - (no title) + (no title)
                            • diff --git "a/2019/06/18/MasonrySnapkit\344\275\277\347\224\250\346\200\273\347\273\223/index.html" "b/2019/06/18/MasonrySnapkit\344\275\277\347\224\250\346\200\273\347\273\223/index.html" index 09710e5..ae3ebdb 100644 --- "a/2019/06/18/MasonrySnapkit\344\275\277\347\224\250\346\200\273\347\273\223/index.html" +++ "b/2019/06/18/MasonrySnapkit\344\275\277\347\224\250\346\200\273\347\273\223/index.html" @@ -235,7 +235,7 @@

                              @@ -307,19 +307,19 @@

                              Recent Posts

            @@ -216,19 +216,19 @@

            Recent Posts

            • - (no title) + (no title)
            • - (no title) + (no title)
            • - (no title) + (no title)
            • - (no title) + (no title)
            • diff --git "a/2019/06/18/React-Native\345\274\200\345\217\221iOS\346\211\223\345\214\205/index.html" "b/2019/06/18/React-Native\345\274\200\345\217\221iOS\346\211\223\345\214\205/index.html" index 1ecaafa..d0443b3 100644 --- "a/2019/06/18/React-Native\345\274\200\345\217\221iOS\346\211\223\345\214\205/index.html" +++ "b/2019/06/18/React-Native\345\274\200\345\217\221iOS\346\211\223\345\214\205/index.html" @@ -125,7 +125,7 @@

              - Share + Share @@ -197,19 +197,19 @@

              Recent Posts

              • - (no title) + (no title)
              • - (no title) + (no title)
              • - (no title) + (no title)
              • - (no title) + (no title)
              • diff --git "a/2019/06/18/ReactNative\344\270\255ref\344\275\277\347\224\250/index.html" "b/2019/06/18/ReactNative\344\270\255ref\344\275\277\347\224\250/index.html" index 6afeb1b..1427dbe 100644 --- "a/2019/06/18/ReactNative\344\270\255ref\344\275\277\347\224\250/index.html" +++ "b/2019/06/18/ReactNative\344\270\255ref\344\275\277\347\224\250/index.html" @@ -104,7 +104,7 @@

                - Share + Share @@ -113,7 +113,7 @@

                - + Newer
                @@ -176,19 +176,19 @@

                Recent Posts

                • - (no title) + (no title)
                • - (no title) + (no title)
                • - (no title) + (no title)
                • - (no title) + (no title)
                • diff --git "a/2019/06/18/Swift \345\215\217\350\256\256\344\275\277\347\224\250/index.html" "b/2019/06/18/Swift \345\215\217\350\256\256\344\275\277\347\224\250/index.html" index b5643bb..876e499 100644 --- "a/2019/06/18/Swift \345\215\217\350\256\256\344\275\277\347\224\250/index.html" +++ "b/2019/06/18/Swift \345\215\217\350\256\256\344\275\277\347\224\250/index.html" @@ -129,7 +129,7 @@

                  - Share + Share @@ -138,7 +138,7 @@

                  - + Newer
                  @@ -148,7 +148,7 @@

                  + Older
                  @@ -201,19 +201,19 @@

                  Recent Posts

                  • - (no title) + (no title)
                  • - (no title) + (no title)
                  • - (no title) + (no title)
                  • - (no title) + (no title)
                  • diff --git "a/2019/06/18/Swift \345\237\272\347\241\200\347\237\245\350\257\206/index.html" "b/2019/06/18/Swift \345\237\272\347\241\200\347\237\245\350\257\206/index.html" index d3ff38b..dc4afcd 100644 --- "a/2019/06/18/Swift \345\237\272\347\241\200\347\237\245\350\257\206/index.html" +++ "b/2019/06/18/Swift \345\237\272\347\241\200\347\237\245\350\257\206/index.html" @@ -176,7 +176,7 @@

                    - Share + Share @@ -185,7 +185,7 @@

                    - + Newer
                    @@ -195,7 +195,7 @@

                    + Older
                    @@ -248,19 +248,19 @@

                    Recent Posts

                    • - (no title) + (no title)
                    • - (no title) + (no title)
                    • - (no title) + (no title)
                    • - (no title) + (no title)
                    • diff --git "a/2019/06/18/SwiftLint\344\275\277\347\224\250/index.html" "b/2019/06/18/SwiftLint\344\275\277\347\224\250/index.html" index feb1811..7b06381 100644 --- "a/2019/06/18/SwiftLint\344\275\277\347\224\250/index.html" +++ "b/2019/06/18/SwiftLint\344\275\277\347\224\250/index.html" @@ -82,7 +82,7 @@

                    @@ -154,19 +154,19 @@

                    Recent Posts

                    • - (no title) + (no title)
                    • - (no title) + (no title)
                    • - (no title) + (no title)
                    • - (no title) + (no title)
                    • diff --git "a/2019/06/18/Swift\344\270\213\346\240\207\345\222\214\350\277\220\347\256\227\347\254\246\351\207\215\350\275\275/index.html" "b/2019/06/18/Swift\344\270\213\346\240\207\345\222\214\350\277\220\347\256\227\347\254\246\351\207\215\350\275\275/index.html" index d731d35..2c97168 100644 --- "a/2019/06/18/Swift\344\270\213\346\240\207\345\222\214\350\277\220\347\256\227\347\254\246\351\207\215\350\275\275/index.html" +++ "b/2019/06/18/Swift\344\270\213\346\240\207\345\222\214\350\277\220\347\256\227\347\254\246\351\207\215\350\275\275/index.html" @@ -132,7 +132,7 @@

                      @@ -151,7 +151,7 @@

                      - + Older
                      @@ -204,19 +204,19 @@

                      Recent Posts

                      • - (no title) + (no title)
                      • - (no title) + (no title)
                      • - (no title) + (no title)
                      • - (no title) + (no title)
                      • diff --git "a/2019/06/18/Swift\345\215\217\350\256\256\345\210\253\345\220\215/index.html" "b/2019/06/18/Swift\345\215\217\350\256\256\345\210\253\345\220\215/index.html" index 929f2d0..aa28385 100644 --- "a/2019/06/18/Swift\345\215\217\350\256\256\345\210\253\345\220\215/index.html" +++ "b/2019/06/18/Swift\345\215\217\350\256\256\345\210\253\345\220\215/index.html" @@ -216,7 +216,7 @@

                        - Share + Share @@ -225,7 +225,7 @@

                        - + Newer
                        @@ -288,19 +288,19 @@

                        Recent Posts

                        • - (no title) + (no title)
                        • - (no title) + (no title)
                        • - (no title) + (no title)
                        • - (no title) + (no title)
                        • diff --git "a/2019/06/18/Swift\345\215\217\350\256\256\346\211\251\345\261\225/index.html" "b/2019/06/18/Swift\345\215\217\350\256\256\346\211\251\345\261\225/index.html" index f6d00df..eaae360 100644 --- "a/2019/06/18/Swift\345\215\217\350\256\256\346\211\251\345\261\225/index.html" +++ "b/2019/06/18/Swift\345\215\217\350\256\256\346\211\251\345\261\225/index.html" @@ -110,7 +110,7 @@

                          @@ -182,19 +182,19 @@

                          Recent Posts

                          • - (no title) + (no title)
                          • - (no title) + (no title)
                          • - (no title) + (no title)
                          • - (no title) + (no title)
                          • diff --git "a/2019/06/18/Swift\346\263\233\345\236\213/index.html" "b/2019/06/18/Swift\346\263\233\345\236\213/index.html" index 08e0d53..01a6f2d 100644 --- "a/2019/06/18/Swift\346\263\233\345\236\213/index.html" +++ "b/2019/06/18/Swift\346\263\233\345\236\213/index.html" @@ -106,7 +106,7 @@

                            - Share + Share @@ -178,19 +178,19 @@

                            Recent Posts

                            • - (no title) + (no title)
                            • - (no title) + (no title)
                            • - (no title) + (no title)
                            • - (no title) + (no title)
                            • diff --git "a/2019/06/18/Swift\351\224\231\350\257\257\345\244\204\347\220\206/index.html" "b/2019/06/18/Swift\351\224\231\350\257\257\345\244\204\347\220\206/index.html" index 6acb5bf..28527b5 100644 --- "a/2019/06/18/Swift\351\224\231\350\257\257\345\244\204\347\220\206/index.html" +++ "b/2019/06/18/Swift\351\224\231\350\257\257\345\244\204\347\220\206/index.html" @@ -87,7 +87,7 @@

                              throw

                        @@ -96,7 +96,7 @@

                        throw

                  @@ -252,23 +252,23 @@

                  Recent Posts

                  diff --git a/2019/06/18/Flutter-md/index.html b/2019/06/18/Flutter-md/index.html new file mode 100644 index 0000000..7e3ca62 --- /dev/null +++ b/2019/06/18/Flutter-md/index.html @@ -0,0 +1,217 @@ + + + + + + + + Flutter.md | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
                  +
                  + +
                  +
                  + +
                  + + +
                  + + +

                  + Flutter.md +

                  + + +
                  + +
                  + +

                  flutter安装

                  +
                  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
                  // 导入头文件
                  import 'package:flutter/material.dart';

                  void main() => (MyApp());

                  // 定义类, 继承

                  class MyApp extends StatelessWidget {
                  // 重写
                  @override

                  Widget build(BuildContext context) {

                  // 返回一窗口
                  return MaterialApp(
                  title:'Flutter',
                  home: Scaffold(
                  appBar: AppBar(
                  title: Text('flutter 初体验')
                  ),

                  body: Center(
                  child: Text('flutter 学习之路'),
                  ),
                  ),
                  );
                  }
                  }
                  +
                  1
                  2
                  3
                  4
                  5
                  6
                  7
                  8
                  9
                  10
                  11
                  12
                  13
                  14
                  15
                  16
                  17
                  18
                  19
                  20
                  21
                  22
                  23
                  24
                  25
                  26
                  27
                  28
                  29
                  30
                  31
                  32
                  33
                  34
                  35
                  36
                  37
                  38
                  39
                  40
                  41
                  42
                  43
                  44
                  45
                  46
                  47
                  48
                  49
                  50
                  51
                  52
                  53
                  54
                  55
                  56
                  57
                  58
                  59
                  60
                  61
                  62
                  63
                  64
                  65
                  66
                  67
                  68
                  69
                  70
                  71
                  72
                  73
                  74
                  75
                  76
                  77
                  78
                  79
                  80
                  81
                  82
                  83
                  84
                  85
                  86
                  87
                  88
                  89
                  90
                  91
                  92
                  93
                  94
                  95
                  96
                  97
                  98
                  99
                  100
                  101
                  102
                  103
                  104
                  105
                  106
                  107
                  108
                  109
                  110
                  111
                  112

                  import 'package:flutter/material.dart';

                  void main() => runApp(MyApp());

                  class MyApp extends StatelessWidget {
                  // This widget is the root of your application.
                  @override
                  Widget build(BuildContext context) {
                  return MaterialApp(
                  title: 'Flutter Demo',
                  theme: ThemeData(
                  // This is the theme of your application.
                  //
                  // Try running your application with "flutter run". You'll see the
                  // application has a blue toolbar. Then, without quitting the app, try
                  // changing the primarySwatch below to Colors.green and then invoke
                  // "hot reload" (press "r" in the console where you ran "flutter run",
                  // or simply save your changes to "hot reload" in a Flutter IDE).
                  // Notice that the counter didn't reset back to zero; the application
                  // is not restarted.
                  primarySwatch: Colors.blue,
                  ),
                  home: MyHomePage(title: 'Flutter Demo Home Page'),
                  );
                  }
                  }

                  class MyHomePage extends StatefulWidget {
                  MyHomePage({Key key, this.title}) : super(key: key);

                  // This widget is the home page of your application. It is stateful, meaning
                  // that it has a State object (defined below) that contains fields that affect
                  // how it looks.

                  // This class is the configuration for the state. It holds the values (in this
                  // case the title) provided by the parent (in this case the App widget) and
                  // used by the build method of the State. Fields in a Widget subclass are
                  // always marked "final".

                  final String title;

                  @override
                  _MyHomePageState createState() => _MyHomePageState();
                  }

                  class _MyHomePageState extends State<MyHomePage> {
                  int _counter = 0;

                  void _incrementCounter() {
                  setState(() {
                  // This call to setState tells the Flutter framework that something has
                  // changed in this State, which causes it to rerun the build method below
                  // so that the display can reflect the updated values. If we changed
                  // _counter without calling setState(), then the build method would not be
                  // called again, and so nothing would appear to happen.
                  _counter++;
                  });
                  }

                  @override
                  Widget build(BuildContext context) {
                  // This method is rerun every time setState is called, for instance as done
                  // by the _incrementCounter method above.
                  //
                  // The Flutter framework has been optimized to make rerunning build methods
                  // fast, so that you can just rebuild anything that needs updating rather
                  // than having to individually change instances of widgets.
                  return Scaffold(
                  appBar: AppBar(
                  // Here we take the value from the MyHomePage object that was created by
                  // the App.build method, and use it to set our appbar title.
                  title: Text(widget.title),
                  ),
                  body: Center(
                  // Center is a layout widget. It takes a single child and positions it
                  // in the middle of the parent.
                  child: Column(
                  // Column is also layout widget. It takes a list of children and
                  // arranges them vertically. By default, it sizes itself to fit its
                  // children horizontally, and tries to be as tall as its parent.
                  //
                  // Invoke "debug painting" (press "p" in the console, choose the
                  // "Toggle Debug Paint" action from the Flutter Inspector in Android
                  // Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
                  // to see the wireframe for each widget.
                  //
                  // Column has various properties to control how it sizes itself and
                  // how it positions its children. Here we use mainAxisAlignment to
                  // center the children vertically; the main axis here is the vertical
                  // axis because Columns are vertical (the cross axis would be
                  // horizontal).
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[
                  Text(
                  '你可以点击按钮来修改文字,我是热重载的结果',
                  ),
                  Text(
                  '$_counter',
                  style: Theme.of(context).textTheme.display1,
                  ),
                  ],
                  ),
                  ),
                  floatingActionButton: FloatingActionButton(
                  onPressed: _incrementCounter,
                  tooltip: 'Increment',
                  child: Icon(Icons.add),
                  ), // This trailing comma makes auto-formatting nicer for build methods.
                  );
                  }
                  }
                  + +
                  + +
                  + + + + + +
                  + +
                  + + + +
                  +
                  + +
                  + +
                  +
                  +
                  + + + + + + + + + + + + + + +
                  + + \ No newline at end of file diff --git "a/2019/06/18/Flutter\345\205\245\351\227\250/index.html" "b/2019/06/18/Flutter\345\205\245\351\227\250/index.html" index fdd085c..2200a16 100644 --- "a/2019/06/18/Flutter\345\205\245\351\227\250/index.html" +++ "b/2019/06/18/Flutter\345\205\245\351\227\250/index.html" @@ -83,7 +83,7 @@

                  - Share + Share @@ -155,23 +155,23 @@

                  Recent Posts

                  diff --git "a/2019/06/18/Flutter\345\270\270\347\224\250\347\273\204\344\273\266/index.html" "b/2019/06/18/Flutter\345\270\270\347\224\250\347\273\204\344\273\266/index.html" index a53f758..7cec5f6 100644 --- "a/2019/06/18/Flutter\345\270\270\347\224\250\347\273\204\344\273\266/index.html" +++ "b/2019/06/18/Flutter\345\270\270\347\224\250\347\273\204\344\273\266/index.html" @@ -115,7 +115,7 @@

                  - Share + Share @@ -187,23 +187,23 @@

                  Recent Posts

                  diff --git "a/2019/06/18/GCD \344\277\241\345\217\267\351\207\217/index.html" "b/2019/06/18/GCD \344\277\241\345\217\267\351\207\217/index.html" index 52cac71..53546b1 100644 --- "a/2019/06/18/GCD \344\277\241\345\217\267\351\207\217/index.html" +++ "b/2019/06/18/GCD \344\277\241\345\217\267\351\207\217/index.html" @@ -122,7 +122,7 @@

                  - Share + Share @@ -194,23 +194,23 @@

                  Recent Posts

                  diff --git "a/2019/06/18/JavaScript\345\237\272\347\241\200/index.html" "b/2019/06/18/JavaScript\345\237\272\347\241\200/index.html" index 4817837..9b315c8 100644 --- "a/2019/06/18/JavaScript\345\237\272\347\241\200/index.html" +++ "b/2019/06/18/JavaScript\345\237\272\347\241\200/index.html" @@ -130,7 +130,7 @@

                  数组 @@ -202,23 +202,23 @@

                  Recent Posts

                  diff --git "a/2019/06/18/MasonrySnapkit\344\275\277\347\224\250\346\200\273\347\273\223/index.html" "b/2019/06/18/MasonrySnapkit\344\275\277\347\224\250\346\200\273\347\273\223/index.html" index ae3ebdb..c1b4cf2 100644 --- "a/2019/06/18/MasonrySnapkit\344\275\277\347\224\250\346\200\273\347\273\223/index.html" +++ "b/2019/06/18/MasonrySnapkit\344\275\277\347\224\250\346\200\273\347\273\223/index.html" @@ -235,7 +235,7 @@

                  @@ -307,23 +307,23 @@

                  Recent Posts

                  diff --git "a/2019/06/18/RN\346\212\245\351\224\231/index.html" "b/2019/06/18/RN\346\212\245\351\224\231/index.html" index ea071ff..5e33b3f 100644 --- "a/2019/06/18/RN\346\212\245\351\224\231/index.html" +++ "b/2019/06/18/RN\346\212\245\351\224\231/index.html" @@ -144,7 +144,7 @@

                  - Share + Share @@ -216,23 +216,23 @@

                  Recent Posts

                  diff --git "a/2019/06/18/React-Native\345\274\200\345\217\221iOS\346\211\223\345\214\205/index.html" "b/2019/06/18/React-Native\345\274\200\345\217\221iOS\346\211\223\345\214\205/index.html" index d0443b3..586b44c 100644 --- "a/2019/06/18/React-Native\345\274\200\345\217\221iOS\346\211\223\345\214\205/index.html" +++ "b/2019/06/18/React-Native\345\274\200\345\217\221iOS\346\211\223\345\214\205/index.html" @@ -125,7 +125,7 @@

                  - Share + Share @@ -197,23 +197,23 @@

                  Recent Posts

                  diff --git "a/2019/06/18/ReactNative\344\270\255ref\344\275\277\347\224\250/index.html" "b/2019/06/18/ReactNative\344\270\255ref\344\275\277\347\224\250/index.html" index 1427dbe..ad1586e 100644 --- "a/2019/06/18/ReactNative\344\270\255ref\344\275\277\347\224\250/index.html" +++ "b/2019/06/18/ReactNative\344\270\255ref\344\275\277\347\224\250/index.html" @@ -104,7 +104,7 @@

                  - Share + Share @@ -176,23 +176,23 @@

                  Recent Posts

                  diff --git "a/2019/06/18/Swift \345\215\217\350\256\256\344\275\277\347\224\250/index.html" "b/2019/06/18/Swift \345\215\217\350\256\256\344\275\277\347\224\250/index.html" index 876e499..b611e71 100644 --- "a/2019/06/18/Swift \345\215\217\350\256\256\344\275\277\347\224\250/index.html" +++ "b/2019/06/18/Swift \345\215\217\350\256\256\344\275\277\347\224\250/index.html" @@ -129,7 +129,7 @@

                  - Share + Share @@ -201,23 +201,23 @@

                  Recent Posts

                  diff --git "a/2019/06/18/Swift \345\237\272\347\241\200\347\237\245\350\257\206/index.html" "b/2019/06/18/Swift \345\237\272\347\241\200\347\237\245\350\257\206/index.html" index dc4afcd..4896d99 100644 --- "a/2019/06/18/Swift \345\237\272\347\241\200\347\237\245\350\257\206/index.html" +++ "b/2019/06/18/Swift \345\237\272\347\241\200\347\237\245\350\257\206/index.html" @@ -176,7 +176,7 @@

                  - Share + Share @@ -248,23 +248,23 @@

                  Recent Posts

                  diff --git "a/2019/06/18/SwiftLint\344\275\277\347\224\250/index.html" "b/2019/06/18/SwiftLint\344\275\277\347\224\250/index.html" index 7b06381..4178680 100644 --- "a/2019/06/18/SwiftLint\344\275\277\347\224\250/index.html" +++ "b/2019/06/18/SwiftLint\344\275\277\347\224\250/index.html" @@ -82,7 +82,7 @@

                @@ -154,23 +154,23 @@

                Recent Posts

                diff --git "a/2019/06/18/Swift\344\270\213\346\240\207\345\222\214\350\277\220\347\256\227\347\254\246\351\207\215\350\275\275/index.html" "b/2019/06/18/Swift\344\270\213\346\240\207\345\222\214\350\277\220\347\256\227\347\254\246\351\207\215\350\275\275/index.html" index 2c97168..a427e43 100644 --- "a/2019/06/18/Swift\344\270\213\346\240\207\345\222\214\350\277\220\347\256\227\347\254\246\351\207\215\350\275\275/index.html" +++ "b/2019/06/18/Swift\344\270\213\346\240\207\345\222\214\350\277\220\347\256\227\347\254\246\351\207\215\350\275\275/index.html" @@ -132,7 +132,7 @@

                @@ -204,23 +204,23 @@

                Recent Posts

                diff --git "a/2019/06/18/Swift\345\215\217\350\256\256\345\210\253\345\220\215/index.html" "b/2019/06/18/Swift\345\215\217\350\256\256\345\210\253\345\220\215/index.html" index aa28385..bb741a3 100644 --- "a/2019/06/18/Swift\345\215\217\350\256\256\345\210\253\345\220\215/index.html" +++ "b/2019/06/18/Swift\345\215\217\350\256\256\345\210\253\345\220\215/index.html" @@ -216,7 +216,7 @@

                - Share + Share @@ -225,7 +225,7 @@

                - + Newer
                @@ -288,23 +288,23 @@

                Recent Posts

                diff --git "a/2019/06/18/Swift\345\215\217\350\256\256\346\211\251\345\261\225/index.html" "b/2019/06/18/Swift\345\215\217\350\256\256\346\211\251\345\261\225/index.html" index eaae360..f90e977 100644 --- "a/2019/06/18/Swift\345\215\217\350\256\256\346\211\251\345\261\225/index.html" +++ "b/2019/06/18/Swift\345\215\217\350\256\256\346\211\251\345\261\225/index.html" @@ -110,7 +110,7 @@

                @@ -182,23 +182,23 @@

                Recent Posts

                diff --git "a/2019/06/18/Swift\346\263\233\345\236\213/index.html" "b/2019/06/18/Swift\346\263\233\345\236\213/index.html" index 01a6f2d..86ff0d0 100644 --- "a/2019/06/18/Swift\346\263\233\345\236\213/index.html" +++ "b/2019/06/18/Swift\346\263\233\345\236\213/index.html" @@ -106,7 +106,7 @@

                - Share + Share @@ -178,23 +178,23 @@

                Recent Posts

                diff --git "a/2019/06/18/Swift\351\224\231\350\257\257\345\244\204\347\220\206/index.html" "b/2019/06/18/Swift\351\224\231\350\257\257\345\244\204\347\220\206/index.html" index 28527b5..d488cf9 100644 --- "a/2019/06/18/Swift\351\224\231\350\257\257\345\244\204\347\220\206/index.html" +++ "b/2019/06/18/Swift\351\224\231\350\257\257\345\244\204\347\220\206/index.html" @@ -87,7 +87,7 @@

                throw

                @@ -96,7 +96,7 @@

                throw

              -

              动态添加方法

                -
              • perforSelector: 实际上是考察class_addMethod 的方法使用
              • +

                内存管理方案

                  +
                • 小对象使用 TaggedPointer
                • +
                • 64 位架构下面使用的是 NONPONINT_ISA 内存管理方案,非指针型的 isa,内部存储了一些
                • +
                • 散列表结构 (Side Tables() 结构)
                    +
                  • 自旋锁
                  • +
                  • 引用计数表
                  • +
                  • 弱引用表
                  • +
                  • Side Tables() 结构
                  -

                  动态方法解析

                    -
                  • @dynamic关键字
                  • -
                  • 动态运行时语言将函数决议推迟到运行时
                  • -
                  • 编译时不生成setter/getter 方法,而是在运行时才添加具体的执行函数
                  • +
                  -

                  问题

                  [obj foo] 和 obj_msgSend()函数之间有什么关系?

                    -
                  • [obj foo]经过编译器处理过后会变成 obj_msgSend()有两个参数,一个是obj,第二个参数是foo 选择器
                  • +

                    为什么不是一个SideTable,而是多个组成了Side Tables

                    如果所有对象的存储都放在一张大表当中,因为每个对象是在不同的线程中创建的,要操作其中一个对象时,需要将表加锁处理保证数据安全,要等锁释放之后才能操作下一个对象,此时存在效率问题,引入分离锁技术方案

                    +
                      +
                    • 分离锁:把引用计数表分成多张表进行,
                    • +
                    • Side Tables 本质是一张Hash 表
                    -

                    runtime 如何通过Selector找到对应的IMP地址的?

                    消息传递机制

                    -

                    能否向编译后的类增加实例变量

                    编译之前就完成了实例变量的布局,
                    runtime_r_t 表示是readonly 的,编译后的类是不能添加实例变量的,可以向动态添加的类中添加实例变量

                    +

                    +

                    自旋锁

                    是“忙等”的锁,如果当前锁已被其他线程获取,当前线程会不断探测是否被释放,会第一时间获取,而其他锁比如信号量当它获取不到锁时,会把自己的线程阻塞休眠,等到其他线程释放这个锁时再唤醒这个锁

                    +
                      +
                    • 适用于轻量访问
                    • +
                    +

                    引用计数表

                    使用过Hash表来实现,hash查找提高了查找高效率,插入和获取是通过Hash 算法来是实现的,避免了for循环

                    +

                    alloc 实现

                    经过一系列调用,最终调用了C函数 calloc, 并没有引用计数+1

                    +

                    retain 实现

                    底层经过了2次Hash表查找

                    +

                    +

                    release 实现

                    和 retain 实现相反

                    +

                    dealloc 实现原理

                    +

                    +

                    objc_destructInstance() 实现

                    +

                    clearDeallocating() 实现

                    +

                    MRC

                    手动引用计数

                    +

                    ARC

                      +
                    • 编译器在对应位置自动插入retain 和 release 操作,并和runtime协作达到自动引用计数管理内存的效果

                      +
                    • +
                    • ARC 中禁止手动调用 retain/release

                      +
                    • +
                    • ARC 中新增 weeak、strong 等关键字
                    • +
                    +

                    弱引用管理

                    被声明为__weak 的对象指针经过编译后会进过调用以下方法,在最终的weak_register_no_lock() 方法中进行弱引用变量的添加。添加的位置是通过Hash算法查找的

                    +

                    +

                    weak 修饰的对象,释放时置为nil 如何实现的

                    当一个对象被 dealloc 之后,在dealloc内部实现当中会调用弱引用清除的相关函数,在相关函数当中会根据当前对象指针查找弱引用表,把当前对象下对应的弱应用都取出,遍历弱应用指针并置为nil。

                    +

                    自动释放池

                    是以栈为结点通过双向链表的形式组合而成,和线程一一对应

                    +

                    Autoreleasepool

                    +

                    循环引用

                    自循环引用

                    一个对象中有拥有一个 强持有他的 obj,如果给 obj 赋值为原对象就会造成自循环引用

                    +

                    相互循环引用

                      +
                    • 代理
                    • +
                    • Block
                    • +
                    • NSTimer
                    • +
                    • 大环多循环
                    • +
                    +

                    多循环引用

                    +

                    解决循环引用的方案

                    __weak

                    解决相互循环引用

                    +

                    __block

                    +

                    __unsafe_unretained

                    一般不建议使用

                    +

                    @@ -257,7 +273,7 @@

                    @@ -353,7 +369,220 @@

                    - Share + Share + + + + + +

    + + + +
    + +
    + + +
    + +

    Swift中内存管理

    +

    强引用循环和 weak 关键字

    房东

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class Landlord {
    var name: String
    var house: House?

    init(name: String) {
    self.name = name
    }

    deinit {
    print("Landlord内存释放")
    }
    }
    +

    房子

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class House {
    var name: String
    var landlord: Landlord?

    init(person: String) {
    self.name = person
    }

    deinit {
    print("House内存释放")
    }
    }
    +

    循环引用

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    func strongRetain() {
    var landlord: Landlord? = Landlord(name: "老李")
    var house: House? = House(person: "望京府邸")
    // 房东有个房子
    landlord?.house = house
    // 房子有个主人
    house?.landlord = landlord

    landlord = nil
    house = nil
    }
    +
      +
    • 虽然 landlord = nil 和 house = nil,但是 Landlord 和 House 内存空间并没有释放 (并未执行 deinit 函数)

      +
    • +
    • 原因:因为两个内存空间相互引用,引用计数不为0,形成了强循环引用

      +
    • +
    • 解决办法:使用weak 关键字来修饰两者中其中一个变量

      +
        +
      • 使用weak修饰的变量必须是可选类型,可以赋值为nil
      • +
      • 使用weak修饰必须是var 类型变量
      • +
      +
    • +
    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class House {
    var name: String
    weak var landlord: Landlord?

    init(person: String) {
    self.name = person
    }

    deinit {
    print("House内存释放")
    }
    }
    +

    unowned 关键字的使用

    相互循环引用的两个变量有一个是可选的

    有个身份证的类,每个身份证肯定对应一个人

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    class ID {
    var number: String
    let person: Landlord

    init(person: Landlord, number: String) {
    self.person = person
    self.number = number
    }

    deinit {
    print("ID内存释放")
    }
    }

    ```

    房东
    +

    class Landlord {
    var name: String
    var house: House?
    /// 身份证可以为nil,
    var id: ID?

    +
    init(name: String) {
    +    self.name = name
    +}
    +
    +deinit {
    +    print("Landlord内存释放")
    +}
    +

    }

    +
    1
    2

    循环引用
    +

    func unownedTest() {
    var person: Landlord? = Landlord(name: “老王”)
    var id: ID? = ID(person: person!, number: “10010”)

    +
    person?.id = id
    +person = nil
    +id = nil
    +

    }

    +
    1
    2
    3
    4
    5
    6
    7
    8

    - person 和 id 之间有强引用关系
    - 每个id必须有一个对应的人并且是固定的只能使用let 修饰,不能为nil, 而id可以为空
    - unowned和 weak 作用相同都是弱引用,只是unowned 修改的变量不能是nil (可选型的)
    - unowned只能使用在 class 类型上,不能修饰函数类型,例如闭包存在循环引用时不能使用unowned修饰
    - 解决方法:
    - 此时可以通过使用 weak 修饰 id 的方式,因为 id 可以为nil
    - let 修饰并且不能为nil 这种情况可以使用 unowned 修饰解决
    +
    class ID {
    +    var number: String
    +    unowned let person: Landlord
    +
    +    init(person: Landlord, number: String) {
    +        self.person = person
    +        self.number = number
    +    }
    +
    +    deinit {
    +        print("ID内存释放")
    +    }
    +}
    +
    1
    2
    3
    4
    	
    **unowned有一定的危险性**

    因为unowned 修饰的对象不能赋值为nil,所以执行下面的代码会crash,
    +

    func unownedTest() {
    var person: Landlord? = Landlord(name: “老王”)
    var id: ID? = ID(person: person!, number: “10010”)

    +
    person?.id = id
    +
    +// 提前释放 person 内存
    +person = nil
    +
    +// 获取身份证对应的人
    +print(id?.person) // 此处奔溃,
    +let owner = id?.person
    +id = nil
    +

    }

    1
    2

    正确的写法因该是先将🆔设置为nil,再将 Person 设置为nil,再调用 print(id?.person) 就不会再报错

    +

    func unownedTest() {
    var person: Landlord? = Landlord(name: “老王”)
    var id: ID? = ID(person: person!, number: “10010”)

    +
    person?.id = id
    +
    +id = nil
    +
    +// 提前释放 person 内存
    +person = nil
    +
    +// 获取身份证对应的人
    +print(id?.person) // 此处奔溃,
    +let owner = id?.person
    +

    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

    #### 相互循环引用的两个变量都不是可选的

    使用隐式可选类型

    ### 闭包中的强引用循环

    - 并不是所有的闭包都会产生循环引用,只有相互引用的两个对象会产生循环引用

    - 使用swift闭包捕获列表,在闭包的声明参数之前使用 [unowned self], 不需要解包

    - 使用 unowned表示这个对象不能为空,可能存在风险,如果对象为空就会crash


    使用 unowned 对象不能为空,可能存在风险

    +

    class Closure {
    var closure: ((Int) -> Void)?

    +
    init() {
    +    closure = { [unowned self] index in
    +        print(self)
    +        print(index)
    +    }
    +}
    +

    }

    1
    2

    使用 weak, 此时需要解包

    +

    closure = { [weak self] index in
    // 解包
    if let self = self {
    print(self)
    print(index)
    }
    }

    1
    2

    强制解包

    +

    closure = { [weak self] index in
    // 强制解包
    print(self!)
    print(index)
    }
    `

    + + +
    + +
    + +
    + + + +
    + +
    + + +
    + +

    ReactNative 中使用 TypeScript

    +

    从RN0.57版本开始已经可以直接支持typescript,无需任何配置。在保留根入口文件index.js的前提下,其他文件都可以直接使用.ts或.tsx后缀。但这一由babel进行的转码过程并不进行实际的类型检查,因此仍然需要编辑器和插件来共同进行类型检查

    +
    +

    TypeScript和JavaScript对比

    两者深度对比

    +

    最大的区别:

    +
      +
    • TypeScript是强类型语⾔和静态类型语⾔(需要指定类型)
    • +
    • JavaScript是弱类型语⾔和动态类型语⾔(不需要指定类型)
    • +
    +

    TypeScript

    由微软开发的自由和开源的编程语言,是JavaScript的一个严格超集,并添加了可选的静态类型和基于类的面向对象编程

    +

    TypeScript定义

    +

    安装及语法

    教程

    +
    安装
    1
    npm install -g typescript
    +
    安装 typescript 依赖管理器 typings
    1
    npm install -g typings
    +
    安装 typings 依赖
    1
    2
    typings install npm~react --save
    typings install dt~react-native --globals --save
    +
    使用 yarn 安装

    如果已经安装了 yarn 安装如下:

    +
    1
    2
    yarn add tslib @types/react @types/react-native
    yarn add --dev react-native-typescript-transformer typescript
    +
    创建 tsconfig.json 配置文件
      +
    • cd 到 ReactNative 项目根文件夹下

      +
      1
      tsc --init
      +
    • +
    • 修改 tsconfig.json 文件

      +

      如果一个目录下存在一个tsconfig.json文件,那么它意味着这个目录是TypeScript项目的根目录。 tsconfig.json文件中指定了用来编译这个项目的根文件和编译选项

      +

      tsconfig.json文件配置详解

      +

      TypeScript配置文件tsconfig简析

      +

      务必设置”jsx”:”react”
      注意多余的注释可能会不兼容,需要移除

      +
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      {
      "compilerOptions": {
      "target": "es6",
      "allowJs": true,
      "jsx": "react",
      "outDir": "./dist",
      "sourceMap": true,
      "noImplicitAny": false
      },

      // "files"属性 :指定一个包含相对或绝对文件路径的列表。 "include"和"exclude"属性指定一个文件glob匹配模式列表

      "include": [
      "typings/**/*.d.ts",
      "src/**/*.ts",
      "src/**/*.tsx"
      ],
      "exclude": [
      "node_modules"
      ]
      }
      +
    • +
    • cd 到项目根目录中新建存放 typescripe 源代码的文件夹

      +
    • +
    +
    建立 rn-cli.config.js 文件,使用支持typescript的transfomer
    1
    2
    3
    4
    5
    6
    7
    8
    module.exports = {
    getTransformModulePath() {
    return require.resolve('react-native-typescript-transformer');
    },
    getSourceExts() {
    return ['ts', 'tsx'];
    }
    }
    +
    修改 .babelrc 文件
    1
    2
    3
    4
    {
    "presets": ["react-native"],
    "plugins": ["transform-decorators-legacy"]
    }
    +
    修改项目中文件导入
      +
    • 入口index.jsApp.js的文件名请不要修改,项目根目录下新建src文件夹
    • +
    • src文件夹下新建 .tsx 后缀的文件
    • +
    • .tsx 后缀的文件引用react的写法有所区别

      +
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      import * as React from 'react'
      import { Text, View } from 'react-native';
      import { Component } from 'react';

      export default class HelloWorld extends Component{
      render() {
      return (
      <View>
      <Text>Hello!</Text>
      <Text>Typescript</Text>
      </View>
      );
      }
      }
      +
    • +
    • App.js中使用

      +
        +
      • App.js 导入方式修改为如下方式

        +
        1
        2
        3
        4
        	import './src';
        ```

        - 导入 .tsx 类型的组建
        +

        import HelloWorld from “./src/HelloWorld”;

        +
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11

        ### 配置jsconfig

        `jsconfig.json`目录中存在文件表明该目录是JavaScript项目的根目录。该jsconfig.json文件指定根文件和JavaScript语言服务提供的功能选项

        [jsconfig详解](https://code.visualstudio.com/docs/languages/jsconfig)

        [jsconfig.json](https://jeasonstudio.gitbooks.io/vscode-cn-doc/content/md/%E8%AF%AD%E8%A8%80/javascript.html?q=)


        ##### 新建 `jsconfig.json` 文件
        +
      • +
      +
    • +
    +

    touch jsconfig.json

    1
    2

    ##### 编辑 `jsconfig.json` 文件

    +

    {
    “compilerOptions”: {
    “allowJs”: true,
    “allowSyntheticDefaultImports”: true,
    “emitDecoratorMetadata”: true
    },
    “exclude”: [
    “node_modules”,
    “rnApp/Tools” // 项目根目录下新建的存放RN中所用的一些工具类
    ]
    }

    +
    1
    2
    3


    ### package.json 文件配置
    +

    {
    “name”: “RNProject”,
    “version”: “0.0.1”,
    “private”: true,
    “scripts”: { // 通过设置这个可以使npm调用一些命令脚本,封装一些功能
    “start”: “node node_modules/react-native/local-cli/cli.js start”,
    “test”: “jest”
    },
    “dependencies”: { // 发布后依赖的包
    “@types/react”: “^16.4.16”,
    “@types/react-native”: “^0.57.4”,
    “react”: “16.5.0”,
    “react-native”: “0.57.2”,
    “react-transform-hmr”: “^1.0.4”,
    “tslib”: “^1.9.3”
    },

    +

    “devDependencies”: { // 开发中依赖的其它包
    “babel-jest”: “23.6.0”,
    “jest”: “23.6.0”,
    “metro-react-native-babel-preset”: “0.48.0”,
    “react-native-typescript-transformer”: “^1.2.10”,
    “react-test-renderer”: “16.5.0”,
    “typescript”: “^3.1.2”
    },

    +

    “jest”: { // JavaScript 的单元测试框架
    “preset”: “react-native”
    }
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9

    ##### `–save` 和 `-–save-dev` 区别

    - --save参数表示将该模块写入dependencies属性,
    - --save-dev表示将该模块写入devDependencies属性。

    ##### 添加开发依赖库

    - 类型检查

    +
    npm install @types/jest --save-dev
    +npm install @types/react --save-dev
    +npm install @types/react-native --save-dev 
    +
    +
    1
    - 装饰器转化核心插件
    + +npm install babel-plugin-transform-decorators-legacy --save-dev +
    1
    2
    	
    - 测试框架
    + +npm install react-addons-test-utils --save-dev +npm install react-native-mock --save-dev +
    1
    2

    ##### 删除依赖库
    +

    npm uninstall @types/jest –save

    1
    2


    +

    npm uninstall @types/jest –save-dev

    1
    2

    ##### 彻底删除,-d 表示 devDependencies, -O 表示 optionalDependencies 中

    +

    npm uninstall -s -D -O @types/jest

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    ### 报错汇总

    ##### 错误1 `Cannot find module 'react-transform-hmr/lib/index.js'`

    [解决1](https://stackoverflow.com/questions/50142561/cannot-find-module-react-transform-hmr-lib-index-js?noredirect=1&lq=1)

    [解决2](https://github.com/facebook/react-native/issues/21530)

    - 安装 `react-transform-hmr`
    +
    npm install react-transform-hmr --save
    +
    1
    2

    - 清除缓存重新启动服务
    + +npm start --reset-cache +``` +
    + +
    + @@ -567,7 +796,7 @@
    - Share + Share @@ -577,111 +806,9 @@
    +
    -
    - - -
    - -

    ReactNative 中使用 TypeScript

    -

    从RN0.57版本开始已经可以直接支持typescript,无需任何配置。在保留根入口文件index.js的前提下,其他文件都可以直接使用.ts或.tsx后缀。但这一由babel进行的转码过程并不进行实际的类型检查,因此仍然需要编辑器和插件来共同进行类型检查

    -
    -

    TypeScript和JavaScript对比

    两者深度对比

    -

    最大的区别:

    -
      -
    • TypeScript是强类型语⾔和静态类型语⾔(需要指定类型)
    • -
    • JavaScript是弱类型语⾔和动态类型语⾔(不需要指定类型)
    • -
    -

    TypeScript

    由微软开发的自由和开源的编程语言,是JavaScript的一个严格超集,并添加了可选的静态类型和基于类的面向对象编程

    -

    TypeScript定义

    -

    安装及语法

    教程

    -
    安装
    1
    npm install -g typescript
    -
    安装 typescript 依赖管理器 typings
    1
    npm install -g typings
    -
    安装 typings 依赖
    1
    2
    typings install npm~react --save
    typings install dt~react-native --globals --save
    -
    使用 yarn 安装

    如果已经安装了 yarn 安装如下:

    -
    1
    2
    yarn add tslib @types/react @types/react-native
    yarn add --dev react-native-typescript-transformer typescript
    -
    创建 tsconfig.json 配置文件
      -
    • cd 到 ReactNative 项目根文件夹下

      -
      1
      tsc --init
      -
    • -
    • 修改 tsconfig.json 文件

      -

      如果一个目录下存在一个tsconfig.json文件,那么它意味着这个目录是TypeScript项目的根目录。 tsconfig.json文件中指定了用来编译这个项目的根文件和编译选项

      -

      tsconfig.json文件配置详解

      -

      TypeScript配置文件tsconfig简析

      -

      务必设置”jsx”:”react”
      注意多余的注释可能会不兼容,需要移除

      -
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      {
      "compilerOptions": {
      "target": "es6",
      "allowJs": true,
      "jsx": "react",
      "outDir": "./dist",
      "sourceMap": true,
      "noImplicitAny": false
      },

      // "files"属性 :指定一个包含相对或绝对文件路径的列表。 "include"和"exclude"属性指定一个文件glob匹配模式列表

      "include": [
      "typings/**/*.d.ts",
      "src/**/*.ts",
      "src/**/*.tsx"
      ],
      "exclude": [
      "node_modules"
      ]
      }
      -
    • -
    • cd 到项目根目录中新建存放 typescripe 源代码的文件夹

      -
    • -
    -
    建立 rn-cli.config.js 文件,使用支持typescript的transfomer
    1
    2
    3
    4
    5
    6
    7
    8
    module.exports = {
    getTransformModulePath() {
    return require.resolve('react-native-typescript-transformer');
    },
    getSourceExts() {
    return ['ts', 'tsx'];
    }
    }
    -
    修改 .babelrc 文件
    1
    2
    3
    4
    {
    "presets": ["react-native"],
    "plugins": ["transform-decorators-legacy"]
    }
    -
    修改项目中文件导入
      -
    • 入口index.jsApp.js的文件名请不要修改,项目根目录下新建src文件夹
    • -
    • src文件夹下新建 .tsx 后缀的文件
    • -
    • .tsx 后缀的文件引用react的写法有所区别

      -
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      import * as React from 'react'
      import { Text, View } from 'react-native';
      import { Component } from 'react';

      export default class HelloWorld extends Component{
      render() {
      return (
      <View>
      <Text>Hello!</Text>
      <Text>Typescript</Text>
      </View>
      );
      }
      }
      -
    • -
    • App.js中使用

      -
        -
      • App.js 导入方式修改为如下方式

        -
        1
        2
        3
        4
        	import './src';
        ```

        - 导入 .tsx 类型的组建
        -

        import HelloWorld from “./src/HelloWorld”;

        -
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11

        ### 配置jsconfig

        `jsconfig.json`目录中存在文件表明该目录是JavaScript项目的根目录。该jsconfig.json文件指定根文件和JavaScript语言服务提供的功能选项

        [jsconfig详解](https://code.visualstudio.com/docs/languages/jsconfig)

        [jsconfig.json](https://jeasonstudio.gitbooks.io/vscode-cn-doc/content/md/%E8%AF%AD%E8%A8%80/javascript.html?q=)


        ##### 新建 `jsconfig.json` 文件
        -
      • -
      -
    • -
    -

    touch jsconfig.json

    1
    2

    ##### 编辑 `jsconfig.json` 文件

    -

    {
    “compilerOptions”: {
    “allowJs”: true,
    “allowSyntheticDefaultImports”: true,
    “emitDecoratorMetadata”: true
    },
    “exclude”: [
    “node_modules”,
    “rnApp/Tools” // 项目根目录下新建的存放RN中所用的一些工具类
    ]
    }

    -
    1
    2
    3


    ### package.json 文件配置
    -

    {
    “name”: “RNProject”,
    “version”: “0.0.1”,
    “private”: true,
    “scripts”: { // 通过设置这个可以使npm调用一些命令脚本,封装一些功能
    “start”: “node node_modules/react-native/local-cli/cli.js start”,
    “test”: “jest”
    },
    “dependencies”: { // 发布后依赖的包
    “@types/react”: “^16.4.16”,
    “@types/react-native”: “^0.57.4”,
    “react”: “16.5.0”,
    “react-native”: “0.57.2”,
    “react-transform-hmr”: “^1.0.4”,
    “tslib”: “^1.9.3”
    },

    -

    “devDependencies”: { // 开发中依赖的其它包
    “babel-jest”: “23.6.0”,
    “jest”: “23.6.0”,
    “metro-react-native-babel-preset”: “0.48.0”,
    “react-native-typescript-transformer”: “^1.2.10”,
    “react-test-renderer”: “16.5.0”,
    “typescript”: “^3.1.2”
    },

    -

    “jest”: { // JavaScript 的单元测试框架
    “preset”: “react-native”
    }
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9

    ##### `–save` 和 `-–save-dev` 区别

    - --save参数表示将该模块写入dependencies属性,
    - --save-dev表示将该模块写入devDependencies属性。

    ##### 添加开发依赖库

    - 类型检查

    -
    npm install @types/jest --save-dev
    -npm install @types/react --save-dev
    -npm install @types/react-native --save-dev 
    -
    -
    1
    - 装饰器转化核心插件
    - -npm install babel-plugin-transform-decorators-legacy --save-dev -
    1
    2
    	
    - 测试框架
    - -npm install react-addons-test-utils --save-dev -npm install react-native-mock --save-dev -
    1
    2

    ##### 删除依赖库
    -

    npm uninstall @types/jest –save

    1
    2


    -

    npm uninstall @types/jest –save-dev

    1
    2

    ##### 彻底删除,-d 表示 devDependencies, -O 表示 optionalDependencies 中

    -

    npm uninstall -s -D -O @types/jest

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    ### 报错汇总

    ##### 错误1 `Cannot find module 'react-transform-hmr/lib/index.js'`

    [解决1](https://stackoverflow.com/questions/50142561/cannot-find-module-react-transform-hmr-lib-index-js?noredirect=1&lq=1)

    [解决2](https://github.com/facebook/react-native/issues/21530)

    - 安装 `react-transform-hmr`
    -
    npm install react-transform-hmr --save
    -
    1
    2

    - 清除缓存重新启动服务
    - -npm start --reset-cache -``` -
    - -
    - -
    - -
    - - - -
    - @@ -978,100 +1031,7 @@

    - Share - - - -

    - -
    - - - -
    - -
    - - -
    - -

    swift类型检查和类型转换

    -

    is 关键字类型检查

    判断父类和子类之间的关系

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    func typeCheck() {
    let programmer = Programmer(name: "老张-php")
    if dev is iOSDeveloper {
    print("iOS 开发者")
    } else {
    print("其他开发者")
    }

    if programmer is Coder {
    print("老张-php是一农个码农")
    } else {
    print("")
    }
    }
    -

    as 关键字类型转换

    1
    2
    let ios = iosDev as? Programmer
    let ios2 = iosDev as! Coder
    -

    判断是否遵守了协议

    swift 中协议可以当作是一个类型使用

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    et ios = iosDev as? Programmer
    let ios2 = iosDev as Coder

    // 是否遵守了 Person 协议
    if iosDev is Person {
    print("遵守了Person协议")
    }

    let devs = [dev, iosDev, programmer] as [Any]

    for dev in devs {
    if let dev = dev as? Person {
    print(dev)
    print("遵守Person协议")
    } else {
    print("未遵守Person协议")
    }
    }
    -

    AnyObject Any

    1
    2
    3
    4
    var anyObjectArray: [AnyObject] = [CGFloat(0.5) as AnyObject,
    1 as AnyObject,
    "string" as AnyObject,
    iOSODev]
    -

    swift是面向函数编程, 函数是一等公民,数组中可以存入一个函数,函数表达的是一个过程,不是一个名词或者物体,所以函数不是一个对象,需要使用 any 关键字

    -
    1
    2
    3
    4
    var anyArray: [Any] = [CGFloat(0.5),
    1,
    "string",
    iOSODev]
    -

    放入函数

    -
    1
    anyArray.append({ (a: Int) -> (Int) in return a * a })
    - - -
    - -
    - -
    - - - -
    - -
    - - -
    - -

    ReactNative配置.npm 和cnpm 相关

    npm

    Nodejs的包管理工具

    -

    npm 常用命令

      -
    • 更新或卸载

      -
      1
      npm update/uninstall moduleName
      -
    • -
    • 查看当前目录下已安装的包

      -
      1
      npm list
      -
    • -
    • 查看全局安装的包的路径

      -
      1
      npm root -g
      -
    • -
    • 全部命令

      -
      1
      npm help
      -
    • -
    -

    cnpm

    因为npm安装插件是从国外服务器下载,受网络影响大,可能出现异常,cnpm 淘宝镜像

    -

    安装

    1
    npm install -g cnpm --registry=https://registry.npm.taobao.org
    -

    设置镜像源

    1
    2
    cnpm set registry http://registry.cnpm.taobao.com
    npm set registry http://registry.cnpm.taobao.com
    -

    配置 .npmrc 文件

    前端项目开发离不开安装各种npm依赖包,可以选择远程的仓库也可以选择本地的仓库,但更改仓库地址需要在安装时控制台打命令,比较麻烦, 而npmrc可以很方便地解决上面问题

    -

    .npmrc 原理

    当安装项目的依赖包时,会查找并读取项目根目录下的.npmrc文件的配置,自动指定仓库地址

    -
      -
    • 打开 .npmrc

      -
      1
      open .npmrc
      -
    • -
    -
      -
    • 编辑文件, 指定特定的库的镜像源, 比如以@test开头的库镜像为:http://registry.cnpm.test.com/ 而其他的正常

      -
      1
      2
      @test:registry=http://registry.cnpm.lietou.com/
      registry=https://registry.npm.taobao.org/
      -
    • -
    - - -
    - @@ -1131,23 +1091,23 @@

    Recent Posts

    diff --git a/page/3/index.html b/page/3/index.html index f9afd9e..4e46867 100644 --- a/page/3/index.html +++ b/page/3/index.html @@ -62,6 +62,62 @@

    +
    + +
    + + +
    + +

    ReactNative配置.npm 和cnpm 相关

    npm

    Nodejs的包管理工具

    +

    npm 常用命令

      +
    • 更新或卸载

      +
      1
      npm update/uninstall moduleName
      +
    • +
    • 查看当前目录下已安装的包

      +
      1
      npm list
      +
    • +
    • 查看全局安装的包的路径

      +
      1
      npm root -g
      +
    • +
    • 全部命令

      +
      1
      npm help
      +
    • +
    +

    cnpm

    因为npm安装插件是从国外服务器下载,受网络影响大,可能出现异常,cnpm 淘宝镜像

    +

    安装

    1
    npm install -g cnpm --registry=https://registry.npm.taobao.org
    +

    设置镜像源

    1
    2
    cnpm set registry http://registry.cnpm.taobao.com
    npm set registry http://registry.cnpm.taobao.com
    +

    配置 .npmrc 文件

    前端项目开发离不开安装各种npm依赖包,可以选择远程的仓库也可以选择本地的仓库,但更改仓库地址需要在安装时控制台打命令,比较麻烦, 而npmrc可以很方便地解决上面问题

    +

    .npmrc 原理

    当安装项目的依赖包时,会查找并读取项目根目录下的.npmrc文件的配置,自动指定仓库地址

    +
      +
    • 打开 .npmrc

      +
      1
      open .npmrc
      +
    • +
    +
      +
    • 编辑文件, 指定特定的库的镜像源, 比如以@test开头的库镜像为:http://registry.cnpm.test.com/ 而其他的正常

      +
      1
      2
      @test:registry=http://registry.cnpm.lietou.com/
      registry=https://registry.npm.taobao.org/
      +
    • +
    + + +
    + +
    + +
    + + + - - - -
    - -
    - - -
    - -

    Swift已有项目中集成ReactNative

    -

    本教程针对于已经有的Swift项目集成ReactNative

    -

    创建新的空目录

    为了不影响原有的swift项目目录结构,建议先创建一个空的文件夹,例如SwiftRN

    -

    -

    创建package.json文件

    终端进入创建的SwiftRN文件目录下,创建package.json文件

    -
    1
    touch package.json
    -
    修改package.json文件,指定各个版本
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    {
    "name": "SwiftRN",
    "version": "0.0.1",
    "private": true,
    "scripts": {
    "start": "node node_modules/react-native/local-cli/cli.js start",
    "test": "jest"
    },
    "dependencies": {
    "react": "16.3.1",
    "react-native": "^0.56.0"
    }
    }
    -
    执行npm install

    在创建package.json所在的文件目录下执行,在此过程中可以遇到的错误可能较多

    -
    1
    npm install
    -
    添加index.js文件
    1
    touch index.js
    -
    修改文件内容
    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
    import {
    AppRegistry,
    Text,
    View,
    } from 'react-native';

    import React, { Component } from 'react';

    // 用于最后在项目中测试
    class SwiftRNHomeView extends Component {
    render() {
    return(
    <View style={{backgroundColor:'white'}}>
    <Text style={{fontSize: 20, marginTop: 200}}>Hellow world</Text>
    </View>
    );
    }
    }


    class MyApp extends Component {
    render() {
    return <SwiftRNHomeView></SwiftRNHomeView>;
    }
    }

    AppRegistry.registerComponent('SwiftRN', () => MyApp);
    -

    配置swift项目

    将新创建的Swift文件目录下的所有文件拷贝到Swift项目所在的根目录下

    -

    -

    使用Pod集成RN

    swift项目采用了pod管理第三方库,在Podfile文件中加入下面代码

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    pod 'React', :path => './node_modules/react-native', :subspecs => [
    'Core',
    'RCTText',
    'RCTNetwork',
    'RCTWebSocket', # 这个模块是用于调试功能的
    'CxxBridge', # Include this for RN >= 0.47,不引入的话会报错误或者警告
    'DevSupport', # Include this to enable In-App Devmenu if RN >= 0.43,不引入的话会报错误或者警告
    'RCTAnimation', 不引入的话会报错误或者警告
    ]


    # 如果你的RN版本 >= 0.42.0,请加入下面这行,不引入的话会报错误或者警告
    pod "yoga", :path => "./node_modules/react-native/ReactCommon/yoga"
    pod 'DoubleConversion', :podspec => './node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
    pod 'glog', :podspec => './node_modules/react-native/third-party-podspecs/glog.podspec'
    pod 'Folly', :podspec => './node_modules/react-native/third-party-podspecs/Folly.podspec'
    -

    创建桥接文件

      -
    • 如果原项目中有SwiftDemo-Bridging-Header.h类似这个文件可以跳过。
    • -
    • 如果从来没有创建过桥接文件,创建一个OC的类会自动弹出是否需要创建桥接文件,直接创建即可,最后把无用的OC文件删掉
    • -
    -

    引用RN头文件

    在桥接文件中导入头文件

    -
    1
    2
    #import <React/RCTRootView.h>
    #import <React/RCTBundleURLProvider.h>
    -

    使用RN页面

    在模拟器中使用RN的页面, 确保在 info.plist 文件中添加了Allow Arbitrary Loads=YES

    -
      -
    • moduleName 对应的名字和index.js中 AppRegistry.registerComponent(‘SwiftRN’, () => MyApp)注册的名字必须相同
    • -
    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    import UIKit

    class RNTestController: UIViewController {

    override func viewDidLoad() {
    super.viewDidLoad()
    let url = URL(string: "http://localhost:8081/index.bundle?platform=ios")!
    let rootView = RCTRootView(
    bundleURL: url,
    moduleName: "SwiftRN", //这里的名字必须和AppRegistry.registerComponent('MyApp', () => MyApp)中注册的名字一样
    initialProperties: nil,
    launchOptions: nil
    )
    view = rootView
    }
    }
    - - -
    - @@ -473,7 +477,7 @@

    throw

    @@ -483,10 +487,10 @@

    throw

    -
    +
    @@ -495,147 +499,37 @@

    throw

    -

    Swift协议和别名

    -

    协议

    某些属性的集合,可以有多个协议组成,具有的特点

    -
      -
    1. 可以声明函数,但是不能实现,由实现这协议的具体结构或者类来实现
    2. -
    3. 声明的函数如果有参数也不能设置参数的默认值, 默认参数被认为也是函的一种实现
    4. -
    5. 可以声明属性,必须是计算型属性 只能使用var 修饰,不能是存储型属,必须在 {} 中明确指出属性的读取类型 例如 {get set}
    6. -
    7. 遵守协议的类可选择存储型属性或者计算型属性来实现协议中定义的属性
    8. -
    9. protocol Person: class { } 表示此协议只能被类来遵守,结构体能遵守
    10. -
    11. 协议可以看着是一种类型,如果协议中的属性是只读的,如果是协议类型只能访问get 方法, 但是遵守协议的具体类或者结构可以设置属性的读和写

      -
    12. -
    13. 协议中的构造函数

      -
      - 如果父类中遵守了协议中的构造函数,子类中必须重写父类中的构造函数并且使用require修饰构造函数,表示继承此类的子类也必须实现该构造函数
      -- 如果class 使用了final 修饰,表示不能被继承,此时不需要 require 关键词修饰
      -
    14. -
    -

    类型别名

      -
    • 定义别名 typealias, 供扩展使用

      -
      1
      2
      3
      4
      5
      6
      7
      8
      typealias Length = Double
      extension Double {
      var km: Length {return self * 1_000.0}
      var m: Length {return self * 100.0}
      var cm: Length {return self / 10.0}
      /// 英尺
      var ft: Length { return self / 3.28084}
      }
      -
    • -
    -
    使用
    -
    -
    1
    let distance: Length = 10.5.km
    -
      -
    • 定义别名防止硬编码

      -
      1
      2
      	// 音频采样率
      typealias AudioSample = UInt64
      -
    • -
    -

    协议中 typealias

    协议中定义一个属性,该属性的类型根据不同的情况返回不同的类型(我理解为类似泛型),具体的类型由遵守此协议的类或者结构指定

    -
      -
    • 定义协议中别名
    • -
    -
    1
    2
    3
    4
    5
    protocol WeightCalculable {
    // 属性重量根据不同体积返回的可能是Int,可以可能是double类型,所以使用别名, 协议中使用associatedtype关键词,
    associatedtype WeightType
    var weight: WeightType { get }
    }
    -

    扩展 Int 中的吨单位

    -
    1
    2
    3
    4
    extension Int {
    typealias Weight = Int
    var t: Weight {return self * 1_000}
    }
    -
      -
    • 使用协议中别名
    • -
    -
      -
    • 一部手机的时使用该协议WeightCalculable
    • -
    -
    1
    2
    3
    4
    5
    6
    class iPhone: WeightCalculable {
    typealias WeightType = Double
    var weight: WeightType {
    return 0.5
    }
    }
    -
      -
    • 一辆大卡时使用该协议WeightCalculable
    • -
    -
    1
    2
    3
    4
    5
    6
    7
    8
    class Car: WeightCalculable {
    typealias WeightType = Int
    let weight: WeightType

    init(weight: Int) {
    self.weight = weight
    }
    }
    -
    1
    let bigCar = Car(weight: 8_000.t) // 8 吨
    -

    面向协议编程

      -
    • 定义比赛协议,具有 赢/输/总场次/胜率 等特性
    • +

      Swift已有项目中集成ReactNative

      +

      本教程针对于已经有的Swift项目集成ReactNative

      +

      创建新的空目录

      为了不影响原有的swift项目目录结构,建议先创建一个空的文件夹,例如SwiftRN

      +

      +

      创建package.json文件

      终端进入创建的SwiftRN文件目录下,创建package.json文件

      +
      1
      touch package.json
      +
      修改package.json文件,指定各个版本
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      {
      "name": "SwiftRN",
      "version": "0.0.1",
      "private": true,
      "scripts": {
      "start": "node node_modules/react-native/local-cli/cli.js start",
      "test": "jest"
      },
      "dependencies": {
      "react": "16.3.1",
      "react-native": "^0.56.0"
      }
      }
      +
      执行npm install

      在创建package.json所在的文件目录下执行,在此过程中可以遇到的错误可能较多

      +
      1
      npm install
      +
      添加index.js文件
      1
      touch index.js
      +
      修改文件内容
      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
      import {
      AppRegistry,
      Text,
      View,
      } from 'react-native';

      import React, { Component } from 'react';

      // 用于最后在项目中测试
      class SwiftRNHomeView extends Component {
      render() {
      return(
      <View style={{backgroundColor:'white'}}>
      <Text style={{fontSize: 20, marginTop: 200}}>Hellow world</Text>
      </View>
      );
      }
      }


      class MyApp extends Component {
      render() {
      return <SwiftRNHomeView></SwiftRNHomeView>;
      }
      }

      AppRegistry.registerComponent('SwiftRN', () => MyApp);
      +

      配置swift项目

      将新创建的Swift文件目录下的所有文件拷贝到Swift项目所在的根目录下

      +

      +

      使用Pod集成RN

      swift项目采用了pod管理第三方库,在Podfile文件中加入下面代码

      +
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      pod 'React', :path => './node_modules/react-native', :subspecs => [
      'Core',
      'RCTText',
      'RCTNetwork',
      'RCTWebSocket', # 这个模块是用于调试功能的
      'CxxBridge', # Include this for RN >= 0.47,不引入的话会报错误或者警告
      'DevSupport', # Include this to enable In-App Devmenu if RN >= 0.43,不引入的话会报错误或者警告
      'RCTAnimation', 不引入的话会报错误或者警告
      ]


      # 如果你的RN版本 >= 0.42.0,请加入下面这行,不引入的话会报错误或者警告
      pod "yoga", :path => "./node_modules/react-native/ReactCommon/yoga"
      pod 'DoubleConversion', :podspec => './node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
      pod 'glog', :podspec => './node_modules/react-native/third-party-podspecs/glog.podspec'
      pod 'Folly', :podspec => './node_modules/react-native/third-party-podspecs/Folly.podspec'
      +

      创建桥接文件

        +
      • 如果原项目中有SwiftDemo-Bridging-Header.h类似这个文件可以跳过。
      • +
      • 如果从来没有创建过桥接文件,创建一个OC的类会自动弹出是否需要创建桥接文件,直接创建即可,最后把无用的OC文件删掉
      -
      1
      2
      3
      4
      5
      6
      protocol Recordable: CustomStringConvertible {
      var wins: Int { get }
      var losses: Int { get }

      func winningPerent() -> Double
      }
      +

      引用RN头文件

      在桥接文件中导入头文件

      +
      1
      2
      #import <React/RCTRootView.h>
      #import <React/RCTBundleURLProvider.h>
      +

      使用RN页面

      在模拟器中使用RN的页面, 确保在 info.plist 文件中添加了Allow Arbitrary Loads=YES

        -
      • 协议中方法或者属性的默认实现
      • -
      -
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      extension Recordable {
      // CustomStringConvertible 协议默认实现
      var description: String {
      return String(format: "WINS: %d---LOSSES: %d", [wins, losses])
      }

      // 默认实现总场数计算型属性
      var totalGames: Int {
      return wins + losses
      }

      // 默认实现的方法
      func shoutWins() {
      print("come on", wins, "times!")
      }
      }
      -

      篮球比赛:

        -
      • Equatable 协议 - 相等比较 需要重载 ==
      • -
      • Comparable 协议 - 是否可比较 ,重载 < 可直接调用sort()函数
      • -
      • CustomStringConvertible 协议 - 打印信息,需重写 description
      • -
      • Recordable协议 - 比赛协议,没有平局
      • +
      • moduleName 对应的名字和index.js中 AppRegistry.registerComponent(‘SwiftRN’, () => MyApp)注册的名字必须相同
      -
      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
      struct BasketballRecord:Equatable, Comparable, CustomStringConvertible, Recordable {
      var wins: Int
      var losses: Int


      /// 在具体定义的类型中实现 协议中的属性或者方法就会覆盖协议中的
      var totalGames: Int = 200

      var description: String {
      return "本次比赛胜: " + String(wins) + "负" + "\(losses)"
      }

      // 胜率
      func winningPerent() -> Double {
      return (Double(wins) / Double(totalGames))
      }

      // swift4.1 后,不需要再重写此方法,swift 会自动合成,如果不想让某一个属性参与比较,就需要重写该方法
      static func == (lhs: BasketballRecord, rhs: BasketballRecord) -> Bool {
      return lhs.wins == rhs.wins && lhs.losses == rhs.losses
      }

      // Comparable
      static func < (lhs: BasketballRecord, rhs: BasketballRecord) -> Bool {
      if lhs.wins != rhs.wins {
      return lhs.wins < rhs.wins
      }
      return lhs.losses > rhs.losses
      }
      }
      ```

      #### 足球比赛:

      有平局的情况,原来的协议不能满足,需要增加一个平局的协议

      - 定义平局协议
      -

      protocol Tieable {
      /// 平局,可读写
      var ties: Int { get set }

      -

      }

      1
      2
      3
      4

      - 足球比赛需要遵守的协议 `Recordable` 和 `Tieable`

      - 总场数需要加上平局的场数,需要实现totalGames的get方法

      -

      struct FootableRecord: Recordable, Tieable {
      var wins: Int
      var losses: Int
      // 平局数
      var ties: Int

      -
      // 总场数
      -var totalGames: Int {
      -    return wins + losses + ties
      -}
      -
      -var description: String {
      -    return "本次比赛胜: " + String(wins) + "负" + "\(losses)"
      -}
      -
      -func winningPerent() -> Double {
      -    return (Double(wins) / Double(totalGames))
      -}
      -

      }

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12

      - 足球比赛中以上写法存在的问题

      - 所有有平局的比赛计算总场次的逻辑都是一样,每次都要写一次实现
      - 单一修改 Recordable 协议不能解决问题,
      - 存在平局和没有平局两种情况 totalGames 的计算逻辑不相同

      - 解决办法:
      - 扩展遵守了`Recordable`协议的类型,前提条件是:这个类型遵守了 Tieable 同时也遵守了 `Recordable ` 协议,可以理解为如果该类型遵守了 `Tieable `协议之后,`Recordable` 协议中的 totalGames 属性实现是另一种方式了,不在是以前的 `totalGames` 中直接返回 `wins + losses` 的值


      定义协议

      -

      protocol Recordable: CustomStringConvertible {
      var wins: Int { get }
      var losses: Int { get }

      -
      func winningPerent() -> Double
      -

      }

      1
      2

      协议默认实现

      -

      extension Recordable {
      var description: String {
      return String(format: “WINS: %d—LOSSES: %d”, [wins, losses])
      }

      -
      // 默认实现总场数计算型属性
      -var totalGames: Int {
      -    return wins + losses
      -}
      -
      -// 默认实现胜率
      -func winningPerent() -> Double {
      -    return (Double(wins) / Double(totalGames))
      -}
      -
      -// 默认实现的方法
      -func shoutWins() {
      -    print("come on", wins, "times!")
      -}
      -

      }

      -
      1
      2

      扩展遵守了Tieable 的 类型的 Recordable 协议
      -

      extension Recordable where Self: Tieable {
      var totalGames: Int {
      return wins + losses + ties
      }
      }

      -
      1
      2
      3
      4
      5
      6

      ### 协议聚合

      如果对某一类型需要限制他必须遵守某些协议,可以使用协议聚合来定义

      比如: 有个奖赏协议
      -

      protocol Prizable {
      func isPrizable() -> Bool
      }

      -
      1
      2

      篮球比赛遵守此协议
      -

      // 篮球比赛奖赏
      extension BasketballRecord: Prizable {
      func isPrizable() -> Bool {
      return totalGames > 10 && winningPerent() > 0.5
      }
      }

      -
      1
      2

      足球比赛奖赏遵守此协议
      -

      extension FootableRecord: Prizable {
      func isPrizable() -> Bool {
      return wins > 1
      }
      }

      1
      2

      现在有某一个学生也遵守了此协议

      -

      struct Student: Prizable {
      var name: String
      var score: Int

      -
      func isPrizable() -> Bool {
      -    return score >= 60
      -}
      -

      }

      -
      1
      2

      定义奖赏的方法,参数类型必须是遵守了此协议的结构或者类型
      -

      private func award(_ one: Prizable) {
      if one.isPrizable() {
      print(one)
      print(“恭喜获得奖励”)
      } else {
      print(one)
      print(“很遗憾”)
      }
      }

      1
      2

      如果说 BasketballRecord 这个类还遵守了其他的协议,例如遵守了 `Recordable` 协议, 并且这个协议也遵守了 `CustomStringConvertible` 并且默认实现了`description` 的`get` 方法

      -

      // MARK: - 比赛协议,具有 赢。输。总场次。胜率 特性
      protocol Recordable: CustomStringConvertible {
      var wins: Int { get }
      var losses: Int { get }

      -
      func winningPerent() -> Double
      -

      }

      -
      1
      2

      `Recordable` 默认实现
      -

      extension Recordable {
      var description: String {
      return String(format: “WINS: %d—LOSSES: %d”, [wins, losses])
      }

      -
      // 默认实现总场数计算型属性
      -var totalGames: Int {
      -    return wins + losses
      -}
      -
      -// 默认实现胜率
      -func winningPerent() -> Double {
      -    return (Double(wins) / Double(totalGames))
      -}
      -
      -// 默认实现的方法
      -func shoutWins() {
      -    print("come on", wins, "times!")
      -}
      -

      }

      1
      2
      3
      4
      5

      此时如果 `Student` 类还是调用 `award` 方法的话,print(one) 打印的信息将是`Recordable `中默认实现的内容,因此需要约束`award`函数的参数必须遵守两个协议让`Student` 也重写自己的`description `属性的`get`方法,不能再让 `Prizable` 扩展 默认实现

      - swift 3 写法: protocol<A, B>
      - swift 4 写法: A & B

      -

      private func award2(_ one: Prizable & CustomStringConvertible) {
      if one.isPrizable() {
      print(one)
      print(“恭喜获得奖励”)
      } else {
      print(one)
      print(“很遗憾”)
      }
      }

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10

      ### 泛型约束


      - 定义一个函数,找出一个学生数组中分数最大的
      - 参数:一个学生数组,都遵守了 Comparable 的类型
      - 返回值:某个遵守了 Comparable 的类型实例
      - 此时函数报错 `Protocol 'Comparable' can only be used as a generic constraint because it has Self or associated type requirements`,

      因为 Comparable 协议中定义的方法 public static func < (lhs: Self, rhs: Self) -> Bool 的参数类型是Self,是具体的某个类型

      -

      func maxScore(seq: [Comparable]) -> Comparable { }

      1
      2
      3
      4
      5


      - 如果需要定义一个函数实现在一个数组中找出需要奖励的人的名字该如何实现呢
      - 参数:遵守两个协议 Comparable 和 Prizable 协议, 并且使用泛型
      - 返回值:返回值是可选值,有可能没有任何奖励的对象

      -

      func topPrizable<T: Comparable & Prizable>(seq: [T]) -> T? {
      return seq.reduce(nil) { (tempTop: T?, condender: T) in
      guard condender.isPrizable() else { return tempTop }
      // 解包 condender 失败, 上一层验证了他必须是奖励的那个
      guard let tempTop = tempTop else { return condender }
      return max(tempTop, condender)
      }
      }

      -

      `

      +
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      import UIKit

      class RNTestController: UIViewController {

      override func viewDidLoad() {
      super.viewDidLoad()
      let url = URL(string: "http://localhost:8081/index.bundle?platform=ios")!
      let rootView = RCTRootView(
      bundleURL: url,
      moduleName: "SwiftRN", //这里的名字必须和AppRegistry.registerComponent('MyApp', () => MyApp)中注册的名字一样
      initialProperties: nil,
      launchOptions: nil
      )
      view = rootView
      }
      }
    @@ -695,23 +589,23 @@

    Recent Posts

    diff --git a/page/4/index.html b/page/4/index.html index c45d3d8..c35fd50 100644 --- a/page/4/index.html +++ b/page/4/index.html @@ -62,6 +62,168 @@

    +
    + +
    + + +
    + +

    Swift协议和别名

    +

    协议

    某些属性的集合,可以有多个协议组成,具有的特点

    +
      +
    1. 可以声明函数,但是不能实现,由实现这协议的具体结构或者类来实现
    2. +
    3. 声明的函数如果有参数也不能设置参数的默认值, 默认参数被认为也是函的一种实现
    4. +
    5. 可以声明属性,必须是计算型属性 只能使用var 修饰,不能是存储型属,必须在 {} 中明确指出属性的读取类型 例如 {get set}
    6. +
    7. 遵守协议的类可选择存储型属性或者计算型属性来实现协议中定义的属性
    8. +
    9. protocol Person: class { } 表示此协议只能被类来遵守,结构体能遵守
    10. +
    11. 协议可以看着是一种类型,如果协议中的属性是只读的,如果是协议类型只能访问get 方法, 但是遵守协议的具体类或者结构可以设置属性的读和写

      +
    12. +
    13. 协议中的构造函数

      +
      - 如果父类中遵守了协议中的构造函数,子类中必须重写父类中的构造函数并且使用require修饰构造函数,表示继承此类的子类也必须实现该构造函数
      +- 如果class 使用了final 修饰,表示不能被继承,此时不需要 require 关键词修饰
      +
    14. +
    +

    类型别名

      +
    • 定义别名 typealias, 供扩展使用

      +
      1
      2
      3
      4
      5
      6
      7
      8
      typealias Length = Double
      extension Double {
      var km: Length {return self * 1_000.0}
      var m: Length {return self * 100.0}
      var cm: Length {return self / 10.0}
      /// 英尺
      var ft: Length { return self / 3.28084}
      }
      +
    • +
    +
    使用
    +
    +
    1
    let distance: Length = 10.5.km
    +
      +
    • 定义别名防止硬编码

      +
      1
      2
      	// 音频采样率
      typealias AudioSample = UInt64
      +
    • +
    +

    协议中 typealias

    协议中定义一个属性,该属性的类型根据不同的情况返回不同的类型(我理解为类似泛型),具体的类型由遵守此协议的类或者结构指定

    +
      +
    • 定义协议中别名
    • +
    +
    1
    2
    3
    4
    5
    protocol WeightCalculable {
    // 属性重量根据不同体积返回的可能是Int,可以可能是double类型,所以使用别名, 协议中使用associatedtype关键词,
    associatedtype WeightType
    var weight: WeightType { get }
    }
    +

    扩展 Int 中的吨单位

    +
    1
    2
    3
    4
    extension Int {
    typealias Weight = Int
    var t: Weight {return self * 1_000}
    }
    +
      +
    • 使用协议中别名
    • +
    +
      +
    • 一部手机的时使用该协议WeightCalculable
    • +
    +
    1
    2
    3
    4
    5
    6
    class iPhone: WeightCalculable {
    typealias WeightType = Double
    var weight: WeightType {
    return 0.5
    }
    }
    +
      +
    • 一辆大卡时使用该协议WeightCalculable
    • +
    +
    1
    2
    3
    4
    5
    6
    7
    8
    class Car: WeightCalculable {
    typealias WeightType = Int
    let weight: WeightType

    init(weight: Int) {
    self.weight = weight
    }
    }
    +
    1
    let bigCar = Car(weight: 8_000.t) // 8 吨
    +

    面向协议编程

      +
    • 定义比赛协议,具有 赢/输/总场次/胜率 等特性
    • +
    +
    1
    2
    3
    4
    5
    6
    protocol Recordable: CustomStringConvertible {
    var wins: Int { get }
    var losses: Int { get }

    func winningPerent() -> Double
    }
    +
      +
    • 协议中方法或者属性的默认实现
    • +
    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    extension Recordable {
    // CustomStringConvertible 协议默认实现
    var description: String {
    return String(format: "WINS: %d---LOSSES: %d", [wins, losses])
    }

    // 默认实现总场数计算型属性
    var totalGames: Int {
    return wins + losses
    }

    // 默认实现的方法
    func shoutWins() {
    print("come on", wins, "times!")
    }
    }
    +

    篮球比赛:

      +
    • Equatable 协议 - 相等比较 需要重载 ==
    • +
    • Comparable 协议 - 是否可比较 ,重载 < 可直接调用sort()函数
    • +
    • CustomStringConvertible 协议 - 打印信息,需重写 description
    • +
    • Recordable协议 - 比赛协议,没有平局
    • +
    +
    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
    struct BasketballRecord:Equatable, Comparable, CustomStringConvertible, Recordable {
    var wins: Int
    var losses: Int


    /// 在具体定义的类型中实现 协议中的属性或者方法就会覆盖协议中的
    var totalGames: Int = 200

    var description: String {
    return "本次比赛胜: " + String(wins) + "负" + "\(losses)"
    }

    // 胜率
    func winningPerent() -> Double {
    return (Double(wins) / Double(totalGames))
    }

    // swift4.1 后,不需要再重写此方法,swift 会自动合成,如果不想让某一个属性参与比较,就需要重写该方法
    static func == (lhs: BasketballRecord, rhs: BasketballRecord) -> Bool {
    return lhs.wins == rhs.wins && lhs.losses == rhs.losses
    }

    // Comparable
    static func < (lhs: BasketballRecord, rhs: BasketballRecord) -> Bool {
    if lhs.wins != rhs.wins {
    return lhs.wins < rhs.wins
    }
    return lhs.losses > rhs.losses
    }
    }
    ```

    #### 足球比赛:

    有平局的情况,原来的协议不能满足,需要增加一个平局的协议

    - 定义平局协议
    +

    protocol Tieable {
    /// 平局,可读写
    var ties: Int { get set }

    +

    }

    1
    2
    3
    4

    - 足球比赛需要遵守的协议 `Recordable` 和 `Tieable`

    - 总场数需要加上平局的场数,需要实现totalGames的get方法

    +

    struct FootableRecord: Recordable, Tieable {
    var wins: Int
    var losses: Int
    // 平局数
    var ties: Int

    +
    // 总场数
    +var totalGames: Int {
    +    return wins + losses + ties
    +}
    +
    +var description: String {
    +    return "本次比赛胜: " + String(wins) + "负" + "\(losses)"
    +}
    +
    +func winningPerent() -> Double {
    +    return (Double(wins) / Double(totalGames))
    +}
    +

    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    - 足球比赛中以上写法存在的问题

    - 所有有平局的比赛计算总场次的逻辑都是一样,每次都要写一次实现
    - 单一修改 Recordable 协议不能解决问题,
    - 存在平局和没有平局两种情况 totalGames 的计算逻辑不相同

    - 解决办法:
    - 扩展遵守了`Recordable`协议的类型,前提条件是:这个类型遵守了 Tieable 同时也遵守了 `Recordable ` 协议,可以理解为如果该类型遵守了 `Tieable `协议之后,`Recordable` 协议中的 totalGames 属性实现是另一种方式了,不在是以前的 `totalGames` 中直接返回 `wins + losses` 的值


    定义协议

    +

    protocol Recordable: CustomStringConvertible {
    var wins: Int { get }
    var losses: Int { get }

    +
    func winningPerent() -> Double
    +

    }

    1
    2

    协议默认实现

    +

    extension Recordable {
    var description: String {
    return String(format: “WINS: %d—LOSSES: %d”, [wins, losses])
    }

    +
    // 默认实现总场数计算型属性
    +var totalGames: Int {
    +    return wins + losses
    +}
    +
    +// 默认实现胜率
    +func winningPerent() -> Double {
    +    return (Double(wins) / Double(totalGames))
    +}
    +
    +// 默认实现的方法
    +func shoutWins() {
    +    print("come on", wins, "times!")
    +}
    +

    }

    +
    1
    2

    扩展遵守了Tieable 的 类型的 Recordable 协议
    +

    extension Recordable where Self: Tieable {
    var totalGames: Int {
    return wins + losses + ties
    }
    }

    +
    1
    2
    3
    4
    5
    6

    ### 协议聚合

    如果对某一类型需要限制他必须遵守某些协议,可以使用协议聚合来定义

    比如: 有个奖赏协议
    +

    protocol Prizable {
    func isPrizable() -> Bool
    }

    +
    1
    2

    篮球比赛遵守此协议
    +

    // 篮球比赛奖赏
    extension BasketballRecord: Prizable {
    func isPrizable() -> Bool {
    return totalGames > 10 && winningPerent() > 0.5
    }
    }

    +
    1
    2

    足球比赛奖赏遵守此协议
    +

    extension FootableRecord: Prizable {
    func isPrizable() -> Bool {
    return wins > 1
    }
    }

    1
    2

    现在有某一个学生也遵守了此协议

    +

    struct Student: Prizable {
    var name: String
    var score: Int

    +
    func isPrizable() -> Bool {
    +    return score >= 60
    +}
    +

    }

    +
    1
    2

    定义奖赏的方法,参数类型必须是遵守了此协议的结构或者类型
    +

    private func award(_ one: Prizable) {
    if one.isPrizable() {
    print(one)
    print(“恭喜获得奖励”)
    } else {
    print(one)
    print(“很遗憾”)
    }
    }

    1
    2

    如果说 BasketballRecord 这个类还遵守了其他的协议,例如遵守了 `Recordable` 协议, 并且这个协议也遵守了 `CustomStringConvertible` 并且默认实现了`description` 的`get` 方法

    +

    // MARK: - 比赛协议,具有 赢。输。总场次。胜率 特性
    protocol Recordable: CustomStringConvertible {
    var wins: Int { get }
    var losses: Int { get }

    +
    func winningPerent() -> Double
    +

    }

    +
    1
    2

    `Recordable` 默认实现
    +

    extension Recordable {
    var description: String {
    return String(format: “WINS: %d—LOSSES: %d”, [wins, losses])
    }

    +
    // 默认实现总场数计算型属性
    +var totalGames: Int {
    +    return wins + losses
    +}
    +
    +// 默认实现胜率
    +func winningPerent() -> Double {
    +    return (Double(wins) / Double(totalGames))
    +}
    +
    +// 默认实现的方法
    +func shoutWins() {
    +    print("come on", wins, "times!")
    +}
    +

    }

    1
    2
    3
    4
    5

    此时如果 `Student` 类还是调用 `award` 方法的话,print(one) 打印的信息将是`Recordable `中默认实现的内容,因此需要约束`award`函数的参数必须遵守两个协议让`Student` 也重写自己的`description `属性的`get`方法,不能再让 `Prizable` 扩展 默认实现

    - swift 3 写法: protocol<A, B>
    - swift 4 写法: A & B

    +

    private func award2(_ one: Prizable & CustomStringConvertible) {
    if one.isPrizable() {
    print(one)
    print(“恭喜获得奖励”)
    } else {
    print(one)
    print(“很遗憾”)
    }
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    ### 泛型约束


    - 定义一个函数,找出一个学生数组中分数最大的
    - 参数:一个学生数组,都遵守了 Comparable 的类型
    - 返回值:某个遵守了 Comparable 的类型实例
    - 此时函数报错 `Protocol 'Comparable' can only be used as a generic constraint because it has Self or associated type requirements`,

    因为 Comparable 协议中定义的方法 public static func < (lhs: Self, rhs: Self) -> Bool 的参数类型是Self,是具体的某个类型

    +

    func maxScore(seq: [Comparable]) -> Comparable { }

    1
    2
    3
    4
    5


    - 如果需要定义一个函数实现在一个数组中找出需要奖励的人的名字该如何实现呢
    - 参数:遵守两个协议 Comparable 和 Prizable 协议, 并且使用泛型
    - 返回值:返回值是可选值,有可能没有任何奖励的对象

    +

    func topPrizable<T: Comparable & Prizable>(seq: [T]) -> T? {
    return seq.reduce(nil) { (tempTop: T?, condender: T) in
    guard condender.isPrizable() else { return tempTop }
    // 解包 condender 失败, 上一层验证了他必须是奖励的那个
    guard let tempTop = tempTop else { return condender }
    return max(tempTop, condender)
    }
    }

    +

    `

    + + +
    + +
    + +
    + + + - - - -
    - -
    - - -
    - -

    Masonry使用

    基本使用官网Demo非常详细,以下总结的是常见问题和技巧

    -

    安装遇到的问题

      -
    • pod 安装后出现⚠️,项目运行报错

      -
      1
      [UIView mas_makeConstraints:]: unrecognized selector sent to instance
      -

      github

      -

      解决方法

      -
    • -
    • 使用lastBaseline属性crash

      -

      解决办法

      -
    • -
    -

    使用时注意点

      -
    • edgesForExtendedLayout 方法

      -

      重写此方法的目的是为了确定布局位置从什么位置开始什么位置结束,

      -
      1
      2
      3
      - (UIRectEdge)edgesForExtendedLayout {
      return UIRectEdgeNone;
      }
      -
    • -
    -
    **重写后**
    -![](https://ws1.sinaimg.cn/large/006tNc79ly1fsgcn3fmo0j30s61iyalk.jpg)
    -
    -**重写前**
    -![](https://ws4.sinaimg.cn/large/006tNc79ly1fsgcokvpdwj30s61iy7eq.jpg)
    -
      -
    • translucent 设置UINavigationBar是否半透明, 如果设置了导航栏的styleUIBarStyleBlackTranslucent 就为true

      -

      设置为false不透明效果

      -
    • -
    -
      -
    • extendedLayoutIncludesOpaqueBars 导航栏不透明条件下是否可以扩展,默认是NO不可以扩展

      -
      1
      2
      self.navigationController?.navigationBar.isTranslucent = false
      self.extendedLayoutIncludesOpaqueBars = true
      -
    • -
    -
    **设置不透明,设置可以扩展此时从坐标的(0,0)点开始布局**
    -![](https://ws2.sinaimg.cn/large/006tNc79ly1fsggem13mjj30ow0j83zy.jpg)    
    -
      -
    • inset 的使用,当约束控件的属性相同时不用再考虑正负值的问题,例如要约束一个控件左边距离另一控件的右边位置时还需要使用负值情况

      -
      1
      2
      3
      4
      5
      6
      7
      [yellowdView makeConstraints:^(MASConstraintMaker *make) {
      make.height.equalTo(@150);
      make.top.equalTo(self).inset(220);
      make.leading.equalTo(self).inset(10);
      make.trailing.equalTo(greenView.leading).inset(10);
      make.width.equalTo(greenView);
      }];
      -
    • -
    • edges的使用,约束边距更方便

      -
      1
      2
      3
      [edgeView remakeConstraints:^(MASConstraintMaker *make) {
      make.edges.equalTo(lasView).insets(UIEdgeInsetsMake(10, 5, 5, 5));
      }];
      -
    • -
    -
      -
    • Priority 设置约束的优先级

      -
        -
      • priorityHigh
      • -
      • priorityMedium
      • -
      • priorityLow
      • -
      -
    • -
    • makeConstraintsupdateConstraintsremakeConstraints三者区别

      -
        -
      • makeConstraints 添加约束
      • -
      • updateConstraints 更新约束,更新之前会查找一边控件已经存在的约束,没有就添加,有就更新到最新的约束
      • -
      • remakeConstraints删除控件以前的所有约束重新添加约束
      • -
      -
    • -
    • 设置控件或者某一约束的’key’ 方便冲突时⚠️查找

      -
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      	greenView.mas_key = @"greenView";
      [greenView makeConstraints:^(MASConstraintMaker *make) {
      make.height.equalTo(yellowdView).key(@"greenView-height");
      make.top.equalTo(yellowdView).key(@"greenView-top");
      make.width.equalTo(yellowdView).key(@"greenView-width");
      make.trailing.equalTo(self).inset(10).key(@"greenView-trailing");
      // 冲突约束
      // make.leading.equalTo(greenView.trailing).inset(10).key(@"greenView-leading");
      }];
      ```

      - 使用 `MASAttachKeys`批量给`view`设置key
      -

      MASAttachKeys(greenView, yellowdView);

      -
      1
      2
      3
      4
      	
      ![](https://ws3.sinaimg.cn/large/006tNc79ly1fsgalvd4l6j31a20modnp.jpg)

      - `dividedBy`和 `multipliedBy` 约束宽高比
      -

      [topInnerView makeConstraints:^(MASConstraintMaker *make) {

      -
      // 设置自己的宽高比是3:1
      -make.width.equalTo( topInnerView.height).multipliedBy(3); // 乘因数
      -
      -// 设置宽高并且设置他们的优先级最低
      -make.width.height.lessThanOrEqualTo(topView);
      -make.width.height.equalTo(topView).priorityLow();
      -
      -// 设置位置
      -make.top.equalTo(topView);
      -

      }];

      -
      1
      - 多个控件批量约束
      -

      NSValue sizeValue = [NSValue valueWithCGSize:CGSizeMake(100, 100)];
      [@[blueView, redView, yellowView] makeConstraints:^(MASConstraintMaker
      make) {

      -
      make.size.equalTo(sizeValue);
      -

      }];

      -

      [@[blueView, redView, yellowView] mas_makeConstraints:^(MASConstraintMaker *make) {

      -
      make.top.equalTo(self).inset(20);
      -

      }];

      -

      [blueView makeConstraints:^(MASConstraintMaker *make) {

      -
      make.left.equalTo(self).inset(20);
      -

      }];

      -

      [redView makeConstraints:^(MASConstraintMaker *make) {

      -
      make.left.equalTo(blueView.right).inset(10);
      -

      }];

      -

      [yellowView makeConstraints:^(MASConstraintMaker *make) {

      -
      make.left.equalTo(redView.right).inset(10);
      -

      }];

      -
      1
      2
      3
      4

      ![批量约束](https://ws1.sinaimg.cn/large/006tNc79ly1fsgaz670hvj30s61iyn2l.jpg)

      **以上代码也可以简化为**
      -

      [redView makeConstraints:^(MASConstraintMaker *make) {

      -
      make.left.equalTo(blueView.right).inset(10);
      -

      }];

      -

      [yellowView makeConstraints:^(MASConstraintMaker *make) {

      -
      make.left.equalTo(redView.right).inset(10);
      -

      }];

      -
    • -
    -
    [blueView makeConstraints:^(MASConstraintMaker *make) {
    -    make.top.left.equalTo(self);
    -    make.size.equalTo(@[redView, yellowView, sizeValue]);
    -}];
    -
    -
    1
    2
    3
    4
    5
    	![](https://ws2.sinaimg.cn/large/006tNc79ly1fsgbhz8lw5j30s61iyte5.jpg)

    ## Snapkit 使用

    - Swift 中`edgesForExtendedLayout:UIRectEdge` 扩展布局的边缘是一个属性,默认是`All`
    - -self.edgesForExtendedLayout = [] -
    1
    2
    3
    4

    在Swift中闭包参数可以使用$0、$1、$2 一次代表第一个,第二个,第三个参数

    - 使用$0 来代替闭包参数
    - -nullDataImageView.snp.makeConstraints { - $0.top.equalTo(view).offset(44) - $0.centerX.equalTo(view) - $0.width.height.equalTo(200) -} -
    1
    2
    3
    4
    5
    6
    7
    	
    - `deactivate` 和 `activate` 使用

    - `activate` 激活指定的约束
    - `deactivate` 移除指定的约束

    添加约束
    - -nullDataImageView.snp.makeConstraints { - nullDataViewTopConstraint = $0.top.equalTo(view).offset(80).constraint - nullDataViewLeftConstraint = $0.left.equalTo(view).inset(10).constraint - $0.width.height.equalTo(200) -} -
    1
    2

    移除对应约束,设置约束再激活约束
    - -nullDataViewLeftConstraint?.deactivate() -nullDataViewLeftConstraint?.update(inset: 100) -nullDataViewLeftConstraint?.activate() -UIView.animate(withDuration: 1.5) { - self.view.layoutIfNeeded() -} -
    1
    2
    3
    4

    - 更新约束

    - 添加约束
    - -nullDataImageView.snp.makeConstraints { - nullDataViewTopConstraint = $0.top.equalTo(view).offset(64).constraint - $0.centerX.equalTo(view) - $0.width.height.equalTo(200) -} -
    1
    2

    - 更新约束
    - -nullDataViewTopConstraint?.update(inset: 84) -UIView.animate(withDuration: 0.5, delay: 0, options: .curveEaseOut, animations: { - self.view.layoutIfNeeded() -}) { (finished) in - print("动画执行完毕") -} -
    1
    2
    	
    - 添加`debug`模式中的 `key`使用`labeled()`方法
    - -nullDataImageView.snp.makeConstraints { - nullDataViewTopConstraint = $0.top.equalTo(view).offset(80).constraint - nullDataViewLeftConstraint = $0.left.equalTo(view).inset(10).constraint - $0.width.height.equalTo(200).labeled("width和height约束") -} -``` -
      -
    • 其他注意点都和Masnory相同
    • -
    - - -
    - @@ -908,23 +891,23 @@

    Recent Posts

    diff --git a/page/5/index.html b/page/5/index.html index 8eed115..b59c41a 100644 --- a/page/5/index.html +++ b/page/5/index.html @@ -62,6 +62,185 @@

    +
    + +
    + + +
    + +

    Masonry使用

    基本使用官网Demo非常详细,以下总结的是常见问题和技巧

    +

    安装遇到的问题

      +
    • pod 安装后出现⚠️,项目运行报错

      +
      1
      [UIView mas_makeConstraints:]: unrecognized selector sent to instance
      +

      github

      +

      解决方法

      +
    • +
    • 使用lastBaseline属性crash

      +

      解决办法

      +
    • +
    +

    使用时注意点

      +
    • edgesForExtendedLayout 方法

      +

      重写此方法的目的是为了确定布局位置从什么位置开始什么位置结束,

      +
      1
      2
      3
      - (UIRectEdge)edgesForExtendedLayout {
      return UIRectEdgeNone;
      }
      +
    • +
    +
    **重写后**
    +![](https://ws1.sinaimg.cn/large/006tNc79ly1fsgcn3fmo0j30s61iyalk.jpg)
    +
    +**重写前**
    +![](https://ws4.sinaimg.cn/large/006tNc79ly1fsgcokvpdwj30s61iy7eq.jpg)
    +
      +
    • translucent 设置UINavigationBar是否半透明, 如果设置了导航栏的styleUIBarStyleBlackTranslucent 就为true

      +

      设置为false不透明效果

      +
    • +
    +
      +
    • extendedLayoutIncludesOpaqueBars 导航栏不透明条件下是否可以扩展,默认是NO不可以扩展

      +
      1
      2
      self.navigationController?.navigationBar.isTranslucent = false
      self.extendedLayoutIncludesOpaqueBars = true
      +
    • +
    +
    **设置不透明,设置可以扩展此时从坐标的(0,0)点开始布局**
    +![](https://ws2.sinaimg.cn/large/006tNc79ly1fsggem13mjj30ow0j83zy.jpg)    
    +
      +
    • inset 的使用,当约束控件的属性相同时不用再考虑正负值的问题,例如要约束一个控件左边距离另一控件的右边位置时还需要使用负值情况

      +
      1
      2
      3
      4
      5
      6
      7
      [yellowdView makeConstraints:^(MASConstraintMaker *make) {
      make.height.equalTo(@150);
      make.top.equalTo(self).inset(220);
      make.leading.equalTo(self).inset(10);
      make.trailing.equalTo(greenView.leading).inset(10);
      make.width.equalTo(greenView);
      }];
      +
    • +
    • edges的使用,约束边距更方便

      +
      1
      2
      3
      [edgeView remakeConstraints:^(MASConstraintMaker *make) {
      make.edges.equalTo(lasView).insets(UIEdgeInsetsMake(10, 5, 5, 5));
      }];
      +
    • +
    +
      +
    • Priority 设置约束的优先级

      +
        +
      • priorityHigh
      • +
      • priorityMedium
      • +
      • priorityLow
      • +
      +
    • +
    • makeConstraintsupdateConstraintsremakeConstraints三者区别

      +
        +
      • makeConstraints 添加约束
      • +
      • updateConstraints 更新约束,更新之前会查找一边控件已经存在的约束,没有就添加,有就更新到最新的约束
      • +
      • remakeConstraints删除控件以前的所有约束重新添加约束
      • +
      +
    • +
    • 设置控件或者某一约束的’key’ 方便冲突时⚠️查找

      +
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      	greenView.mas_key = @"greenView";
      [greenView makeConstraints:^(MASConstraintMaker *make) {
      make.height.equalTo(yellowdView).key(@"greenView-height");
      make.top.equalTo(yellowdView).key(@"greenView-top");
      make.width.equalTo(yellowdView).key(@"greenView-width");
      make.trailing.equalTo(self).inset(10).key(@"greenView-trailing");
      // 冲突约束
      // make.leading.equalTo(greenView.trailing).inset(10).key(@"greenView-leading");
      }];
      ```

      - 使用 `MASAttachKeys`批量给`view`设置key
      +

      MASAttachKeys(greenView, yellowdView);

      +
      1
      2
      3
      4
      	
      ![](https://ws3.sinaimg.cn/large/006tNc79ly1fsgalvd4l6j31a20modnp.jpg)

      - `dividedBy`和 `multipliedBy` 约束宽高比
      +

      [topInnerView makeConstraints:^(MASConstraintMaker *make) {

      +
      // 设置自己的宽高比是3:1
      +make.width.equalTo( topInnerView.height).multipliedBy(3); // 乘因数
      +
      +// 设置宽高并且设置他们的优先级最低
      +make.width.height.lessThanOrEqualTo(topView);
      +make.width.height.equalTo(topView).priorityLow();
      +
      +// 设置位置
      +make.top.equalTo(topView);
      +

      }];

      +
      1
      - 多个控件批量约束
      +

      NSValue sizeValue = [NSValue valueWithCGSize:CGSizeMake(100, 100)];
      [@[blueView, redView, yellowView] makeConstraints:^(MASConstraintMaker
      make) {

      +
      make.size.equalTo(sizeValue);
      +

      }];

      +

      [@[blueView, redView, yellowView] mas_makeConstraints:^(MASConstraintMaker *make) {

      +
      make.top.equalTo(self).inset(20);
      +

      }];

      +

      [blueView makeConstraints:^(MASConstraintMaker *make) {

      +
      make.left.equalTo(self).inset(20);
      +

      }];

      +

      [redView makeConstraints:^(MASConstraintMaker *make) {

      +
      make.left.equalTo(blueView.right).inset(10);
      +

      }];

      +

      [yellowView makeConstraints:^(MASConstraintMaker *make) {

      +
      make.left.equalTo(redView.right).inset(10);
      +

      }];

      +
      1
      2
      3
      4

      ![批量约束](https://ws1.sinaimg.cn/large/006tNc79ly1fsgaz670hvj30s61iyn2l.jpg)

      **以上代码也可以简化为**
      +

      [redView makeConstraints:^(MASConstraintMaker *make) {

      +
      make.left.equalTo(blueView.right).inset(10);
      +

      }];

      +

      [yellowView makeConstraints:^(MASConstraintMaker *make) {

      +
      make.left.equalTo(redView.right).inset(10);
      +

      }];

      +
    • +
    +
    [blueView makeConstraints:^(MASConstraintMaker *make) {
    +    make.top.left.equalTo(self);
    +    make.size.equalTo(@[redView, yellowView, sizeValue]);
    +}];
    +
    +
    1
    2
    3
    4
    5
    	![](https://ws2.sinaimg.cn/large/006tNc79ly1fsgbhz8lw5j30s61iyte5.jpg)

    ## Snapkit 使用

    - Swift 中`edgesForExtendedLayout:UIRectEdge` 扩展布局的边缘是一个属性,默认是`All`
    + +self.edgesForExtendedLayout = [] +
    1
    2
    3
    4

    在Swift中闭包参数可以使用$0、$1、$2 一次代表第一个,第二个,第三个参数

    - 使用$0 来代替闭包参数
    + +nullDataImageView.snp.makeConstraints { + $0.top.equalTo(view).offset(44) + $0.centerX.equalTo(view) + $0.width.height.equalTo(200) +} +
    1
    2
    3
    4
    5
    6
    7
    	
    - `deactivate` 和 `activate` 使用

    - `activate` 激活指定的约束
    - `deactivate` 移除指定的约束

    添加约束
    + +nullDataImageView.snp.makeConstraints { + nullDataViewTopConstraint = $0.top.equalTo(view).offset(80).constraint + nullDataViewLeftConstraint = $0.left.equalTo(view).inset(10).constraint + $0.width.height.equalTo(200) +} +
    1
    2

    移除对应约束,设置约束再激活约束
    + +nullDataViewLeftConstraint?.deactivate() +nullDataViewLeftConstraint?.update(inset: 100) +nullDataViewLeftConstraint?.activate() +UIView.animate(withDuration: 1.5) { + self.view.layoutIfNeeded() +} +
    1
    2
    3
    4

    - 更新约束

    - 添加约束
    + +nullDataImageView.snp.makeConstraints { + nullDataViewTopConstraint = $0.top.equalTo(view).offset(64).constraint + $0.centerX.equalTo(view) + $0.width.height.equalTo(200) +} +
    1
    2

    - 更新约束
    + +nullDataViewTopConstraint?.update(inset: 84) +UIView.animate(withDuration: 0.5, delay: 0, options: .curveEaseOut, animations: { + self.view.layoutIfNeeded() +}) { (finished) in + print("动画执行完毕") +} +
    1
    2
    	
    - 添加`debug`模式中的 `key`使用`labeled()`方法
    + +nullDataImageView.snp.makeConstraints { + nullDataViewTopConstraint = $0.top.equalTo(view).offset(80).constraint + nullDataViewLeftConstraint = $0.left.equalTo(view).inset(10).constraint + $0.width.height.equalTo(200).labeled("width和height约束") +} +``` +
      +
    • 其他注意点都和Masnory相同
    • +
    + + +
    + +
    + +
    + + +
    @@ -526,7 +705,7 @@

    - Share + Share @@ -602,7 +781,7 @@

    - Share + Share @@ -712,7 +891,7 @@

    Runlo

    @@ -888,81 +1067,7 @@

    - Share - - - -

    - -

    - - - -
    - -
    - - -
    - - -

    - iOS 即使通讯网络知识篇 -

    - - -
    - -
    - -

    网络七层协议

    读《图解TCP/IP》第5版以及看过的关于网络文章之后自己的理解

    -

    OSI (Open System Interconnec) 译为 开发式系统互联 七层模型从下到上一次是:物理层 -> 数据链路层 —> 网络层 -> 传输层 -> 会话层 -> 表示层 -》 应用层

    -

    数据每次经过一层之后就会在数据头部增加一个首部信息

    -
    物理层:网卡,网线,集线器,中继器,调制解调器
      -
    • 定义设备标准,如网线,光纤接口类型,将0和1转化为电信号的强和弱,达到传输比特流目的,这层数据叫比特
    • -
    -
    数据链路层:网桥,交换机
      -
    • 传输的地址帧,并且有检测错误功能,保证数据可靠传输, 这层数据叫帧
    • -
    -
    网络层:路由器
      -
    • 两个计算机通信时可能会经过多个数据链路或者通信子网,网络层将数据链路层的帧组成数据包,包中有封装好的包头,其中含有逻辑地址信息(源站点和目的站点),此层为数据包选择合适的路由和交换结点,IP协议产生,这层数据叫着数据包
    • -
    -
    传输层:
      -
    • 解决数据如何在网络中传输,这个层负责获取全部信息,因此,它必须跟踪数据单元碎片、乱序到达的 数据包和其它在传输过程中可能发生的危险。第4层为上层提供端到端(最终用户到最终用户)的透明的、可靠的数据传输服务。所为透明的传输是指在通信过程中 传输层对上层屏蔽了通信传输系统的具体细节。传输层协议的代表包括:TCP、UDP、SPX等
    • -
    -
    会话层:
      -
    • 这一层也可以称为会晤层或对话层,在会话层及以上的高层次中,数据传送的单位不再另外命名,而是统称为报文。会话层不参与具体的传输,它提供包括访问验证和会话管理在内的建立和维护应用之间通信的机制。如服务器验证用户登录便是由会话层完成的
    • -
    -
    表示层:
      -
    • 这一层主要解决拥护信息的语法表示问题。它将欲交换的数据从适合于某一用户的抽象语法,转换为适合于OSI系统内部使用的传送语法。即提供格式化的表示和转换数据服务。数据的压缩和解压缩, 加密和解密等工作都由表示层负责。
    • -
    -
    应用层:HTTP 协议
      -
    • 如何包装数据
    • -
    -

    通常从上到下会分成四层

    -

    TCP/IP 通常是指TCP/IP协议族,是一组不同协议组合在一起构成的协议族

    -
      -
    • 应用层:FTP 应用层协议
    • -
    • 传输层:TCP,UDP 传输层协议,TCP 提供了一个可靠的服务
    • -
    • 网络层:IP,ICMP 网络层协议,传输时不可靠的
    • -
    • 链路层:以太网协议
    • -
    -

    Socket

    为了解决不同计算机上进程间通信问题
    服务器在指定的端口上监听,然后生成一对儿新的socket 进行通讯,一个线程对应一个socket

    -

    英文翻译为“插座”通常称为”套接字”, 本质是对于TCP/IP的封装,有客户端 socket 和 服务端socket

    -
      -
    • 流式 Socket (STREAM): 是一种面向连接的socket,针对于面向连接的TCP服务应用,因为安全所以效率低
    • -
    • 数据报式Socket (DATAGRAM): 是一种无连接的SOcket,正对于无连接的UDP服务应用,无序,不安全,需要在接收端分析重排或者要求重发,所以效率高
    • -
    - - -
    - @@ -1022,23 +1127,23 @@

    Recent Posts

    diff --git a/page/6/index.html b/page/6/index.html index 8346a61..d29e21a 100644 --- a/page/6/index.html +++ b/page/6/index.html @@ -62,6 +62,80 @@

    +
    + +
    + + +
    + + +

    + iOS 即使通讯网络知识篇 +

    + + +
    + +
    + +

    网络七层协议

    读《图解TCP/IP》第5版以及看过的关于网络文章之后自己的理解

    +

    OSI (Open System Interconnec) 译为 开发式系统互联 七层模型从下到上一次是:物理层 -> 数据链路层 —> 网络层 -> 传输层 -> 会话层 -> 表示层 -》 应用层

    +

    数据每次经过一层之后就会在数据头部增加一个首部信息

    +
    物理层:网卡,网线,集线器,中继器,调制解调器
      +
    • 定义设备标准,如网线,光纤接口类型,将0和1转化为电信号的强和弱,达到传输比特流目的,这层数据叫比特
    • +
    +
    数据链路层:网桥,交换机
      +
    • 传输的地址帧,并且有检测错误功能,保证数据可靠传输, 这层数据叫帧
    • +
    +
    网络层:路由器
      +
    • 两个计算机通信时可能会经过多个数据链路或者通信子网,网络层将数据链路层的帧组成数据包,包中有封装好的包头,其中含有逻辑地址信息(源站点和目的站点),此层为数据包选择合适的路由和交换结点,IP协议产生,这层数据叫着数据包
    • +
    +
    传输层:
      +
    • 解决数据如何在网络中传输,这个层负责获取全部信息,因此,它必须跟踪数据单元碎片、乱序到达的 数据包和其它在传输过程中可能发生的危险。第4层为上层提供端到端(最终用户到最终用户)的透明的、可靠的数据传输服务。所为透明的传输是指在通信过程中 传输层对上层屏蔽了通信传输系统的具体细节。传输层协议的代表包括:TCP、UDP、SPX等
    • +
    +
    会话层:
      +
    • 这一层也可以称为会晤层或对话层,在会话层及以上的高层次中,数据传送的单位不再另外命名,而是统称为报文。会话层不参与具体的传输,它提供包括访问验证和会话管理在内的建立和维护应用之间通信的机制。如服务器验证用户登录便是由会话层完成的
    • +
    +
    表示层:
      +
    • 这一层主要解决拥护信息的语法表示问题。它将欲交换的数据从适合于某一用户的抽象语法,转换为适合于OSI系统内部使用的传送语法。即提供格式化的表示和转换数据服务。数据的压缩和解压缩, 加密和解密等工作都由表示层负责。
    • +
    +
    应用层:HTTP 协议
      +
    • 如何包装数据
    • +
    +

    通常从上到下会分成四层

    +

    TCP/IP 通常是指TCP/IP协议族,是一组不同协议组合在一起构成的协议族

    +
      +
    • 应用层:FTP 应用层协议
    • +
    • 传输层:TCP,UDP 传输层协议,TCP 提供了一个可靠的服务
    • +
    • 网络层:IP,ICMP 网络层协议,传输时不可靠的
    • +
    • 链路层:以太网协议
    • +
    +

    Socket

    为了解决不同计算机上进程间通信问题
    服务器在指定的端口上监听,然后生成一对儿新的socket 进行通讯,一个线程对应一个socket

    +

    英文翻译为“插座”通常称为”套接字”, 本质是对于TCP/IP的封装,有客户端 socket 和 服务端socket

    +
      +
    • 流式 Socket (STREAM): 是一种面向连接的socket,针对于面向连接的TCP服务应用,因为安全所以效率低
    • +
    • 数据报式Socket (DATAGRAM): 是一种无连接的SOcket,正对于无连接的UDP服务应用,无序,不安全,需要在接收端分析重排或者要求重发,所以效率高
    • +
    + + +
    + +
    + +
    + + +

    - Share + Share @@ -937,7 +1011,7 @@

    - Share + Share @@ -949,55 +1023,6 @@

    - -
    - - -
    - - -

    - OC和JS交互 -

    - - -
    - -
    - -

    WebView相关属性

    autoResizingMask 是UIView的属性,作用是实现子控件相对于父控件的自动布局

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    autoResizingMask 是UIViewAutoresizing 默认是 UIViewAutoresizingNone

    UIViewAutoresizingNone = 0,
    UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
    UIViewAutoresizingFlexibleWidth = 1 << 1,
    UIViewAutoresizingFlexibleRightMargin = 1 << 2,
    UIViewAutoresizingFlexibleTopMargin = 1 << 3,
    UIViewAutoresizingFlexibleHeight = 1 << 4,
    UIViewAutoresizingFlexibleBottomMargin = 1 << 5
    -

    各属性解释:

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    UIViewAutoresizingNone
    不会随父视图的改变而改变

    UIViewAutoresizingFlexibleLeftMargin

    自动调整view与父视图左边距,以保证右边距不变

    UIViewAutoresizingFlexibleWidth
    自动调整view的宽度,保证左边距和右边距不变

    UIViewAutoresizingFlexibleRightMargin
    自动调整view与父视图右边距,以保证左边距不变

    UIViewAutoresizingFlexibleTopMargin
    自动调整view与父视图上边距,以保证下边距不变

    UIViewAutoresizingFlexibleHeight
    自动调整view的高度,以保证上边距和下边距不变

    UIViewAutoresizingFlexibleBottomMargin
    自动调整view与父视图的下边距,以保证上边距不变
    -

    OC 调用js

    webViewDidFinishLoad:(UIWebView *)webView方法中

    -
      -
    • 删除增加结点
    • -
    • 添加点击事件
    • -
    -

    js调用OC

    webView:(UIWebview )webView shouldStartLoadWithRequest:(NSURLRequest )request方法中

    -

    调用js页面点击按钮后保存图片到相册,必须调用系统提供的方法,不能自己自定义方法

    - - -
    - -
    - -

    - - -
    - -

    - - - -
    - -
    - - -
    - - -

    - WKWebiView 使用 -

    - - -
    - -
    - -

    WKWebiView iOS8.0 出现,性能要远远好于 iOS 2.0时出现的 UIWebView,主要记录WKWebView 的创建和代理方法

    创建WKWebView

    WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds];
    -self.webView = webView;
    -[self.view addSubview:webView];
    -webView.UIDelegate = self;
    -webView.navigationDelegate = self;
    -[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.baidu.com"]]];
    -

    遵守协议 WKNavgationDelegate WKUIDelegate

    -

    代理方法

      -
    • WKNavgationDelegate 方法

      -
        -
      • 发送请求之前决定是否跳转

        -
        1
        2
        3
        4
        5
        6
        7
        - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {

        decisionHandler(WKNavigationActionPolicyAllow);
        // 不允许跳转
        decisionHandler(WKNavigationActionPolicyCancel);
        NSLog(@"decidePolicyForNavigationAction");
        }
        -
      • -
      • 收到相应之后决定是否跳转

        -
        1
        2
        3
        4
        5
        6
        7
        8
        9
        - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {

        decisionHandler(WKNavigationResponsePolicyAllow);

        decisionHandler(WKNavigationResponsePolicyCancel);

        NSLog(@"decidePolicyForNavigationResponse");

        }
        -
      • -
      • 开始加载时调用

        -
        1
        2
        3
        4
        - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation {

        NSLog(@"didStartProvisionalNavigation");
        }
        -
      • -
      • 接受到服务器的跳转请求时调用

        -
        1
        2
        3
        - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation {
        NSLog(@"didReceiveServerRedirectForProvisionalNavigation");
        }
        -
      • -
      -
    • -
    -
    - 页面加载失败时调用 **存在缓存问题**
    -
    -
    1
    2
    3
    4
    - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {

    NSLog(@"error= %@", error);
    }
    - - -- 内容开始返回时调用 - -
    1
    2
    3
    4
    - (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation {

    NSLog(@"didCommitNavigation");
    }
    -
      -
    • 内容返回成功后调用

      -
      1
      2
      3
      4
      - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation {

      NSLog(@"didFinishNavigation");
      }
      -
    • -
    • 开始返回错误时调用

      -
      1
      2
      3
      4
      - (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {

      NSLog(@"didFailNavigation");
      }
      -
    • -
    -
      -
    • 目前不知道什么时候调用

      -
      1
      2
      3
      - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler {
      NSLog(@"didReceiveAuthenticationChallenge");
      }
      -
        -
      • 目前不知道什么时候调用

        -
        1
        2
        3
        4
        - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView API_AVAILABLE(macosx(10.11), ios(9.0)) {

        NSLog(@"webViewWebContentProcessDidTerminate");
        }
        -
      • -
      -
    • -
    - - -
    -
    - Share + Share @@ -1045,23 +1005,23 @@

    Recent Posts

    diff --git a/page/8/index.html b/page/8/index.html index 15647e3..22fa8c6 100644 --- a/page/8/index.html +++ b/page/8/index.html @@ -62,6 +62,95 @@

    +
    + +
    + + +
    + + +

    + WKWebiView 使用 +

    + + +
    + +
    + +

    WKWebiView iOS8.0 出现,性能要远远好于 iOS 2.0时出现的 UIWebView,主要记录WKWebView 的创建和代理方法

    创建WKWebView

    WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds];
    +self.webView = webView;
    +[self.view addSubview:webView];
    +webView.UIDelegate = self;
    +webView.navigationDelegate = self;
    +[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.baidu.com"]]];
    +

    遵守协议 WKNavgationDelegate WKUIDelegate

    +

    代理方法

      +
    • WKNavgationDelegate 方法

      +
        +
      • 发送请求之前决定是否跳转

        +
        1
        2
        3
        4
        5
        6
        7
        - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {

        decisionHandler(WKNavigationActionPolicyAllow);
        // 不允许跳转
        decisionHandler(WKNavigationActionPolicyCancel);
        NSLog(@"decidePolicyForNavigationAction");
        }
        +
      • +
      • 收到相应之后决定是否跳转

        +
        1
        2
        3
        4
        5
        6
        7
        8
        9
        - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {

        decisionHandler(WKNavigationResponsePolicyAllow);

        decisionHandler(WKNavigationResponsePolicyCancel);

        NSLog(@"decidePolicyForNavigationResponse");

        }
        +
      • +
      • 开始加载时调用

        +
        1
        2
        3
        4
        - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation {

        NSLog(@"didStartProvisionalNavigation");
        }
        +
      • +
      • 接受到服务器的跳转请求时调用

        +
        1
        2
        3
        - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation {
        NSLog(@"didReceiveServerRedirectForProvisionalNavigation");
        }
        +
      • +
      +
    • +
    +
    - 页面加载失败时调用 **存在缓存问题**
    +
    +
    1
    2
    3
    4
    - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {

    NSLog(@"error= %@", error);
    }
    + + +- 内容开始返回时调用 + +
    1
    2
    3
    4
    - (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation {

    NSLog(@"didCommitNavigation");
    }
    +
      +
    • 内容返回成功后调用

      +
      1
      2
      3
      4
      - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation {

      NSLog(@"didFinishNavigation");
      }
      +
    • +
    • 开始返回错误时调用

      +
      1
      2
      3
      4
      - (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {

      NSLog(@"didFailNavigation");
      }
      +
    • +
    +
      +
    • 目前不知道什么时候调用

      +
      1
      2
      3
      - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler {
      NSLog(@"didReceiveAuthenticationChallenge");
      }
      +
        +
      • 目前不知道什么时候调用

        +
        1
        2
        3
        4
        - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView API_AVAILABLE(macosx(10.11), ios(9.0)) {

        NSLog(@"webViewWebContentProcessDidTerminate");
        }
        +
      • +
      +
    • +
    + + +
    + +
    + +
    + + +

    数据结构

    objc_object 结构体

    objc_class

    对象、类对象、元类对象的区别

    • 类对象存储实例方法列表等信息
    • 元类对象存锤类方法列表等信息

    消息传递

    void objc_msgSend(void /* id self, SEL op, ... */)

    经过编译器转变后
    [self class] <==> objc_msgSend(self, @selector(class))
    第一个参数是消息传递的接收者self, 第二个参数是传递的消息名称(选择器)

    void objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */)
    第一个参数是一个结构体指针

    //
    struuch objc_super {
    __unsafe_unretained id receiver; // 就是当前的对象
    }

    [superclass] <==> objc_msgSendSuper(super, @selector(class))

    无论调用[super class] 还是 [self class] 最终这条消息的接收者都是当前对象

    图1

    消息传递过程

    图2

    问题1

    打印结果都是 Phone

    原因:

    • 无论调用[super class] 还是 [self class] 最终这条消息的接收者都是当前对象
    • [self class] 在Phone的类对象中查找class 方法,没有去父类查找,父类也没有,如图1顺次往上查找到根类,调用根类中class 的具体实现
    • [super class]只是跨越了当前类,直接从当前类的父类中查找 class 方法,父类中没有class 方法,如图1所示只能继续往上查找到根类对象,在NSObject 中有class 方法实现,所以接受者任然是当前的对象

    缓存查找

    根据给定的SEL(方法选择器)通过一个函数来映射出bucket_t 在数组当中的位置,本质上是哈希查找

    当前类中查找

    当前类中存在着对应的方法列表

    • 对于已经排序好的列表,采用二分查找算法查找对应的执行函数
    • 对于没有排序的列表,采用一般遍历查找方法查找对应的执行函数

    父类逐级查找

    图3

    • 最关键是通过当前类的 superclass 成员变量去查找父类,或者访问父类
    • curClass 的父类是否为 nil?如果是NSObject 类,他的父类为空,所以会结束查找
    • 如果当前类有父类就要去该父类的缓存中查找,如果在缓存中命中,那么就结束查找,如果缓存中没有,就需要遍历当前类的父类的方法列表,如果没有就遍历父类的父类查找,沿着superclass指针逐级向上查找,直到查找到NSObject,再去Superclass。如果还是没有为nil时就结束父类逐级查找

    消息转发

    resolveInstanceMethod:

    • 该方法是类方法不是实例方法,参数是SEL 方法选择器类型
    • 返回值是一个BOOL值
    • 告诉系统是否要解决当前实例方法的实现
    • 如果返回YES,结束消息转发
    • 如果返回NO,系统会给第二次机会处理这个消息,会回掉forwardingTargetForSelector:

    forwardingTargetForSelector:

    • 参数是 SEL 方法选择器
    • 返回值是 id 类型,说明有哪个对象来处理,转发的对象是谁,如果返回了转发目标就会结束当前调用
    • 如果返回为nil,系统会调用 methodSignatureForSelector:方法,最后一次机会处理这个消息

    methodSignatureForSelector:

    • 参数是 SEL 方法选择器
    • 返回值是一个对象,实际上这个对象是对methodSignatureForSelector 方法的参数,参数个数,参数类型和返回值的包装
    • 如果返回一个方法签名的话,就会调用 forwardInvocation: 方法
    • 如果返回为nil,标记为消息无法处理,程序crash

    forwardInvocation:

    • 如果此方法不能处理消息,程序crash

    代码示例

    .h 文件

    1
    2
    3
    4
    @interface RunTimeObject : NSObject
    // 只声明,不实现,验证消息转发机制
    - (void)test;
    @end

    .m 文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    @implementation RunTimeObject

    // 实现消息转发流程 中的方法
    /**
    是否要解决当前实例方法的实现
    */
    + (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(test)) {
    NSLog(@"resolveInstanceMethod:");
    // 如果返回YES,消息转发就会结束
    // return YES;

    return NO;
    } else {

    NSLog(@"super resolveInstanceMethod:");
    // 返回父类的默认调用
    return [super resolveInstanceMethod:sel];
    }
    }


    /**
    - 参数是 SEL 方法选择器
    - 返回值是 id 类型,说明有哪个对象来处理,转发的对象是谁,如果返回了转发目标就会结束当前调用
    - 如果返回为nil,系统会调用 methodSignatureForSelector:方法,最后一次机会处理这个消息
    */
    - (id)forwardingTargetForSelector:(SEL)aSelector {
    NSLog(@" forwardingTargetForSelector: ");
    return nil;
    }

    /**
    - 参数是 SEL 方法选择器
    - 返回值是一个对象,实际上这个对象是对`methodSignatureForSelector` 方法的参数,参数个数,参数类型和返回值的包装
    - 如果返回一个方法签名的话,就会调用 forwardInvocation: 方法
    - 如果返回为nil,标记为消息无法处理,程序crash

    */
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    if (aSelector == @selector(test)) {
    NSLog(@"methodSignatureForSelector:");
    /**
    v: 表示这个方法返回值是 void

    固定参数@: 表示参数类型是 id, 即self
    固定参数`:` : 表示参数类型是选择器类型,即 @selector(test)
    */
    return [NSMethodSignature signatureWithObjCTypes:"v@:"];
    } else {

    NSLog(@"super methodSignatureForSelector:");
    // 返回父类的默认调用
    return [super methodSignatureForSelector:aSelector];
    }
    }


    - (void)forwardInvocation:(NSInvocation *)anInvocation {
    NSLog(@"forwardInvocation:");
    }

    @end

    调用

    1
    2
    3
    4
    - (void)runTiemTest {
    RunTimeObject *obj = [[RunTimeObject alloc] init];
    [obj test];
    }

    结果:

    1
    2
    3
    4
    5
    resolveInstanceMethod:
    forwardingTargetForSelector:
    methodSignatureForSelector:
    super resolveInstanceMethod:
    forwardInvocation:

    为类动态添加方法

    methond Swizzling

    使用场景

    • 页面中的进出添加统计信息,使用 methond Swizzling 替换viewillAppear 方法

    动态添加方法

    • perforSelector: 实际上是考察class_addMethod 的方法使用

    动态方法解析

    • @dynamic关键字
    • 动态运行时语言将函数决议推迟到运行时
    • 编译时不生成setter/getter 方法,而是在运行时才添加具体的执行函数

    问题

    [obj foo] 和 obj_msgSend()函数之间有什么关系?

    • [obj foo]经过编译器处理过后会变成 obj_msgSend()有两个参数,一个是obj,第二个参数是foo 选择器

    runtime 如何通过Selector找到对应的IMP地址的?

    消息传递机制

    能否向编译后的类增加实例变量

    编译之前就完成了实例变量的布局,
    runtime_r_t 表示是readonly 的,编译后的类是不能添加实例变量的,可以向动态添加的类中添加实例变量

    ]]> @@ -135,10 +148,10 @@ - - /2019/06/18/%E7%AC%AC%E4%BA%94%E7%AB%A0Runtime%E7%9B%B8%E5%85%B3%E9%9D%A2%E8%AF%95/ + + /2019/06/18/%E7%AC%AC%E5%85%AD%E7%AB%A0%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86%E9%9D%A2%E8%AF%95/ - Runtime 相关问题

    数据结构

    objc_object 结构体

    objc_class

    对象、类对象、元类对象的区别

    • 类对象存储实例方法列表等信息
    • 元类对象存锤类方法列表等信息

    消息传递

    void objc_msgSend(void /* id self, SEL op, ... */)

    经过编译器转变后
    [self class] <==> objc_msgSend(self, @selector(class))
    第一个参数是消息传递的接收者self, 第二个参数是传递的消息名称(选择器)

    void objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */)
    第一个参数是一个结构体指针

    //
    struuch objc_super {
    __unsafe_unretained id receiver; // 就是当前的对象
    }

    [superclass] <==> objc_msgSendSuper(super, @selector(class))

    无论调用[super class] 还是 [self class] 最终这条消息的接收者都是当前对象

    图1

    消息传递过程

    图2

    问题1

    打印结果都是 Phone

    原因:

    • 无论调用[super class] 还是 [self class] 最终这条消息的接收者都是当前对象
    • [self class] 在Phone的类对象中查找class 方法,没有去父类查找,父类也没有,如图1顺次往上查找到根类,调用根类中class 的具体实现
    • [super class]只是跨越了当前类,直接从当前类的父类中查找 class 方法,父类中没有class 方法,如图1所示只能继续往上查找到根类对象,在NSObject 中有class 方法实现,所以接受者任然是当前的对象

    缓存查找

    根据给定的SEL(方法选择器)通过一个函数来映射出bucket_t 在数组当中的位置,本质上是哈希查找

    当前类中查找

    当前类中存在着对应的方法列表

    • 对于已经排序好的列表,采用二分查找算法查找对应的执行函数
    • 对于没有排序的列表,采用一般遍历查找方法查找对应的执行函数

    父类逐级查找

    图3

    • 最关键是通过当前类的 superclass 成员变量去查找父类,或者访问父类
    • curClass 的父类是否为 nil?如果是NSObject 类,他的父类为空,所以会结束查找
    • 如果当前类有父类就要去该父类的缓存中查找,如果在缓存中命中,那么就结束查找,如果缓存中没有,就需要遍历当前类的父类的方法列表,如果没有就遍历父类的父类查找,沿着superclass指针逐级向上查找,直到查找到NSObject,再去Superclass。如果还是没有为nil时就结束父类逐级查找

    消息转发

    resolveInstanceMethod:

    • 该方法是类方法不是实例方法,参数是SEL 方法选择器类型
    • 返回值是一个BOOL值
    • 告诉系统是否要解决当前实例方法的实现
    • 如果返回YES,结束消息转发
    • 如果返回NO,系统会给第二次机会处理这个消息,会回掉forwardingTargetForSelector:

    forwardingTargetForSelector:

    • 参数是 SEL 方法选择器
    • 返回值是 id 类型,说明有哪个对象来处理,转发的对象是谁,如果返回了转发目标就会结束当前调用
    • 如果返回为nil,系统会调用 methodSignatureForSelector:方法,最后一次机会处理这个消息

    methodSignatureForSelector:

    • 参数是 SEL 方法选择器
    • 返回值是一个对象,实际上这个对象是对methodSignatureForSelector 方法的参数,参数个数,参数类型和返回值的包装
    • 如果返回一个方法签名的话,就会调用 forwardInvocation: 方法
    • 如果返回为nil,标记为消息无法处理,程序crash

    forwardInvocation:

    • 如果此方法不能处理消息,程序crash

    代码示例

    .h 文件

    1
    2
    3
    4
    @interface RunTimeObject : NSObject
    // 只声明,不实现,验证消息转发机制
    - (void)test;
    @end

    .m 文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    @implementation RunTimeObject

    // 实现消息转发流程 中的方法
    /**
    是否要解决当前实例方法的实现
    */
    + (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(test)) {
    NSLog(@"resolveInstanceMethod:");
    // 如果返回YES,消息转发就会结束
    // return YES;

    return NO;
    } else {

    NSLog(@"super resolveInstanceMethod:");
    // 返回父类的默认调用
    return [super resolveInstanceMethod:sel];
    }
    }


    /**
    - 参数是 SEL 方法选择器
    - 返回值是 id 类型,说明有哪个对象来处理,转发的对象是谁,如果返回了转发目标就会结束当前调用
    - 如果返回为nil,系统会调用 methodSignatureForSelector:方法,最后一次机会处理这个消息
    */
    - (id)forwardingTargetForSelector:(SEL)aSelector {
    NSLog(@" forwardingTargetForSelector: ");
    return nil;
    }

    /**
    - 参数是 SEL 方法选择器
    - 返回值是一个对象,实际上这个对象是对`methodSignatureForSelector` 方法的参数,参数个数,参数类型和返回值的包装
    - 如果返回一个方法签名的话,就会调用 forwardInvocation: 方法
    - 如果返回为nil,标记为消息无法处理,程序crash

    */
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    if (aSelector == @selector(test)) {
    NSLog(@"methodSignatureForSelector:");
    /**
    v: 表示这个方法返回值是 void

    固定参数@: 表示参数类型是 id, 即self
    固定参数`:` : 表示参数类型是选择器类型,即 @selector(test)
    */
    return [NSMethodSignature signatureWithObjCTypes:"v@:"];
    } else {

    NSLog(@"super methodSignatureForSelector:");
    // 返回父类的默认调用
    return [super methodSignatureForSelector:aSelector];
    }
    }


    - (void)forwardInvocation:(NSInvocation *)anInvocation {
    NSLog(@"forwardInvocation:");
    }

    @end

    调用

    1
    2
    3
    4
    - (void)runTiemTest {
    RunTimeObject *obj = [[RunTimeObject alloc] init];
    [obj test];
    }

    结果:

    1
    2
    3
    4
    5
    resolveInstanceMethod:
    forwardingTargetForSelector:
    methodSignatureForSelector:
    super resolveInstanceMethod:
    forwardInvocation:

    为类动态添加方法

    methond Swizzling

    使用场景

    • 页面中的进出添加统计信息,使用 methond Swizzling 替换viewillAppear 方法

    动态添加方法

    • perforSelector: 实际上是考察class_addMethod 的方法使用

    动态方法解析

    • @dynamic关键字
    • 动态运行时语言将函数决议推迟到运行时
    • 编译时不生成setter/getter 方法,而是在运行时才添加具体的执行函数

    问题

    [obj foo] 和 obj_msgSend()函数之间有什么关系?

    • [obj foo]经过编译器处理过后会变成 obj_msgSend()有两个参数,一个是obj,第二个参数是foo 选择器

    runtime 如何通过Selector找到对应的IMP地址的?

    消息传递机制

    能否向编译后的类增加实例变量

    编译之前就完成了实例变量的布局,
    runtime_r_t 表示是readonly 的,编译后的类是不能添加实例变量的,可以向动态添加的类中添加实例变量

    ]]> + 内存管理

    内存布局

    • stack 栈是从高地址向低地址扩展,所以栈是向下增长的,对象和block copy 后都会放在堆上
    • heap 堆区向上增长
    • 散列表方式

      • 自旋锁
        • 自旋锁是“忙等” 的锁
      • 引用计数表
      • 弱应用表

    内存管理方案

    • 小对象使用 TaggedPointer
    • 64 位架构下面使用的是 NONPONINT_ISA 内存管理方案,非指针型的 isa,内部存储了一些
    • 散列表结构 (Side Tables() 结构)
      • 自旋锁
      • 引用计数表
      • 弱引用表
      • Side Tables() 结构

    为什么不是一个SideTable,而是多个组成了Side Tables

    如果所有对象的存储都放在一张大表当中,因为每个对象是在不同的线程中创建的,要操作其中一个对象时,需要将表加锁处理保证数据安全,要等锁释放之后才能操作下一个对象,此时存在效率问题,引入分离锁技术方案

    • 分离锁:把引用计数表分成多张表进行,
    • Side Tables 本质是一张Hash 表

    自旋锁

    是“忙等”的锁,如果当前锁已被其他线程获取,当前线程会不断探测是否被释放,会第一时间获取,而其他锁比如信号量当它获取不到锁时,会把自己的线程阻塞休眠,等到其他线程释放这个锁时再唤醒这个锁

    • 适用于轻量访问

    引用计数表

    使用过Hash表来实现,hash查找提高了查找高效率,插入和获取是通过Hash 算法来是实现的,避免了for循环

    alloc 实现

    经过一系列调用,最终调用了C函数 calloc, 并没有引用计数+1

    retain 实现

    底层经过了2次Hash表查找

    release 实现

    和 retain 实现相反

    dealloc 实现原理

    objc_destructInstance() 实现

    clearDeallocating() 实现

    MRC

    手动引用计数

    ARC

    • 编译器在对应位置自动插入retain 和 release 操作,并和runtime协作达到自动引用计数管理内存的效果

    • ARC 中禁止手动调用 retain/release

    • ARC 中新增 weeak、strong 等关键字

    弱引用管理

    被声明为__weak 的对象指针经过编译后会进过调用以下方法,在最终的weak_register_no_lock() 方法中进行弱引用变量的添加。添加的位置是通过Hash算法查找的

    weak 修饰的对象,释放时置为nil 如何实现的

    当一个对象被 dealloc 之后,在dealloc内部实现当中会调用弱引用清除的相关函数,在相关函数当中会根据当前对象指针查找弱引用表,把当前对象下对应的弱应用都取出,遍历弱应用指针并置为nil。

    自动释放池

    是以栈为结点通过双向链表的形式组合而成,和线程一一对应

    Autoreleasepool

    循环引用

    自循环引用

    一个对象中有拥有一个 强持有他的 obj,如果给 obj 赋值为原对象就会造成自循环引用

    相互循环引用

    • 代理
    • Block
    • NSTimer
    • 大环多循环

    多循环引用

    解决循环引用的方案

    __weak

    解决相互循环引用

    __block

    __unsafe_unretained

    一般不建议使用

    ]]>
    @@ -174,10 +187,10 @@ - - /2019/06/18/%E3%80%8A52%E4%B8%AA%E6%96%B9%E6%B3%95%E7%AC%AC%E4%B8%80%E7%AB%A0%E3%80%8B/ + + /2019/06/18/swift%E4%B8%AD%E5%BC%BA%E5%BC%95%E7%94%A8%E5%BE%AA%E7%8E%AF%E5%92%8Cweak%E5%85%B3%E9%94%AE%E5%AD%97/ - 《52个方法-第一章》

    一、OC起源

    • OC使用的是“消息结构”而不是“函数调用”
    • 两者区别:
      • 消息结构:在运行时所执行的代码由运行环境来决定,无论是否多态总会在运行时才会去查找所要执行的方法
        1
        2
        NSObject *objc = [NSObject new];
        [objc performWith: parameter1 and: parameter2];
    - 函数调用:由编译器决定,如果函数多态,编译时就要查处到底应该执行那个函数实现
    1
    2
    Object *objc = new Object;
    objc -> perform(parameter1, parameter2);
    • 消息结构中编译器不会关心接收消息的对象是何种类型,接收消息的对象问题也要在运行时处理,此过程叫做“动态绑定”

    要点:

    1. OC语言使用的动态绑定的消息结构,在运行时所执行的代码由运行环境来决定,无论是否多态总会在运行时才会去查找所要执行的方法,
    2. OC 中对象总是分配在“堆空间”,而绝不会分配在”栈“上,不带 “*”的变量或者结构体等可能会使用“栈空间”

    二、类头文件尽量少导入其他头文件

    要点:

    1. 除非必要,否则不要引入头文件,使用向前声明降低类之间的耦合
    2. 有时无法使用向前声明,应该把该类遵守协议的声明放到“class-continuation分类”(匿名分类)中。
    3. 如果“class-continuation分类”也不行的话,就把协议单独放在一个文件中,再将其引入### 三、多用字面量语法,少用与之等价的方法

    三、多用字面量语法,少用与之等价的方法

    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
    - (void)tempMethond {
    // OC1.0 起,支持简单方式创建基本类型对象

    // - 使用字符串字面量:简介易读
    NSString *someString = @"Effective Objective-C 2.0";
    // 传统方式
    NSString *someStrongOld = [NSString stringWithFormat:@"Effective Objective-C 2.0"];


    // - 字面数值
    NSNumber *someNumber = @1;
    NSNumber *floatNumber = @2.5f;
    NSNumber *doubleNumber = @3.1415926;
    NSNumber *boolNumber = @YES;
    NSNumber *charNumber = @'a';
    // 字面量语法用于表达式
    int x = 5;
    float y = 6.23f;
    NSNumber *expressionNumber = @(x * y);


    // - 字面量数组
    NSArray *animals = @[@"cat", @"dog", @"mouse", @"badger"];
    NSString *dog = animals[1];

    // arrayWithObjects 和 字面量 的区别:
    // arrayWithObjects 方法依次处理各个参数,直到发现nil为止
    // 字面量数组遇到nil直接抛出异常
    NSArray *animalsOld = [NSArray arrayWithObjects:@"cat", @"dog", @"mouse", @"badger", nil];
    NSString *dogOld = [animalsOld objectAtIndex:1];


    // - 字面量字典
    // 如果有nil直接抛出异常
    NSDictionary *personData = @{@"firtName": @"Matt",
    @"lastName": @"Galloway",
    @"age": @28};
    NSString *lastName = personData[@"lastName"];

    // dictionaryWithObjectsAndKeys 方法依次处理各个参数,直到发现nil为止
    NSDictionary *personDataOld = [NSDictionary dictionaryWithObjectsAndKeys:@"Matt",@"firtName",
    @"Galloway", @"lastName",
    [NSNumber numberWithInteger:28], @"age", nil];

    NSString *lastNameOld = [personData objectForKey:@"lastName"];


    // - 可变数组和字典
    NSMutableArray *mutableArray = animals.mutableCopy;
    mutableArray[1] = @"dog";

    NSMutableDictionary *mutableDict = [personData mutableCopy];
    mutableDict[@"lastName"] = @"Galloway";
    }

    要点:

    1. 使用字面量语法创建字符串,数值,数组,字典简明扼要
    2. 通过下标操作获取数组,字典对应的元素
    3. 使用字面量创建数组,字典时若有nil ,会抛出异常,确保值里不含nil### 五、用枚举表示状态、选项、状态码

    四、多用类型常量,少用#define预处理指令

    宏定义
    • 缺点:
      • 可以把所有相同的字符串值都替换为定义的值,
        • 定义出来的常量没有具体的类型信息
        • 如果有人重新定义了宏定义,不会有警告或者提醒,导致程序中值不一样
    类型常量
    • 命名规则:

      • 如果只是在实现文件.m 中使用,则在前面加字母k
      • 如果此常量在类之外也可见,则以类名为前缀,(第19条详细了解命名习惯)
    • 声明位置:

      • 总是喜欢在头文件中声明宏定义,可能和常量名冲突,
      • 所有引入这个头文件的类都会出现宏定义或者常量,相当于声明了一个名叫kAnimationDuration的全局变量
      • 应该加上前缀,表明其所属的具体类,例如:EOCViewClassAnimationDuration
    • 常量从右往左解读依次是: 具体类型,不可修改

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      // const 修饰的是常量不希望别人修改, NSString * 指向NSString对象
      extern NSString *const EOCStringConstant;


      //两者写法都可以
      extern const NSTimeInterval EOCAnimatedViewAnimationDuration;
      extern NSTimeInterval const EOCAnimatedViewAnimationDuration1;
      ```

      - 如果不打算公开常量,可以定义在 .m 实现文件里

      static const NSTimeInterval kAnimationDuration = 0.3;

      1
      2
      3

      - static关键字
      - static 修饰意味着该变量仅在定义此变量的编译单元(实现文件.m)中可见,作用域就是当前.m文件
      static const NSTimeInterval kAnimationDuration = 0.3;
      1
      2
      3
      - 如果需要声明一个外部可见的常值变量,需要在头文件中声明并使用 extern 关键词修饰 "extern 

      在 .h 文件中
      extern NSString *const EOCStringConstant;// 两者写法都可以extern const NSTimeInterval EOCAnimatedViewAnimationDuration;extern NSTimeInterval const EOCAnimatedViewAnimationDuration1;
      1
      2

      在 .m 文件中
      NSString *const EOCStringConstant = @"VALUE";const NSTimeInterval EOCAnimatedViewAnimationDuration = 0.3;NSTimeInterval const EOCAnimatedViewAnimationDuration1 = 0.3;
      1
      2
      3
      4
      5
      6
      7
      8
      9

      **要点:**

      1. 定义常量时static const定义编译单元(实现文件.m)中可见的常量,无需为其名称加前缀
      2. 在头文件中使用extern 声明全局常量,并在实现文件中定义其值,这种常量要出现在全局符号表中,所以加相关类作为前缀

      ### 五、用枚举表示状态、选项、状态码

      - 普通枚举

      enum EOCConnectionState {

      EOCConnectionStateDisconnected, // 断开连接EOCConnectionStateConnecting,   // 连接中...EOCConnectionStateConnected,    // 已连接

      };

      1
      普通枚举如果要使用如下:

      enum EOCConnectionState status = EOCConnectionStateConnected;

      1
      2

      如果每次都不需要输入enum关键字,只需要typedef关键字重新定义枚举类型

      EOCConnectionState status1 = EOCConnectionStateConnecting;

      1
      2

      - 设置枚举的初始值

      enum EOCConnectionStateConnectionState {

      EOCConnectionStateConnectionStateDisconnected = 1, // 断开连接EOCConnectionStateConnectionStateConnecting,   // 连接中...EOCConnectionStateConnectionStateConnected,    // 已连接

      };

      1
      2
      3
      4
      5

      - 可选枚举

      - << 位操作符(左移), 表示往左移动N位,使用 位运算符 | (或)来组合使用
      - 使用位运算符 &(与) 判断是否开启了某个选项

      enum UIViewAutoresizing {

      UIViewAutoresizingNone                 = 0,UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,UIViewAutoresizingFlexibleWidth        = 1 << 1,UIViewAutoresizingFlexibleRightMargin  = 1 << 2,UIViewAutoresizingFlexibleTopMargin    = 1 << 3,UIViewAutoresizingFlexibleHeight       = 1 << 4,UIViewAutoresizingFlexibleBottomMargin = 1 << 5

      }

      1
      2
      3
      4

      - 使用宏定义枚举

      普通枚举类型(老式语法定义枚举)

      typedef enum : NSUInteger {

      EOCConnectionState2DisConnected,EOCConnectionState2DisConnecting,EOCConnectionState2Connected,

      } EOCConnectionState2;

      1
      2

      NS_ENUM 宏定义的枚举展开后:

      typedef enum EOCConnectionState_3 : NSUInteger EOCConnectionState_3;
      enum EOCConnectionState_3 : NSUInteger {

      EOCConnectionState_3DisConnected,EOCConnectionState_3DisConnecting,EOCConnectionState_3Connected,

      };

      1
      2

      - 定义新特性枚举

      typedef NS_ENUM(NSUInteger, EOCConnectionState3) {

      EOCConnectionState3DisConnected,EOCConnectionState3DisConnecting,EOCConnectionState3Connected,

      };

      1
      2
      3
      4

      - 枚举使用

      .h文件

      #import <UIKit/UIKit.h>

      typedef enum EOCConnectionState EOCConnectionState;

      typedef NS_ENUM(NSUInteger, EOCConnectionState3) {

      EOCConnectionState3DisConnected,EOCConnectionState3DisConnecting,EOCConnectionState3Connected,

      };

    @interface EOCEnum : NSObject@property (nonatomic, assign) enum EOCConnectionState state;@property (nonatomic, assign) EOCConnectionState3 state3;@end
    1
    2

    .m 文件中
    #import "EOCEnum.h"@implementation EOCEnum- (void)testEnum { // 未使用typedef 关键字 enum EOCConnectionState status = EOCConnectionStateConnected; NSLog(@"%u", status); // 使用typedef 关键字 EOCConnectionState status1 = EOCConnectionStateConnecting; NSLog(@"%u", status1); UIViewAutoresizing resizing = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; if (resizing & UIViewAutoresizingFlexibleWidth) { // 设置了 UIViewAutoresizingFlexibleWidth 约束 }}// UIKit 试图所支持的设置显示方向- (UIInterfaceOrientationMask)supportedInterfaceOrientations { return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft;}// 枚举使用- (void)userEnum { switch (_state3) { case EOCConnectionState3DisConnected: // ... break; case EOCConnectionState3DisConnecting: // ... break; case EOCConnectionState3Connected: // ... break; default: // 最好不用,防止以后增加一种状态机之后,编译器不发出警告 break; }}@end```

    要点:

    1. 使用枚举表示状态机的状态、传递给方法的选项以及状态码等值,给枚举值起名时要注重易懂
    2. 如果一个类型可以使用状态机的状态来表示,并且多个选项可同时使用,那么就定义为可选的枚举类型,枚举各值定义为2的幂,以便通过“按位或”操作组合
    3. 使用NS_ENUM 和 NS_OPTIONS 宏来定义枚举,并指明底层的数据类型。这样可以确保枚举是用开发者所选的底层数据类型实现出来的,而不会采用编译器所选的类型
    4. switch语句处理枚举类型时不要实现default 分支,如果以后加入枚举的新状态之后,编译器会发出警告
    ]]>
    + Swift中内存管理

    强引用循环和 weak 关键字

    房东

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class Landlord {
    var name: String
    var house: House?

    init(name: String) {
    self.name = name
    }

    deinit {
    print("Landlord内存释放")
    }
    }

    房子

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class House {
    var name: String
    var landlord: Landlord?

    init(person: String) {
    self.name = person
    }

    deinit {
    print("House内存释放")
    }
    }

    循环引用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    func strongRetain() {
    var landlord: Landlord? = Landlord(name: "老李")
    var house: House? = House(person: "望京府邸")
    // 房东有个房子
    landlord?.house = house
    // 房子有个主人
    house?.landlord = landlord

    landlord = nil
    house = nil
    }
    • 虽然 landlord = nil 和 house = nil,但是 Landlord 和 House 内存空间并没有释放 (并未执行 deinit 函数)

    • 原因:因为两个内存空间相互引用,引用计数不为0,形成了强循环引用

    • 解决办法:使用weak 关键字来修饰两者中其中一个变量

      • 使用weak修饰的变量必须是可选类型,可以赋值为nil
      • 使用weak修饰必须是var 类型变量
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class House {
    var name: String
    weak var landlord: Landlord?

    init(person: String) {
    self.name = person
    }

    deinit {
    print("House内存释放")
    }
    }

    unowned 关键字的使用

    相互循环引用的两个变量有一个是可选的

    有个身份证的类,每个身份证肯定对应一个人

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    class ID {
    var number: String
    let person: Landlord

    init(person: Landlord, number: String) {
    self.person = person
    self.number = number
    }

    deinit {
    print("ID内存释放")
    }
    }

    ```

    房东

    class Landlord {
    var name: String
    var house: House?
    /// 身份证可以为nil,
    var id: ID?

    init(name: String) {    self.name = name}deinit {    print("Landlord内存释放")}

    }

    1
    2

    循环引用

    func unownedTest() {
    var person: Landlord? = Landlord(name: “老王”)
    var id: ID? = ID(person: person!, number: “10010”)

    person?.id = idperson = nilid = nil

    }

    1
    2
    3
    4
    5
    6
    7
    8

    - person 和 id 之间有强引用关系
    - 每个id必须有一个对应的人并且是固定的只能使用let 修饰,不能为nil, 而id可以为空
    - unowned和 weak 作用相同都是弱引用,只是unowned 修改的变量不能是nil (可选型的)
    - unowned只能使用在 class 类型上,不能修饰函数类型,例如闭包存在循环引用时不能使用unowned修饰
    - 解决方法:
    - 此时可以通过使用 weak 修饰 id 的方式,因为 id 可以为nil
    - let 修饰并且不能为nil 这种情况可以使用 unowned 修饰解决
    class ID {    var number: String    unowned let person: Landlord    init(person: Landlord, number: String) {        self.person = person        self.number = number    }    deinit {        print("ID内存释放")    }}
    1
    2
    3
    4

    **unowned有一定的危险性**

    因为unowned 修饰的对象不能赋值为nil,所以执行下面的代码会crash,

    func unownedTest() {
    var person: Landlord? = Landlord(name: “老王”)
    var id: ID? = ID(person: person!, number: “10010”)

    person?.id = id// 提前释放 person 内存person = nil// 获取身份证对应的人print(id?.person) // 此处奔溃,let owner = id?.personid = nil

    }

    1
    2

    正确的写法因该是先将🆔设置为nil,再将 Person 设置为nil,再调用 print(id?.person) 就不会再报错

    func unownedTest() {
    var person: Landlord? = Landlord(name: “老王”)
    var id: ID? = ID(person: person!, number: “10010”)

    person?.id = idid = nil// 提前释放 person 内存person = nil// 获取身份证对应的人print(id?.person) // 此处奔溃,let owner = id?.person

    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

    #### 相互循环引用的两个变量都不是可选的

    使用隐式可选类型

    ### 闭包中的强引用循环

    - 并不是所有的闭包都会产生循环引用,只有相互引用的两个对象会产生循环引用

    - 使用swift闭包捕获列表,在闭包的声明参数之前使用 [unowned self], 不需要解包

    - 使用 unowned表示这个对象不能为空,可能存在风险,如果对象为空就会crash


    使用 unowned 对象不能为空,可能存在风险

    class Closure {
    var closure: ((Int) -> Void)?

    init() {    closure = { [unowned self] index in        print(self)        print(index)    }}

    }

    1
    2

    使用 weak, 此时需要解包

    closure = { [weak self] index in
    // 解包
    if let self = self {
    print(self)
    print(index)
    }
    }

    1
    2

    强制解包

    closure = { [weak self] index in
    // 强制解包
    print(self!)
    print(index)
    }
    `

    ]]>
    @@ -200,10 +213,10 @@ - - /2019/06/18/%E4%B8%80%E8%87%AA%E5%8A%A8%E5%BC%95%E7%94%A8%E8%AE%A1%E6%95%B0/ + + /2019/06/18/%E3%80%8A52%E4%B8%AA%E6%96%B9%E6%B3%95%E7%AC%AC%E4%B8%80%E7%AB%A0%E3%80%8B/ - 自动引用计数

    什么是自动引用计数

    内存管理中对引用采取自动计数的技术

    内存管理

    • 自己生成的对象,自己所持有
      • alloc
      • new
      • copy
      • mutableCopy
    • 非自己生成的对象,自己也能持有
      • retain
    • 不再需要自己持有的对象时释放
      • release
    • 非自己持有的对象无法释放

    • 废弃对象

      • dealloc

    __strong

    __strong 修饰符表示对对象的“强引用”

    __unsafe_unretained 修饰符

    __unsafe_unretained 是不安全的所有权修饰符, 被它修饰的变量不属于编译器的内存管理对象

    1
    id __unsafe_unretained objc = [[NSObject alloc] init];

    Xcode 提示:Assigning retained object to unsafe_unretained variable; object will be released after assignment

    将保留对象赋给unsafe_unretain变量;对象将在赋值后释放

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    id __unsafe_unretained obj1 = nil;

    {
    id __unsafe_unretained obj = [[NSObject alloc] init];

    // 自己生成并持有对象
    // 因为obj0 变量是__strong,强引用,所以自己持有对象
    id __strong obj0 = [[NSObject alloc] init];

    // obj0 变量赋值给你obj1
    // ojb1 变量不持有对象的强应用,也不持有弱引用
    obj1 = obj0;


    // 输出 obj1 变量表示的对象
    NSLog(@"A: %@", obj1);
    } // obj0变量 超出了其作用域,强引用失效自动释放自己持有的对象,除此之外没有其他持有者,所以废弃该对象




    // 输出 obj1 变量表示的对象
    // obj1 变量表示的对象因为无持有者已经销毁,坏的内存访问
    NSLog(@"B: %@", obj1);
    ]]>
    + 《52个方法-第一章》

    一、OC起源

    • OC使用的是“消息结构”而不是“函数调用”
    • 两者区别:
      • 消息结构:在运行时所执行的代码由运行环境来决定,无论是否多态总会在运行时才会去查找所要执行的方法
        1
        2
        NSObject *objc = [NSObject new];
        [objc performWith: parameter1 and: parameter2];
    - 函数调用:由编译器决定,如果函数多态,编译时就要查处到底应该执行那个函数实现
    1
    2
    Object *objc = new Object;
    objc -> perform(parameter1, parameter2);
    • 消息结构中编译器不会关心接收消息的对象是何种类型,接收消息的对象问题也要在运行时处理,此过程叫做“动态绑定”

    要点:

    1. OC语言使用的动态绑定的消息结构,在运行时所执行的代码由运行环境来决定,无论是否多态总会在运行时才会去查找所要执行的方法,
    2. OC 中对象总是分配在“堆空间”,而绝不会分配在”栈“上,不带 “*”的变量或者结构体等可能会使用“栈空间”

    二、类头文件尽量少导入其他头文件

    要点:

    1. 除非必要,否则不要引入头文件,使用向前声明降低类之间的耦合
    2. 有时无法使用向前声明,应该把该类遵守协议的声明放到“class-continuation分类”(匿名分类)中。
    3. 如果“class-continuation分类”也不行的话,就把协议单独放在一个文件中,再将其引入### 三、多用字面量语法,少用与之等价的方法

    三、多用字面量语法,少用与之等价的方法

    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
    - (void)tempMethond {
    // OC1.0 起,支持简单方式创建基本类型对象

    // - 使用字符串字面量:简介易读
    NSString *someString = @"Effective Objective-C 2.0";
    // 传统方式
    NSString *someStrongOld = [NSString stringWithFormat:@"Effective Objective-C 2.0"];


    // - 字面数值
    NSNumber *someNumber = @1;
    NSNumber *floatNumber = @2.5f;
    NSNumber *doubleNumber = @3.1415926;
    NSNumber *boolNumber = @YES;
    NSNumber *charNumber = @'a';
    // 字面量语法用于表达式
    int x = 5;
    float y = 6.23f;
    NSNumber *expressionNumber = @(x * y);


    // - 字面量数组
    NSArray *animals = @[@"cat", @"dog", @"mouse", @"badger"];
    NSString *dog = animals[1];

    // arrayWithObjects 和 字面量 的区别:
    // arrayWithObjects 方法依次处理各个参数,直到发现nil为止
    // 字面量数组遇到nil直接抛出异常
    NSArray *animalsOld = [NSArray arrayWithObjects:@"cat", @"dog", @"mouse", @"badger", nil];
    NSString *dogOld = [animalsOld objectAtIndex:1];


    // - 字面量字典
    // 如果有nil直接抛出异常
    NSDictionary *personData = @{@"firtName": @"Matt",
    @"lastName": @"Galloway",
    @"age": @28};
    NSString *lastName = personData[@"lastName"];

    // dictionaryWithObjectsAndKeys 方法依次处理各个参数,直到发现nil为止
    NSDictionary *personDataOld = [NSDictionary dictionaryWithObjectsAndKeys:@"Matt",@"firtName",
    @"Galloway", @"lastName",
    [NSNumber numberWithInteger:28], @"age", nil];

    NSString *lastNameOld = [personData objectForKey:@"lastName"];


    // - 可变数组和字典
    NSMutableArray *mutableArray = animals.mutableCopy;
    mutableArray[1] = @"dog";

    NSMutableDictionary *mutableDict = [personData mutableCopy];
    mutableDict[@"lastName"] = @"Galloway";
    }

    要点:

    1. 使用字面量语法创建字符串,数值,数组,字典简明扼要
    2. 通过下标操作获取数组,字典对应的元素
    3. 使用字面量创建数组,字典时若有nil ,会抛出异常,确保值里不含nil### 五、用枚举表示状态、选项、状态码

    四、多用类型常量,少用#define预处理指令

    宏定义
    • 缺点:
      • 可以把所有相同的字符串值都替换为定义的值,
        • 定义出来的常量没有具体的类型信息
        • 如果有人重新定义了宏定义,不会有警告或者提醒,导致程序中值不一样
    类型常量
    • 命名规则:

      • 如果只是在实现文件.m 中使用,则在前面加字母k
      • 如果此常量在类之外也可见,则以类名为前缀,(第19条详细了解命名习惯)
    • 声明位置:

      • 总是喜欢在头文件中声明宏定义,可能和常量名冲突,
      • 所有引入这个头文件的类都会出现宏定义或者常量,相当于声明了一个名叫kAnimationDuration的全局变量
      • 应该加上前缀,表明其所属的具体类,例如:EOCViewClassAnimationDuration
    • 常量从右往左解读依次是: 具体类型,不可修改

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      // const 修饰的是常量不希望别人修改, NSString * 指向NSString对象
      extern NSString *const EOCStringConstant;


      //两者写法都可以
      extern const NSTimeInterval EOCAnimatedViewAnimationDuration;
      extern NSTimeInterval const EOCAnimatedViewAnimationDuration1;
      ```

      - 如果不打算公开常量,可以定义在 .m 实现文件里

      static const NSTimeInterval kAnimationDuration = 0.3;

      1
      2
      3

      - static关键字
      - static 修饰意味着该变量仅在定义此变量的编译单元(实现文件.m)中可见,作用域就是当前.m文件
      static const NSTimeInterval kAnimationDuration = 0.3;
      1
      2
      3
      - 如果需要声明一个外部可见的常值变量,需要在头文件中声明并使用 extern 关键词修饰 "extern 

      在 .h 文件中
      extern NSString *const EOCStringConstant;// 两者写法都可以extern const NSTimeInterval EOCAnimatedViewAnimationDuration;extern NSTimeInterval const EOCAnimatedViewAnimationDuration1;
      1
      2

      在 .m 文件中
      NSString *const EOCStringConstant = @"VALUE";const NSTimeInterval EOCAnimatedViewAnimationDuration = 0.3;NSTimeInterval const EOCAnimatedViewAnimationDuration1 = 0.3;
      1
      2
      3
      4
      5
      6
      7
      8
      9

      **要点:**

      1. 定义常量时static const定义编译单元(实现文件.m)中可见的常量,无需为其名称加前缀
      2. 在头文件中使用extern 声明全局常量,并在实现文件中定义其值,这种常量要出现在全局符号表中,所以加相关类作为前缀

      ### 五、用枚举表示状态、选项、状态码

      - 普通枚举

      enum EOCConnectionState {

      EOCConnectionStateDisconnected, // 断开连接EOCConnectionStateConnecting,   // 连接中...EOCConnectionStateConnected,    // 已连接

      };

      1
      普通枚举如果要使用如下:

      enum EOCConnectionState status = EOCConnectionStateConnected;

      1
      2

      如果每次都不需要输入enum关键字,只需要typedef关键字重新定义枚举类型

      EOCConnectionState status1 = EOCConnectionStateConnecting;

      1
      2

      - 设置枚举的初始值

      enum EOCConnectionStateConnectionState {

      EOCConnectionStateConnectionStateDisconnected = 1, // 断开连接EOCConnectionStateConnectionStateConnecting,   // 连接中...EOCConnectionStateConnectionStateConnected,    // 已连接

      };

      1
      2
      3
      4
      5

      - 可选枚举

      - << 位操作符(左移), 表示往左移动N位,使用 位运算符 | (或)来组合使用
      - 使用位运算符 &(与) 判断是否开启了某个选项

      enum UIViewAutoresizing {

      UIViewAutoresizingNone                 = 0,UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,UIViewAutoresizingFlexibleWidth        = 1 << 1,UIViewAutoresizingFlexibleRightMargin  = 1 << 2,UIViewAutoresizingFlexibleTopMargin    = 1 << 3,UIViewAutoresizingFlexibleHeight       = 1 << 4,UIViewAutoresizingFlexibleBottomMargin = 1 << 5

      }

      1
      2
      3
      4

      - 使用宏定义枚举

      普通枚举类型(老式语法定义枚举)

      typedef enum : NSUInteger {

      EOCConnectionState2DisConnected,EOCConnectionState2DisConnecting,EOCConnectionState2Connected,

      } EOCConnectionState2;

      1
      2

      NS_ENUM 宏定义的枚举展开后:

      typedef enum EOCConnectionState_3 : NSUInteger EOCConnectionState_3;
      enum EOCConnectionState_3 : NSUInteger {

      EOCConnectionState_3DisConnected,EOCConnectionState_3DisConnecting,EOCConnectionState_3Connected,

      };

      1
      2

      - 定义新特性枚举

      typedef NS_ENUM(NSUInteger, EOCConnectionState3) {

      EOCConnectionState3DisConnected,EOCConnectionState3DisConnecting,EOCConnectionState3Connected,

      };

      1
      2
      3
      4

      - 枚举使用

      .h文件

      #import <UIKit/UIKit.h>

      typedef enum EOCConnectionState EOCConnectionState;

      typedef NS_ENUM(NSUInteger, EOCConnectionState3) {

      EOCConnectionState3DisConnected,EOCConnectionState3DisConnecting,EOCConnectionState3Connected,

      };

    @interface EOCEnum : NSObject@property (nonatomic, assign) enum EOCConnectionState state;@property (nonatomic, assign) EOCConnectionState3 state3;@end
    1
    2

    .m 文件中
    #import "EOCEnum.h"@implementation EOCEnum- (void)testEnum { // 未使用typedef 关键字 enum EOCConnectionState status = EOCConnectionStateConnected; NSLog(@"%u", status); // 使用typedef 关键字 EOCConnectionState status1 = EOCConnectionStateConnecting; NSLog(@"%u", status1); UIViewAutoresizing resizing = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; if (resizing & UIViewAutoresizingFlexibleWidth) { // 设置了 UIViewAutoresizingFlexibleWidth 约束 }}// UIKit 试图所支持的设置显示方向- (UIInterfaceOrientationMask)supportedInterfaceOrientations { return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft;}// 枚举使用- (void)userEnum { switch (_state3) { case EOCConnectionState3DisConnected: // ... break; case EOCConnectionState3DisConnecting: // ... break; case EOCConnectionState3Connected: // ... break; default: // 最好不用,防止以后增加一种状态机之后,编译器不发出警告 break; }}@end```

    要点:

    1. 使用枚举表示状态机的状态、传递给方法的选项以及状态码等值,给枚举值起名时要注重易懂
    2. 如果一个类型可以使用状态机的状态来表示,并且多个选项可同时使用,那么就定义为可选的枚举类型,枚举各值定义为2的幂,以便通过“按位或”操作组合
    3. 使用NS_ENUM 和 NS_OPTIONS 宏来定义枚举,并指明底层的数据类型。这样可以确保枚举是用开发者所选的底层数据类型实现出来的,而不会采用编译器所选的类型
    4. switch语句处理枚举类型时不要实现default 分支,如果以后加入枚举的新状态之后,编译器会发出警告
    ]]>
    @@ -213,10 +226,10 @@ - - /2019/06/18/swift%E4%B8%AD%E5%BC%BA%E5%BC%95%E7%94%A8%E5%BE%AA%E7%8E%AF%E5%92%8Cweak%E5%85%B3%E9%94%AE%E5%AD%97/ + + /2019/06/18/%E4%B8%80%E8%87%AA%E5%8A%A8%E5%BC%95%E7%94%A8%E8%AE%A1%E6%95%B0/ - Swift中内存管理

    强引用循环和 weak 关键字

    房东

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class Landlord {
    var name: String
    var house: House?

    init(name: String) {
    self.name = name
    }

    deinit {
    print("Landlord内存释放")
    }
    }

    房子

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class House {
    var name: String
    var landlord: Landlord?

    init(person: String) {
    self.name = person
    }

    deinit {
    print("House内存释放")
    }
    }

    循环引用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    func strongRetain() {
    var landlord: Landlord? = Landlord(name: "老李")
    var house: House? = House(person: "望京府邸")
    // 房东有个房子
    landlord?.house = house
    // 房子有个主人
    house?.landlord = landlord

    landlord = nil
    house = nil
    }
    • 虽然 landlord = nil 和 house = nil,但是 Landlord 和 House 内存空间并没有释放 (并未执行 deinit 函数)

    • 原因:因为两个内存空间相互引用,引用计数不为0,形成了强循环引用

    • 解决办法:使用weak 关键字来修饰两者中其中一个变量

      • 使用weak修饰的变量必须是可选类型,可以赋值为nil
      • 使用weak修饰必须是var 类型变量
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class House {
    var name: String
    weak var landlord: Landlord?

    init(person: String) {
    self.name = person
    }

    deinit {
    print("House内存释放")
    }
    }

    unowned 关键字的使用

    相互循环引用的两个变量有一个是可选的

    有个身份证的类,每个身份证肯定对应一个人

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    class ID {
    var number: String
    let person: Landlord

    init(person: Landlord, number: String) {
    self.person = person
    self.number = number
    }

    deinit {
    print("ID内存释放")
    }
    }

    ```

    房东

    class Landlord {
    var name: String
    var house: House?
    /// 身份证可以为nil,
    var id: ID?

    init(name: String) {    self.name = name}deinit {    print("Landlord内存释放")}

    }

    1
    2

    循环引用

    func unownedTest() {
    var person: Landlord? = Landlord(name: “老王”)
    var id: ID? = ID(person: person!, number: “10010”)

    person?.id = idperson = nilid = nil

    }

    1
    2
    3
    4
    5
    6
    7
    8

    - person 和 id 之间有强引用关系
    - 每个id必须有一个对应的人并且是固定的只能使用let 修饰,不能为nil, 而id可以为空
    - unowned和 weak 作用相同都是弱引用,只是unowned 修改的变量不能是nil (可选型的)
    - unowned只能使用在 class 类型上,不能修饰函数类型,例如闭包存在循环引用时不能使用unowned修饰
    - 解决方法:
    - 此时可以通过使用 weak 修饰 id 的方式,因为 id 可以为nil
    - let 修饰并且不能为nil 这种情况可以使用 unowned 修饰解决
    class ID {    var number: String    unowned let person: Landlord    init(person: Landlord, number: String) {        self.person = person        self.number = number    }    deinit {        print("ID内存释放")    }}
    1
    2
    3
    4

    **unowned有一定的危险性**

    因为unowned 修饰的对象不能赋值为nil,所以执行下面的代码会crash,

    func unownedTest() {
    var person: Landlord? = Landlord(name: “老王”)
    var id: ID? = ID(person: person!, number: “10010”)

    person?.id = id// 提前释放 person 内存person = nil// 获取身份证对应的人print(id?.person) // 此处奔溃,let owner = id?.personid = nil

    }

    1
    2

    正确的写法因该是先将🆔设置为nil,再将 Person 设置为nil,再调用 print(id?.person) 就不会再报错

    func unownedTest() {
    var person: Landlord? = Landlord(name: “老王”)
    var id: ID? = ID(person: person!, number: “10010”)

    person?.id = idid = nil// 提前释放 person 内存person = nil// 获取身份证对应的人print(id?.person) // 此处奔溃,let owner = id?.person

    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

    #### 相互循环引用的两个变量都不是可选的

    使用隐式可选类型

    ### 闭包中的强引用循环

    - 并不是所有的闭包都会产生循环引用,只有相互引用的两个对象会产生循环引用

    - 使用swift闭包捕获列表,在闭包的声明参数之前使用 [unowned self], 不需要解包

    - 使用 unowned表示这个对象不能为空,可能存在风险,如果对象为空就会crash


    使用 unowned 对象不能为空,可能存在风险

    class Closure {
    var closure: ((Int) -> Void)?

    init() {    closure = { [unowned self] index in        print(self)        print(index)    }}

    }

    1
    2

    使用 weak, 此时需要解包

    closure = { [weak self] index in
    // 解包
    if let self = self {
    print(self)
    print(index)
    }
    }

    1
    2

    强制解包

    closure = { [weak self] index in
    // 强制解包
    print(self!)
    print(index)
    }
    `

    ]]>
    + 自动引用计数

    什么是自动引用计数

    内存管理中对引用采取自动计数的技术

    内存管理

    • 自己生成的对象,自己所持有
      • alloc
      • new
      • copy
      • mutableCopy
    • 非自己生成的对象,自己也能持有
      • retain
    • 不再需要自己持有的对象时释放
      • release
    • 非自己持有的对象无法释放

    • 废弃对象

      • dealloc

    __strong

    __strong 修饰符表示对对象的“强引用”

    __unsafe_unretained 修饰符

    __unsafe_unretained 是不安全的所有权修饰符, 被它修饰的变量不属于编译器的内存管理对象

    1
    id __unsafe_unretained objc = [[NSObject alloc] init];

    Xcode 提示:Assigning retained object to unsafe_unretained variable; object will be released after assignment

    将保留对象赋给unsafe_unretain变量;对象将在赋值后释放

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    id __unsafe_unretained obj1 = nil;

    {
    id __unsafe_unretained obj = [[NSObject alloc] init];

    // 自己生成并持有对象
    // 因为obj0 变量是__strong,强引用,所以自己持有对象
    id __strong obj0 = [[NSObject alloc] init];

    // obj0 变量赋值给你obj1
    // ojb1 变量不持有对象的强应用,也不持有弱引用
    obj1 = obj0;


    // 输出 obj1 变量表示的对象
    NSLog(@"A: %@", obj1);
    } // obj0变量 超出了其作用域,强引用失效自动释放自己持有的对象,除此之外没有其他持有者,所以废弃该对象




    // 输出 obj1 变量表示的对象
    // obj1 变量表示的对象因为无持有者已经销毁,坏的内存访问
    NSLog(@"B: %@", obj1);
    ]]>
    @@ -226,10 +239,10 @@ - - /2019/06/18/iOS%E9%9D%A2%E8%AF%95OC%E8%AF%AD%E8%A8%80%E7%89%B9%E6%80%A7/ + + /2019/06/18/is%20%E5%85%B3%E9%94%AE%E5%AD%97%E7%B1%BB%E5%9E%8B%E6%A3%80%E6%9F%A5/ - OC 语言特性

    分类

    原理

    由运行时来决议的,不同分类当中含有同名方法 谁最终生效取决于谁最终参与编译,最后参编译的同名分类方法会最终生效,假如分类方法中添加的方法和宿主类中的某一个方法名相同,分类中的方法会覆盖宿主类中的同名方法,覆盖是指在消息传递过程中优先查找数组靠前的元素,如果查找到了同名方法就直接调用,实际上宿主类的同名方法实现仍然是存在的,我们可可以通过一些手段调用到原有类的同名方法的实现

    使用场景

    • 申明私有方法,分类的 .m 文件中申明私有方法啊,对外不暴露
    • 对类中的代码进行抽取分解
    • 把Framework的私有方法公开化

    特点

    • 运行时决议,运行时通过runtime 才把分类中的方法添加到了宿主类上
    • 可以为系统类添加方法
    • 有多个分类中存在同名方法时,最后编译的方法会最先生效
    • 分类中有和宿主类同名方法时,底层中通过内存copy将分类方法“覆盖”宿主类方法
    • 名字相同的分类编译会报错

    分类中可以添加的内容

    • 实例方法
    • 类方法
    • 协议
    • 属性,实际上只是声明了 getter / setter 方法,并没有添加成员变量

    关联对象技术

    关联对象由 AssocicationsManager 管理并在 AssociationsHashMap 上存储
    所有对象的关联内容都在同一个全局容器中

    • 给分类添加成员变量,通过关联对象的方式

    扩展

    使用场景

    • 声明私有属性
    • 声明私有方法
    • 声明私有成员变量

    特点

    • 编译时决议
    • 只以声明的形式存在,多数情况下寄生在宿主类的.m中
    • 不能为系统添加扩展

    代理

    • 代理设计模式,传递方式是一对一
    • 使用weak 避免循环引用

    通知

    使用观察者模式来实现的用于跨层传递消息的机制

    KVO

    KVO 是系统对观察者模式的又一实现,使用isa 混写技术(isa - swizzling)来动态运行时为某一个类添加一个子类重写了它的setter 方法,同时将原有类的isa指针指向了新创建的类上

    • KVO 是OC对观察者模式的又一实现
    • apple 使用isa 混写技术(isa - swizzling)来实现KVO
      • 给我A类注册一个观察者时本质上是调用系统的 Observer for keyPath 方法,系统会创建一个NSKVONotifiying_A 的类
    • 使用 setter 方法设置值KVO才能生效
    • 使用setValue:forKey: 改变值KVO才能生效
    • 使用下划线的成员变量直接修改值需要手动添加KVO才能生效(增加 willChangeValueForKey 和 didChangeValueForKey 两个方法)

    KVC

    apple提供的键值编码技术

    • (nullable id)valueForKey:(NSString *)key;
    • (void)setValue:(nullable id)value forKey:(NSString *)key;

      以上两个方法中的key是没有任何限制的,只要知道对应的成员变量名称,就可以对私有的成员变量设置和操作,这违背了面向对象的编程思想

    (void)setValue:(nullable id)value forKey:(NSString *)key; 方法调用流程

    • 先判断是否有跟key 相关的setter 方法,如果有就直接调用,结束调用
    • 如果没有再判断是否存在实例变量,如果存在直接给赋值,结束调用
    • 如果说实例变量不存在,会去调用 - (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key; 抛出异常

    属性关键字

    读写权限

    • readonly

    • readwrite 系统默认

    原子性

    • atomic (系统默认)保证赋值和获取(对成员属性的直接获取和赋值)时线程安全,并不代表操作和访问,如果atomic修饰的一个数组,只能保证独具改数组的读取和赋值时线程安全,并不能保证对数据操作(增加/删除数组元素)时是安全的
    • nonatomic

    引用技术

    • assign
      • 修饰基本数据类型,
      • 修饰对象时不改变其引用计数,
      • 会产生悬垂指针
    • weak
      • 不改变被修饰对象的引用计数,
      • 所指对象被释放之后指针自动置为nil

    两者区别:

    • weak可以修饰对象,而assign 既可以修饰对象也可以修饰基本数据类型
    • assign 修饰的对象,对象释放后指针仍然指向原对象的内存地址,而weak 修饰的对象,释放后会自动置为nil

    问题1

    weak 修饰的对象为什么释放后会自动置为nil

    问题2

    @property (copy) NSMutableArray *array; 有什么问题

    • 被copy修饰后,如果赋值过来的是NSMutableArray,copy之后就是NSArray,如果赋值过来的是NSAarray,copy 之后仍然是NSArray,有可能会调用array的添加元素方法会导致crash

    copy 关键词

    • 浅拷贝:内存地址的复制,让目标对象指针和源对象指向同一块内存空间,
      • 会增加对象的引用计数
      • 并没有一个新的内存分配
    • 深拷贝:目标对象指针和源对象指针分别指向两块内容相同的内存空间
      • 不会增加源对象的引用计数
      • 有新对象的内存分配

    MRC 重写 retain修饰的变量的setter 方法

    1
    2
    3
    4
    5
    6
    7
    8

    - (void)setObj:(id)obj {
    if (_obj != obj) {
    [_obj release];
    }
    _obj = [obj retain];

    }

    判断是为了防止异常处理,如果不做 if 判断,当传入的 obj 对象正好是原来的_obj 对象,对原对象尽行releas 操作,实际上也会对传入的对象进行releas 操作进行释放,此时如果再通过obj指针访问废弃的对象时就会导致carsh

    ]]>
    + swift类型检查和类型转换

    is 关键字类型检查

    判断父类和子类之间的关系

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    func typeCheck() {
    let programmer = Programmer(name: "老张-php")
    if dev is iOSDeveloper {
    print("iOS 开发者")
    } else {
    print("其他开发者")
    }

    if programmer is Coder {
    print("老张-php是一农个码农")
    } else {
    print("")
    }
    }

    as 关键字类型转换

    1
    2
    let ios = iosDev as? Programmer
    let ios2 = iosDev as! Coder

    判断是否遵守了协议

    swift 中协议可以当作是一个类型使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    et ios = iosDev as? Programmer
    let ios2 = iosDev as Coder

    // 是否遵守了 Person 协议
    if iosDev is Person {
    print("遵守了Person协议")
    }

    let devs = [dev, iosDev, programmer] as [Any]

    for dev in devs {
    if let dev = dev as? Person {
    print(dev)
    print("遵守Person协议")
    } else {
    print("未遵守Person协议")
    }
    }

    AnyObject Any

    1
    2
    3
    4
    var anyObjectArray: [AnyObject] = [CGFloat(0.5) as AnyObject,
    1 as AnyObject,
    "string" as AnyObject,
    iOSODev]

    swift是面向函数编程, 函数是一等公民,数组中可以存入一个函数,函数表达的是一个过程,不是一个名词或者物体,所以函数不是一个对象,需要使用 any 关键字

    1
    2
    3
    4
    var anyArray: [Any] = [CGFloat(0.5),
    1,
    "string",
    iOSODev]

    放入函数

    1
    anyArray.append({ (a: Int) -> (Int) in return a * a })
    ]]>
    @@ -239,10 +252,10 @@ - - /2019/06/18/is%20%E5%85%B3%E9%94%AE%E5%AD%97%E7%B1%BB%E5%9E%8B%E6%A3%80%E6%9F%A5/ + + /2019/06/18/iOS%E9%9D%A2%E8%AF%95OC%E8%AF%AD%E8%A8%80%E7%89%B9%E6%80%A7/ - swift类型检查和类型转换

    is 关键字类型检查

    判断父类和子类之间的关系

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    func typeCheck() {
    let programmer = Programmer(name: "老张-php")
    if dev is iOSDeveloper {
    print("iOS 开发者")
    } else {
    print("其他开发者")
    }

    if programmer is Coder {
    print("老张-php是一农个码农")
    } else {
    print("")
    }
    }

    as 关键字类型转换

    1
    2
    let ios = iosDev as? Programmer
    let ios2 = iosDev as! Coder

    判断是否遵守了协议

    swift 中协议可以当作是一个类型使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    et ios = iosDev as? Programmer
    let ios2 = iosDev as Coder

    // 是否遵守了 Person 协议
    if iosDev is Person {
    print("遵守了Person协议")
    }

    let devs = [dev, iosDev, programmer] as [Any]

    for dev in devs {
    if let dev = dev as? Person {
    print(dev)
    print("遵守Person协议")
    } else {
    print("未遵守Person协议")
    }
    }

    AnyObject Any

    1
    2
    3
    4
    var anyObjectArray: [AnyObject] = [CGFloat(0.5) as AnyObject,
    1 as AnyObject,
    "string" as AnyObject,
    iOSODev]

    swift是面向函数编程, 函数是一等公民,数组中可以存入一个函数,函数表达的是一个过程,不是一个名词或者物体,所以函数不是一个对象,需要使用 any 关键字

    1
    2
    3
    4
    var anyArray: [Any] = [CGFloat(0.5),
    1,
    "string",
    iOSODev]

    放入函数

    1
    anyArray.append({ (a: Int) -> (Int) in return a * a })
    ]]>
    + OC 语言特性

    分类

    原理

    由运行时来决议的,不同分类当中含有同名方法 谁最终生效取决于谁最终参与编译,最后参编译的同名分类方法会最终生效,假如分类方法中添加的方法和宿主类中的某一个方法名相同,分类中的方法会覆盖宿主类中的同名方法,覆盖是指在消息传递过程中优先查找数组靠前的元素,如果查找到了同名方法就直接调用,实际上宿主类的同名方法实现仍然是存在的,我们可可以通过一些手段调用到原有类的同名方法的实现

    使用场景

    • 申明私有方法,分类的 .m 文件中申明私有方法啊,对外不暴露
    • 对类中的代码进行抽取分解
    • 把Framework的私有方法公开化

    特点

    • 运行时决议,运行时通过runtime 才把分类中的方法添加到了宿主类上
    • 可以为系统类添加方法
    • 有多个分类中存在同名方法时,最后编译的方法会最先生效
    • 分类中有和宿主类同名方法时,底层中通过内存copy将分类方法“覆盖”宿主类方法
    • 名字相同的分类编译会报错

    分类中可以添加的内容

    • 实例方法
    • 类方法
    • 协议
    • 属性,实际上只是声明了 getter / setter 方法,并没有添加成员变量

    关联对象技术

    关联对象由 AssocicationsManager 管理并在 AssociationsHashMap 上存储
    所有对象的关联内容都在同一个全局容器中

    • 给分类添加成员变量,通过关联对象的方式

    扩展

    使用场景

    • 声明私有属性
    • 声明私有方法
    • 声明私有成员变量

    特点

    • 编译时决议
    • 只以声明的形式存在,多数情况下寄生在宿主类的.m中
    • 不能为系统添加扩展

    代理

    • 代理设计模式,传递方式是一对一
    • 使用weak 避免循环引用

    通知

    使用观察者模式来实现的用于跨层传递消息的机制

    KVO

    KVO 是系统对观察者模式的又一实现,使用isa 混写技术(isa - swizzling)来动态运行时为某一个类添加一个子类重写了它的setter 方法,同时将原有类的isa指针指向了新创建的类上

    • KVO 是OC对观察者模式的又一实现
    • apple 使用isa 混写技术(isa - swizzling)来实现KVO
      • 给我A类注册一个观察者时本质上是调用系统的 Observer for keyPath 方法,系统会创建一个NSKVONotifiying_A 的类
    • 使用 setter 方法设置值KVO才能生效
    • 使用setValue:forKey: 改变值KVO才能生效
    • 使用下划线的成员变量直接修改值需要手动添加KVO才能生效(增加 willChangeValueForKey 和 didChangeValueForKey 两个方法)

    KVC

    apple提供的键值编码技术

    • (nullable id)valueForKey:(NSString *)key;
    • (void)setValue:(nullable id)value forKey:(NSString *)key;

      以上两个方法中的key是没有任何限制的,只要知道对应的成员变量名称,就可以对私有的成员变量设置和操作,这违背了面向对象的编程思想

    (void)setValue:(nullable id)value forKey:(NSString *)key; 方法调用流程

    • 先判断是否有跟key 相关的setter 方法,如果有就直接调用,结束调用
    • 如果没有再判断是否存在实例变量,如果存在直接给赋值,结束调用
    • 如果说实例变量不存在,会去调用 - (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key; 抛出异常

    属性关键字

    读写权限

    • readonly

    • readwrite 系统默认

    原子性

    • atomic (系统默认)保证赋值和获取(对成员属性的直接获取和赋值)时线程安全,并不代表操作和访问,如果atomic修饰的一个数组,只能保证独具改数组的读取和赋值时线程安全,并不能保证对数据操作(增加/删除数组元素)时是安全的
    • nonatomic

    引用技术

    • assign
      • 修饰基本数据类型,
      • 修饰对象时不改变其引用计数,
      • 会产生悬垂指针
    • weak
      • 不改变被修饰对象的引用计数,
      • 所指对象被释放之后指针自动置为nil

    两者区别:

    • weak可以修饰对象,而assign 既可以修饰对象也可以修饰基本数据类型
    • assign 修饰的对象,对象释放后指针仍然指向原对象的内存地址,而weak 修饰的对象,释放后会自动置为nil

    问题1

    weak 修饰的对象为什么释放后会自动置为nil

    问题2

    @property (copy) NSMutableArray *array; 有什么问题

    • 被copy修饰后,如果赋值过来的是NSMutableArray,copy之后就是NSArray,如果赋值过来的是NSAarray,copy 之后仍然是NSArray,有可能会调用array的添加元素方法会导致crash

    copy 关键词

    • 浅拷贝:内存地址的复制,让目标对象指针和源对象指向同一块内存空间,
      • 会增加对象的引用计数
      • 并没有一个新的内存分配
    • 深拷贝:目标对象指针和源对象指针分别指向两块内容相同的内存空间
      • 不会增加源对象的引用计数
      • 有新对象的内存分配

    MRC 重写 retain修饰的变量的setter 方法

    1
    2
    3
    4
    5
    6
    7
    8

    - (void)setObj:(id)obj {
    if (_obj != obj) {
    [_obj release];
    }
    _obj = [obj retain];

    }

    判断是为了防止异常处理,如果不做 if 判断,当传入的 obj 对象正好是原来的_obj 对象,对原对象尽行releas 操作,实际上也会对传入的对象进行releas 操作进行释放,此时如果再通过obj指针访问废弃的对象时就会导致carsh

    ]]>
    @@ -356,10 +369,10 @@ - - /2019/06/18/Swift%E9%A1%B9%E7%9B%AE%E4%B8%AD%E9%9B%86%E6%88%90RN/ + + /2019/06/18/Swift%E9%94%99%E8%AF%AF%E5%A4%84%E7%90%86/ - Swift已有项目中集成ReactNative

    本教程针对于已经有的Swift项目集成ReactNative

    创建新的空目录

    为了不影响原有的swift项目目录结构,建议先创建一个空的文件夹,例如SwiftRN

    创建package.json文件

    终端进入创建的SwiftRN文件目录下,创建package.json文件

    1
    touch package.json
    修改package.json文件,指定各个版本
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    {
    "name": "SwiftRN",
    "version": "0.0.1",
    "private": true,
    "scripts": {
    "start": "node node_modules/react-native/local-cli/cli.js start",
    "test": "jest"
    },
    "dependencies": {
    "react": "16.3.1",
    "react-native": "^0.56.0"
    }
    }
    执行npm install

    在创建package.json所在的文件目录下执行,在此过程中可以遇到的错误可能较多

    1
    npm install
    添加index.js文件
    1
    touch index.js
    修改文件内容
    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
    import {
    AppRegistry,
    Text,
    View,
    } from 'react-native';

    import React, { Component } from 'react';

    // 用于最后在项目中测试
    class SwiftRNHomeView extends Component {
    render() {
    return(
    <View style={{backgroundColor:'white'}}>
    <Text style={{fontSize: 20, marginTop: 200}}>Hellow world</Text>
    </View>
    );
    }
    }


    class MyApp extends Component {
    render() {
    return <SwiftRNHomeView></SwiftRNHomeView>;
    }
    }

    AppRegistry.registerComponent('SwiftRN', () => MyApp);

    配置swift项目

    将新创建的Swift文件目录下的所有文件拷贝到Swift项目所在的根目录下

    使用Pod集成RN

    swift项目采用了pod管理第三方库,在Podfile文件中加入下面代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    pod 'React', :path => './node_modules/react-native', :subspecs => [
    'Core',
    'RCTText',
    'RCTNetwork',
    'RCTWebSocket', # 这个模块是用于调试功能的
    'CxxBridge', # Include this for RN >= 0.47,不引入的话会报错误或者警告
    'DevSupport', # Include this to enable In-App Devmenu if RN >= 0.43,不引入的话会报错误或者警告
    'RCTAnimation', 不引入的话会报错误或者警告
    ]


    # 如果你的RN版本 >= 0.42.0,请加入下面这行,不引入的话会报错误或者警告
    pod "yoga", :path => "./node_modules/react-native/ReactCommon/yoga"
    pod 'DoubleConversion', :podspec => './node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
    pod 'glog', :podspec => './node_modules/react-native/third-party-podspecs/glog.podspec'
    pod 'Folly', :podspec => './node_modules/react-native/third-party-podspecs/Folly.podspec'

    创建桥接文件

    • 如果原项目中有SwiftDemo-Bridging-Header.h类似这个文件可以跳过。
    • 如果从来没有创建过桥接文件,创建一个OC的类会自动弹出是否需要创建桥接文件,直接创建即可,最后把无用的OC文件删掉

    引用RN头文件

    在桥接文件中导入头文件

    1
    2
    #import <React/RCTRootView.h>
    #import <React/RCTBundleURLProvider.h>

    使用RN页面

    在模拟器中使用RN的页面, 确保在 info.plist 文件中添加了Allow Arbitrary Loads=YES

    • moduleName 对应的名字和index.js中 AppRegistry.registerComponent(‘SwiftRN’, () => MyApp)注册的名字必须相同
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    import UIKit

    class RNTestController: UIViewController {

    override func viewDidLoad() {
    super.viewDidLoad()
    let url = URL(string: "http://localhost:8081/index.bundle?platform=ios")!
    let rootView = RCTRootView(
    bundleURL: url,
    moduleName: "SwiftRN", //这里的名字必须和AppRegistry.registerComponent('MyApp', () => MyApp)中注册的名字一样
    initialProperties: nil,
    launchOptions: nil
    )
    view = rootView
    }
    }
    ]]>
    + Swift 中错误处理

    assert 断言

    只在debug 模式下程序会自动退出

    1
    public func assert(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)

    assertionFailure 断言错误

    只在debug 模式下程序会自动退出, 没有条件

    1
    public func assertionFailure(_ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)

    precondition

    releas 模式程序也会退出

    1
    public func precondition(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)

    throw

    ]]>
    @@ -369,10 +382,10 @@ - - /2019/06/18/Swift%E9%94%99%E8%AF%AF%E5%A4%84%E7%90%86/ + + /2019/06/18/Swift%E9%A1%B9%E7%9B%AE%E4%B8%AD%E9%9B%86%E6%88%90RN/ - Swift 中错误处理

    assert 断言

    只在debug 模式下程序会自动退出

    1
    public func assert(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)

    assertionFailure 断言错误

    只在debug 模式下程序会自动退出, 没有条件

    1
    public func assertionFailure(_ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)

    precondition

    releas 模式程序也会退出

    1
    public func precondition(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)

    throw

    ]]>
    + Swift已有项目中集成ReactNative

    本教程针对于已经有的Swift项目集成ReactNative

    创建新的空目录

    为了不影响原有的swift项目目录结构,建议先创建一个空的文件夹,例如SwiftRN

    创建package.json文件

    终端进入创建的SwiftRN文件目录下,创建package.json文件

    1
    touch package.json
    修改package.json文件,指定各个版本
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    {
    "name": "SwiftRN",
    "version": "0.0.1",
    "private": true,
    "scripts": {
    "start": "node node_modules/react-native/local-cli/cli.js start",
    "test": "jest"
    },
    "dependencies": {
    "react": "16.3.1",
    "react-native": "^0.56.0"
    }
    }
    执行npm install

    在创建package.json所在的文件目录下执行,在此过程中可以遇到的错误可能较多

    1
    npm install
    添加index.js文件
    1
    touch index.js
    修改文件内容
    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
    import {
    AppRegistry,
    Text,
    View,
    } from 'react-native';

    import React, { Component } from 'react';

    // 用于最后在项目中测试
    class SwiftRNHomeView extends Component {
    render() {
    return(
    <View style={{backgroundColor:'white'}}>
    <Text style={{fontSize: 20, marginTop: 200}}>Hellow world</Text>
    </View>
    );
    }
    }


    class MyApp extends Component {
    render() {
    return <SwiftRNHomeView></SwiftRNHomeView>;
    }
    }

    AppRegistry.registerComponent('SwiftRN', () => MyApp);

    配置swift项目

    将新创建的Swift文件目录下的所有文件拷贝到Swift项目所在的根目录下

    使用Pod集成RN

    swift项目采用了pod管理第三方库,在Podfile文件中加入下面代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    pod 'React', :path => './node_modules/react-native', :subspecs => [
    'Core',
    'RCTText',
    'RCTNetwork',
    'RCTWebSocket', # 这个模块是用于调试功能的
    'CxxBridge', # Include this for RN >= 0.47,不引入的话会报错误或者警告
    'DevSupport', # Include this to enable In-App Devmenu if RN >= 0.43,不引入的话会报错误或者警告
    'RCTAnimation', 不引入的话会报错误或者警告
    ]


    # 如果你的RN版本 >= 0.42.0,请加入下面这行,不引入的话会报错误或者警告
    pod "yoga", :path => "./node_modules/react-native/ReactCommon/yoga"
    pod 'DoubleConversion', :podspec => './node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
    pod 'glog', :podspec => './node_modules/react-native/third-party-podspecs/glog.podspec'
    pod 'Folly', :podspec => './node_modules/react-native/third-party-podspecs/Folly.podspec'

    创建桥接文件

    • 如果原项目中有SwiftDemo-Bridging-Header.h类似这个文件可以跳过。
    • 如果从来没有创建过桥接文件,创建一个OC的类会自动弹出是否需要创建桥接文件,直接创建即可,最后把无用的OC文件删掉

    引用RN头文件

    在桥接文件中导入头文件

    1
    2
    #import <React/RCTRootView.h>
    #import <React/RCTBundleURLProvider.h>

    使用RN页面

    在模拟器中使用RN的页面, 确保在 info.plist 文件中添加了Allow Arbitrary Loads=YES

    • moduleName 对应的名字和index.js中 AppRegistry.registerComponent(‘SwiftRN’, () => MyApp)注册的名字必须相同
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    import UIKit

    class RNTestController: UIViewController {

    override func viewDidLoad() {
    super.viewDidLoad()
    let url = URL(string: "http://localhost:8081/index.bundle?platform=ios")!
    let rootView = RCTRootView(
    bundleURL: url,
    moduleName: "SwiftRN", //这里的名字必须和AppRegistry.registerComponent('MyApp', () => MyApp)中注册的名字一样
    initialProperties: nil,
    launchOptions: nil
    )
    view = rootView
    }
    }
    ]]>
    diff --git a/tags/AES-Base64-MD5/index.html b/tags/AES-Base64-MD5/index.html index f56d8c6..5b4a4e9 100644 --- a/tags/AES-Base64-MD5/index.html +++ b/tags/AES-Base64-MD5/index.html @@ -138,23 +138,23 @@

    Recent Posts

    diff --git a/tags/Alamofire/index.html b/tags/Alamofire/index.html index fa6588e..19733c6 100644 --- a/tags/Alamofire/index.html +++ b/tags/Alamofire/index.html @@ -138,23 +138,23 @@

    Recent Posts

    diff --git a/tags/AlertView/index.html b/tags/AlertView/index.html index a4d5dc0..2805ca8 100644 --- a/tags/AlertView/index.html +++ b/tags/AlertView/index.html @@ -138,23 +138,23 @@

    Recent Posts

    diff --git a/tags/GCD/index.html b/tags/GCD/index.html index 76ab200..d335b77 100644 --- a/tags/GCD/index.html +++ b/tags/GCD/index.html @@ -138,23 +138,23 @@

    Recent Posts

    diff --git a/tags/Hexo/index.html b/tags/Hexo/index.html index 6d4ce08..f78d012 100644 --- a/tags/Hexo/index.html +++ b/tags/Hexo/index.html @@ -138,23 +138,23 @@

    Recent Posts

    diff --git a/tags/OC-JS/index.html b/tags/OC-JS/index.html index 70ac134..2337e1e 100644 --- a/tags/OC-JS/index.html +++ b/tags/OC-JS/index.html @@ -138,23 +138,23 @@

    Recent Posts

    diff --git a/tags/Quartz-2D/index.html b/tags/Quartz-2D/index.html index 6003792..befb23e 100644 --- a/tags/Quartz-2D/index.html +++ b/tags/Quartz-2D/index.html @@ -138,23 +138,23 @@

    Recent Posts

    diff --git a/tags/Quartz2D/index.html b/tags/Quartz2D/index.html index 758b0c6..c5627e2 100644 --- a/tags/Quartz2D/index.html +++ b/tags/Quartz2D/index.html @@ -157,23 +157,23 @@

    Recent Posts

    diff --git a/tags/RunTime/index.html b/tags/RunTime/index.html index 0248207..4d53437 100644 --- a/tags/RunTime/index.html +++ b/tags/RunTime/index.html @@ -138,23 +138,23 @@

    Recent Posts

    diff --git a/tags/SDWebImage/index.html b/tags/SDWebImage/index.html index 4872c26..449033d 100644 --- a/tags/SDWebImage/index.html +++ b/tags/SDWebImage/index.html @@ -138,23 +138,23 @@

    Recent Posts

    diff --git a/tags/SQL/index.html b/tags/SQL/index.html index 4ed4ede..7e26be6 100644 --- a/tags/SQL/index.html +++ b/tags/SQL/index.html @@ -138,23 +138,23 @@

    Recent Posts

    diff --git a/tags/Swift/index.html b/tags/Swift/index.html index 1e17e9c..6c13593 100644 --- a/tags/Swift/index.html +++ b/tags/Swift/index.html @@ -5,11 +5,11 @@ - Tag: Swift | aTreey's Blog + Tag: swift | aTreey's Blog - + @@ -76,13 +76,32 @@

    + + + +
    +
    +
    + + + +

    + Swift3.0学习(二)

    @@ -138,23 +157,23 @@

    Recent Posts

    @@ -184,33 +203,6 @@

    Recent Posts

    - - - - - - - - - - - -
    - -o.io/" target="_blank">Hexo - - - - - - - diff --git a/tags/TableView/index.html b/tags/TableView/index.html index 72f5071..58050f1 100644 --- a/tags/TableView/index.html +++ b/tags/TableView/index.html @@ -138,23 +138,23 @@

    Recent Posts

    diff --git a/tags/WKWebView/index.html b/tags/WKWebView/index.html index 8c8b5d2..c6a32d0 100644 --- a/tags/WKWebView/index.html +++ b/tags/WKWebView/index.html @@ -224,23 +224,23 @@

    Recent Posts

    diff --git a/tags/git/index.html b/tags/git/index.html index a07d5f1..4f413cc 100644 --- a/tags/git/index.html +++ b/tags/git/index.html @@ -138,23 +138,23 @@

    Recent Posts

    diff --git "a/tags/\345\274\202\346\255\245/index.html" "b/tags/\345\274\202\346\255\245/index.html" index 8b2270d..6b07ec1 100644 --- "a/tags/\345\274\202\346\255\245/index.html" +++ "b/tags/\345\274\202\346\255\245/index.html" @@ -138,23 +138,23 @@

    Recent Posts

    diff --git "a/tags/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" "b/tags/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" index 9653f22..b512905 100644 --- "a/tags/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" +++ "b/tags/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" @@ -138,23 +138,23 @@

    Recent Posts

    From af180a52d9743b8a4dcc2d7ac3d1d1dfea6e7b14 Mon Sep 17 00:00:00 2001 From: YuHongpeng <480814177@qq.com> Date: Tue, 18 Jun 2019 23:21:51 +0800 Subject: [PATCH 09/18] Site updated: 2019-06-18 23:21:51 --- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../ES6\350\257\255\346\263\225/index.html" | 6 +- 2019/06/18/Flutter-md/index.html | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../18/RN\346\212\245\351\224\231/index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 8 +- .../index.html" | 10 +- .../index.html" | 10 +- .../index.html" | 6 +- .../index.html" | 8 +- .../index.html" | 10 +- .../index.html" | 10 +- .../Swift\346\263\233\345\236\213/index.html" | 8 +- .../index.html" | 6 +- .../index.html" | 8 +- .../index.html" | 6 +- 2019/06/18/docs/15608702427054/index.html | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 10 +- .../index.html" | 10 +- "2019/06/18/npm\345\222\214cnpm/index.html" | 8 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 8 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 8 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 8 +- .../index.html" | 6 +- .../index.html" | 8 +- .../index.html" | 8 +- archives/2016/10/index.html | 4 +- archives/2016/11/index.html | 4 +- archives/2016/12/index.html | 4 +- archives/2016/index.html | 4 +- archives/2016/page/2/index.html | 4 +- archives/2017/01/index.html | 4 +- archives/2017/02/index.html | 4 +- archives/2017/03/index.html | 4 +- archives/2017/04/index.html | 4 +- archives/2017/11/index.html | 4 +- archives/2017/12/index.html | 4 +- archives/2017/index.html | 4 +- archives/2017/page/2/index.html | 4 +- archives/2018/01/index.html | 4 +- archives/2018/02/index.html | 4 +- archives/2018/index.html | 4 +- archives/2019/06/index.html | 14 +- archives/2019/06/page/2/index.html | 8 +- archives/2019/06/page/3/index.html | 4 +- archives/2019/06/page/4/index.html | 12 +- archives/2019/06/page/5/index.html | 4 +- archives/2019/index.html | 14 +- archives/2019/page/2/index.html | 8 +- archives/2019/page/3/index.html | 4 +- archives/2019/page/4/index.html | 12 +- archives/2019/page/5/index.html | 4 +- archives/index.html | 14 +- archives/page/2/index.html | 8 +- archives/page/3/index.html | 4 +- archives/page/4/index.html | 12 +- archives/page/5/index.html | 4 +- archives/page/6/index.html | 4 +- archives/page/7/index.html | 4 +- archives/page/8/index.html | 4 +- index.html | 172 +++++------ page/2/index.html | 96 +++--- page/3/index.html | 24 +- page/4/index.html | 282 +++++++++--------- page/5/index.html | 24 +- page/6/index.html | 24 +- page/7/index.html | 24 +- page/8/index.html | 22 +- search.xml | 66 ++-- tags/AES-Base64-MD5/index.html | 4 +- tags/Alamofire/index.html | 4 +- tags/AlertView/index.html | 4 +- tags/GCD/index.html | 4 +- tags/Hexo/index.html | 4 +- tags/OC-JS/index.html | 4 +- tags/Quartz-2D/index.html | 4 +- tags/Quartz2D/index.html | 4 +- tags/RunTime/index.html | 4 +- tags/SDWebImage/index.html | 4 +- tags/SQL/index.html | 4 +- tags/Swift/index.html | 60 ++-- tags/TableView/index.html | 4 +- tags/WKWebView/index.html | 4 +- tags/git/index.html | 4 +- "tags/\345\274\202\346\255\245/index.html" | 4 +- .../index.html" | 4 +- 139 files changed, 795 insertions(+), 787 deletions(-) diff --git "a/2016/10/13/SQlite\344\275\277\347\224\250\345\255\246\344\271\240/index.html" "b/2016/10/13/SQlite\344\275\277\347\224\250\345\255\246\344\271\240/index.html" index 3f4ca72..7b7b0d7 100644 --- "a/2016/10/13/SQlite\344\275\277\347\224\250\345\255\246\344\271\240/index.html" +++ "b/2016/10/13/SQlite\344\275\277\347\224\250\345\255\246\344\271\240/index.html" @@ -200,7 +200,7 @@

    - Share + Share @@ -281,11 +281,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2016/10/20/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" "b/2016/10/20/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" index 1a0c61f..a9c01b5 100644 --- "a/2016/10/20/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" +++ "b/2016/10/20/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" @@ -94,7 +94,7 @@

    - Share + Share @@ -180,11 +180,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2016/10/27/iOS\345\274\271\346\241\206\345\274\217\345\233\276/index.html" "b/2016/10/27/iOS\345\274\271\346\241\206\345\274\217\345\233\276/index.html" index c8dd24f..ba2c66e 100644 --- "a/2016/10/27/iOS\345\274\271\346\241\206\345\274\217\345\233\276/index.html" +++ "b/2016/10/27/iOS\345\274\271\346\241\206\345\274\217\345\233\276/index.html" @@ -112,7 +112,7 @@

    - Share + Share @@ -198,11 +198,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2016/11/05/Hexo\344\270\252\344\272\272\345\215\232\345\256\242\346\220\255\345\273\272/index.html" "b/2016/11/05/Hexo\344\270\252\344\272\272\345\215\232\345\256\242\346\220\255\345\273\272/index.html" index 1ef36eb..aafaa7f 100644 --- "a/2016/11/05/Hexo\344\270\252\344\272\272\345\215\232\345\256\242\346\220\255\345\273\272/index.html" +++ "b/2016/11/05/Hexo\344\270\252\344\272\272\345\215\232\345\256\242\346\220\255\345\273\272/index.html" @@ -170,7 +170,7 @@

    - Share + Share @@ -256,11 +256,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2016/11/05/TableView\344\275\277\347\224\250\346\200\273\347\273\223/index.html" "b/2016/11/05/TableView\344\275\277\347\224\250\346\200\273\347\273\223/index.html" index 593f3ab..b6b9c0c 100644 --- "a/2016/11/05/TableView\344\275\277\347\224\250\346\200\273\347\273\223/index.html" +++ "b/2016/11/05/TableView\344\275\277\347\224\250\346\200\273\347\273\223/index.html" @@ -157,7 +157,7 @@

    - Share + Share @@ -243,11 +243,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2016/11/20/git\345\255\246\344\271\240\346\200\273\347\273\223/index.html" "b/2016/11/20/git\345\255\246\344\271\240\346\200\273\347\273\223/index.html" index 046822f..3223b85 100644 --- "a/2016/11/20/git\345\255\246\344\271\240\346\200\273\347\273\223/index.html" +++ "b/2016/11/20/git\345\255\246\344\271\240\346\200\273\347\273\223/index.html" @@ -154,7 +154,7 @@

    - Share + Share @@ -240,11 +240,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2016/11/22/OC RunTime\345\255\246\344\271\240/index.html" "b/2016/11/22/OC RunTime\345\255\246\344\271\240/index.html" index 7204e2a..0671bb7 100644 --- "a/2016/11/22/OC RunTime\345\255\246\344\271\240/index.html" +++ "b/2016/11/22/OC RunTime\345\255\246\344\271\240/index.html" @@ -105,7 +105,7 @@

    - Share + Share @@ -191,11 +191,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2016/11/22/Quartz2D\347\273\230\345\210\266\347\272\277\346\235\241/index.html" "b/2016/11/22/Quartz2D\347\273\230\345\210\266\347\272\277\346\235\241/index.html" index d5cf484..27a5a02 100644 --- "a/2016/11/22/Quartz2D\347\273\230\345\210\266\347\272\277\346\235\241/index.html" +++ "b/2016/11/22/Quartz2D\347\273\230\345\210\266\347\272\277\346\235\241/index.html" @@ -142,7 +142,7 @@

    - Share + Share @@ -228,11 +228,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2016/11/22/Swift3.0\345\255\246\344\271\240(\344\272\224)/index.html" "b/2016/11/22/Swift3.0\345\255\246\344\271\240(\344\272\224)/index.html" index 8ef21c9..3106527 100644 --- "a/2016/11/22/Swift3.0\345\255\246\344\271\240(\344\272\224)/index.html" +++ "b/2016/11/22/Swift3.0\345\255\246\344\271\240(\344\272\224)/index.html" @@ -142,7 +142,7 @@

    - Share + Share @@ -228,11 +228,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2016/11/22/Swift3.0\345\255\246\344\271\240(\345\233\233)/index.html" "b/2016/11/22/Swift3.0\345\255\246\344\271\240(\345\233\233)/index.html" index e9ce358..090ff8b 100644 --- "a/2016/11/22/Swift3.0\345\255\246\344\271\240(\345\233\233)/index.html" +++ "b/2016/11/22/Swift3.0\345\255\246\344\271\240(\345\233\233)/index.html" @@ -142,7 +142,7 @@

    - Share + Share @@ -228,11 +228,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2016/11/22/WKWebiView\344\275\277\347\224\250/index.html" "b/2016/11/22/WKWebiView\344\275\277\347\224\250/index.html" index 8bcdb38..f8a0ff3 100644 --- "a/2016/11/22/WKWebiView\344\275\277\347\224\250/index.html" +++ "b/2016/11/22/WKWebiView\344\275\277\347\224\250/index.html" @@ -142,7 +142,7 @@

    - Share + Share @@ -228,11 +228,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2016/12/04/Quartz2D\345\210\235\350\257\206/index.html" "b/2016/12/04/Quartz2D\345\210\235\350\257\206/index.html" index 3dd4dd8..b57bdf8 100644 --- "a/2016/12/04/Quartz2D\345\210\235\350\257\206/index.html" +++ "b/2016/12/04/Quartz2D\345\210\235\350\257\206/index.html" @@ -219,7 +219,7 @@

    - Share + Share @@ -305,11 +305,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2016/12/04/Quartz2D\347\273\230\345\210\266\346\233\262\347\272\277/index.html" "b/2016/12/04/Quartz2D\347\273\230\345\210\266\346\233\262\347\272\277/index.html" index 1d30570..91e1bb7 100644 --- "a/2016/12/04/Quartz2D\347\273\230\345\210\266\346\233\262\347\272\277/index.html" +++ "b/2016/12/04/Quartz2D\347\273\230\345\210\266\346\233\262\347\272\277/index.html" @@ -181,7 +181,7 @@

    - Share + Share @@ -267,11 +267,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2016/12/05/GCD\345\256\236\347\216\260\346\214\211\351\222\256\345\256\232\346\227\266\344\272\256\350\265\267/index.html" "b/2016/12/05/GCD\345\256\236\347\216\260\346\214\211\351\222\256\345\256\232\346\227\266\344\272\256\350\265\267/index.html" index e3819aa..c65a845 100644 --- "a/2016/12/05/GCD\345\256\236\347\216\260\346\214\211\351\222\256\345\256\232\346\227\266\344\272\256\350\265\267/index.html" +++ "b/2016/12/05/GCD\345\256\236\347\216\260\346\214\211\351\222\256\345\256\232\346\227\266\344\272\256\350\265\267/index.html" @@ -138,7 +138,7 @@

    - Share + Share @@ -224,11 +224,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2016/12/10/Quartz2D\347\273\230\345\210\266\345\275\242\347\212\266/index.html" "b/2016/12/10/Quartz2D\347\273\230\345\210\266\345\275\242\347\212\266/index.html" index b6c5c20..9dc39af 100644 --- "a/2016/12/10/Quartz2D\347\273\230\345\210\266\345\275\242\347\212\266/index.html" +++ "b/2016/12/10/Quartz2D\347\273\230\345\210\266\345\275\242\347\212\266/index.html" @@ -103,7 +103,7 @@

    - Share + Share @@ -189,11 +189,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2017/01/18/\345\274\202\346\255\245\344\270\213\350\275\275\345\233\276\347\211\207/index.html" "b/2017/01/18/\345\274\202\346\255\245\344\270\213\350\275\275\345\233\276\347\211\207/index.html" index 4f9ad29..9dfe9a0 100644 --- "a/2017/01/18/\345\274\202\346\255\245\344\270\213\350\275\275\345\233\276\347\211\207/index.html" +++ "b/2017/01/18/\345\274\202\346\255\245\344\270\213\350\275\275\345\233\276\347\211\207/index.html" @@ -139,7 +139,7 @@

    - Share + Share @@ -225,11 +225,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2017/02/02/iOS\351\235\231\346\200\201\345\272\223\345\212\250\346\200\201\345\272\223/index.html" "b/2017/02/02/iOS\351\235\231\346\200\201\345\272\223\345\212\250\346\200\201\345\272\223/index.html" index b424354..e0db4d3 100644 --- "a/2017/02/02/iOS\351\235\231\346\200\201\345\272\223\345\212\250\346\200\201\345\272\223/index.html" +++ "b/2017/02/02/iOS\351\235\231\346\200\201\345\272\223\345\212\250\346\200\201\345\272\223/index.html" @@ -130,7 +130,7 @@

    - Share + Share @@ -216,11 +216,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2017/02/08/SDWebImage\345\255\246\344\271\240/index.html" "b/2017/02/08/SDWebImage\345\255\246\344\271\240/index.html" index 29132ed..ce47cbf 100644 --- "a/2017/02/08/SDWebImage\345\255\246\344\271\240/index.html" +++ "b/2017/02/08/SDWebImage\345\255\246\344\271\240/index.html" @@ -164,7 +164,7 @@

    - Share + Share @@ -250,11 +250,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2017/02/20/OC\344\270\216js\344\272\244\344\272\222\345\255\246\344\271\240/index.html" "b/2017/02/20/OC\344\270\216js\344\272\244\344\272\222\345\255\246\344\271\240/index.html" index 5164ccf..b49dda9 100644 --- "a/2017/02/20/OC\344\270\216js\344\272\244\344\272\222\345\255\246\344\271\240/index.html" +++ "b/2017/02/20/OC\344\270\216js\344\272\244\344\272\222\345\255\246\344\271\240/index.html" @@ -102,7 +102,7 @@

    - Share + Share @@ -188,11 +188,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2017/03/01/Swift3.0\345\255\246\344\271\240(\344\270\200)/index.html" "b/2017/03/01/Swift3.0\345\255\246\344\271\240(\344\270\200)/index.html" index 10f3eb6..99f6959 100644 --- "a/2017/03/01/Swift3.0\345\255\246\344\271\240(\344\270\200)/index.html" +++ "b/2017/03/01/Swift3.0\345\255\246\344\271\240(\344\270\200)/index.html" @@ -160,7 +160,7 @@

    - Share + Share @@ -246,11 +246,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2017/03/05/Swift3.0\345\255\246\344\271\240(\344\272\214)/index.html" "b/2017/03/05/Swift3.0\345\255\246\344\271\240(\344\272\214)/index.html" index f7b30be..80b7ac0 100644 --- "a/2017/03/05/Swift3.0\345\255\246\344\271\240(\344\272\214)/index.html" +++ "b/2017/03/05/Swift3.0\345\255\246\344\271\240(\344\272\214)/index.html" @@ -216,7 +216,7 @@

    - Share + Share @@ -302,11 +302,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2017/03/08/Swift3.0\345\255\246\344\271\240(\344\270\211)/index.html" "b/2017/03/08/Swift3.0\345\255\246\344\271\240(\344\270\211)/index.html" index 1bd78d4..f735bba 100644 --- "a/2017/03/08/Swift3.0\345\255\246\344\271\240(\344\270\211)/index.html" +++ "b/2017/03/08/Swift3.0\345\255\246\344\271\240(\344\270\211)/index.html" @@ -112,7 +112,7 @@

    - Share + Share @@ -198,11 +198,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2017/03/25/\347\237\245\350\257\206\347\202\271\346\200\273\347\273\223/index.html" "b/2017/03/25/\347\237\245\350\257\206\347\202\271\346\200\273\347\273\223/index.html" index add8ab7..96554c8 100644 --- "a/2017/03/25/\347\237\245\350\257\206\347\202\271\346\200\273\347\273\223/index.html" +++ "b/2017/03/25/\347\237\245\350\257\206\347\202\271\346\200\273\347\273\223/index.html" @@ -100,7 +100,7 @@

    @@ -184,11 +184,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2017/04/10/Alamofire\346\272\220\347\240\201\345\255\246\344\271\240/index.html" "b/2017/04/10/Alamofire\346\272\220\347\240\201\345\255\246\344\271\240/index.html" index fc21104..79cf94e 100644 --- "a/2017/04/10/Alamofire\346\272\220\347\240\201\345\255\246\344\271\240/index.html" +++ "b/2017/04/10/Alamofire\346\272\220\347\240\201\345\255\246\344\271\240/index.html" @@ -109,7 +109,7 @@

    - Share + Share @@ -195,11 +195,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2017/11/29/CocoaPods\345\210\266\344\275\234\347\247\201\346\234\211pod/index.html" "b/2017/11/29/CocoaPods\345\210\266\344\275\234\347\247\201\346\234\211pod/index.html" index 82bd8d7..bd0a579 100644 --- "a/2017/11/29/CocoaPods\345\210\266\344\275\234\347\247\201\346\234\211pod/index.html" +++ "b/2017/11/29/CocoaPods\345\210\266\344\275\234\347\247\201\346\234\211pod/index.html" @@ -135,7 +135,7 @@

    - Share + Share

    @@ -219,11 +219,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2017/12/02/\346\234\254\345\234\260\345\267\262\346\234\211\351\241\271\347\233\256\346\267\273\345\212\240Pod/index.html" "b/2017/12/02/\346\234\254\345\234\260\345\267\262\346\234\211\351\241\271\347\233\256\346\267\273\345\212\240Pod/index.html" index e9e0ec1..47a3f1a 100644 --- "a/2017/12/02/\346\234\254\345\234\260\345\267\262\346\234\211\351\241\271\347\233\256\346\267\273\345\212\240Pod/index.html" +++ "b/2017/12/02/\346\234\254\345\234\260\345\267\262\346\234\211\351\241\271\347\233\256\346\267\273\345\212\240Pod/index.html" @@ -106,7 +106,7 @@

    @@ -190,11 +190,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2017/12/14/Mac-\345\256\211\350\243\205MySQL/index.html" "b/2017/12/14/Mac-\345\256\211\350\243\205MySQL/index.html" index 717adef..2e698b5 100644 --- "a/2017/12/14/Mac-\345\256\211\350\243\205MySQL/index.html" +++ "b/2017/12/14/Mac-\345\256\211\350\243\205MySQL/index.html" @@ -116,7 +116,7 @@

    - Share + Share

    @@ -200,11 +200,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2018/01/08/Swift\345\212\240\345\257\206\347\233\270\345\205\263/index.html" "b/2018/01/08/Swift\345\212\240\345\257\206\347\233\270\345\205\263/index.html" index 2cd881f..02e0357 100644 --- "a/2018/01/08/Swift\345\212\240\345\257\206\347\233\270\345\205\263/index.html" +++ "b/2018/01/08/Swift\345\212\240\345\257\206\347\233\270\345\205\263/index.html" @@ -314,7 +314,7 @@

    - Share + Share @@ -400,11 +400,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2018/01/10/\347\275\221\347\273\234\344\270\203\345\261\202\345\215\217\350\256\256/index.html" "b/2018/01/10/\347\275\221\347\273\234\344\270\203\345\261\202\345\215\217\350\256\256/index.html" index f7aed9b..3c86e8a 100644 --- "a/2018/01/10/\347\275\221\347\273\234\344\270\203\345\261\202\345\215\217\350\256\256/index.html" +++ "b/2018/01/10/\347\275\221\347\273\234\344\270\203\345\261\202\345\215\217\350\256\256/index.html" @@ -128,7 +128,7 @@

    Socket @@ -212,11 +212,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2018/01/12/iOS\345\215\263\346\227\266\351\200\232\350\256\257\345\256\236\347\216\260\344\272\214/index.html" "b/2018/01/12/iOS\345\215\263\346\227\266\351\200\232\350\256\257\345\256\236\347\216\260\344\272\214/index.html" index e5cf84d..cc397bf 100644 --- "a/2018/01/12/iOS\345\215\263\346\227\266\351\200\232\350\256\257\345\256\236\347\216\260\344\272\214/index.html" +++ "b/2018/01/12/iOS\345\215\263\346\227\266\351\200\232\350\256\257\345\256\236\347\216\260\344\272\214/index.html" @@ -232,7 +232,7 @@

    - Share + Share

    @@ -316,11 +316,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2018/01/17/\351\235\242\350\257\225\344\270\255\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223/index.html" "b/2018/01/17/\351\235\242\350\257\225\344\270\255\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223/index.html" index 4059df8..be8f0d8 100644 --- "a/2018/01/17/\351\235\242\350\257\225\344\270\255\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223/index.html" +++ "b/2018/01/17/\351\235\242\350\257\225\344\270\255\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223/index.html" @@ -164,7 +164,7 @@

    Runlo @@ -248,11 +248,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2018/01/18/\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223-\344\272\214/index.html" "b/2018/01/18/\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223-\344\272\214/index.html" index 6fdd87e..dba243e 100644 --- "a/2018/01/18/\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223-\344\272\214/index.html" +++ "b/2018/01/18/\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223-\344\272\214/index.html" @@ -130,7 +130,7 @@

    - Share + Share

    @@ -214,11 +214,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2018/01/19/Block\345\255\246\344\271\240\346\216\242\347\251\266/index.html" "b/2018/01/19/Block\345\255\246\344\271\240\346\216\242\347\251\266/index.html" index 0f37f0c..2a59341 100644 --- "a/2018/01/19/Block\345\255\246\344\271\240\346\216\242\347\251\266/index.html" +++ "b/2018/01/19/Block\345\255\246\344\271\240\346\216\242\347\251\266/index.html" @@ -110,7 +110,7 @@

    - Share + Share

    @@ -194,11 +194,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2018/02/03/Mac\345\256\211\350\243\205VirtualBox\350\277\236\346\216\245U\347\233\230/index.html" "b/2018/02/03/Mac\345\256\211\350\243\205VirtualBox\350\277\236\346\216\245U\347\233\230/index.html" index 18a02ed..7093ba7 100644 --- "a/2018/02/03/Mac\345\256\211\350\243\205VirtualBox\350\277\236\346\216\245U\347\233\230/index.html" +++ "b/2018/02/03/Mac\345\256\211\350\243\205VirtualBox\350\277\236\346\216\245U\347\233\230/index.html" @@ -94,7 +94,7 @@

    @@ -178,11 +178,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2019/06/18/Charles \346\212\223\345\214\205/index.html" "b/2019/06/18/Charles \346\212\223\345\214\205/index.html" index f5f3e88..2831443 100644 --- "a/2019/06/18/Charles \346\212\223\345\214\205/index.html" +++ "b/2019/06/18/Charles \346\212\223\345\214\205/index.html" @@ -179,7 +179,7 @@

    - Share + Share

    @@ -263,11 +263,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2019/06/18/ES6\350\257\255\346\263\225/index.html" "b/2019/06/18/ES6\350\257\255\346\263\225/index.html" index 842817c..ac06e13 100644 --- "a/2019/06/18/ES6\350\257\255\346\263\225/index.html" +++ "b/2019/06/18/ES6\350\257\255\346\263\225/index.html" @@ -180,7 +180,7 @@

    13

    @@ -264,11 +264,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git a/2019/06/18/Flutter-md/index.html b/2019/06/18/Flutter-md/index.html index 7e3ca62..e262bde 100644 --- a/2019/06/18/Flutter-md/index.html +++ b/2019/06/18/Flutter-md/index.html @@ -92,7 +92,7 @@

    @@ -167,11 +167,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2019/06/18/Flutter\345\205\245\351\227\250/index.html" "b/2019/06/18/Flutter\345\205\245\351\227\250/index.html" index 2200a16..b9d1774 100644 --- "a/2019/06/18/Flutter\345\205\245\351\227\250/index.html" +++ "b/2019/06/18/Flutter\345\205\245\351\227\250/index.html" @@ -83,7 +83,7 @@

    - Share + Share

    @@ -167,11 +167,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2019/06/18/Flutter\345\270\270\347\224\250\347\273\204\344\273\266/index.html" "b/2019/06/18/Flutter\345\270\270\347\224\250\347\273\204\344\273\266/index.html" index 7cec5f6..f5758f3 100644 --- "a/2019/06/18/Flutter\345\270\270\347\224\250\347\273\204\344\273\266/index.html" +++ "b/2019/06/18/Flutter\345\270\270\347\224\250\347\273\204\344\273\266/index.html" @@ -115,7 +115,7 @@

    - Share + Share

    @@ -199,11 +199,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2019/06/18/GCD \344\277\241\345\217\267\351\207\217/index.html" "b/2019/06/18/GCD \344\277\241\345\217\267\351\207\217/index.html" index 53546b1..dd6e7b6 100644 --- "a/2019/06/18/GCD \344\277\241\345\217\267\351\207\217/index.html" +++ "b/2019/06/18/GCD \344\277\241\345\217\267\351\207\217/index.html" @@ -122,7 +122,7 @@

    - Share + Share

    @@ -206,11 +206,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2019/06/18/JavaScript\345\237\272\347\241\200/index.html" "b/2019/06/18/JavaScript\345\237\272\347\241\200/index.html" index 9b315c8..9b46402 100644 --- "a/2019/06/18/JavaScript\345\237\272\347\241\200/index.html" +++ "b/2019/06/18/JavaScript\345\237\272\347\241\200/index.html" @@ -130,7 +130,7 @@

    数组 @@ -214,11 +214,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2019/06/18/MasonrySnapkit\344\275\277\347\224\250\346\200\273\347\273\223/index.html" "b/2019/06/18/MasonrySnapkit\344\275\277\347\224\250\346\200\273\347\273\223/index.html" index c1b4cf2..bc4522d 100644 --- "a/2019/06/18/MasonrySnapkit\344\275\277\347\224\250\346\200\273\347\273\223/index.html" +++ "b/2019/06/18/MasonrySnapkit\344\275\277\347\224\250\346\200\273\347\273\223/index.html" @@ -235,7 +235,7 @@

    @@ -319,11 +319,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2019/06/18/RN\346\212\245\351\224\231/index.html" "b/2019/06/18/RN\346\212\245\351\224\231/index.html" index 5e33b3f..5385880 100644 --- "a/2019/06/18/RN\346\212\245\351\224\231/index.html" +++ "b/2019/06/18/RN\346\212\245\351\224\231/index.html" @@ -144,7 +144,7 @@

    - Share + Share @@ -228,11 +228,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2019/06/18/React-Native\345\274\200\345\217\221iOS\346\211\223\345\214\205/index.html" "b/2019/06/18/React-Native\345\274\200\345\217\221iOS\346\211\223\345\214\205/index.html" index 586b44c..5a42dfc 100644 --- "a/2019/06/18/React-Native\345\274\200\345\217\221iOS\346\211\223\345\214\205/index.html" +++ "b/2019/06/18/React-Native\345\274\200\345\217\221iOS\346\211\223\345\214\205/index.html" @@ -125,7 +125,7 @@

    - Share + Share @@ -209,11 +209,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2019/06/18/ReactNative\344\270\255ref\344\275\277\347\224\250/index.html" "b/2019/06/18/ReactNative\344\270\255ref\344\275\277\347\224\250/index.html" index ad1586e..6eafca7 100644 --- "a/2019/06/18/ReactNative\344\270\255ref\344\275\277\347\224\250/index.html" +++ "b/2019/06/18/ReactNative\344\270\255ref\344\275\277\347\224\250/index.html" @@ -104,7 +104,7 @@

    - Share + Share @@ -113,7 +113,7 @@

    - + Newer
    @@ -188,11 +188,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2019/06/18/Swift \345\215\217\350\256\256\344\275\277\347\224\250/index.html" "b/2019/06/18/Swift \345\215\217\350\256\256\344\275\277\347\224\250/index.html" index b611e71..1ec2d5a 100644 --- "a/2019/06/18/Swift \345\215\217\350\256\256\344\275\277\347\224\250/index.html" +++ "b/2019/06/18/Swift \345\215\217\350\256\256\344\275\277\347\224\250/index.html" @@ -129,7 +129,7 @@

    - Share + Share @@ -138,7 +138,7 @@

    - + Newer
    @@ -148,7 +148,7 @@

    + Older
    @@ -213,11 +213,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2019/06/18/Swift \345\237\272\347\241\200\347\237\245\350\257\206/index.html" "b/2019/06/18/Swift \345\237\272\347\241\200\347\237\245\350\257\206/index.html" index 4896d99..cb716b4 100644 --- "a/2019/06/18/Swift \345\237\272\347\241\200\347\237\245\350\257\206/index.html" +++ "b/2019/06/18/Swift \345\237\272\347\241\200\347\237\245\350\257\206/index.html" @@ -176,7 +176,7 @@

    - Share + Share @@ -185,7 +185,7 @@

    - + Newer
    @@ -195,7 +195,7 @@

    + Older
    @@ -260,11 +260,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2019/06/18/SwiftLint\344\275\277\347\224\250/index.html" "b/2019/06/18/SwiftLint\344\275\277\347\224\250/index.html" index 4178680..34be9c8 100644 --- "a/2019/06/18/SwiftLint\344\275\277\347\224\250/index.html" +++ "b/2019/06/18/SwiftLint\344\275\277\347\224\250/index.html" @@ -82,7 +82,7 @@

    @@ -166,11 +166,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2019/06/18/Swift\344\270\213\346\240\207\345\222\214\350\277\220\347\256\227\347\254\246\351\207\215\350\275\275/index.html" "b/2019/06/18/Swift\344\270\213\346\240\207\345\222\214\350\277\220\347\256\227\347\254\246\351\207\215\350\275\275/index.html" index a427e43..fc90dcb 100644 --- "a/2019/06/18/Swift\344\270\213\346\240\207\345\222\214\350\277\220\347\256\227\347\254\246\351\207\215\350\275\275/index.html" +++ "b/2019/06/18/Swift\344\270\213\346\240\207\345\222\214\350\277\220\347\256\227\347\254\246\351\207\215\350\275\275/index.html" @@ -132,7 +132,7 @@

    @@ -151,7 +151,7 @@

    - + Older
    @@ -216,11 +216,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2019/06/18/Swift\345\215\217\350\256\256\345\210\253\345\220\215/index.html" "b/2019/06/18/Swift\345\215\217\350\256\256\345\210\253\345\220\215/index.html" index bb741a3..cb335b8 100644 --- "a/2019/06/18/Swift\345\215\217\350\256\256\345\210\253\345\220\215/index.html" +++ "b/2019/06/18/Swift\345\215\217\350\256\256\345\210\253\345\220\215/index.html" @@ -216,7 +216,7 @@

    - Share + Share @@ -225,7 +225,7 @@

    - + Newer
    @@ -235,7 +235,7 @@

    + Older
    @@ -300,11 +300,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2019/06/18/Swift\345\215\217\350\256\256\346\211\251\345\261\225/index.html" "b/2019/06/18/Swift\345\215\217\350\256\256\346\211\251\345\261\225/index.html" index f90e977..92011c8 100644 --- "a/2019/06/18/Swift\345\215\217\350\256\256\346\211\251\345\261\225/index.html" +++ "b/2019/06/18/Swift\345\215\217\350\256\256\346\211\251\345\261\225/index.html" @@ -110,7 +110,7 @@

    @@ -119,7 +119,7 @@

    - + Newer
    @@ -129,7 +129,7 @@

    - + Older
    @@ -194,11 +194,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2019/06/18/Swift\346\263\233\345\236\213/index.html" "b/2019/06/18/Swift\346\263\233\345\236\213/index.html" index 86ff0d0..754ec16 100644 --- "a/2019/06/18/Swift\346\263\233\345\236\213/index.html" +++ "b/2019/06/18/Swift\346\263\233\345\236\213/index.html" @@ -106,7 +106,7 @@

    - Share + Share @@ -115,7 +115,7 @@

    - + Newer
    @@ -190,11 +190,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2019/06/18/Swift\351\224\231\350\257\257\345\244\204\347\220\206/index.html" "b/2019/06/18/Swift\351\224\231\350\257\257\345\244\204\347\220\206/index.html" index d488cf9..9f9ecd1 100644 --- "a/2019/06/18/Swift\351\224\231\350\257\257\345\244\204\347\220\206/index.html" +++ "b/2019/06/18/Swift\351\224\231\350\257\257\345\244\204\347\220\206/index.html" @@ -87,7 +87,7 @@

    throw

    @@ -171,11 +171,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2019/06/18/Swift\351\241\271\347\233\256\344\270\255\351\233\206\346\210\220RN/index.html" "b/2019/06/18/Swift\351\241\271\347\233\256\344\270\255\351\233\206\346\210\220RN/index.html" index ef28bd7..09677e3 100644 --- "a/2019/06/18/Swift\351\241\271\347\233\256\344\270\255\351\233\206\346\210\220RN/index.html" +++ "b/2019/06/18/Swift\351\241\271\347\233\256\344\270\255\351\233\206\346\210\220RN/index.html" @@ -109,7 +109,7 @@

    - Share + Share @@ -128,7 +128,7 @@

    + Older
    @@ -193,11 +193,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2019/06/18/Xcode\344\270\255\344\275\277\347\224\250Debug Memory Graph\346\243\200\346\237\245\345\206\205\345\255\230\346\263\204\346\274\217/index.html" "b/2019/06/18/Xcode\344\270\255\344\275\277\347\224\250Debug Memory Graph\346\243\200\346\237\245\345\206\205\345\255\230\346\263\204\346\274\217/index.html" index 4e1084f..387cd75 100644 --- "a/2019/06/18/Xcode\344\270\255\344\275\277\347\224\250Debug Memory Graph\346\243\200\346\237\245\345\206\205\345\255\230\346\263\204\346\274\217/index.html" +++ "b/2019/06/18/Xcode\344\270\255\344\275\277\347\224\250Debug Memory Graph\346\243\200\346\237\245\345\206\205\345\255\230\346\263\204\346\274\217/index.html" @@ -89,7 +89,7 @@

    @@ -173,11 +173,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git a/2019/06/18/docs/15608702427054/index.html b/2019/06/18/docs/15608702427054/index.html index 4e110e1..e8c857c 100644 --- a/2019/06/18/docs/15608702427054/index.html +++ b/2019/06/18/docs/15608702427054/index.html @@ -81,7 +81,7 @@

    @@ -165,11 +165,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2019/06/18/flutter\345\256\211\350\243\205/index.html" "b/2019/06/18/flutter\345\256\211\350\243\205/index.html" index 5ad7e7e..9cd3627 100644 --- "a/2019/06/18/flutter\345\256\211\350\243\205/index.html" +++ "b/2019/06/18/flutter\345\256\211\350\243\205/index.html" @@ -82,7 +82,7 @@

    @@ -166,11 +166,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2019/06/18/iOS \345\212\250\346\200\201\350\256\276\347\275\256TableHeaderView\351\253\230\345\272\246/index.html" "b/2019/06/18/iOS \345\212\250\346\200\201\350\256\276\347\275\256TableHeaderView\351\253\230\345\272\246/index.html" index 02295f0..00fe705 100644 --- "a/2019/06/18/iOS \345\212\250\346\200\201\350\256\276\347\275\256TableHeaderView\351\253\230\345\272\246/index.html" +++ "b/2019/06/18/iOS \345\212\250\346\200\201\350\256\276\347\275\256TableHeaderView\351\253\230\345\272\246/index.html" @@ -96,7 +96,7 @@

    - Share + Share @@ -180,11 +180,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2019/06/18/iOS\345\274\200\345\217\221_\344\272\216\351\270\277\351\271\217/index.html" "b/2019/06/18/iOS\345\274\200\345\217\221_\344\272\216\351\270\277\351\271\217/index.html" index 4150488..81fae5c 100644 --- "a/2019/06/18/iOS\345\274\200\345\217\221_\344\272\216\351\270\277\351\271\217/index.html" +++ "b/2019/06/18/iOS\345\274\200\345\217\221_\344\272\216\351\270\277\351\271\217/index.html" @@ -130,7 +130,7 @@

    - Share + Share @@ -214,11 +214,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2019/06/18/iOS\351\200\232\350\277\207URL\350\216\267\345\217\226HTML\344\270\255\346\240\207\351\242\230/index.html" "b/2019/06/18/iOS\351\200\232\350\277\207URL\350\216\267\345\217\226HTML\344\270\255\346\240\207\351\242\230/index.html" index b8dc7f0..4094b25 100644 --- "a/2019/06/18/iOS\351\200\232\350\277\207URL\350\216\267\345\217\226HTML\344\270\255\346\240\207\351\242\230/index.html" +++ "b/2019/06/18/iOS\351\200\232\350\277\207URL\350\216\267\345\217\226HTML\344\270\255\346\240\207\351\242\230/index.html" @@ -124,7 +124,7 @@
    @@ -208,11 +208,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2019/06/18/iOS\351\235\242\350\257\225OC\350\257\255\350\250\200\347\211\271\346\200\247/index.html" "b/2019/06/18/iOS\351\235\242\350\257\225OC\350\257\255\350\250\200\347\211\271\346\200\247/index.html" index db2299d..26a71cc 100644 --- "a/2019/06/18/iOS\351\235\242\350\257\225OC\350\257\255\350\250\200\347\211\271\346\200\247/index.html" +++ "b/2019/06/18/iOS\351\235\242\350\257\225OC\350\257\255\350\250\200\347\211\271\346\200\247/index.html" @@ -196,7 +196,7 @@

    - Share + Share @@ -205,7 +205,7 @@

    - + Newer
    @@ -215,7 +215,7 @@

    + Older
    @@ -280,11 +280,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2019/06/18/is \345\205\263\351\224\256\345\255\227\347\261\273\345\236\213\346\243\200\346\237\245/index.html" "b/2019/06/18/is \345\205\263\351\224\256\345\255\227\347\261\273\345\236\213\346\243\200\346\237\245/index.html" index b0de84a..ac39db4 100644 --- "a/2019/06/18/is \345\205\263\351\224\256\345\255\227\347\261\273\345\236\213\346\243\200\346\237\245/index.html" +++ "b/2019/06/18/is \345\205\263\351\224\256\345\255\227\347\261\273\345\236\213\346\243\200\346\237\245/index.html" @@ -91,7 +91,7 @@

    - Share + Share @@ -100,7 +100,7 @@

    - + Newer
    @@ -110,7 +110,7 @@

    + Older
    @@ -175,11 +175,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2019/06/18/npm\345\222\214cnpm/index.html" "b/2019/06/18/npm\345\222\214cnpm/index.html" index 597dd64..2fc9408 100644 --- "a/2019/06/18/npm\345\222\214cnpm/index.html" +++ "b/2019/06/18/npm\345\222\214cnpm/index.html" @@ -110,7 +110,7 @@

    - Share + Share @@ -119,7 +119,7 @@

    - + Newer
    @@ -194,11 +194,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2019/06/18/swift\344\270\255\345\274\272\345\274\225\347\224\250\345\276\252\347\216\257\345\222\214weak\345\205\263\351\224\256\345\255\227/index.html" "b/2019/06/18/swift\344\270\255\345\274\272\345\274\225\347\224\250\345\276\252\347\216\257\345\222\214weak\345\205\263\351\224\256\345\255\227/index.html" index c03a1e4..bde728e 100644 --- "a/2019/06/18/swift\344\270\255\345\274\272\345\274\225\347\224\250\345\276\252\347\216\257\345\222\214weak\345\205\263\351\224\256\345\255\227/index.html" +++ "b/2019/06/18/swift\344\270\255\345\274\272\345\274\225\347\224\250\345\276\252\347\216\257\345\222\214weak\345\205\263\351\224\256\345\255\227/index.html" @@ -165,7 +165,7 @@

    @@ -249,11 +249,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2019/06/18/typescripe \345\256\211\350\243\205\345\217\212\350\257\255\346\263\225/index.html" "b/2019/06/18/typescripe \345\256\211\350\243\205\345\217\212\350\257\255\346\263\225/index.html" index 809b02a..551c375 100644 --- "a/2019/06/18/typescripe \345\256\211\350\243\205\345\217\212\350\257\255\346\263\225/index.html" +++ "b/2019/06/18/typescripe \345\256\211\350\243\205\345\217\212\350\257\255\346\263\225/index.html" @@ -156,7 +156,7 @@
    @@ -240,11 +240,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2019/06/18/\343\200\21252\344\270\252\346\226\271\346\263\225\347\254\254\344\270\200\347\253\240\343\200\213/index.html" "b/2019/06/18/\343\200\21252\344\270\252\346\226\271\346\263\225\347\254\254\344\270\200\347\253\240\343\200\213/index.html" index e58b404..b3cd07d 100644 --- "a/2019/06/18/\343\200\21252\344\270\252\346\226\271\346\263\225\347\254\254\344\270\200\347\253\240\343\200\213/index.html" +++ "b/2019/06/18/\343\200\21252\344\270\252\346\226\271\346\263\225\347\254\254\344\270\200\347\253\240\343\200\213/index.html" @@ -268,7 +268,7 @@
    - Share + Share @@ -352,11 +352,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2019/06/18/\344\270\200\350\207\252\345\212\250\345\274\225\347\224\250\350\256\241\346\225\260/index.html" "b/2019/06/18/\344\270\200\350\207\252\345\212\250\345\274\225\347\224\250\350\256\241\346\225\260/index.html" index 320d652..334f466 100644 --- "a/2019/06/18/\344\270\200\350\207\252\345\212\250\345\274\225\347\224\250\350\256\241\346\225\260/index.html" +++ "b/2019/06/18/\344\270\200\350\207\252\345\212\250\345\274\225\347\224\250\350\256\241\346\225\260/index.html" @@ -112,7 +112,7 @@

    @@ -131,7 +131,7 @@

    - + Older
    @@ -196,11 +196,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2019/06/18/\347\254\254\344\270\203\347\253\240Blocks\351\235\242\350\257\225/index.html" "b/2019/06/18/\347\254\254\344\270\203\347\253\240Blocks\351\235\242\350\257\225/index.html" index 310d47d..6869364 100644 --- "a/2019/06/18/\347\254\254\344\270\203\347\253\240Blocks\351\235\242\350\257\225/index.html" +++ "b/2019/06/18/\347\254\254\344\270\203\347\253\240Blocks\351\235\242\350\257\225/index.html" @@ -162,7 +162,7 @@

    @@ -246,11 +246,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2019/06/18/\347\254\254\344\270\211\347\253\240UI\350\247\206\345\233\276\345\223\215\345\272\224\351\223\276\347\246\273\345\261\217\346\270\262\346\237\223\351\235\242\350\257\225/index.html" "b/2019/06/18/\347\254\254\344\270\211\347\253\240UI\350\247\206\345\233\276\345\223\215\345\272\224\351\223\276\347\246\273\345\261\217\346\270\262\346\237\223\351\235\242\350\257\225/index.html" index a15f885..ce67584 100644 --- "a/2019/06/18/\347\254\254\344\270\211\347\253\240UI\350\247\206\345\233\276\345\223\215\345\272\224\351\223\276\347\246\273\345\261\217\346\270\262\346\237\223\351\235\242\350\257\225/index.html" +++ "b/2019/06/18/\347\254\254\344\270\211\347\253\240UI\350\247\206\345\233\276\345\223\215\345\272\224\351\223\276\347\246\273\345\261\217\346\270\262\346\237\223\351\235\242\350\257\225/index.html" @@ -155,7 +155,7 @@

    - Share + Share @@ -239,11 +239,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/2019/06/18/\347\254\254\344\271\235\347\253\240Runloop\351\235\242\350\257\225/index.html" "b/2019/06/18/\347\254\254\344\271\235\347\253\240Runloop\351\235\242\350\257\225/index.html" index ae69924..5832723 100644 --- "a/2019/06/18/\347\254\254\344\271\235\347\253\240Runloop\351\235\242\350\257\225/index.html" +++ "b/2019/06/18/\347\254\254\344\271\235\347\253\240Runloop\351\235\242\350\257\225/index.html" @@ -84,7 +84,7 @@

    @@ -93,7 +93,7 @@

    + + + + + + + + + + + +

    + +o.io/" target="_blank">Hexo +

    +

    + +

    + + + diff --git a/tags/TableView/index.html b/tags/TableView/index.html index 58050f1..dd61862 100644 --- a/tags/TableView/index.html +++ b/tags/TableView/index.html @@ -150,11 +150,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git a/tags/WKWebView/index.html b/tags/WKWebView/index.html index c6a32d0..a0db24a 100644 --- a/tags/WKWebView/index.html +++ b/tags/WKWebView/index.html @@ -236,11 +236,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git a/tags/git/index.html b/tags/git/index.html index 4f413cc..97d3672 100644 --- a/tags/git/index.html +++ b/tags/git/index.html @@ -150,11 +150,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/tags/\345\274\202\346\255\245/index.html" "b/tags/\345\274\202\346\255\245/index.html" index 6b07ec1..f71f4bf 100644 --- "a/tags/\345\274\202\346\255\245/index.html" +++ "b/tags/\345\274\202\346\255\245/index.html" @@ -150,11 +150,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • diff --git "a/tags/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" "b/tags/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" index b512905..487c31b 100644 --- "a/tags/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" +++ "b/tags/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" @@ -150,11 +150,11 @@

    Recent Posts

  • - (no title) + (no title)
  • - (no title) + (no title)
  • From d4ee4be468389ad8c12a4da32dfe50261cb755db Mon Sep 17 00:00:00 2001 From: YuHongpeng <480814177@qq.com> Date: Tue, 18 Jun 2019 23:25:03 +0800 Subject: [PATCH 10/18] Site updated: 2019-06-18 23:25:02 --- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- .../ES6\350\257\255\346\263\225/index.html" | 12 +- .../index.html" | 222 ++++++++++++ .../index.html" | 14 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- .../18/RN\346\212\245\351\224\231/index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 14 +- .../index.html" | 16 +- .../index.html" | 16 +- .../index.html" | 16 +- .../index.html" | 16 +- .../index.html" | 16 +- .../index.html" | 16 +- .../Swift\346\263\233\345\236\213/index.html" | 16 +- .../index.html" | 12 +- .../index.html" | 14 +- .../index.html" | 12 +- 2019/06/18/docs/15608702427054/index.html | 16 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- "2019/06/18/npm\345\222\214cnpm/index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 12 +- .../index.html" | 14 +- .../index.html" | 12 +- .../index.html" | 14 +- .../index.html" | 14 +- .../index.html" | 16 +- .../index.html" | 16 +- .../index.html" | 16 +- .../index.html" | 16 +- .../index.html" | 16 +- .../index.html" | 14 +- .../index.html" | 14 +- .../index.html" | 14 +- archives/2016/10/index.html | 10 +- archives/2016/11/index.html | 10 +- archives/2016/12/index.html | 10 +- archives/2016/index.html | 10 +- archives/2016/page/2/index.html | 10 +- archives/2017/01/index.html | 10 +- archives/2017/02/index.html | 10 +- archives/2017/03/index.html | 10 +- archives/2017/04/index.html | 10 +- archives/2017/11/index.html | 10 +- archives/2017/12/index.html | 10 +- archives/2017/index.html | 10 +- archives/2017/page/2/index.html | 10 +- archives/2018/01/index.html | 10 +- archives/2018/02/index.html | 10 +- archives/2018/index.html | 10 +- archives/2019/06/index.html | 24 +- archives/2019/06/page/2/index.html | 14 +- archives/2019/06/page/3/index.html | 10 +- archives/2019/06/page/4/index.html | 22 +- archives/2019/06/page/5/index.html | 10 +- archives/2019/index.html | 24 +- archives/2019/page/2/index.html | 14 +- archives/2019/page/3/index.html | 10 +- archives/2019/page/4/index.html | 22 +- archives/2019/page/5/index.html | 10 +- archives/index.html | 24 +- archives/page/2/index.html | 14 +- archives/page/3/index.html | 10 +- archives/page/4/index.html | 22 +- archives/page/5/index.html | 10 +- archives/page/6/index.html | 10 +- archives/page/7/index.html | 10 +- archives/page/8/index.html | 10 +- index.html | 190 +++++----- page/2/index.html | 82 ++--- page/3/index.html | 30 +- page/4/index.html | 340 +++++++++--------- page/5/index.html | 30 +- page/6/index.html | 30 +- page/7/index.html | 30 +- page/8/index.html | 28 +- search.xml | 92 ++--- tags/AES-Base64-MD5/index.html | 10 +- tags/Alamofire/index.html | 10 +- tags/AlertView/index.html | 10 +- tags/Flutter/index.html | 200 +++++++++++ tags/GCD/index.html | 10 +- tags/Hexo/index.html | 10 +- tags/OC-JS/index.html | 10 +- tags/Quartz-2D/index.html | 10 +- tags/Quartz2D/index.html | 10 +- tags/RunTime/index.html | 10 +- tags/SDWebImage/index.html | 10 +- tags/SQL/index.html | 10 +- tags/Swift/index.html | 66 ++-- tags/TableView/index.html | 10 +- tags/WKWebView/index.html | 10 +- tags/git/index.html | 10 +- "tags/\345\274\202\346\255\245/index.html" | 10 +- .../index.html" | 10 +- 140 files changed, 1680 insertions(+), 1256 deletions(-) create mode 100644 "2019/06/18/Flutterr\345\205\245\351\227\250/index.html" create mode 100644 tags/Flutter/index.html diff --git "a/2016/10/13/SQlite\344\275\277\347\224\250\345\255\246\344\271\240/index.html" "b/2016/10/13/SQlite\344\275\277\347\224\250\345\255\246\344\271\240/index.html" index 7b7b0d7..6aea33e 100644 --- "a/2016/10/13/SQlite\344\275\277\347\224\250\345\255\246\344\271\240/index.html" +++ "b/2016/10/13/SQlite\344\275\277\347\224\250\345\255\246\344\271\240/index.html" @@ -200,7 +200,7 @@

    - Share + Share @@ -237,7 +237,7 @@

    Tags

    @@ -247,7 +247,7 @@

    Tags

    @@ -269,7 +269,7 @@

    Recent Posts

    diff --git "a/2016/10/20/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" "b/2016/10/20/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" index a9c01b5..c1fdf73 100644 --- "a/2016/10/20/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" +++ "b/2016/10/20/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" @@ -94,7 +94,7 @@

    - Share + Share @@ -136,7 +136,7 @@

    Tags

    @@ -146,7 +146,7 @@

    Tags

    @@ -168,7 +168,7 @@

    Recent Posts

    diff --git "a/2016/10/27/iOS\345\274\271\346\241\206\345\274\217\345\233\276/index.html" "b/2016/10/27/iOS\345\274\271\346\241\206\345\274\217\345\233\276/index.html" index ba2c66e..64225e4 100644 --- "a/2016/10/27/iOS\345\274\271\346\241\206\345\274\217\345\233\276/index.html" +++ "b/2016/10/27/iOS\345\274\271\346\241\206\345\274\217\345\233\276/index.html" @@ -112,7 +112,7 @@

    - Share + Share @@ -154,7 +154,7 @@

    Tags

    @@ -164,7 +164,7 @@

    Tags

    @@ -186,7 +186,7 @@

    Recent Posts

    diff --git "a/2016/11/05/Hexo\344\270\252\344\272\272\345\215\232\345\256\242\346\220\255\345\273\272/index.html" "b/2016/11/05/Hexo\344\270\252\344\272\272\345\215\232\345\256\242\346\220\255\345\273\272/index.html" index aafaa7f..8eaef4c 100644 --- "a/2016/11/05/Hexo\344\270\252\344\272\272\345\215\232\345\256\242\346\220\255\345\273\272/index.html" +++ "b/2016/11/05/Hexo\344\270\252\344\272\272\345\215\232\345\256\242\346\220\255\345\273\272/index.html" @@ -170,7 +170,7 @@

    - Share + Share @@ -212,7 +212,7 @@

    Tags

    @@ -222,7 +222,7 @@

    Tags

    @@ -244,7 +244,7 @@

    Recent Posts

    diff --git "a/2016/11/05/TableView\344\275\277\347\224\250\346\200\273\347\273\223/index.html" "b/2016/11/05/TableView\344\275\277\347\224\250\346\200\273\347\273\223/index.html" index b6b9c0c..4f423de 100644 --- "a/2016/11/05/TableView\344\275\277\347\224\250\346\200\273\347\273\223/index.html" +++ "b/2016/11/05/TableView\344\275\277\347\224\250\346\200\273\347\273\223/index.html" @@ -157,7 +157,7 @@

    @@ -162,7 +162,7 @@

    Tags

    @@ -172,7 +172,7 @@

    Tags

    @@ -194,7 +194,7 @@

    Recent Posts

    diff --git "a/2019/06/18/JavaScript\345\237\272\347\241\200/index.html" "b/2019/06/18/JavaScript\345\237\272\347\241\200/index.html" index 9b46402..af47d65 100644 --- "a/2019/06/18/JavaScript\345\237\272\347\241\200/index.html" +++ "b/2019/06/18/JavaScript\345\237\272\347\241\200/index.html" @@ -130,7 +130,7 @@

    数组 @@ -170,7 +170,7 @@

    数组

    Tags

    @@ -180,7 +180,7 @@

    Tags

    @@ -202,7 +202,7 @@

    Recent Posts

    diff --git "a/2019/06/18/MasonrySnapkit\344\275\277\347\224\250\346\200\273\347\273\223/index.html" "b/2019/06/18/MasonrySnapkit\344\275\277\347\224\250\346\200\273\347\273\223/index.html" index bc4522d..5fe51a8 100644 --- "a/2019/06/18/MasonrySnapkit\344\275\277\347\224\250\346\200\273\347\273\223/index.html" +++ "b/2019/06/18/MasonrySnapkit\344\275\277\347\224\250\346\200\273\347\273\223/index.html" @@ -235,7 +235,7 @@

    @@ -275,7 +275,7 @@

    Tags

    @@ -285,7 +285,7 @@

    Tags

    @@ -307,7 +307,7 @@

    Recent Posts

    diff --git "a/2019/06/18/RN\346\212\245\351\224\231/index.html" "b/2019/06/18/RN\346\212\245\351\224\231/index.html" index 5385880..7bf14ab 100644 --- "a/2019/06/18/RN\346\212\245\351\224\231/index.html" +++ "b/2019/06/18/RN\346\212\245\351\224\231/index.html" @@ -144,7 +144,7 @@

    - Share + Share @@ -184,7 +184,7 @@

    Tags

    @@ -194,7 +194,7 @@

    Tags

    @@ -216,7 +216,7 @@

    Recent Posts

    diff --git "a/2019/06/18/React-Native\345\274\200\345\217\221iOS\346\211\223\345\214\205/index.html" "b/2019/06/18/React-Native\345\274\200\345\217\221iOS\346\211\223\345\214\205/index.html" index 5a42dfc..bd97942 100644 --- "a/2019/06/18/React-Native\345\274\200\345\217\221iOS\346\211\223\345\214\205/index.html" +++ "b/2019/06/18/React-Native\345\274\200\345\217\221iOS\346\211\223\345\214\205/index.html" @@ -125,7 +125,7 @@

    - Share + Share @@ -165,7 +165,7 @@

    Tags

    @@ -175,7 +175,7 @@

    Tags

    @@ -197,7 +197,7 @@

    Recent Posts

    diff --git "a/2019/06/18/ReactNative\344\270\255ref\344\275\277\347\224\250/index.html" "b/2019/06/18/ReactNative\344\270\255ref\344\275\277\347\224\250/index.html" index 6eafca7..7f93325 100644 --- "a/2019/06/18/ReactNative\344\270\255ref\344\275\277\347\224\250/index.html" +++ "b/2019/06/18/ReactNative\344\270\255ref\344\275\277\347\224\250/index.html" @@ -104,7 +104,7 @@

    - Share + Share @@ -113,7 +113,7 @@

    - + Newer
    @@ -144,7 +144,7 @@

    Tags

    @@ -154,7 +154,7 @@

    Tags

    @@ -176,7 +176,7 @@

    Recent Posts

    diff --git "a/2019/06/18/Swift \345\215\217\350\256\256\344\275\277\347\224\250/index.html" "b/2019/06/18/Swift \345\215\217\350\256\256\344\275\277\347\224\250/index.html" index 1ec2d5a..0299f0e 100644 --- "a/2019/06/18/Swift \345\215\217\350\256\256\344\275\277\347\224\250/index.html" +++ "b/2019/06/18/Swift \345\215\217\350\256\256\344\275\277\347\224\250/index.html" @@ -129,7 +129,7 @@

    - Share + Share @@ -138,7 +138,7 @@

    - + Newer
    @@ -148,7 +148,7 @@

    + Older
    @@ -169,7 +169,7 @@

    Tags

    @@ -179,7 +179,7 @@

    Tags

    @@ -201,7 +201,7 @@

    Recent Posts

    diff --git "a/2019/06/18/Swift \345\237\272\347\241\200\347\237\245\350\257\206/index.html" "b/2019/06/18/Swift \345\237\272\347\241\200\347\237\245\350\257\206/index.html" index cb716b4..f58566d 100644 --- "a/2019/06/18/Swift \345\237\272\347\241\200\347\237\245\350\257\206/index.html" +++ "b/2019/06/18/Swift \345\237\272\347\241\200\347\237\245\350\257\206/index.html" @@ -176,7 +176,7 @@

    - Share + Share @@ -185,7 +185,7 @@

    - + Newer
    @@ -195,7 +195,7 @@

    + Older
    @@ -216,7 +216,7 @@

    Tags

    @@ -226,7 +226,7 @@

    Tags

    @@ -248,7 +248,7 @@

    Recent Posts

    diff --git "a/2019/06/18/SwiftLint\344\275\277\347\224\250/index.html" "b/2019/06/18/SwiftLint\344\275\277\347\224\250/index.html" index 34be9c8..52e2189 100644 --- "a/2019/06/18/SwiftLint\344\275\277\347\224\250/index.html" +++ "b/2019/06/18/SwiftLint\344\275\277\347\224\250/index.html" @@ -82,7 +82,7 @@

    @@ -91,7 +91,7 @@

    + + + + + + + +
    + +
    + + +
    + +

    内存管理

    内存布局

    +
      +
    • stack 栈是从高地址向低地址扩展,所以栈是向下增长的,对象和block copy 后都会放在堆上
    • +
    • heap 堆区向上增长
    • +
    +
      +
    • 散列表方式

      +
        +
      • 自旋锁
          +
        • 自旋锁是“忙等” 的锁
        • +
        +
      • +
      • 引用计数表
      • +
      • 弱应用表
      • +
      +
    • +
    +

    内存管理方案

      +
    • 小对象使用 TaggedPointer
    • +
    • 64 位架构下面使用的是 NONPONINT_ISA 内存管理方案,非指针型的 isa,内部存储了一些
    • +
    • 散列表结构 (Side Tables() 结构)
        +
      • 自旋锁
      • +
      • 引用计数表
      • +
      • 弱引用表
      • +
      • Side Tables() 结构
      • +
      +
    • +
    +

    为什么不是一个SideTable,而是多个组成了Side Tables

    如果所有对象的存储都放在一张大表当中,因为每个对象是在不同的线程中创建的,要操作其中一个对象时,需要将表加锁处理保证数据安全,要等锁释放之后才能操作下一个对象,此时存在效率问题,引入分离锁技术方案

    +
      +
    • 分离锁:把引用计数表分成多张表进行,
    • +
    • Side Tables 本质是一张Hash 表
    • +
    +

    +

    自旋锁

    是“忙等”的锁,如果当前锁已被其他线程获取,当前线程会不断探测是否被释放,会第一时间获取,而其他锁比如信号量当它获取不到锁时,会把自己的线程阻塞休眠,等到其他线程释放这个锁时再唤醒这个锁

    +
      +
    • 适用于轻量访问
    • +
    +

    引用计数表

    使用过Hash表来实现,hash查找提高了查找高效率,插入和获取是通过Hash 算法来是实现的,避免了for循环

    +

    alloc 实现

    经过一系列调用,最终调用了C函数 calloc, 并没有引用计数+1

    +

    retain 实现

    底层经过了2次Hash表查找

    +

    +

    release 实现

    和 retain 实现相反

    +

    dealloc 实现原理

    +

    +

    objc_destructInstance() 实现

    +

    clearDeallocating() 实现

    +

    MRC

    手动引用计数

    +

    ARC

      +
    • 编译器在对应位置自动插入retain 和 release 操作,并和runtime协作达到自动引用计数管理内存的效果

      +
    • +
    • ARC 中禁止手动调用 retain/release

      +
    • +
    • ARC 中新增 weeak、strong 等关键字
    • +
    +

    弱引用管理

    被声明为__weak 的对象指针经过编译后会进过调用以下方法,在最终的weak_register_no_lock() 方法中进行弱引用变量的添加。添加的位置是通过Hash算法查找的

    +

    +

    weak 修饰的对象,释放时置为nil 如何实现的

    当一个对象被 dealloc 之后,在dealloc内部实现当中会调用弱引用清除的相关函数,在相关函数当中会根据当前对象指针查找弱引用表,把当前对象下对应的弱应用都取出,遍历弱应用指针并置为nil。

    +

    自动释放池

    是以栈为结点通过双向链表的形式组合而成,和线程一一对应

    +

    Autoreleasepool

    +

    循环引用

    自循环引用

    一个对象中有拥有一个 强持有他的 obj,如果给 obj 赋值为原对象就会造成自循环引用

    +

    相互循环引用

      +
    • 代理
    • +
    • Block
    • +
    • NSTimer
    • +
    • 大环多循环
    • +
    +

    多循环引用

    +

    解决循环引用的方案

    __weak

    解决相互循环引用

    +

    __block

    +

    __unsafe_unretained

    一般不建议使用

    +

    @@ -189,7 +313,7 @@

    - Share + Share @@ -199,10 +323,10 @@

    +
    @@ -211,81 +335,13 @@

    -

    UITableView 相关

      -
    • 重用机制
    • -
    • 并发访问,数据拷贝问题:主线程删除了一条数据,子线程数据由删除之前的数据源copy而来,子线程网络请求,数据解析,预排版等操作后,回到主线程刷新时已经删除的数据又会重现

      -
        -
      • 主线程数据源删除时对数据进行记录,子线程的数据回来后再进行一次同步删除操作,然后再刷新数据

        -
      • -
      • 使用GCD中的串行队列,将数据删除和数据解析在此队列中进行

        -
      • -
      -
    • -
    -

    UIView 和CALayer 的关系

      -
    • UIView 为 CALayer 提供内容,负责处理触摸事件,参与响应链
    • -
    • CALayer 负责显示内容,分工明确,UIView 的Layer 实际上就是CALayer, backgroundColor 等属性本质是对CALayer 做了一层包装
    • -
    -

    事件传递

    -
      -
    • hitTest 方法是返回响应时间的试图
    • -
    • pointInside withEvent 方法判断点击位置是否在当前视图范围内
    • -
    • 倒序遍历最终响应事件的试图,最后添加的试图会最优先被遍历到
    • -
    -

    需求案例

    -

    只让正方形的view中圆形区域内可以响应事件,四个角的位置不响应事件

    -
    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
    class CircularAreaResponseButton: UIButton {

    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
    if self.isHidden || !self.isUserInteractionEnabled || self.alpha <= 0.01 {
    return nil
    }

    var hitView: UIView?
    if self.point(inside: point, with: event) {
    // 遍历当前视图的子试图
    for subView in subviews {
    // 坐标转换
    let convertPoint = self.convert(point, to: subView)
    if let hitTestView = subView.hitTest(convertPoint, with: event) {
    // 找到了最终响应事件的试图
    hitView = hitTestView
    break;
    }
    }

    if hitView != nil {
    return hitView
    }
    return self
    }
    return nil
    }


    override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
    let x1 = point.x
    let y1 = point.y

    let x2 = self.frame.width / 2.0
    let y2 = self.frame.height / 2.0
    // 使用平面内两点见距离公式计算距离, 并且判断是否 <= 圆的半径
    return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)) <= x2
    }
    }
    -

    视图响应

    UIView 都继承于 UIResponder,都有以下方法

    -
    1
    2
    3
    4
    func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
    func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?)
    func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?)
    func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?)
    -

    如果事件沿着视图响应链一直传递到 UIApplicationDelegate 仍然没有任何一个视图处理事件,程序将会发生什么?

    -
      -
    • 忽略掉了这个事件当作什么都没有发生
    • -
    -

    图像显示原理

    CALayer 的 contents 是位图

    -

    -
      -
    • CPU的工作

      -

      Layout | Display | Prepare | Commite |
      ———|———–|————|———|
      UI布局 | 绘制 | 图片编解码 | 提交位图 |
      文本计算 |

      -
    • -
    • GPU渲染管线

      -

      定点着色 | 图元转配 | 光栅化 | 片段着色 | 片段处理 |
      ——–|——–|——-|———|———|

      -
    • -
    -
    提交到帧缓冲区(frameBuffer)
    -

    UI卡顿掉帧的原因

    1/60 ms 时间内, 由cpu 和GPU 协同产生最终的数据,当 CPU UI布局,文本计算所用的时间较长时,留给GPU渲染,提交到帧缓冲区的时间就会变短,这样以来就会发生掉帧情况

    -

    UIView 的绘制原理

    CPU优化方案

    -
      -
    • 对象的创建、调整、销毁放在子线程,节省cpu的一部分时间
    • -
    • 预排版(布局计算,文本计算)放在子线程中
    • -
    • 预渲染(文本等异步绘制,图片编解码)等
    • -
    -

    GPU优化方案

    -

    在屏渲染

    指GPU的渲染操作是在当前用于显示的屏幕缓冲区域中进行

    -

    离屏渲染

    指GPU在当前屏幕缓冲区域外新开劈了一个缓冲区域进行渲染操作
    指定了UI视图图层的某些属性,标记为视图在未预合成之前不能用于在当前屏幕上直接显示的时候就会发生离屏渲染,例如图层的圆角设置和蒙层设置

    -
      -
    • 离屏渲染何时触发

      -
        -
      • 设置layer 的圆角并且和 maskToBounds 一起使用时
      • -
      • 图层蒙版
      • -
      • 阴影
      • -
      • 光栅化
      • -
      -
    • -
    • 为何要避免离屏渲染

      -
        -
      • 离屏渲染,会增加 GPU 的工作量,GPU工作量的增加就很有可能导致CPU和GPU的总耗时超过1/60s,导致UI卡顿和掉帧
      • -
      -
    • -
    • 避免离屏渲染

      -
    • -
    -

    ###异步绘制

    -

    +

    RunLop

    +

    @@ -381,7 +437,103 @@

    + + +

    + + + +
    + +
    + + +
    + +

    UITableView 相关

      +
    • 重用机制
    • +
    • 并发访问,数据拷贝问题:主线程删除了一条数据,子线程数据由删除之前的数据源copy而来,子线程网络请求,数据解析,预排版等操作后,回到主线程刷新时已经删除的数据又会重现

      +
        +
      • 主线程数据源删除时对数据进行记录,子线程的数据回来后再进行一次同步删除操作,然后再刷新数据

        +
      • +
      • 使用GCD中的串行队列,将数据删除和数据解析在此队列中进行

        +
      • +
      +
    • +
    +

    UIView 和CALayer 的关系

      +
    • UIView 为 CALayer 提供内容,负责处理触摸事件,参与响应链
    • +
    • CALayer 负责显示内容,分工明确,UIView 的Layer 实际上就是CALayer, backgroundColor 等属性本质是对CALayer 做了一层包装
    • +
    +

    事件传递

    +
      +
    • hitTest 方法是返回响应时间的试图
    • +
    • pointInside withEvent 方法判断点击位置是否在当前视图范围内
    • +
    • 倒序遍历最终响应事件的试图,最后添加的试图会最优先被遍历到
    • +
    +

    需求案例

    +

    只让正方形的view中圆形区域内可以响应事件,四个角的位置不响应事件

    +
    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
    class CircularAreaResponseButton: UIButton {

    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
    if self.isHidden || !self.isUserInteractionEnabled || self.alpha <= 0.01 {
    return nil
    }

    var hitView: UIView?
    if self.point(inside: point, with: event) {
    // 遍历当前视图的子试图
    for subView in subviews {
    // 坐标转换
    let convertPoint = self.convert(point, to: subView)
    if let hitTestView = subView.hitTest(convertPoint, with: event) {
    // 找到了最终响应事件的试图
    hitView = hitTestView
    break;
    }
    }

    if hitView != nil {
    return hitView
    }
    return self
    }
    return nil
    }


    override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
    let x1 = point.x
    let y1 = point.y

    let x2 = self.frame.width / 2.0
    let y2 = self.frame.height / 2.0
    // 使用平面内两点见距离公式计算距离, 并且判断是否 <= 圆的半径
    return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)) <= x2
    }
    }
    +

    视图响应

    UIView 都继承于 UIResponder,都有以下方法

    +
    1
    2
    3
    4
    func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
    func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?)
    func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?)
    func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?)
    +

    如果事件沿着视图响应链一直传递到 UIApplicationDelegate 仍然没有任何一个视图处理事件,程序将会发生什么?

    +
      +
    • 忽略掉了这个事件当作什么都没有发生
    • +
    +

    图像显示原理

    CALayer 的 contents 是位图

    +

    +
      +
    • CPU的工作

      +

      Layout | Display | Prepare | Commite |
      ———|———–|————|———|
      UI布局 | 绘制 | 图片编解码 | 提交位图 |
      文本计算 |

      +
    • +
    • GPU渲染管线

      +

      定点着色 | 图元转配 | 光栅化 | 片段着色 | 片段处理 |
      ——–|——–|——-|———|———|

      +
    • +
    +
    提交到帧缓冲区(frameBuffer)
    +

    UI卡顿掉帧的原因

    1/60 ms 时间内, 由cpu 和GPU 协同产生最终的数据,当 CPU UI布局,文本计算所用的时间较长时,留给GPU渲染,提交到帧缓冲区的时间就会变短,这样以来就会发生掉帧情况

    +

    UIView 的绘制原理

    CPU优化方案

    +
      +
    • 对象的创建、调整、销毁放在子线程,节省cpu的一部分时间
    • +
    • 预排版(布局计算,文本计算)放在子线程中
    • +
    • 预渲染(文本等异步绘制,图片编解码)等
    • +
    +

    GPU优化方案

    +

    在屏渲染

    指GPU的渲染操作是在当前用于显示的屏幕缓冲区域中进行

    +

    离屏渲染

    指GPU在当前屏幕缓冲区域外新开劈了一个缓冲区域进行渲染操作
    指定了UI视图图层的某些属性,标记为视图在未预合成之前不能用于在当前屏幕上直接显示的时候就会发生离屏渲染,例如图层的圆角设置和蒙层设置

    +
      +
    • 离屏渲染何时触发

      +
        +
      • 设置layer 的圆角并且和 maskToBounds 一起使用时
      • +
      • 图层蒙版
      • +
      • 阴影
      • +
      • 光栅化
      • +
      +
    • +
    • 为何要避免离屏渲染

      +
        +
      • 离屏渲染,会增加 GPU 的工作量,GPU工作量的增加就很有可能导致CPU和GPU的总耗时超过1/60s,导致UI卡顿和掉帧
      • +
      +
    • +
    • 避免离屏渲染

      +
    • +
    +

    ###异步绘制

    +

    + + +
    + @@ -439,7 +591,7 @@

    @@ -476,7 +628,7 @@

    - Share + Share @@ -532,434 +684,7 @@

    - Share - - - -

    - -
    - - - -
    - -
    - - -
    - -

    Swift中内存管理

    -

    强引用循环和 weak 关键字

    房东

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class Landlord {
    var name: String
    var house: House?

    init(name: String) {
    self.name = name
    }

    deinit {
    print("Landlord内存释放")
    }
    }
    -

    房子

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class House {
    var name: String
    var landlord: Landlord?

    init(person: String) {
    self.name = person
    }

    deinit {
    print("House内存释放")
    }
    }
    -

    循环引用

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    func strongRetain() {
    var landlord: Landlord? = Landlord(name: "老李")
    var house: House? = House(person: "望京府邸")
    // 房东有个房子
    landlord?.house = house
    // 房子有个主人
    house?.landlord = landlord

    landlord = nil
    house = nil
    }
    -
      -
    • 虽然 landlord = nil 和 house = nil,但是 Landlord 和 House 内存空间并没有释放 (并未执行 deinit 函数)

      -
    • -
    • 原因:因为两个内存空间相互引用,引用计数不为0,形成了强循环引用

      -
    • -
    • 解决办法:使用weak 关键字来修饰两者中其中一个变量

      -
        -
      • 使用weak修饰的变量必须是可选类型,可以赋值为nil
      • -
      • 使用weak修饰必须是var 类型变量
      • -
      -
    • -
    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class House {
    var name: String
    weak var landlord: Landlord?

    init(person: String) {
    self.name = person
    }

    deinit {
    print("House内存释放")
    }
    }
    -

    unowned 关键字的使用

    相互循环引用的两个变量有一个是可选的

    有个身份证的类,每个身份证肯定对应一个人

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    class ID {
    var number: String
    let person: Landlord

    init(person: Landlord, number: String) {
    self.person = person
    self.number = number
    }

    deinit {
    print("ID内存释放")
    }
    }

    ```

    房东
    -

    class Landlord {
    var name: String
    var house: House?
    /// 身份证可以为nil,
    var id: ID?

    -
    init(name: String) {
    -    self.name = name
    -}
    -
    -deinit {
    -    print("Landlord内存释放")
    -}
    -

    }

    -
    1
    2

    循环引用
    -

    func unownedTest() {
    var person: Landlord? = Landlord(name: “老王”)
    var id: ID? = ID(person: person!, number: “10010”)

    -
    person?.id = id
    -person = nil
    -id = nil
    -

    }

    -
    1
    2
    3
    4
    5
    6
    7
    8

    - person 和 id 之间有强引用关系
    - 每个id必须有一个对应的人并且是固定的只能使用let 修饰,不能为nil, 而id可以为空
    - unowned和 weak 作用相同都是弱引用,只是unowned 修改的变量不能是nil (可选型的)
    - unowned只能使用在 class 类型上,不能修饰函数类型,例如闭包存在循环引用时不能使用unowned修饰
    - 解决方法:
    - 此时可以通过使用 weak 修饰 id 的方式,因为 id 可以为nil
    - let 修饰并且不能为nil 这种情况可以使用 unowned 修饰解决
    -
    class ID {
    -    var number: String
    -    unowned let person: Landlord
    -
    -    init(person: Landlord, number: String) {
    -        self.person = person
    -        self.number = number
    -    }
    -
    -    deinit {
    -        print("ID内存释放")
    -    }
    -}
    -
    1
    2
    3
    4
    	
    **unowned有一定的危险性**

    因为unowned 修饰的对象不能赋值为nil,所以执行下面的代码会crash,
    -

    func unownedTest() {
    var person: Landlord? = Landlord(name: “老王”)
    var id: ID? = ID(person: person!, number: “10010”)

    -
    person?.id = id
    -
    -// 提前释放 person 内存
    -person = nil
    -
    -// 获取身份证对应的人
    -print(id?.person) // 此处奔溃,
    -let owner = id?.person
    -id = nil
    -

    }

    1
    2

    正确的写法因该是先将🆔设置为nil,再将 Person 设置为nil,再调用 print(id?.person) 就不会再报错

    -

    func unownedTest() {
    var person: Landlord? = Landlord(name: “老王”)
    var id: ID? = ID(person: person!, number: “10010”)

    -
    person?.id = id
    -
    -id = nil
    -
    -// 提前释放 person 内存
    -person = nil
    -
    -// 获取身份证对应的人
    -print(id?.person) // 此处奔溃,
    -let owner = id?.person
    -

    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

    #### 相互循环引用的两个变量都不是可选的

    使用隐式可选类型

    ### 闭包中的强引用循环

    - 并不是所有的闭包都会产生循环引用,只有相互引用的两个对象会产生循环引用

    - 使用swift闭包捕获列表,在闭包的声明参数之前使用 [unowned self], 不需要解包

    - 使用 unowned表示这个对象不能为空,可能存在风险,如果对象为空就会crash


    使用 unowned 对象不能为空,可能存在风险

    -

    class Closure {
    var closure: ((Int) -> Void)?

    -
    init() {
    -    closure = { [unowned self] index in
    -        print(self)
    -        print(index)
    -    }
    -}
    -

    }

    1
    2

    使用 weak, 此时需要解包

    -

    closure = { [weak self] index in
    // 解包
    if let self = self {
    print(self)
    print(index)
    }
    }

    1
    2

    强制解包

    -

    closure = { [weak self] index in
    // 强制解包
    print(self!)
    print(index)
    }
    `

    - - -
    - -
    - -
    - - - -
    - -
    - - -
    - -

    ReactNative 中使用 TypeScript

    -

    从RN0.57版本开始已经可以直接支持typescript,无需任何配置。在保留根入口文件index.js的前提下,其他文件都可以直接使用.ts或.tsx后缀。但这一由babel进行的转码过程并不进行实际的类型检查,因此仍然需要编辑器和插件来共同进行类型检查

    -
    -

    TypeScript和JavaScript对比

    两者深度对比

    -

    最大的区别:

    -
      -
    • TypeScript是强类型语⾔和静态类型语⾔(需要指定类型)
    • -
    • JavaScript是弱类型语⾔和动态类型语⾔(不需要指定类型)
    • -
    -

    TypeScript

    由微软开发的自由和开源的编程语言,是JavaScript的一个严格超集,并添加了可选的静态类型和基于类的面向对象编程

    -

    TypeScript定义

    -

    安装及语法

    教程

    -
    安装
    1
    npm install -g typescript
    -
    安装 typescript 依赖管理器 typings
    1
    npm install -g typings
    -
    安装 typings 依赖
    1
    2
    typings install npm~react --save
    typings install dt~react-native --globals --save
    -
    使用 yarn 安装

    如果已经安装了 yarn 安装如下:

    -
    1
    2
    yarn add tslib @types/react @types/react-native
    yarn add --dev react-native-typescript-transformer typescript
    -
    创建 tsconfig.json 配置文件
      -
    • cd 到 ReactNative 项目根文件夹下

      -
      1
      tsc --init
      -
    • -
    • 修改 tsconfig.json 文件

      -

      如果一个目录下存在一个tsconfig.json文件,那么它意味着这个目录是TypeScript项目的根目录。 tsconfig.json文件中指定了用来编译这个项目的根文件和编译选项

      -

      tsconfig.json文件配置详解

      -

      TypeScript配置文件tsconfig简析

      -

      务必设置”jsx”:”react”
      注意多余的注释可能会不兼容,需要移除

      -
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      {
      "compilerOptions": {
      "target": "es6",
      "allowJs": true,
      "jsx": "react",
      "outDir": "./dist",
      "sourceMap": true,
      "noImplicitAny": false
      },

      // "files"属性 :指定一个包含相对或绝对文件路径的列表。 "include"和"exclude"属性指定一个文件glob匹配模式列表

      "include": [
      "typings/**/*.d.ts",
      "src/**/*.ts",
      "src/**/*.tsx"
      ],
      "exclude": [
      "node_modules"
      ]
      }
      -
    • -
    • cd 到项目根目录中新建存放 typescripe 源代码的文件夹

      -
    • -
    -
    建立 rn-cli.config.js 文件,使用支持typescript的transfomer
    1
    2
    3
    4
    5
    6
    7
    8
    module.exports = {
    getTransformModulePath() {
    return require.resolve('react-native-typescript-transformer');
    },
    getSourceExts() {
    return ['ts', 'tsx'];
    }
    }
    -
    修改 .babelrc 文件
    1
    2
    3
    4
    {
    "presets": ["react-native"],
    "plugins": ["transform-decorators-legacy"]
    }
    -
    修改项目中文件导入
      -
    • 入口index.jsApp.js的文件名请不要修改,项目根目录下新建src文件夹
    • -
    • src文件夹下新建 .tsx 后缀的文件
    • -
    • .tsx 后缀的文件引用react的写法有所区别

      -
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      import * as React from 'react'
      import { Text, View } from 'react-native';
      import { Component } from 'react';

      export default class HelloWorld extends Component{
      render() {
      return (
      <View>
      <Text>Hello!</Text>
      <Text>Typescript</Text>
      </View>
      );
      }
      }
      -
    • -
    • App.js中使用

      -
        -
      • App.js 导入方式修改为如下方式

        -
        1
        2
        3
        4
        	import './src';
        ```

        - 导入 .tsx 类型的组建
        -

        import HelloWorld from “./src/HelloWorld”;

        -
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11

        ### 配置jsconfig

        `jsconfig.json`目录中存在文件表明该目录是JavaScript项目的根目录。该jsconfig.json文件指定根文件和JavaScript语言服务提供的功能选项

        [jsconfig详解](https://code.visualstudio.com/docs/languages/jsconfig)

        [jsconfig.json](https://jeasonstudio.gitbooks.io/vscode-cn-doc/content/md/%E8%AF%AD%E8%A8%80/javascript.html?q=)


        ##### 新建 `jsconfig.json` 文件
        -
      • -
      -
    • -
    -

    touch jsconfig.json

    1
    2

    ##### 编辑 `jsconfig.json` 文件

    -

    {
    “compilerOptions”: {
    “allowJs”: true,
    “allowSyntheticDefaultImports”: true,
    “emitDecoratorMetadata”: true
    },
    “exclude”: [
    “node_modules”,
    “rnApp/Tools” // 项目根目录下新建的存放RN中所用的一些工具类
    ]
    }

    -
    1
    2
    3


    ### package.json 文件配置
    -

    {
    “name”: “RNProject”,
    “version”: “0.0.1”,
    “private”: true,
    “scripts”: { // 通过设置这个可以使npm调用一些命令脚本,封装一些功能
    “start”: “node node_modules/react-native/local-cli/cli.js start”,
    “test”: “jest”
    },
    “dependencies”: { // 发布后依赖的包
    “@types/react”: “^16.4.16”,
    “@types/react-native”: “^0.57.4”,
    “react”: “16.5.0”,
    “react-native”: “0.57.2”,
    “react-transform-hmr”: “^1.0.4”,
    “tslib”: “^1.9.3”
    },

    -

    “devDependencies”: { // 开发中依赖的其它包
    “babel-jest”: “23.6.0”,
    “jest”: “23.6.0”,
    “metro-react-native-babel-preset”: “0.48.0”,
    “react-native-typescript-transformer”: “^1.2.10”,
    “react-test-renderer”: “16.5.0”,
    “typescript”: “^3.1.2”
    },

    -

    “jest”: { // JavaScript 的单元测试框架
    “preset”: “react-native”
    }
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9

    ##### `–save` 和 `-–save-dev` 区别

    - --save参数表示将该模块写入dependencies属性,
    - --save-dev表示将该模块写入devDependencies属性。

    ##### 添加开发依赖库

    - 类型检查

    -
    npm install @types/jest --save-dev
    -npm install @types/react --save-dev
    -npm install @types/react-native --save-dev 
    -
    -
    1
    - 装饰器转化核心插件
    - -npm install babel-plugin-transform-decorators-legacy --save-dev -
    1
    2
    	
    - 测试框架
    - -npm install react-addons-test-utils --save-dev -npm install react-native-mock --save-dev -
    1
    2

    ##### 删除依赖库
    -

    npm uninstall @types/jest –save

    1
    2


    -

    npm uninstall @types/jest –save-dev

    1
    2

    ##### 彻底删除,-d 表示 devDependencies, -O 表示 optionalDependencies 中

    -

    npm uninstall -s -D -O @types/jest

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    ### 报错汇总

    ##### 错误1 `Cannot find module 'react-transform-hmr/lib/index.js'`

    [解决1](https://stackoverflow.com/questions/50142561/cannot-find-module-react-transform-hmr-lib-index-js?noredirect=1&lq=1)

    [解决2](https://github.com/facebook/react-native/issues/21530)

    - 安装 `react-transform-hmr`
    -
    npm install react-transform-hmr --save
    -
    1
    2

    - 清除缓存重新启动服务
    - -npm start --reset-cache -``` -
    - -
    - -
    - -
    - - - -
    - -
    - - -
    - -

    《52个方法-第一章》

    -

    一、OC起源

      -
    • OC使用的是“消息结构”而不是“函数调用”
    • -
    • 两者区别:
        -
      • 消息结构:在运行时所执行的代码由运行环境来决定,无论是否多态总会在运行时才会去查找所要执行的方法
        1
        2
        NSObject *objc = [NSObject new];
        [objc performWith: parameter1 and: parameter2];
        -
      • -
      -
    • -
    -
    - 函数调用:由编译器决定,如果函数多态,编译时就要查处到底应该执行那个函数实现
    -
    -
    1
    2
    Object *objc = new Object;
    objc -> perform(parameter1, parameter2);
    -
      -
    • 消息结构中编译器不会关心接收消息的对象是何种类型,接收消息的对象问题也要在运行时处理,此过程叫做“动态绑定”
    • -
    -

    要点:

    -
      -
    1. OC语言使用的动态绑定的消息结构,在运行时所执行的代码由运行环境来决定,无论是否多态总会在运行时才会去查找所要执行的方法,
    2. -
    3. OC 中对象总是分配在“堆空间”,而绝不会分配在”栈“上,不带 “*”的变量或者结构体等可能会使用“栈空间”
    4. -
    -

    二、类头文件尽量少导入其他头文件

    要点:

    -
      -
    1. 除非必要,否则不要引入头文件,使用向前声明降低类之间的耦合
    2. -
    3. 有时无法使用向前声明,应该把该类遵守协议的声明放到“class-continuation分类”(匿名分类)中。
    4. -
    5. 如果“class-continuation分类”也不行的话,就把协议单独放在一个文件中,再将其引入### 三、多用字面量语法,少用与之等价的方法
    6. -
    -

    三、多用字面量语法,少用与之等价的方法

    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
    - (void)tempMethond {
    // OC1.0 起,支持简单方式创建基本类型对象

    // - 使用字符串字面量:简介易读
    NSString *someString = @"Effective Objective-C 2.0";
    // 传统方式
    NSString *someStrongOld = [NSString stringWithFormat:@"Effective Objective-C 2.0"];


    // - 字面数值
    NSNumber *someNumber = @1;
    NSNumber *floatNumber = @2.5f;
    NSNumber *doubleNumber = @3.1415926;
    NSNumber *boolNumber = @YES;
    NSNumber *charNumber = @'a';
    // 字面量语法用于表达式
    int x = 5;
    float y = 6.23f;
    NSNumber *expressionNumber = @(x * y);


    // - 字面量数组
    NSArray *animals = @[@"cat", @"dog", @"mouse", @"badger"];
    NSString *dog = animals[1];

    // arrayWithObjects 和 字面量 的区别:
    // arrayWithObjects 方法依次处理各个参数,直到发现nil为止
    // 字面量数组遇到nil直接抛出异常
    NSArray *animalsOld = [NSArray arrayWithObjects:@"cat", @"dog", @"mouse", @"badger", nil];
    NSString *dogOld = [animalsOld objectAtIndex:1];


    // - 字面量字典
    // 如果有nil直接抛出异常
    NSDictionary *personData = @{@"firtName": @"Matt",
    @"lastName": @"Galloway",
    @"age": @28};
    NSString *lastName = personData[@"lastName"];

    // dictionaryWithObjectsAndKeys 方法依次处理各个参数,直到发现nil为止
    NSDictionary *personDataOld = [NSDictionary dictionaryWithObjectsAndKeys:@"Matt",@"firtName",
    @"Galloway", @"lastName",
    [NSNumber numberWithInteger:28], @"age", nil];

    NSString *lastNameOld = [personData objectForKey:@"lastName"];


    // - 可变数组和字典
    NSMutableArray *mutableArray = animals.mutableCopy;
    mutableArray[1] = @"dog";

    NSMutableDictionary *mutableDict = [personData mutableCopy];
    mutableDict[@"lastName"] = @"Galloway";
    }
    -

    要点:

    -
      -
    1. 使用字面量语法创建字符串,数值,数组,字典简明扼要
    2. -
    3. 通过下标操作获取数组,字典对应的元素
    4. -
    5. 使用字面量创建数组,字典时若有nil ,会抛出异常,确保值里不含nil### 五、用枚举表示状态、选项、状态码
    6. -
    -

    四、多用类型常量,少用#define预处理指令

    宏定义
      -
    • 缺点:
        -
      • 可以把所有相同的字符串值都替换为定义的值,
          -
        • 定义出来的常量没有具体的类型信息
        • -
        • 如果有人重新定义了宏定义,不会有警告或者提醒,导致程序中值不一样
        • -
        -
      • -
      -
    • -
    -
    类型常量
      -
    • 命名规则:

      -
        -
      • 如果只是在实现文件.m 中使用,则在前面加字母k
      • -
      • 如果此常量在类之外也可见,则以类名为前缀,(第19条详细了解命名习惯)
      • -
      -
    • -
    • 声明位置:

      -
        -
      • 总是喜欢在头文件中声明宏定义,可能和常量名冲突,
      • -
      • 所有引入这个头文件的类都会出现宏定义或者常量,相当于声明了一个名叫kAnimationDuration的全局变量
      • -
      • 应该加上前缀,表明其所属的具体类,例如:EOCViewClassAnimationDuration
      • -
      -
    • -
    • 常量从右往左解读依次是: 具体类型,不可修改

      -
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      	// const 修饰的是常量不希望别人修改, NSString * 指向NSString对象
      extern NSString *const EOCStringConstant;


      // 两者写法都可以
      extern const NSTimeInterval EOCAnimatedViewAnimationDuration;
      extern NSTimeInterval const EOCAnimatedViewAnimationDuration1;
      ```

      - 如果不打算公开常量,可以定义在 .m 实现文件里
      -

      static const NSTimeInterval kAnimationDuration = 0.3;

      -
      1
      2
      3

      - static关键字
      - static 修饰意味着该变量仅在定义此变量的编译单元(实现文件.m)中可见,作用域就是当前.m文件
      -
      static const NSTimeInterval kAnimationDuration = 0.3;
      -
      1
      2
      3
      - 如果需要声明一个外部可见的常值变量,需要在头文件中声明并使用 extern 关键词修饰 "extern 

      在 .h 文件中
      - -extern NSString *const EOCStringConstant; - -// 两者写法都可以 -extern const NSTimeInterval EOCAnimatedViewAnimationDuration; -extern NSTimeInterval const EOCAnimatedViewAnimationDuration1; -
      1
      2

      在 .m 文件中
      - -NSString *const EOCStringConstant = @"VALUE"; -const NSTimeInterval EOCAnimatedViewAnimationDuration = 0.3; -NSTimeInterval const EOCAnimatedViewAnimationDuration1 = 0.3; -
      1
      2
      3
      4
      5
      6
      7
      8
      9

      **要点:**

      1. 定义常量时static const定义编译单元(实现文件.m)中可见的常量,无需为其名称加前缀
      2. 在头文件中使用extern 声明全局常量,并在实现文件中定义其值,这种常量要出现在全局符号表中,所以加相关类作为前缀

      ### 五、用枚举表示状态、选项、状态码

      - 普通枚举
      -

      enum EOCConnectionState {

      -
      EOCConnectionStateDisconnected, // 断开连接
      -EOCConnectionStateConnecting,   // 连接中...
      -EOCConnectionStateConnected,    // 已连接
      -

      };

      -
      1
      普通枚举如果要使用如下:
      -

      enum EOCConnectionState status = EOCConnectionStateConnected;

      -
      1
      2

      如果每次都不需要输入enum关键字,只需要typedef关键字重新定义枚举类型
      -

      EOCConnectionState status1 = EOCConnectionStateConnecting;

      -
      1
      2

      - 设置枚举的初始值
      -

      enum EOCConnectionStateConnectionState {

      -
      EOCConnectionStateConnectionStateDisconnected = 1, // 断开连接
      -EOCConnectionStateConnectionStateConnecting,   // 连接中...
      -EOCConnectionStateConnectionStateConnected,    // 已连接
      -

      };

      -
      1
      2
      3
      4
      5

      - 可选枚举

      - << 位操作符(左移), 表示往左移动N位,使用 位运算符 | (或)来组合使用
      - 使用位运算符 &(与) 判断是否开启了某个选项
      -

      enum UIViewAutoresizing {

      -
      UIViewAutoresizingNone                 = 0,
      -UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,
      -UIViewAutoresizingFlexibleWidth        = 1 << 1,
      -UIViewAutoresizingFlexibleRightMargin  = 1 << 2,
      -UIViewAutoresizingFlexibleTopMargin    = 1 << 3,
      -UIViewAutoresizingFlexibleHeight       = 1 << 4,
      -UIViewAutoresizingFlexibleBottomMargin = 1 << 5
      -

      }

      -
      1
      2
      3
      4

      - 使用宏定义枚举

      普通枚举类型(老式语法定义枚举)
      -

      typedef enum : NSUInteger {

      -
      EOCConnectionState2DisConnected,
      -EOCConnectionState2DisConnecting,
      -EOCConnectionState2Connected,
      -

      } EOCConnectionState2;

      -
      1
      2

      NS_ENUM 宏定义的枚举展开后:
      -

      typedef enum EOCConnectionState_3 : NSUInteger EOCConnectionState_3;
      enum EOCConnectionState_3 : NSUInteger {

      -
      EOCConnectionState_3DisConnected,
      -EOCConnectionState_3DisConnecting,
      -EOCConnectionState_3Connected,
      -

      };

      -
      1
      2

      - 定义新特性枚举
      -

      typedef NS_ENUM(NSUInteger, EOCConnectionState3) {

      -
      EOCConnectionState3DisConnected,
      -EOCConnectionState3DisConnecting,
      -EOCConnectionState3Connected,
      -

      };

      -
      1
      2
      3
      4

      - 枚举使用

      .h文件
      -

      #import <UIKit/UIKit.h>

      -

      typedef enum EOCConnectionState EOCConnectionState;

      -

      typedef NS_ENUM(NSUInteger, EOCConnectionState3) {

      -
      EOCConnectionState3DisConnected,
      -EOCConnectionState3DisConnecting,
      -EOCConnectionState3Connected,
      -

      };

      -
    • -
    -
    @interface EOCEnum : NSObject
    -@property (nonatomic, assign) enum EOCConnectionState state;
    -@property (nonatomic, assign) EOCConnectionState3 state3;
    -@end
    -
    -
    1
    2

    .m 文件中
    - -#import "EOCEnum.h" - -@implementation EOCEnum - -- (void)testEnum { - // 未使用typedef 关键字 - enum EOCConnectionState status = EOCConnectionStateConnected; - NSLog(@"%u", status); - - // 使用typedef 关键字 - EOCConnectionState status1 = EOCConnectionStateConnecting; - NSLog(@"%u", status1); - - UIViewAutoresizing resizing = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - if (resizing & UIViewAutoresizingFlexibleWidth) { - // 设置了 UIViewAutoresizingFlexibleWidth 约束 - } -} - -// UIKit 试图所支持的设置显示方向 -- (UIInterfaceOrientationMask)supportedInterfaceOrientations { - return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft; -} - -// 枚举使用 -- (void)userEnum { - switch (_state3) { - case EOCConnectionState3DisConnected: - // ... - break; - - case EOCConnectionState3DisConnecting: - // ... - break; - - case EOCConnectionState3Connected: - // ... - break; - - default: // 最好不用,防止以后增加一种状态机之后,编译器不发出警告 - break; - } -} - -@end -``` -

    要点:

    -
      -
    1. 使用枚举表示状态机的状态、传递给方法的选项以及状态码等值,给枚举值起名时要注重易懂
    2. -
    3. 如果一个类型可以使用状态机的状态来表示,并且多个选项可同时使用,那么就定义为可选的枚举类型,枚举各值定义为2的幂,以便通过“按位或”操作组合
    4. -
    5. 使用NS_ENUM 和 NS_OPTIONS 宏来定义枚举,并指明底层的数据类型。这样可以确保枚举是用开发者所选的底层数据类型实现出来的,而不会采用编译器所选的类型
    6. -
    7. switch语句处理枚举类型时不要实现default 分支,如果以后加入枚举的新状态之后,编译器会发出警告
    8. -
    - - -
    - @@ -973,7 +698,7 @@
    - 12348 + 12349 @@ -1019,23 +744,23 @@

    Recent Posts

    diff --git a/page/3/index.html b/page/3/index.html index 3334685..e5abc36 100644 --- a/page/3/index.html +++ b/page/3/index.html @@ -62,6 +62,433 @@

    +
    + +
    + + +
    + +

    Swift中内存管理

    +

    强引用循环和 weak 关键字

    房东

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class Landlord {
    var name: String
    var house: House?

    init(name: String) {
    self.name = name
    }

    deinit {
    print("Landlord内存释放")
    }
    }
    +

    房子

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class House {
    var name: String
    var landlord: Landlord?

    init(person: String) {
    self.name = person
    }

    deinit {
    print("House内存释放")
    }
    }
    +

    循环引用

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    func strongRetain() {
    var landlord: Landlord? = Landlord(name: "老李")
    var house: House? = House(person: "望京府邸")
    // 房东有个房子
    landlord?.house = house
    // 房子有个主人
    house?.landlord = landlord

    landlord = nil
    house = nil
    }
    +
      +
    • 虽然 landlord = nil 和 house = nil,但是 Landlord 和 House 内存空间并没有释放 (并未执行 deinit 函数)

      +
    • +
    • 原因:因为两个内存空间相互引用,引用计数不为0,形成了强循环引用

      +
    • +
    • 解决办法:使用weak 关键字来修饰两者中其中一个变量

      +
        +
      • 使用weak修饰的变量必须是可选类型,可以赋值为nil
      • +
      • 使用weak修饰必须是var 类型变量
      • +
      +
    • +
    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class House {
    var name: String
    weak var landlord: Landlord?

    init(person: String) {
    self.name = person
    }

    deinit {
    print("House内存释放")
    }
    }
    +

    unowned 关键字的使用

    相互循环引用的两个变量有一个是可选的

    有个身份证的类,每个身份证肯定对应一个人

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    class ID {
    var number: String
    let person: Landlord

    init(person: Landlord, number: String) {
    self.person = person
    self.number = number
    }

    deinit {
    print("ID内存释放")
    }
    }

    ```

    房东
    +

    class Landlord {
    var name: String
    var house: House?
    /// 身份证可以为nil,
    var id: ID?

    +
    init(name: String) {
    +    self.name = name
    +}
    +
    +deinit {
    +    print("Landlord内存释放")
    +}
    +

    }

    +
    1
    2

    循环引用
    +

    func unownedTest() {
    var person: Landlord? = Landlord(name: “老王”)
    var id: ID? = ID(person: person!, number: “10010”)

    +
    person?.id = id
    +person = nil
    +id = nil
    +

    }

    +
    1
    2
    3
    4
    5
    6
    7
    8

    - person 和 id 之间有强引用关系
    - 每个id必须有一个对应的人并且是固定的只能使用let 修饰,不能为nil, 而id可以为空
    - unowned和 weak 作用相同都是弱引用,只是unowned 修改的变量不能是nil (可选型的)
    - unowned只能使用在 class 类型上,不能修饰函数类型,例如闭包存在循环引用时不能使用unowned修饰
    - 解决方法:
    - 此时可以通过使用 weak 修饰 id 的方式,因为 id 可以为nil
    - let 修饰并且不能为nil 这种情况可以使用 unowned 修饰解决
    +
    class ID {
    +    var number: String
    +    unowned let person: Landlord
    +
    +    init(person: Landlord, number: String) {
    +        self.person = person
    +        self.number = number
    +    }
    +
    +    deinit {
    +        print("ID内存释放")
    +    }
    +}
    +
    1
    2
    3
    4
    	
    **unowned有一定的危险性**

    因为unowned 修饰的对象不能赋值为nil,所以执行下面的代码会crash,
    +

    func unownedTest() {
    var person: Landlord? = Landlord(name: “老王”)
    var id: ID? = ID(person: person!, number: “10010”)

    +
    person?.id = id
    +
    +// 提前释放 person 内存
    +person = nil
    +
    +// 获取身份证对应的人
    +print(id?.person) // 此处奔溃,
    +let owner = id?.person
    +id = nil
    +

    }

    1
    2

    正确的写法因该是先将🆔设置为nil,再将 Person 设置为nil,再调用 print(id?.person) 就不会再报错

    +

    func unownedTest() {
    var person: Landlord? = Landlord(name: “老王”)
    var id: ID? = ID(person: person!, number: “10010”)

    +
    person?.id = id
    +
    +id = nil
    +
    +// 提前释放 person 内存
    +person = nil
    +
    +// 获取身份证对应的人
    +print(id?.person) // 此处奔溃,
    +let owner = id?.person
    +

    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

    #### 相互循环引用的两个变量都不是可选的

    使用隐式可选类型

    ### 闭包中的强引用循环

    - 并不是所有的闭包都会产生循环引用,只有相互引用的两个对象会产生循环引用

    - 使用swift闭包捕获列表,在闭包的声明参数之前使用 [unowned self], 不需要解包

    - 使用 unowned表示这个对象不能为空,可能存在风险,如果对象为空就会crash


    使用 unowned 对象不能为空,可能存在风险

    +

    class Closure {
    var closure: ((Int) -> Void)?

    +
    init() {
    +    closure = { [unowned self] index in
    +        print(self)
    +        print(index)
    +    }
    +}
    +

    }

    1
    2

    使用 weak, 此时需要解包

    +

    closure = { [weak self] index in
    // 解包
    if let self = self {
    print(self)
    print(index)
    }
    }

    1
    2

    强制解包

    +

    closure = { [weak self] index in
    // 强制解包
    print(self!)
    print(index)
    }
    `

    + + +
    + +
    + +
    + + + +
    + +
    + + +
    + +

    ReactNative 中使用 TypeScript

    +

    从RN0.57版本开始已经可以直接支持typescript,无需任何配置。在保留根入口文件index.js的前提下,其他文件都可以直接使用.ts或.tsx后缀。但这一由babel进行的转码过程并不进行实际的类型检查,因此仍然需要编辑器和插件来共同进行类型检查

    +
    +

    TypeScript和JavaScript对比

    两者深度对比

    +

    最大的区别:

    +
      +
    • TypeScript是强类型语⾔和静态类型语⾔(需要指定类型)
    • +
    • JavaScript是弱类型语⾔和动态类型语⾔(不需要指定类型)
    • +
    +

    TypeScript

    由微软开发的自由和开源的编程语言,是JavaScript的一个严格超集,并添加了可选的静态类型和基于类的面向对象编程

    +

    TypeScript定义

    +

    安装及语法

    教程

    +
    安装
    1
    npm install -g typescript
    +
    安装 typescript 依赖管理器 typings
    1
    npm install -g typings
    +
    安装 typings 依赖
    1
    2
    typings install npm~react --save
    typings install dt~react-native --globals --save
    +
    使用 yarn 安装

    如果已经安装了 yarn 安装如下:

    +
    1
    2
    yarn add tslib @types/react @types/react-native
    yarn add --dev react-native-typescript-transformer typescript
    +
    创建 tsconfig.json 配置文件
      +
    • cd 到 ReactNative 项目根文件夹下

      +
      1
      tsc --init
      +
    • +
    • 修改 tsconfig.json 文件

      +

      如果一个目录下存在一个tsconfig.json文件,那么它意味着这个目录是TypeScript项目的根目录。 tsconfig.json文件中指定了用来编译这个项目的根文件和编译选项

      +

      tsconfig.json文件配置详解

      +

      TypeScript配置文件tsconfig简析

      +

      务必设置”jsx”:”react”
      注意多余的注释可能会不兼容,需要移除

      +
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      {
      "compilerOptions": {
      "target": "es6",
      "allowJs": true,
      "jsx": "react",
      "outDir": "./dist",
      "sourceMap": true,
      "noImplicitAny": false
      },

      // "files"属性 :指定一个包含相对或绝对文件路径的列表。 "include"和"exclude"属性指定一个文件glob匹配模式列表

      "include": [
      "typings/**/*.d.ts",
      "src/**/*.ts",
      "src/**/*.tsx"
      ],
      "exclude": [
      "node_modules"
      ]
      }
      +
    • +
    • cd 到项目根目录中新建存放 typescripe 源代码的文件夹

      +
    • +
    +
    建立 rn-cli.config.js 文件,使用支持typescript的transfomer
    1
    2
    3
    4
    5
    6
    7
    8
    module.exports = {
    getTransformModulePath() {
    return require.resolve('react-native-typescript-transformer');
    },
    getSourceExts() {
    return ['ts', 'tsx'];
    }
    }
    +
    修改 .babelrc 文件
    1
    2
    3
    4
    {
    "presets": ["react-native"],
    "plugins": ["transform-decorators-legacy"]
    }
    +
    修改项目中文件导入
      +
    • 入口index.jsApp.js的文件名请不要修改,项目根目录下新建src文件夹
    • +
    • src文件夹下新建 .tsx 后缀的文件
    • +
    • .tsx 后缀的文件引用react的写法有所区别

      +
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      import * as React from 'react'
      import { Text, View } from 'react-native';
      import { Component } from 'react';

      export default class HelloWorld extends Component{
      render() {
      return (
      <View>
      <Text>Hello!</Text>
      <Text>Typescript</Text>
      </View>
      );
      }
      }
      +
    • +
    • App.js中使用

      +
        +
      • App.js 导入方式修改为如下方式

        +
        1
        2
        3
        4
        	import './src';
        ```

        - 导入 .tsx 类型的组建
        +

        import HelloWorld from “./src/HelloWorld”;

        +
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11

        ### 配置jsconfig

        `jsconfig.json`目录中存在文件表明该目录是JavaScript项目的根目录。该jsconfig.json文件指定根文件和JavaScript语言服务提供的功能选项

        [jsconfig详解](https://code.visualstudio.com/docs/languages/jsconfig)

        [jsconfig.json](https://jeasonstudio.gitbooks.io/vscode-cn-doc/content/md/%E8%AF%AD%E8%A8%80/javascript.html?q=)


        ##### 新建 `jsconfig.json` 文件
        +
      • +
      +
    • +
    +

    touch jsconfig.json

    1
    2

    ##### 编辑 `jsconfig.json` 文件

    +

    {
    “compilerOptions”: {
    “allowJs”: true,
    “allowSyntheticDefaultImports”: true,
    “emitDecoratorMetadata”: true
    },
    “exclude”: [
    “node_modules”,
    “rnApp/Tools” // 项目根目录下新建的存放RN中所用的一些工具类
    ]
    }

    +
    1
    2
    3


    ### package.json 文件配置
    +

    {
    “name”: “RNProject”,
    “version”: “0.0.1”,
    “private”: true,
    “scripts”: { // 通过设置这个可以使npm调用一些命令脚本,封装一些功能
    “start”: “node node_modules/react-native/local-cli/cli.js start”,
    “test”: “jest”
    },
    “dependencies”: { // 发布后依赖的包
    “@types/react”: “^16.4.16”,
    “@types/react-native”: “^0.57.4”,
    “react”: “16.5.0”,
    “react-native”: “0.57.2”,
    “react-transform-hmr”: “^1.0.4”,
    “tslib”: “^1.9.3”
    },

    +

    “devDependencies”: { // 开发中依赖的其它包
    “babel-jest”: “23.6.0”,
    “jest”: “23.6.0”,
    “metro-react-native-babel-preset”: “0.48.0”,
    “react-native-typescript-transformer”: “^1.2.10”,
    “react-test-renderer”: “16.5.0”,
    “typescript”: “^3.1.2”
    },

    +

    “jest”: { // JavaScript 的单元测试框架
    “preset”: “react-native”
    }
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9

    ##### `–save` 和 `-–save-dev` 区别

    - --save参数表示将该模块写入dependencies属性,
    - --save-dev表示将该模块写入devDependencies属性。

    ##### 添加开发依赖库

    - 类型检查

    +
    npm install @types/jest --save-dev
    +npm install @types/react --save-dev
    +npm install @types/react-native --save-dev 
    +
    +
    1
    - 装饰器转化核心插件
    + +npm install babel-plugin-transform-decorators-legacy --save-dev +
    1
    2
    	
    - 测试框架
    + +npm install react-addons-test-utils --save-dev +npm install react-native-mock --save-dev +
    1
    2

    ##### 删除依赖库
    +

    npm uninstall @types/jest –save

    1
    2


    +

    npm uninstall @types/jest –save-dev

    1
    2

    ##### 彻底删除,-d 表示 devDependencies, -O 表示 optionalDependencies 中

    +

    npm uninstall -s -D -O @types/jest

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    ### 报错汇总

    ##### 错误1 `Cannot find module 'react-transform-hmr/lib/index.js'`

    [解决1](https://stackoverflow.com/questions/50142561/cannot-find-module-react-transform-hmr-lib-index-js?noredirect=1&lq=1)

    [解决2](https://github.com/facebook/react-native/issues/21530)

    - 安装 `react-transform-hmr`
    +
    npm install react-transform-hmr --save
    +
    1
    2

    - 清除缓存重新启动服务
    + +npm start --reset-cache +``` +
    + +
    + +
    + +
    + + + +
    + +
    + + +
    + +

    《52个方法-第一章》

    +

    一、OC起源

      +
    • OC使用的是“消息结构”而不是“函数调用”
    • +
    • 两者区别:
        +
      • 消息结构:在运行时所执行的代码由运行环境来决定,无论是否多态总会在运行时才会去查找所要执行的方法
        1
        2
        NSObject *objc = [NSObject new];
        [objc performWith: parameter1 and: parameter2];
        +
      • +
      +
    • +
    +
    - 函数调用:由编译器决定,如果函数多态,编译时就要查处到底应该执行那个函数实现
    +
    +
    1
    2
    Object *objc = new Object;
    objc -> perform(parameter1, parameter2);
    +
      +
    • 消息结构中编译器不会关心接收消息的对象是何种类型,接收消息的对象问题也要在运行时处理,此过程叫做“动态绑定”
    • +
    +

    要点:

    +
      +
    1. OC语言使用的动态绑定的消息结构,在运行时所执行的代码由运行环境来决定,无论是否多态总会在运行时才会去查找所要执行的方法,
    2. +
    3. OC 中对象总是分配在“堆空间”,而绝不会分配在”栈“上,不带 “*”的变量或者结构体等可能会使用“栈空间”
    4. +
    +

    二、类头文件尽量少导入其他头文件

    要点:

    +
      +
    1. 除非必要,否则不要引入头文件,使用向前声明降低类之间的耦合
    2. +
    3. 有时无法使用向前声明,应该把该类遵守协议的声明放到“class-continuation分类”(匿名分类)中。
    4. +
    5. 如果“class-continuation分类”也不行的话,就把协议单独放在一个文件中,再将其引入### 三、多用字面量语法,少用与之等价的方法
    6. +
    +

    三、多用字面量语法,少用与之等价的方法

    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
    - (void)tempMethond {
    // OC1.0 起,支持简单方式创建基本类型对象

    // - 使用字符串字面量:简介易读
    NSString *someString = @"Effective Objective-C 2.0";
    // 传统方式
    NSString *someStrongOld = [NSString stringWithFormat:@"Effective Objective-C 2.0"];


    // - 字面数值
    NSNumber *someNumber = @1;
    NSNumber *floatNumber = @2.5f;
    NSNumber *doubleNumber = @3.1415926;
    NSNumber *boolNumber = @YES;
    NSNumber *charNumber = @'a';
    // 字面量语法用于表达式
    int x = 5;
    float y = 6.23f;
    NSNumber *expressionNumber = @(x * y);


    // - 字面量数组
    NSArray *animals = @[@"cat", @"dog", @"mouse", @"badger"];
    NSString *dog = animals[1];

    // arrayWithObjects 和 字面量 的区别:
    // arrayWithObjects 方法依次处理各个参数,直到发现nil为止
    // 字面量数组遇到nil直接抛出异常
    NSArray *animalsOld = [NSArray arrayWithObjects:@"cat", @"dog", @"mouse", @"badger", nil];
    NSString *dogOld = [animalsOld objectAtIndex:1];


    // - 字面量字典
    // 如果有nil直接抛出异常
    NSDictionary *personData = @{@"firtName": @"Matt",
    @"lastName": @"Galloway",
    @"age": @28};
    NSString *lastName = personData[@"lastName"];

    // dictionaryWithObjectsAndKeys 方法依次处理各个参数,直到发现nil为止
    NSDictionary *personDataOld = [NSDictionary dictionaryWithObjectsAndKeys:@"Matt",@"firtName",
    @"Galloway", @"lastName",
    [NSNumber numberWithInteger:28], @"age", nil];

    NSString *lastNameOld = [personData objectForKey:@"lastName"];


    // - 可变数组和字典
    NSMutableArray *mutableArray = animals.mutableCopy;
    mutableArray[1] = @"dog";

    NSMutableDictionary *mutableDict = [personData mutableCopy];
    mutableDict[@"lastName"] = @"Galloway";
    }
    +

    要点:

    +
      +
    1. 使用字面量语法创建字符串,数值,数组,字典简明扼要
    2. +
    3. 通过下标操作获取数组,字典对应的元素
    4. +
    5. 使用字面量创建数组,字典时若有nil ,会抛出异常,确保值里不含nil### 五、用枚举表示状态、选项、状态码
    6. +
    +

    四、多用类型常量,少用#define预处理指令

    宏定义
      +
    • 缺点:
        +
      • 可以把所有相同的字符串值都替换为定义的值,
          +
        • 定义出来的常量没有具体的类型信息
        • +
        • 如果有人重新定义了宏定义,不会有警告或者提醒,导致程序中值不一样
        • +
        +
      • +
      +
    • +
    +
    类型常量
      +
    • 命名规则:

      +
        +
      • 如果只是在实现文件.m 中使用,则在前面加字母k
      • +
      • 如果此常量在类之外也可见,则以类名为前缀,(第19条详细了解命名习惯)
      • +
      +
    • +
    • 声明位置:

      +
        +
      • 总是喜欢在头文件中声明宏定义,可能和常量名冲突,
      • +
      • 所有引入这个头文件的类都会出现宏定义或者常量,相当于声明了一个名叫kAnimationDuration的全局变量
      • +
      • 应该加上前缀,表明其所属的具体类,例如:EOCViewClassAnimationDuration
      • +
      +
    • +
    • 常量从右往左解读依次是: 具体类型,不可修改

      +
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      	// const 修饰的是常量不希望别人修改, NSString * 指向NSString对象
      extern NSString *const EOCStringConstant;


      // 两者写法都可以
      extern const NSTimeInterval EOCAnimatedViewAnimationDuration;
      extern NSTimeInterval const EOCAnimatedViewAnimationDuration1;
      ```

      - 如果不打算公开常量,可以定义在 .m 实现文件里
      +

      static const NSTimeInterval kAnimationDuration = 0.3;

      +
      1
      2
      3

      - static关键字
      - static 修饰意味着该变量仅在定义此变量的编译单元(实现文件.m)中可见,作用域就是当前.m文件
      +
      static const NSTimeInterval kAnimationDuration = 0.3;
      +
      1
      2
      3
      - 如果需要声明一个外部可见的常值变量,需要在头文件中声明并使用 extern 关键词修饰 "extern 

      在 .h 文件中
      + +extern NSString *const EOCStringConstant; + +// 两者写法都可以 +extern const NSTimeInterval EOCAnimatedViewAnimationDuration; +extern NSTimeInterval const EOCAnimatedViewAnimationDuration1; +
      1
      2

      在 .m 文件中
      + +NSString *const EOCStringConstant = @"VALUE"; +const NSTimeInterval EOCAnimatedViewAnimationDuration = 0.3; +NSTimeInterval const EOCAnimatedViewAnimationDuration1 = 0.3; +
      1
      2
      3
      4
      5
      6
      7
      8
      9

      **要点:**

      1. 定义常量时static const定义编译单元(实现文件.m)中可见的常量,无需为其名称加前缀
      2. 在头文件中使用extern 声明全局常量,并在实现文件中定义其值,这种常量要出现在全局符号表中,所以加相关类作为前缀

      ### 五、用枚举表示状态、选项、状态码

      - 普通枚举
      +

      enum EOCConnectionState {

      +
      EOCConnectionStateDisconnected, // 断开连接
      +EOCConnectionStateConnecting,   // 连接中...
      +EOCConnectionStateConnected,    // 已连接
      +

      };

      +
      1
      普通枚举如果要使用如下:
      +

      enum EOCConnectionState status = EOCConnectionStateConnected;

      +
      1
      2

      如果每次都不需要输入enum关键字,只需要typedef关键字重新定义枚举类型
      +

      EOCConnectionState status1 = EOCConnectionStateConnecting;

      +
      1
      2

      - 设置枚举的初始值
      +

      enum EOCConnectionStateConnectionState {

      +
      EOCConnectionStateConnectionStateDisconnected = 1, // 断开连接
      +EOCConnectionStateConnectionStateConnecting,   // 连接中...
      +EOCConnectionStateConnectionStateConnected,    // 已连接
      +

      };

      +
      1
      2
      3
      4
      5

      - 可选枚举

      - << 位操作符(左移), 表示往左移动N位,使用 位运算符 | (或)来组合使用
      - 使用位运算符 &(与) 判断是否开启了某个选项
      +

      enum UIViewAutoresizing {

      +
      UIViewAutoresizingNone                 = 0,
      +UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,
      +UIViewAutoresizingFlexibleWidth        = 1 << 1,
      +UIViewAutoresizingFlexibleRightMargin  = 1 << 2,
      +UIViewAutoresizingFlexibleTopMargin    = 1 << 3,
      +UIViewAutoresizingFlexibleHeight       = 1 << 4,
      +UIViewAutoresizingFlexibleBottomMargin = 1 << 5
      +

      }

      +
      1
      2
      3
      4

      - 使用宏定义枚举

      普通枚举类型(老式语法定义枚举)
      +

      typedef enum : NSUInteger {

      +
      EOCConnectionState2DisConnected,
      +EOCConnectionState2DisConnecting,
      +EOCConnectionState2Connected,
      +

      } EOCConnectionState2;

      +
      1
      2

      NS_ENUM 宏定义的枚举展开后:
      +

      typedef enum EOCConnectionState_3 : NSUInteger EOCConnectionState_3;
      enum EOCConnectionState_3 : NSUInteger {

      +
      EOCConnectionState_3DisConnected,
      +EOCConnectionState_3DisConnecting,
      +EOCConnectionState_3Connected,
      +

      };

      +
      1
      2

      - 定义新特性枚举
      +

      typedef NS_ENUM(NSUInteger, EOCConnectionState3) {

      +
      EOCConnectionState3DisConnected,
      +EOCConnectionState3DisConnecting,
      +EOCConnectionState3Connected,
      +

      };

      +
      1
      2
      3
      4

      - 枚举使用

      .h文件
      +

      #import <UIKit/UIKit.h>

      +

      typedef enum EOCConnectionState EOCConnectionState;

      +

      typedef NS_ENUM(NSUInteger, EOCConnectionState3) {

      +
      EOCConnectionState3DisConnected,
      +EOCConnectionState3DisConnecting,
      +EOCConnectionState3Connected,
      +

      };

      +
    • +
    +
    @interface EOCEnum : NSObject
    +@property (nonatomic, assign) enum EOCConnectionState state;
    +@property (nonatomic, assign) EOCConnectionState3 state3;
    +@end
    +
    +
    1
    2

    .m 文件中
    + +#import "EOCEnum.h" + +@implementation EOCEnum + +- (void)testEnum { + // 未使用typedef 关键字 + enum EOCConnectionState status = EOCConnectionStateConnected; + NSLog(@"%u", status); + + // 使用typedef 关键字 + EOCConnectionState status1 = EOCConnectionStateConnecting; + NSLog(@"%u", status1); + + UIViewAutoresizing resizing = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + if (resizing & UIViewAutoresizingFlexibleWidth) { + // 设置了 UIViewAutoresizingFlexibleWidth 约束 + } +} + +// UIKit 试图所支持的设置显示方向 +- (UIInterfaceOrientationMask)supportedInterfaceOrientations { + return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft; +} + +// 枚举使用 +- (void)userEnum { + switch (_state3) { + case EOCConnectionState3DisConnected: + // ... + break; + + case EOCConnectionState3DisConnecting: + // ... + break; + + case EOCConnectionState3Connected: + // ... + break; + + default: // 最好不用,防止以后增加一种状态机之后,编译器不发出警告 + break; + } +} + +@end +``` +

    要点:

    +
      +
    1. 使用枚举表示状态机的状态、传递给方法的选项以及状态码等值,给枚举值起名时要注重易懂
    2. +
    3. 如果一个类型可以使用状态机的状态来表示,并且多个选项可同时使用,那么就定义为可选的枚举类型,枚举各值定义为2的幂,以便通过“按位或”操作组合
    4. +
    5. 使用NS_ENUM 和 NS_OPTIONS 宏来定义枚举,并指明底层的数据类型。这样可以确保枚举是用开发者所选的底层数据类型实现出来的,而不会采用编译器所选的类型
    6. +
    7. switch语句处理枚举类型时不要实现default 分支,如果以后加入枚举的新状态之后,编译器会发出警告
    8. +
    + + +
    + +
    + +
    + + + - - - -
    - -
    - - -
    - -

    Swift 泛型

    泛型定义–wikipedia

      -
    1. 在程序编码中一些包含类型参数的类型,也就是说泛型的参数只可以代表类,不能代表个别对象。(这是当今较常见的定义)
    2. -
    3. 在程序编码中一些包含参数的类。其参数可以代表类或对象等等。(现在人们大多把这称作模板)
    4. -
    -

    泛型的用处

    泛型可以实现传入一个指定类型后做一些操作然后返回后还是原来指定的类型,你可以指定特定的类型(特定是指约束)

    -

    泛型和 Any 的区别

    区别:Any能代表任何类型,但是不能返回某个具体的类型,都需要使用 as 去转化,而泛型正好相反

    -
      -
    • Any 可以代表任何类型,除了class之外还可以代表struct,enum,
    • -
    • AnyObject 可以代码任何 class 的类型, swift 中基本类型, ArrayDictionary 都是 struct 类型,都不能使用 AnyObject 来接受
    • -
    -

    Swift中Any 和 AnyObject用法

    -

    泛型函数和泛型参数

    swapTwoValues是一个泛型函数,可以接收或者返回任何类型

    -
    1
    2
    3
    4
    5
    func swapTwoValues<T>(inout a: T, inout b: T) {
    let temporaryA = a
    a = b
    b = temporaryA
    }
    -
      -
    • swapTwoValues 函数名后面的 <T> 表示这个 T 类型是函数所定义的一个类型, T 只是一个类型的占位符,不用关心具体类型

      -
    • -
    • (inout a: T, inout b: T) 中a和b两个参数类型T是泛型参数

      -
    • -
    -

    类型约束

    两个泛型参数的泛型函数

    -
    1
    2
    3
    func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
    // function body goes here
    }
    -
      -
    • 泛型参数T约束条件:必须是SomeClass类的子类
    • -
    • 泛型参数U约束条件:必须遵守SomeProtocol协议
    • -
    • 使用where指定约束
    • -
    - - -
    - -
    - -
    - - - -
    - -
    - - -
    - -

    Swift 中错误处理

    -

    assert 断言

    只在debug 模式下程序会自动退出

    -
    1
    public func assert(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)
    -

    assertionFailure 断言错误

    只在debug 模式下程序会自动退出, 没有条件

    -
    1
    public func assertionFailure(_ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)
    -

    precondition

    releas 模式程序也会退出

    -
    1
    public func precondition(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)
    -

    throw

    - -
    - -
    - -
    - - - -
    - -
    - - -
    - -

    Swift协议和别名

    -

    协议

    某些属性的集合,可以有多个协议组成,具有的特点

    -
      -
    1. 可以声明函数,但是不能实现,由实现这协议的具体结构或者类来实现
    2. -
    3. 声明的函数如果有参数也不能设置参数的默认值, 默认参数被认为也是函的一种实现
    4. -
    5. 可以声明属性,必须是计算型属性 只能使用var 修饰,不能是存储型属,必须在 {} 中明确指出属性的读取类型 例如 {get set}
    6. -
    7. 遵守协议的类可选择存储型属性或者计算型属性来实现协议中定义的属性
    8. -
    9. protocol Person: class { } 表示此协议只能被类来遵守,结构体能遵守
    10. -
    11. 协议可以看着是一种类型,如果协议中的属性是只读的,如果是协议类型只能访问get 方法, 但是遵守协议的具体类或者结构可以设置属性的读和写

      -
    12. -
    13. 协议中的构造函数

      -
      - 如果父类中遵守了协议中的构造函数,子类中必须重写父类中的构造函数并且使用require修饰构造函数,表示继承此类的子类也必须实现该构造函数
      -- 如果class 使用了final 修饰,表示不能被继承,此时不需要 require 关键词修饰
      -
    14. -
    -

    类型别名

      -
    • 定义别名 typealias, 供扩展使用

      -
      1
      2
      3
      4
      5
      6
      7
      8
      typealias Length = Double
      extension Double {
      var km: Length {return self * 1_000.0}
      var m: Length {return self * 100.0}
      var cm: Length {return self / 10.0}
      /// 英尺
      var ft: Length { return self / 3.28084}
      }
      -
    • -
    -
    使用
    -
    -
    1
    let distance: Length = 10.5.km
    -
      -
    • 定义别名防止硬编码

      -
      1
      2
      	// 音频采样率
      typealias AudioSample = UInt64
      -
    • -
    -

    协议中 typealias

    协议中定义一个属性,该属性的类型根据不同的情况返回不同的类型(我理解为类似泛型),具体的类型由遵守此协议的类或者结构指定

    -
      -
    • 定义协议中别名
    • -
    -
    1
    2
    3
    4
    5
    protocol WeightCalculable {
    // 属性重量根据不同体积返回的可能是Int,可以可能是double类型,所以使用别名, 协议中使用associatedtype关键词,
    associatedtype WeightType
    var weight: WeightType { get }
    }
    -

    扩展 Int 中的吨单位

    -
    1
    2
    3
    4
    extension Int {
    typealias Weight = Int
    var t: Weight {return self * 1_000}
    }
    -
      -
    • 使用协议中别名
    • -
    -
      -
    • 一部手机的时使用该协议WeightCalculable
    • -
    -
    1
    2
    3
    4
    5
    6
    class iPhone: WeightCalculable {
    typealias WeightType = Double
    var weight: WeightType {
    return 0.5
    }
    }
    -
      -
    • 一辆大卡时使用该协议WeightCalculable
    • -
    -
    1
    2
    3
    4
    5
    6
    7
    8
    class Car: WeightCalculable {
    typealias WeightType = Int
    let weight: WeightType

    init(weight: Int) {
    self.weight = weight
    }
    }
    -
    1
    let bigCar = Car(weight: 8_000.t) // 8 吨
    -

    面向协议编程

      -
    • 定义比赛协议,具有 赢/输/总场次/胜率 等特性
    • -
    -
    1
    2
    3
    4
    5
    6
    protocol Recordable: CustomStringConvertible {
    var wins: Int { get }
    var losses: Int { get }

    func winningPerent() -> Double
    }
    -
      -
    • 协议中方法或者属性的默认实现
    • -
    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    extension Recordable {
    // CustomStringConvertible 协议默认实现
    var description: String {
    return String(format: "WINS: %d---LOSSES: %d", [wins, losses])
    }

    // 默认实现总场数计算型属性
    var totalGames: Int {
    return wins + losses
    }

    // 默认实现的方法
    func shoutWins() {
    print("come on", wins, "times!")
    }
    }
    -

    篮球比赛:

      -
    • Equatable 协议 - 相等比较 需要重载 ==
    • -
    • Comparable 协议 - 是否可比较 ,重载 < 可直接调用sort()函数
    • -
    • CustomStringConvertible 协议 - 打印信息,需重写 description
    • -
    • Recordable协议 - 比赛协议,没有平局
    • -
    -
    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
    struct BasketballRecord:Equatable, Comparable, CustomStringConvertible, Recordable {
    var wins: Int
    var losses: Int


    /// 在具体定义的类型中实现 协议中的属性或者方法就会覆盖协议中的
    var totalGames: Int = 200

    var description: String {
    return "本次比赛胜: " + String(wins) + "负" + "\(losses)"
    }

    // 胜率
    func winningPerent() -> Double {
    return (Double(wins) / Double(totalGames))
    }

    // swift4.1 后,不需要再重写此方法,swift 会自动合成,如果不想让某一个属性参与比较,就需要重写该方法
    static func == (lhs: BasketballRecord, rhs: BasketballRecord) -> Bool {
    return lhs.wins == rhs.wins && lhs.losses == rhs.losses
    }

    // Comparable
    static func < (lhs: BasketballRecord, rhs: BasketballRecord) -> Bool {
    if lhs.wins != rhs.wins {
    return lhs.wins < rhs.wins
    }
    return lhs.losses > rhs.losses
    }
    }
    ```

    #### 足球比赛:

    有平局的情况,原来的协议不能满足,需要增加一个平局的协议

    - 定义平局协议
    -

    protocol Tieable {
    /// 平局,可读写
    var ties: Int { get set }

    -

    }

    1
    2
    3
    4

    - 足球比赛需要遵守的协议 `Recordable` 和 `Tieable`

    - 总场数需要加上平局的场数,需要实现totalGames的get方法

    -

    struct FootableRecord: Recordable, Tieable {
    var wins: Int
    var losses: Int
    // 平局数
    var ties: Int

    -
    // 总场数
    -var totalGames: Int {
    -    return wins + losses + ties
    -}
    -
    -var description: String {
    -    return "本次比赛胜: " + String(wins) + "负" + "\(losses)"
    -}
    -
    -func winningPerent() -> Double {
    -    return (Double(wins) / Double(totalGames))
    -}
    -

    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    - 足球比赛中以上写法存在的问题

    - 所有有平局的比赛计算总场次的逻辑都是一样,每次都要写一次实现
    - 单一修改 Recordable 协议不能解决问题,
    - 存在平局和没有平局两种情况 totalGames 的计算逻辑不相同

    - 解决办法:
    - 扩展遵守了`Recordable`协议的类型,前提条件是:这个类型遵守了 Tieable 同时也遵守了 `Recordable ` 协议,可以理解为如果该类型遵守了 `Tieable `协议之后,`Recordable` 协议中的 totalGames 属性实现是另一种方式了,不在是以前的 `totalGames` 中直接返回 `wins + losses` 的值


    定义协议

    -

    protocol Recordable: CustomStringConvertible {
    var wins: Int { get }
    var losses: Int { get }

    -
    func winningPerent() -> Double
    -

    }

    1
    2

    协议默认实现

    -

    extension Recordable {
    var description: String {
    return String(format: “WINS: %d—LOSSES: %d”, [wins, losses])
    }

    -
    // 默认实现总场数计算型属性
    -var totalGames: Int {
    -    return wins + losses
    -}
    -
    -// 默认实现胜率
    -func winningPerent() -> Double {
    -    return (Double(wins) / Double(totalGames))
    -}
    -
    -// 默认实现的方法
    -func shoutWins() {
    -    print("come on", wins, "times!")
    -}
    -

    }

    -
    1
    2

    扩展遵守了Tieable 的 类型的 Recordable 协议
    -

    extension Recordable where Self: Tieable {
    var totalGames: Int {
    return wins + losses + ties
    }
    }

    -
    1
    2
    3
    4
    5
    6

    ### 协议聚合

    如果对某一类型需要限制他必须遵守某些协议,可以使用协议聚合来定义

    比如: 有个奖赏协议
    -

    protocol Prizable {
    func isPrizable() -> Bool
    }

    -
    1
    2

    篮球比赛遵守此协议
    -

    // 篮球比赛奖赏
    extension BasketballRecord: Prizable {
    func isPrizable() -> Bool {
    return totalGames > 10 && winningPerent() > 0.5
    }
    }

    -
    1
    2

    足球比赛奖赏遵守此协议
    -

    extension FootableRecord: Prizable {
    func isPrizable() -> Bool {
    return wins > 1
    }
    }

    1
    2

    现在有某一个学生也遵守了此协议

    -

    struct Student: Prizable {
    var name: String
    var score: Int

    -
    func isPrizable() -> Bool {
    -    return score >= 60
    -}
    -

    }

    -
    1
    2

    定义奖赏的方法,参数类型必须是遵守了此协议的结构或者类型
    -

    private func award(_ one: Prizable) {
    if one.isPrizable() {
    print(one)
    print(“恭喜获得奖励”)
    } else {
    print(one)
    print(“很遗憾”)
    }
    }

    1
    2

    如果说 BasketballRecord 这个类还遵守了其他的协议,例如遵守了 `Recordable` 协议, 并且这个协议也遵守了 `CustomStringConvertible` 并且默认实现了`description` 的`get` 方法

    -

    // MARK: - 比赛协议,具有 赢。输。总场次。胜率 特性
    protocol Recordable: CustomStringConvertible {
    var wins: Int { get }
    var losses: Int { get }

    -
    func winningPerent() -> Double
    -

    }

    -
    1
    2

    `Recordable` 默认实现
    -

    extension Recordable {
    var description: String {
    return String(format: “WINS: %d—LOSSES: %d”, [wins, losses])
    }

    -
    // 默认实现总场数计算型属性
    -var totalGames: Int {
    -    return wins + losses
    -}
    -
    -// 默认实现胜率
    -func winningPerent() -> Double {
    -    return (Double(wins) / Double(totalGames))
    -}
    -
    -// 默认实现的方法
    -func shoutWins() {
    -    print("come on", wins, "times!")
    -}
    -

    }

    1
    2
    3
    4
    5

    此时如果 `Student` 类还是调用 `award` 方法的话,print(one) 打印的信息将是`Recordable `中默认实现的内容,因此需要约束`award`函数的参数必须遵守两个协议让`Student` 也重写自己的`description `属性的`get`方法,不能再让 `Prizable` 扩展 默认实现

    - swift 3 写法: protocol<A, B>
    - swift 4 写法: A & B

    -

    private func award2(_ one: Prizable & CustomStringConvertible) {
    if one.isPrizable() {
    print(one)
    print(“恭喜获得奖励”)
    } else {
    print(one)
    print(“很遗憾”)
    }
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    ### 泛型约束


    - 定义一个函数,找出一个学生数组中分数最大的
    - 参数:一个学生数组,都遵守了 Comparable 的类型
    - 返回值:某个遵守了 Comparable 的类型实例
    - 此时函数报错 `Protocol 'Comparable' can only be used as a generic constraint because it has Self or associated type requirements`,

    因为 Comparable 协议中定义的方法 public static func < (lhs: Self, rhs: Self) -> Bool 的参数类型是Self,是具体的某个类型

    -

    func maxScore(seq: [Comparable]) -> Comparable { }

    1
    2
    3
    4
    5


    - 如果需要定义一个函数实现在一个数组中找出需要奖励的人的名字该如何实现呢
    - 参数:遵守两个协议 Comparable 和 Prizable 协议, 并且使用泛型
    - 返回值:返回值是可选值,有可能没有任何奖励的对象

    -

    func topPrizable<T: Comparable & Prizable>(seq: [T]) -> T? {
    return seq.reduce(nil) { (tempTop: T?, condender: T) in
    guard condender.isPrizable() else { return tempTop }
    // 解包 condender 失败, 上一层验证了他必须是奖励的那个
    guard let tempTop = tempTop else { return condender }
    return max(tempTop, condender)
    }
    }

    -

    `

    - - -
    - @@ -751,7 +931,7 @@

    - 123458 + 123459

    @@ -797,23 +977,23 @@

    Recent Posts

    diff --git a/page/4/index.html b/page/4/index.html index c18b59e..885b07f 100644 --- a/page/4/index.html +++ b/page/4/index.html @@ -62,6 +62,253 @@

    +
    + +
    + + +
    + +

    Swift 中错误处理

    +

    assert 断言

    只在debug 模式下程序会自动退出

    +
    1
    public func assert(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)
    +

    assertionFailure 断言错误

    只在debug 模式下程序会自动退出, 没有条件

    +
    1
    public func assertionFailure(_ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)
    +

    precondition

    releas 模式程序也会退出

    +
    1
    public func precondition(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)
    +

    throw

    + +
    + +
    + +
    + + + +
    + +
    + + +
    + +

    Swift 泛型

    泛型定义–wikipedia

      +
    1. 在程序编码中一些包含类型参数的类型,也就是说泛型的参数只可以代表类,不能代表个别对象。(这是当今较常见的定义)
    2. +
    3. 在程序编码中一些包含参数的类。其参数可以代表类或对象等等。(现在人们大多把这称作模板)
    4. +
    +

    泛型的用处

    泛型可以实现传入一个指定类型后做一些操作然后返回后还是原来指定的类型,你可以指定特定的类型(特定是指约束)

    +

    泛型和 Any 的区别

    区别:Any能代表任何类型,但是不能返回某个具体的类型,都需要使用 as 去转化,而泛型正好相反

    +
      +
    • Any 可以代表任何类型,除了class之外还可以代表struct,enum,
    • +
    • AnyObject 可以代码任何 class 的类型, swift 中基本类型, ArrayDictionary 都是 struct 类型,都不能使用 AnyObject 来接受
    • +
    +

    Swift中Any 和 AnyObject用法

    +

    泛型函数和泛型参数

    swapTwoValues是一个泛型函数,可以接收或者返回任何类型

    +
    1
    2
    3
    4
    5
    func swapTwoValues<T>(inout a: T, inout b: T) {
    let temporaryA = a
    a = b
    b = temporaryA
    }
    +
      +
    • swapTwoValues 函数名后面的 <T> 表示这个 T 类型是函数所定义的一个类型, T 只是一个类型的占位符,不用关心具体类型

      +
    • +
    • (inout a: T, inout b: T) 中a和b两个参数类型T是泛型参数

      +
    • +
    +

    类型约束

    两个泛型参数的泛型函数

    +
    1
    2
    3
    func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
    // function body goes here
    }
    +
      +
    • 泛型参数T约束条件:必须是SomeClass类的子类
    • +
    • 泛型参数U约束条件:必须遵守SomeProtocol协议
    • +
    • 使用where指定约束
    • +
    + + +
    + +
    + +
    + + + +
    + +
    + + +
    + +

    Swift协议和别名

    +

    协议

    某些属性的集合,可以有多个协议组成,具有的特点

    +
      +
    1. 可以声明函数,但是不能实现,由实现这协议的具体结构或者类来实现
    2. +
    3. 声明的函数如果有参数也不能设置参数的默认值, 默认参数被认为也是函的一种实现
    4. +
    5. 可以声明属性,必须是计算型属性 只能使用var 修饰,不能是存储型属,必须在 {} 中明确指出属性的读取类型 例如 {get set}
    6. +
    7. 遵守协议的类可选择存储型属性或者计算型属性来实现协议中定义的属性
    8. +
    9. protocol Person: class { } 表示此协议只能被类来遵守,结构体能遵守
    10. +
    11. 协议可以看着是一种类型,如果协议中的属性是只读的,如果是协议类型只能访问get 方法, 但是遵守协议的具体类或者结构可以设置属性的读和写

      +
    12. +
    13. 协议中的构造函数

      +
      - 如果父类中遵守了协议中的构造函数,子类中必须重写父类中的构造函数并且使用require修饰构造函数,表示继承此类的子类也必须实现该构造函数
      +- 如果class 使用了final 修饰,表示不能被继承,此时不需要 require 关键词修饰
      +
    14. +
    +

    类型别名

      +
    • 定义别名 typealias, 供扩展使用

      +
      1
      2
      3
      4
      5
      6
      7
      8
      typealias Length = Double
      extension Double {
      var km: Length {return self * 1_000.0}
      var m: Length {return self * 100.0}
      var cm: Length {return self / 10.0}
      /// 英尺
      var ft: Length { return self / 3.28084}
      }
      +
    • +
    +
    使用
    +
    +
    1
    let distance: Length = 10.5.km
    +
      +
    • 定义别名防止硬编码

      +
      1
      2
      	// 音频采样率
      typealias AudioSample = UInt64
      +
    • +
    +

    协议中 typealias

    协议中定义一个属性,该属性的类型根据不同的情况返回不同的类型(我理解为类似泛型),具体的类型由遵守此协议的类或者结构指定

    +
      +
    • 定义协议中别名
    • +
    +
    1
    2
    3
    4
    5
    protocol WeightCalculable {
    // 属性重量根据不同体积返回的可能是Int,可以可能是double类型,所以使用别名, 协议中使用associatedtype关键词,
    associatedtype WeightType
    var weight: WeightType { get }
    }
    +

    扩展 Int 中的吨单位

    +
    1
    2
    3
    4
    extension Int {
    typealias Weight = Int
    var t: Weight {return self * 1_000}
    }
    +
      +
    • 使用协议中别名
    • +
    +
      +
    • 一部手机的时使用该协议WeightCalculable
    • +
    +
    1
    2
    3
    4
    5
    6
    class iPhone: WeightCalculable {
    typealias WeightType = Double
    var weight: WeightType {
    return 0.5
    }
    }
    +
      +
    • 一辆大卡时使用该协议WeightCalculable
    • +
    +
    1
    2
    3
    4
    5
    6
    7
    8
    class Car: WeightCalculable {
    typealias WeightType = Int
    let weight: WeightType

    init(weight: Int) {
    self.weight = weight
    }
    }
    +
    1
    let bigCar = Car(weight: 8_000.t) // 8 吨
    +

    面向协议编程

      +
    • 定义比赛协议,具有 赢/输/总场次/胜率 等特性
    • +
    +
    1
    2
    3
    4
    5
    6
    protocol Recordable: CustomStringConvertible {
    var wins: Int { get }
    var losses: Int { get }

    func winningPerent() -> Double
    }
    +
      +
    • 协议中方法或者属性的默认实现
    • +
    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    extension Recordable {
    // CustomStringConvertible 协议默认实现
    var description: String {
    return String(format: "WINS: %d---LOSSES: %d", [wins, losses])
    }

    // 默认实现总场数计算型属性
    var totalGames: Int {
    return wins + losses
    }

    // 默认实现的方法
    func shoutWins() {
    print("come on", wins, "times!")
    }
    }
    +

    篮球比赛:

      +
    • Equatable 协议 - 相等比较 需要重载 ==
    • +
    • Comparable 协议 - 是否可比较 ,重载 < 可直接调用sort()函数
    • +
    • CustomStringConvertible 协议 - 打印信息,需重写 description
    • +
    • Recordable协议 - 比赛协议,没有平局
    • +
    +
    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
    struct BasketballRecord:Equatable, Comparable, CustomStringConvertible, Recordable {
    var wins: Int
    var losses: Int


    /// 在具体定义的类型中实现 协议中的属性或者方法就会覆盖协议中的
    var totalGames: Int = 200

    var description: String {
    return "本次比赛胜: " + String(wins) + "负" + "\(losses)"
    }

    // 胜率
    func winningPerent() -> Double {
    return (Double(wins) / Double(totalGames))
    }

    // swift4.1 后,不需要再重写此方法,swift 会自动合成,如果不想让某一个属性参与比较,就需要重写该方法
    static func == (lhs: BasketballRecord, rhs: BasketballRecord) -> Bool {
    return lhs.wins == rhs.wins && lhs.losses == rhs.losses
    }

    // Comparable
    static func < (lhs: BasketballRecord, rhs: BasketballRecord) -> Bool {
    if lhs.wins != rhs.wins {
    return lhs.wins < rhs.wins
    }
    return lhs.losses > rhs.losses
    }
    }
    ```

    #### 足球比赛:

    有平局的情况,原来的协议不能满足,需要增加一个平局的协议

    - 定义平局协议
    +

    protocol Tieable {
    /// 平局,可读写
    var ties: Int { get set }

    +

    }

    1
    2
    3
    4

    - 足球比赛需要遵守的协议 `Recordable` 和 `Tieable`

    - 总场数需要加上平局的场数,需要实现totalGames的get方法

    +

    struct FootableRecord: Recordable, Tieable {
    var wins: Int
    var losses: Int
    // 平局数
    var ties: Int

    +
    // 总场数
    +var totalGames: Int {
    +    return wins + losses + ties
    +}
    +
    +var description: String {
    +    return "本次比赛胜: " + String(wins) + "负" + "\(losses)"
    +}
    +
    +func winningPerent() -> Double {
    +    return (Double(wins) / Double(totalGames))
    +}
    +

    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    - 足球比赛中以上写法存在的问题

    - 所有有平局的比赛计算总场次的逻辑都是一样,每次都要写一次实现
    - 单一修改 Recordable 协议不能解决问题,
    - 存在平局和没有平局两种情况 totalGames 的计算逻辑不相同

    - 解决办法:
    - 扩展遵守了`Recordable`协议的类型,前提条件是:这个类型遵守了 Tieable 同时也遵守了 `Recordable ` 协议,可以理解为如果该类型遵守了 `Tieable `协议之后,`Recordable` 协议中的 totalGames 属性实现是另一种方式了,不在是以前的 `totalGames` 中直接返回 `wins + losses` 的值


    定义协议

    +

    protocol Recordable: CustomStringConvertible {
    var wins: Int { get }
    var losses: Int { get }

    +
    func winningPerent() -> Double
    +

    }

    1
    2

    协议默认实现

    +

    extension Recordable {
    var description: String {
    return String(format: “WINS: %d—LOSSES: %d”, [wins, losses])
    }

    +
    // 默认实现总场数计算型属性
    +var totalGames: Int {
    +    return wins + losses
    +}
    +
    +// 默认实现胜率
    +func winningPerent() -> Double {
    +    return (Double(wins) / Double(totalGames))
    +}
    +
    +// 默认实现的方法
    +func shoutWins() {
    +    print("come on", wins, "times!")
    +}
    +

    }

    +
    1
    2

    扩展遵守了Tieable 的 类型的 Recordable 协议
    +

    extension Recordable where Self: Tieable {
    var totalGames: Int {
    return wins + losses + ties
    }
    }

    +
    1
    2
    3
    4
    5
    6

    ### 协议聚合

    如果对某一类型需要限制他必须遵守某些协议,可以使用协议聚合来定义

    比如: 有个奖赏协议
    +

    protocol Prizable {
    func isPrizable() -> Bool
    }

    +
    1
    2

    篮球比赛遵守此协议
    +

    // 篮球比赛奖赏
    extension BasketballRecord: Prizable {
    func isPrizable() -> Bool {
    return totalGames > 10 && winningPerent() > 0.5
    }
    }

    +
    1
    2

    足球比赛奖赏遵守此协议
    +

    extension FootableRecord: Prizable {
    func isPrizable() -> Bool {
    return wins > 1
    }
    }

    1
    2

    现在有某一个学生也遵守了此协议

    +

    struct Student: Prizable {
    var name: String
    var score: Int

    +
    func isPrizable() -> Bool {
    +    return score >= 60
    +}
    +

    }

    +
    1
    2

    定义奖赏的方法,参数类型必须是遵守了此协议的结构或者类型
    +

    private func award(_ one: Prizable) {
    if one.isPrizable() {
    print(one)
    print(“恭喜获得奖励”)
    } else {
    print(one)
    print(“很遗憾”)
    }
    }

    1
    2

    如果说 BasketballRecord 这个类还遵守了其他的协议,例如遵守了 `Recordable` 协议, 并且这个协议也遵守了 `CustomStringConvertible` 并且默认实现了`description` 的`get` 方法

    +

    // MARK: - 比赛协议,具有 赢。输。总场次。胜率 特性
    protocol Recordable: CustomStringConvertible {
    var wins: Int { get }
    var losses: Int { get }

    +
    func winningPerent() -> Double
    +

    }

    +
    1
    2

    `Recordable` 默认实现
    +

    extension Recordable {
    var description: String {
    return String(format: “WINS: %d—LOSSES: %d”, [wins, losses])
    }

    +
    // 默认实现总场数计算型属性
    +var totalGames: Int {
    +    return wins + losses
    +}
    +
    +// 默认实现胜率
    +func winningPerent() -> Double {
    +    return (Double(wins) / Double(totalGames))
    +}
    +
    +// 默认实现的方法
    +func shoutWins() {
    +    print("come on", wins, "times!")
    +}
    +

    }

    1
    2
    3
    4
    5

    此时如果 `Student` 类还是调用 `award` 方法的话,print(one) 打印的信息将是`Recordable `中默认实现的内容,因此需要约束`award`函数的参数必须遵守两个协议让`Student` 也重写自己的`description `属性的`get`方法,不能再让 `Prizable` 扩展 默认实现

    - swift 3 写法: protocol<A, B>
    - swift 4 写法: A & B

    +

    private func award2(_ one: Prizable & CustomStringConvertible) {
    if one.isPrizable() {
    print(one)
    print(“恭喜获得奖励”)
    } else {
    print(one)
    print(“很遗憾”)
    }
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    ### 泛型约束


    - 定义一个函数,找出一个学生数组中分数最大的
    - 参数:一个学生数组,都遵守了 Comparable 的类型
    - 返回值:某个遵守了 Comparable 的类型实例
    - 此时函数报错 `Protocol 'Comparable' can only be used as a generic constraint because it has Self or associated type requirements`,

    因为 Comparable 协议中定义的方法 public static func < (lhs: Self, rhs: Self) -> Bool 的参数类型是Self,是具体的某个类型

    +

    func maxScore(seq: [Comparable]) -> Comparable { }

    1
    2
    3
    4
    5


    - 如果需要定义一个函数实现在一个数组中找出需要奖励的人的名字该如何实现呢
    - 参数:遵守两个协议 Comparable 和 Prizable 协议, 并且使用泛型
    - 返回值:返回值是可选值,有可能没有任何奖励的对象

    +

    func topPrizable<T: Comparable & Prizable>(seq: [T]) -> T? {
    return seq.reduce(nil) { (tempTop: T?, condender: T) in
    guard condender.isPrizable() else { return tempTop }
    // 解包 condender 失败, 上一层验证了他必须是奖励的那个
    guard let tempTop = tempTop else { return condender }
    return max(tempTop, condender)
    }
    }

    +

    `

    + + +
    + +
    + +
    + + + - - - -
    - -
    - - -
    - -

    Reat Native ref 使用

    -

    State 状态的使用

    在父控件中指定,整个生命周期讲不再改变
    需求:一段闪烁的文字, Props 属性显示文字内容创建时初始化,State控制随时间变化是否显示,在父控件中

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    class Blink extends Component{
    // 构造
    constructor(props) {
    super(props);
    // 初始状态
    this.state = {showWithText:true};
    this.state = {}
    setInterval(() => {
    this.setState(previousState => {
    return {showWithText: !previousState.showWithText}
    });
    }, 100);
    }
    render() {
    let display= this.state.showWithText ? this.props.text : ' ';
    return(
    <Text>{display}</Text>
    );
    }
    }
    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    export default class App extends Component<Props> {
    render() {
    return(
    <View style={styles.container}>
    <Blink
    text='I love to blink'
    />

    <Blink
    text='猜猜猜'
    />

    <Blink
    text='Blink is great'
    />
    </View>
    );
    }
    }
    -

    Props 属性的使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 定义一个组件
    class Greeting extends Component {
    render() {
    return(
    <Text> Hello {this.props.name}!
    </Text>
    );
    }
    }
    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    export default class App extends Component<Props> {
    render() {
    return(
    <View style={styles.container}>
    <Image source={{uri:'https://upload.wikimedia.org/wikipedia/commons/d/de/Bananavarieties.jpg'}}
    style={{width: 190, height: 110}}
    />
    <Text style={styles.instructions}>
    使用Props属性
    </Text>
    <View>
    <Greeting name='React-Native'
    />
    <Greeting name='iOS'
    />
    <Greeting name='Swift'
    />
    </View>

    </View>
    );
    }
    }
    -

    ref的使用

      -
    • 可以理解为组件渲染后指向组件的一个应用,可以通过ref获取到真实的组件(类似指针?)
    • -
    -

    定义

    -
    1
    ref='scrollView'
    -

    获取

    -
    1
    2
    let scrollView = this.refs.scrollView
    let scrollView = this.refs['scrollView']
    -
      -
    • 可以通过ref访问组件的属性和方法
    • -
    -

    ES6 定时器

      -
    • 开启定时器
    • -
    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    let scrollView = this.refs.scrollView;
    // 2.4timer 定时器的写法
    this.timer = setInterval(
    ()=>{
    var tempPage = 0;
    // 修改banner索引
    if ((this.state.currentPage+1) >= ImageData.data.length) {
    tempPage = 0;
    } else {
    tempPage = this.state.currentPage+1;
    }

    // 更新状态
    this.setState({
    currentPage: tempPage
    })

    let offSet_x = width * tempPage
    scrollView.scrollResponderScrollTo({x:offSet_x, y:0, animated: true})
    },
    this.state.duration
    );
    -
      -
    • 暂停定时器
    • -
    -
    1
    this.timer && clearInterval(this.timer)
    - - -
    - @@ -548,7 +745,7 @@

    - Share + Share @@ -617,262 +814,7 @@

    - Share - - - -

    - -
    - - - -
    - -
    - - -
    - -

    js 学习

    -

    数据类型

    1
    2
    3
    4
    5
    6
    7
    8
    var fullName;
    // undefined

    fullName
    // undefined

    fullName + 2
    // NaN
    -
      -
    • 判断类型名称

      -
      1
      2
      3
      4
      fullName = '张'
      "张"
      typeof(fullName)
      // "string"
      -
    • -
    -
    1
    2
    3
    4
    var weight = 100;
    undefined
    typeof(weight)
    // "number"
    - - - -
    1
    2
    3
    4
    var firstName = '王', lastName = '张'
    undefined
    firstName + lastName
    // "王张"
    -
      -
    • 类型不相同时转换后在拼接

      -
      1
      2
      3
      4
      var weightIncrease = '2.5斤'
      undefined
      weight + weightIncrease
      // "1002.5斤"
      -
    • -
    -

    数组

    var array = [];
    -undefined
    -array.length
    -// 0
    -typeof(array)
    -// "object"
    -
      -
    • 增加元素

      -
      1
      2
      3
      4
      array.push('1')
      // 4
      array
      // (4) ["ab", "cbd", "fcg", "1"]
      -
    • -
    • 数组前面添加

      -
    • -
    -
    1
    2
    3
    4
    array.unshift('0')
    // 6
    array
    // (6) ["0", "ab", "cbd", "fcg", "1", Array(2)]
    -
      -
    • 删除最后元素

      -
      1
      2
      3
      4
      array.pop() // 返回删除的元素
      // (2) ["2", "3"]
      array
      // (5) ["0", "ab", "cbd", "fcg", "1"]
      -
    • -
    • 删除第一个元素

      -
      1
      2
      3
      4
      5
      6
      	array.shift()
      // "0"
      array
      // (4) ["ab", "cbd", "fcg", "1"]
      ```
      - 删除指定的值,但数组元素没有被删
      -

      delete array[2]
      // true
      array
      // (4) [“ab”, “cbd”, empty, “1”]

      -
      1
      2
      	
      - 删除指定的元素
      -

      array.splice(1)
      // (3) [“cbd”, empty, “1”]
      array
      // [“ab”]

      -
      1
      2
      3
      4
      	
      ## 函数

      ### 调用调用
      -
    • -
    -

    alertMessage();

    -

    function alertMessageWithParamter(message) {
    alert(message)
    }

    -

    alertMessageWithParamter(‘有参数的函数’)
    alertMessageWithParamter(250)
    alertMessageWithParamter(
    console.log(‘test’)
    )

    -
    1
    ### 函数表达式,使用函数声明的方式
    -

    var alertMessage_expression = function expression_alert (message) {
    alert(message)
    }

    -

    alertMessage_expression(‘匿名函数调用’)

    -

    expression_alert(‘函数表达式’)
    `

    - - -
    - -
    - -
    - - - -
    - -
    - - -
    - -

    Masonry使用

    基本使用官网Demo非常详细,以下总结的是常见问题和技巧

    -

    安装遇到的问题

      -
    • pod 安装后出现⚠️,项目运行报错

      -
      1
      [UIView mas_makeConstraints:]: unrecognized selector sent to instance
      -

      github

      -

      解决方法

      -
    • -
    • 使用lastBaseline属性crash

      -

      解决办法

      -
    • -
    -

    使用时注意点

      -
    • edgesForExtendedLayout 方法

      -

      重写此方法的目的是为了确定布局位置从什么位置开始什么位置结束,

      -
      1
      2
      3
      - (UIRectEdge)edgesForExtendedLayout {
      return UIRectEdgeNone;
      }
      -
    • -
    -
    **重写后**
    -![](https://ws1.sinaimg.cn/large/006tNc79ly1fsgcn3fmo0j30s61iyalk.jpg)
    -
    -**重写前**
    -![](https://ws4.sinaimg.cn/large/006tNc79ly1fsgcokvpdwj30s61iy7eq.jpg)
    -
      -
    • translucent 设置UINavigationBar是否半透明, 如果设置了导航栏的styleUIBarStyleBlackTranslucent 就为true

      -

      设置为false不透明效果

      -
    • -
    -
      -
    • extendedLayoutIncludesOpaqueBars 导航栏不透明条件下是否可以扩展,默认是NO不可以扩展

      -
      1
      2
      self.navigationController?.navigationBar.isTranslucent = false
      self.extendedLayoutIncludesOpaqueBars = true
      -
    • -
    -
    **设置不透明,设置可以扩展此时从坐标的(0,0)点开始布局**
    -![](https://ws2.sinaimg.cn/large/006tNc79ly1fsggem13mjj30ow0j83zy.jpg)    
    -
      -
    • inset 的使用,当约束控件的属性相同时不用再考虑正负值的问题,例如要约束一个控件左边距离另一控件的右边位置时还需要使用负值情况

      -
      1
      2
      3
      4
      5
      6
      7
      [yellowdView makeConstraints:^(MASConstraintMaker *make) {
      make.height.equalTo(@150);
      make.top.equalTo(self).inset(220);
      make.leading.equalTo(self).inset(10);
      make.trailing.equalTo(greenView.leading).inset(10);
      make.width.equalTo(greenView);
      }];
      -
    • -
    • edges的使用,约束边距更方便

      -
      1
      2
      3
      [edgeView remakeConstraints:^(MASConstraintMaker *make) {
      make.edges.equalTo(lasView).insets(UIEdgeInsetsMake(10, 5, 5, 5));
      }];
      -
    • -
    -
      -
    • Priority 设置约束的优先级

      -
        -
      • priorityHigh
      • -
      • priorityMedium
      • -
      • priorityLow
      • -
      -
    • -
    • makeConstraintsupdateConstraintsremakeConstraints三者区别

      -
        -
      • makeConstraints 添加约束
      • -
      • updateConstraints 更新约束,更新之前会查找一边控件已经存在的约束,没有就添加,有就更新到最新的约束
      • -
      • remakeConstraints删除控件以前的所有约束重新添加约束
      • -
      -
    • -
    • 设置控件或者某一约束的’key’ 方便冲突时⚠️查找

      -
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      	greenView.mas_key = @"greenView";
      [greenView makeConstraints:^(MASConstraintMaker *make) {
      make.height.equalTo(yellowdView).key(@"greenView-height");
      make.top.equalTo(yellowdView).key(@"greenView-top");
      make.width.equalTo(yellowdView).key(@"greenView-width");
      make.trailing.equalTo(self).inset(10).key(@"greenView-trailing");
      // 冲突约束
      // make.leading.equalTo(greenView.trailing).inset(10).key(@"greenView-leading");
      }];
      ```

      - 使用 `MASAttachKeys`批量给`view`设置key
      -

      MASAttachKeys(greenView, yellowdView);

      -
      1
      2
      3
      4
      	
      ![](https://ws3.sinaimg.cn/large/006tNc79ly1fsgalvd4l6j31a20modnp.jpg)

      - `dividedBy`和 `multipliedBy` 约束宽高比
      -

      [topInnerView makeConstraints:^(MASConstraintMaker *make) {

      -
      // 设置自己的宽高比是3:1
      -make.width.equalTo( topInnerView.height).multipliedBy(3); // 乘因数
      -
      -// 设置宽高并且设置他们的优先级最低
      -make.width.height.lessThanOrEqualTo(topView);
      -make.width.height.equalTo(topView).priorityLow();
      -
      -// 设置位置
      -make.top.equalTo(topView);
      -

      }];

      -
      1
      - 多个控件批量约束
      -

      NSValue sizeValue = [NSValue valueWithCGSize:CGSizeMake(100, 100)];
      [@[blueView, redView, yellowView] makeConstraints:^(MASConstraintMaker
      make) {

      -
      make.size.equalTo(sizeValue);
      -

      }];

      -

      [@[blueView, redView, yellowView] mas_makeConstraints:^(MASConstraintMaker *make) {

      -
      make.top.equalTo(self).inset(20);
      -

      }];

      -

      [blueView makeConstraints:^(MASConstraintMaker *make) {

      -
      make.left.equalTo(self).inset(20);
      -

      }];

      -

      [redView makeConstraints:^(MASConstraintMaker *make) {

      -
      make.left.equalTo(blueView.right).inset(10);
      -

      }];

      -

      [yellowView makeConstraints:^(MASConstraintMaker *make) {

      -
      make.left.equalTo(redView.right).inset(10);
      -

      }];

      -
      1
      2
      3
      4

      ![批量约束](https://ws1.sinaimg.cn/large/006tNc79ly1fsgaz670hvj30s61iyn2l.jpg)

      **以上代码也可以简化为**
      -

      [redView makeConstraints:^(MASConstraintMaker *make) {

      -
      make.left.equalTo(blueView.right).inset(10);
      -

      }];

      -

      [yellowView makeConstraints:^(MASConstraintMaker *make) {

      -
      make.left.equalTo(redView.right).inset(10);
      -

      }];

      -
    • -
    -
    [blueView makeConstraints:^(MASConstraintMaker *make) {
    -    make.top.left.equalTo(self);
    -    make.size.equalTo(@[redView, yellowView, sizeValue]);
    -}];
    -
    -
    1
    2
    3
    4
    5
    	![](https://ws2.sinaimg.cn/large/006tNc79ly1fsgbhz8lw5j30s61iyte5.jpg)

    ## Snapkit 使用

    - Swift 中`edgesForExtendedLayout:UIRectEdge` 扩展布局的边缘是一个属性,默认是`All`
    - -self.edgesForExtendedLayout = [] -
    1
    2
    3
    4

    在Swift中闭包参数可以使用$0、$1、$2 一次代表第一个,第二个,第三个参数

    - 使用$0 来代替闭包参数
    - -nullDataImageView.snp.makeConstraints { - $0.top.equalTo(view).offset(44) - $0.centerX.equalTo(view) - $0.width.height.equalTo(200) -} -
    1
    2
    3
    4
    5
    6
    7
    	
    - `deactivate` 和 `activate` 使用

    - `activate` 激活指定的约束
    - `deactivate` 移除指定的约束

    添加约束
    - -nullDataImageView.snp.makeConstraints { - nullDataViewTopConstraint = $0.top.equalTo(view).offset(80).constraint - nullDataViewLeftConstraint = $0.left.equalTo(view).inset(10).constraint - $0.width.height.equalTo(200) -} -
    1
    2

    移除对应约束,设置约束再激活约束
    - -nullDataViewLeftConstraint?.deactivate() -nullDataViewLeftConstraint?.update(inset: 100) -nullDataViewLeftConstraint?.activate() -UIView.animate(withDuration: 1.5) { - self.view.layoutIfNeeded() -} -
    1
    2
    3
    4

    - 更新约束

    - 添加约束
    - -nullDataImageView.snp.makeConstraints { - nullDataViewTopConstraint = $0.top.equalTo(view).offset(64).constraint - $0.centerX.equalTo(view) - $0.width.height.equalTo(200) -} -
    1
    2

    - 更新约束
    - -nullDataViewTopConstraint?.update(inset: 84) -UIView.animate(withDuration: 0.5, delay: 0, options: .curveEaseOut, animations: { - self.view.layoutIfNeeded() -}) { (finished) in - print("动画执行完毕") -} -
    1
    2
    	
    - 添加`debug`模式中的 `key`使用`labeled()`方法
    - -nullDataImageView.snp.makeConstraints { - nullDataViewTopConstraint = $0.top.equalTo(view).offset(80).constraint - nullDataViewLeftConstraint = $0.left.equalTo(view).inset(10).constraint - $0.width.height.equalTo(200).labeled("width和height约束") -} -``` -
      -
    • 其他注意点都和Masnory相同
    • -
    - - -
    - @@ -886,7 +828,7 @@

    - 1234568 + 1234569

    @@ -932,23 +874,23 @@

    Recent Posts

    diff --git a/page/5/index.html b/page/5/index.html index 4372da8..158002f 100644 --- a/page/5/index.html +++ b/page/5/index.html @@ -62,10 +62,10 @@

    -
    +
    @@ -74,46 +74,111 @@

    -

    flutter 常用组件

    -

    组件

      -
    • StatefulWidget
        -
      • 具有可变状态的窗口部件,使用应用时可随时变化,例如进度条
      • +

        Reat Native ref 使用

        +

        State 状态的使用

        在父控件中指定,整个生命周期讲不再改变
        需求:一段闪烁的文字, Props 属性显示文字内容创建时初始化,State控制随时间变化是否显示,在父控件中

        +
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        class Blink extends Component{
        // 构造
        constructor(props) {
        super(props);
        // 初始状态
        this.state = {showWithText:true};
        this.state = {}
        setInterval(() => {
        this.setState(previousState => {
        return {showWithText: !previousState.showWithText}
        });
        }, 100);
        }
        render() {
        let display= this.state.showWithText ? this.props.text : ' ';
        return(
        <Text>{display}</Text>
        );
        }
        }
        +
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        export default class App extends Component<Props> {
        render() {
        return(
        <View style={styles.container}>
        <Blink
        text='I love to blink'
        />

        <Blink
        text='猜猜猜'
        />

        <Blink
        text='Blink is great'
        />
        </View>
        );
        }
        }
        +

        Props 属性的使用

        1
        2
        3
        4
        5
        6
        7
        8
        9
        // 定义一个组件
        class Greeting extends Component {
        render() {
        return(
        <Text> Hello {this.props.name}!
        </Text>
        );
        }
        }
        +
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        export default class App extends Component<Props> {
        render() {
        return(
        <View style={styles.container}>
        <Image source={{uri:'https://upload.wikimedia.org/wikipedia/commons/d/de/Bananavarieties.jpg'}}
        style={{width: 190, height: 110}}
        />
        <Text style={styles.instructions}>
        使用Props属性
        </Text>
        <View>
        <Greeting name='React-Native'
        />
        <Greeting name='iOS'
        />
        <Greeting name='Swift'
        />
        </View>

        </View>
        );
        }
        }
        +

        ref的使用

          +
        • 可以理解为组件渲染后指向组件的一个应用,可以通过ref获取到真实的组件(类似指针?)
        - -
      • StatelessWidget
          -
        • 不可变窗口部件
        • +

          定义

          +
          1
          ref='scrollView'
          +

          获取

          +
          1
          2
          let scrollView = this.refs.scrollView
          let scrollView = this.refs['scrollView']
          +
            +
          • 可以通过ref访问组件的属性和方法
          • +
          +

          ES6 定时器

            +
          • 开启定时器
          • +
          +
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          21
          22
          let scrollView = this.refs.scrollView;
          // 2.4timer 定时器的写法
          this.timer = setInterval(
          ()=>{
          var tempPage = 0;
          // 修改banner索引
          if ((this.state.currentPage+1) >= ImageData.data.length) {
          tempPage = 0;
          } else {
          tempPage = this.state.currentPage+1;
          }

          // 更新状态
          this.setState({
          currentPage: tempPage
          })

          let offSet_x = width * tempPage
          scrollView.scrollResponderScrollTo({x:offSet_x, y:0, animated: true})
          },
          this.state.duration
          );
          +
            +
          • 暂停定时器
          +
          1
          this.timer && clearInterval(this.timer)
          + + +
    + +

    + +

    + + + +
    + +
    + + +
    + +

    js 学习

    +

    数据类型

    1
    2
    3
    4
    5
    6
    7
    8
    var fullName;
    // undefined

    fullName
    // undefined

    fullName + 2
    // NaN
    +
      +
    • 判断类型名称

      +
      1
      2
      3
      4
      fullName = '张'
      "张"
      typeof(fullName)
      // "string"
    -

    Text 组件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    //  Text 组件
    class MyText extends StatelessWidget{
    @override
    Widget build(BuildContext context){
    return Text('reacctNative、 flutter 学习之路是非常坚信不疑的,作为一个程序员,因该对于技术有着追求,不断的提高自己才是王道,可是道理都懂依然过不好这一生',
    maxLines: 2,
    overflow: TextOverflow.fade,
    textAlign: TextAlign.center,
    style: TextStyle(
    fontSize: 20.0,
    color: Colors.deepOrange,
    decoration: TextDecoration.underline,
    decorationColor: Colors.redAccent,
    decorationStyle: TextDecorationStyle.dotted
    ),
    );
    }
    }
    -

    COntainer 组件

    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

    // Container容器组件
    class MyContainer extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
    return Container(
    child: Text('Container 容器中有文字',
    style: TextStyle(
    backgroundColor: Colors.cyan,
    ),
    ),
    alignment: Alignment.center, // 容器中内容对其方式
    width: 300.0,
    height: 200.0,
    padding: const EdgeInsets.all(10), // 容器内边距
    margin: const EdgeInsets.fromLTRB(10, 5, 10, 5), // container 和外部控件的边距
    decoration: new BoxDecoration(
    gradient: const LinearGradient(
    colors: [Colors.lightBlue, Colors.greenAccent, Colors.yellow]
    ),

    // 设置 border 的宽度
    border: Border.all(width: 5, color: Colors.black)

    ),
    );
    }
    }
    -

    Image 组件

    fit属性

      -
    • BoxFit.fill:全图显示,图片会被拉伸,并充满父容器。
    • -
    • BoxFit.contain:全图显示,显示原比例,可能会有空隙。
    • -
    • BoxFit.cover:显示可能拉伸,可能裁切,充满(图片要充满整个容器,还不变形)。
    • -
    • BoxFit.fitWidth:宽度充满(横向充满),显示可能拉伸,可能裁切。
    • -
    • BoxFit.fitHeight :高度充满(竖向充满),显示可能拉伸,可能裁切。
    • -
    • BoxFit.scaleDown:效果和contain差不多,但是此属性不允许显示超过源图片大小,可小不可大。
    • +
      1
      2
      3
      4
      var weight = 100;
      undefined
      typeof(weight)
      // "number"
      + + + +
      1
      2
      3
      4
      var firstName = '王', lastName = '张'
      undefined
      firstName + lastName
      // "王张"
      +
        +
      • 类型不相同时转换后在拼接

        +
        1
        2
        3
        4
        var weightIncrease = '2.5斤'
        undefined
        weight + weightIncrease
        // "1002.5斤"
        +
      -

      图片的混合模式

        -
      • color:是要混合的颜色,如果你只设置color是没有意义的。
      • -
      • colorBlendMode: 是混合模式。
      • +

        数组

        var array = [];
        +undefined
        +array.length
        +// 0
        +typeof(array)
        +// "object"
        +
          +
        • 增加元素

          +
          1
          2
          3
          4
          array.push('1')
          // 4
          array
          // (4) ["ab", "cbd", "fcg", "1"]
          +
        • +
        • 数组前面添加

          +
        -

        repeat 图片填充模式

          -
        • ImageRepeat.repeat : 横向和纵向都进行重复,直到铺满整个画布。
        • -
        • ImageRepeat.repeatX: 横向重复,纵向不重复。
        • -
        • ImageRepeat.repeatY:纵向重复,横向不重复
        • +
          1
          2
          3
          4
          array.unshift('0')
          // 6
          array
          // (6) ["0", "ab", "cbd", "fcg", "1", Array(2)]
          +
            +
          • 删除最后元素

            +
            1
            2
            3
            4
            array.pop() // 返回删除的元素
            // (2) ["2", "3"]
            array
            // (5) ["0", "ab", "cbd", "fcg", "1"]
            +
          • +
          • 删除第一个元素

            +
            1
            2
            3
            4
            5
            6
            	array.shift()
            // "0"
            array
            // (4) ["ab", "cbd", "fcg", "1"]
            ```
            - 删除指定的值,但数组元素没有被删
            +

            delete array[2]
            // true
            array
            // (4) [“ab”, “cbd”, empty, “1”]

            +
            1
            2
            	
            - 删除指定的元素
            +

            array.splice(1)
            // (3) [“cbd”, empty, “1”]
            array
            // [“ab”]

            +
            1
            2
            3
            4
            	
            ## 函数

            ### 调用调用
            +
          -
          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
          class MyImage extends StatelessWidget {
          @override
          Widget build(BuildContext context){
          return Container(
          child: new Image.network(
          // 'https://images.xiaozhuanlan.com/photo/2019/28ed76123023e46483fcf0ae5c2ba11b.png',
          'https://p.ampmake.com/mall/product/9666f00e-f02a-46ce-9b7c-090bf6aba9aa.png',

          // 1. 设置填充模式
          // fit: BoxFit.scaleDown,
          // fit: BoxFit.none,
          // fit: BoxFit.fill,
          // fit: BoxFit.fitHeight,
          // fit: BoxFit.fitWidth,

          // 保持原图比例
          // fit: BoxFit.contain,

          // 充满整个容器,图片被裁切
          // fit: BoxFit.cover,
          // scale: 1.0,

          // 2. 图片混合
          color: Colors.red,
          colorBlendMode: BlendMode.overlay,

          // 3. reap
          repeat: ImageRepeat.repeat,
          ),
          width: 300.0,
          height: 200.0,
          color: Colors.lightBlue,
          );
          }
          }
          -

          ListView 列表组件

          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
          class MyListView extends StatelessWidget{
          @override
          Widget build(BuildContext contex){

          return ListView(
          children: <Widget>[
          new ListTile(
          leading: new Icon(Icons.camera),
          title: Text('相册'),
          ),

          new ListTile(
          leading: new Icon(Icons.camera_alt),
          title: Text('相机'),
          ),

          new ListTile(
          leading: new Icon(Icons.camera_enhance),
          title: Text('相机1'),
          ),

          new ListTile(
          leading: new Icon(Icons.camera_front),
          title: Text('相机2'),
          ),

          new ListTile(
          leading: new Icon(Icons.camera_rear),
          title: Text('相机3'),
          ),
          ],
          );
          }
          }
          -

          ListView.builder 适合列表项比较多(或者无限)的情况

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          class MyListViewBuilder extends StatelessWidget {

          @override
          Widget build(BuildContext context){
          return ListView.builder(
          itemCount: 100, // 数量
          itemExtent: 40, // Tile 高度
          itemBuilder: (BuildContext context, int index){
          return ListTile(title: Text("$index"));
          }
          );
          }
          }
          -

          横向列表

          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
          class MyHorizontalListView extends StatelessWidget {
          @override
          Widget build(BuildContext context) {
          return Container(
          height: 100,
          child: ListView(
          // 设置滚动方式
          scrollDirection: Axis.horizontal,
          children: <Widget>[
          new Container(
          width: 180.0,
          color: Colors.green,
          ),
          new Container(
          width: 180.0,
          color: Colors.yellow,
          ),
          new Container(
          width: 180.0,
          color: Colors.blue,
          ),
          new Container(
          width: 180.0,
          color: Colors.cyan,
          ),
          new Container(
          width: 180.0,
          color: Colors.purple,
          ),
          ],
          ),
          );
          }
          }
          -

          动态列表和构造方法的创建

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          class MyDynamicListView extends StatelessWidget {
          // 声明参数
          final List<String> items;
          // 构造方法
          MyDynamicListView({Key key, @required this.items}):super(key:key);
          @override
          Widget build(BuildContext context) {
          return Container(
          child: new ListView.builder(
          itemCount: items.length,
          itemBuilder: (context, index){
          return ListTile(
          title: new Text('通过构造方法创建的动态列表${items[index]}'),
          );
          },
          ),
          );
          }
          }
          -

          GridView 网格列表的使用

          +

          alertMessage();

          +

          function alertMessageWithParamter(message) {
          alert(message)
          }

          +

          alertMessageWithParamter(‘有参数的函数’)
          alertMessageWithParamter(250)
          alertMessageWithParamter(
          console.log(‘test’)
          )

          +
          1
          ### 函数表达式,使用函数声明的方式
          +

          var alertMessage_expression = function expression_alert (message) {
          alert(message)
          }

          +

          alertMessage_expression(‘匿名函数调用’)

          +

          expression_alert(‘函数表达式’)
          `

          +
    @@ -123,10 +188,10 @@

    +
    @@ -135,53 +200,164 @@

    -

    GCD信号量用法

    -

    对于异步网络请求相互依赖问题一般用三种方式解决:

    +

    Masonry使用

    基本使用官网Demo非常详细,以下总结的是常见问题和技巧

    +

    安装遇到的问题

      +
    • pod 安装后出现⚠️,项目运行报错

      +
      1
      [UIView mas_makeConstraints:]: unrecognized selector sent to instance
      +

      github

      +

      解决方法

      +
    • +
    • 使用lastBaseline属性crash

      +

      解决办法

      +
    • +
    +

    使用时注意点

      +
    • edgesForExtendedLayout 方法

      +

      重写此方法的目的是为了确定布局位置从什么位置开始什么位置结束,

      +
      1
      2
      3
      - (UIRectEdge)edgesForExtendedLayout {
      return UIRectEdgeNone;
      }
      +
    • +
    +
    **重写后**
    +![](https://ws1.sinaimg.cn/large/006tNc79ly1fsgcn3fmo0j30s61iyalk.jpg)
    +
    +**重写前**
    +![](https://ws4.sinaimg.cn/large/006tNc79ly1fsgcokvpdwj30s61iy7eq.jpg)
    +
      +
    • translucent 设置UINavigationBar是否半透明, 如果设置了导航栏的styleUIBarStyleBlackTranslucent 就为true

      +

      设置为false不透明效果

      +
    • +
      -
    • 网络请求嵌套
    • -
    • 使用 BlockOperation
    • -
    • GCD DispatchGroup
    • -
    • GCD semaphore(信号量)
    • +
    • extendedLayoutIncludesOpaqueBars 导航栏不透明条件下是否可以扩展,默认是NO不可以扩展

      +
      1
      2
      self.navigationController?.navigationBar.isTranslucent = false
      self.extendedLayoutIncludesOpaqueBars = true
      +
    -

    GCD 信号量

      -
    • semaphore 值 <= 0 阻塞
    • -
    • semaphore semaphore.signal() 使信号量值增加
    • -
    • semaphore.wait() 等待,可设置时长
    • +
      **设置不透明,设置可以扩展此时从坐标的(0,0)点开始布局**
      +![](https://ws2.sinaimg.cn/large/006tNc79ly1fsggem13mjj30ow0j83zy.jpg)    
      +
        +
      • inset 的使用,当约束控件的属性相同时不用再考虑正负值的问题,例如要约束一个控件左边距离另一控件的右边位置时还需要使用负值情况

        +
        1
        2
        3
        4
        5
        6
        7
        [yellowdView makeConstraints:^(MASConstraintMaker *make) {
        make.height.equalTo(@150);
        make.top.equalTo(self).inset(220);
        make.leading.equalTo(self).inset(10);
        make.trailing.equalTo(greenView.leading).inset(10);
        make.width.equalTo(greenView);
        }];
        +
      • +
      • edges的使用,约束边距更方便

        +
        1
        2
        3
        [edgeView remakeConstraints:^(MASConstraintMaker *make) {
        make.edges.equalTo(lasView).insets(UIEdgeInsetsMake(10, 5, 5, 5));
        }];
        +
      -

      request – 3 依赖 request – 1 和 request – 2,1 和 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
      private func gcdSemaphore() {
      let semaphore = DispatchSemaphore(value: 0)
      DispatchQueue.global().async {
      for i in 0...300 {
      print("request -- 1")
      }
      semaphore.signal()
      }

      DispatchQueue.global().async {
      for i in 0...200 {
      print("request -- 2")
      }
      semaphore.signal()
      }


      DispatchQueue.global().async {
      semaphore.wait()
      semaphore.wait()
      for i in 0...160 {
      print("request -- 3")
      }
      }
      }
      -

      request – 3 ,request – 2 和 request – 1,依次相互依赖

      -
      private func gcdSemaphore() {
      -        let semaphore = DispatchSemaphore(value: 0)
      -        DispatchQueue.global().async {
      -            for i in 0...300 {
      -                print("request -- 1")
      -            }
      -            semaphore.signal()
      -        }
      +
        +
      • Priority 设置约束的优先级

        +
          +
        • priorityHigh
        • +
        • priorityMedium
        • +
        • priorityLow
        • +
        +
      • +
      • makeConstraintsupdateConstraintsremakeConstraints三者区别

        +
          +
        • makeConstraints 添加约束
        • +
        • updateConstraints 更新约束,更新之前会查找一边控件已经存在的约束,没有就添加,有就更新到最新的约束
        • +
        • remakeConstraints删除控件以前的所有约束重新添加约束
        • +
        +
      • +
      • 设置控件或者某一约束的’key’ 方便冲突时⚠️查找

        +
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        	greenView.mas_key = @"greenView";
        [greenView makeConstraints:^(MASConstraintMaker *make) {
        make.height.equalTo(yellowdView).key(@"greenView-height");
        make.top.equalTo(yellowdView).key(@"greenView-top");
        make.width.equalTo(yellowdView).key(@"greenView-width");
        make.trailing.equalTo(self).inset(10).key(@"greenView-trailing");
        // 冲突约束
        // make.leading.equalTo(greenView.trailing).inset(10).key(@"greenView-leading");
        }];
        ```

        - 使用 `MASAttachKeys`批量给`view`设置key
        +

        MASAttachKeys(greenView, yellowdView);

        +
        1
        2
        3
        4
        	
        ![](https://ws3.sinaimg.cn/large/006tNc79ly1fsgalvd4l6j31a20modnp.jpg)

        - `dividedBy`和 `multipliedBy` 约束宽高比
        +

        [topInnerView makeConstraints:^(MASConstraintMaker *make) {

        +
        // 设置自己的宽高比是3:1
        +make.width.equalTo( topInnerView.height).multipliedBy(3); // 乘因数
        +
        +// 设置宽高并且设置他们的优先级最低
        +make.width.height.lessThanOrEqualTo(topView);
        +make.width.height.equalTo(topView).priorityLow();
        +
        +// 设置位置
        +make.top.equalTo(topView);
        +

        }];

        +
        1
        - 多个控件批量约束
        +

        NSValue sizeValue = [NSValue valueWithCGSize:CGSizeMake(100, 100)];
        [@[blueView, redView, yellowView] makeConstraints:^(MASConstraintMaker
        make) {

        +
        make.size.equalTo(sizeValue);
        +

        }];

        +

        [@[blueView, redView, yellowView] mas_makeConstraints:^(MASConstraintMaker *make) {

        +
        make.top.equalTo(self).inset(20);
        +

        }];

        +

        [blueView makeConstraints:^(MASConstraintMaker *make) {

        +
        make.left.equalTo(self).inset(20);
        +

        }];

        +

        [redView makeConstraints:^(MASConstraintMaker *make) {

        +
        make.left.equalTo(blueView.right).inset(10);
        +

        }];

        +

        [yellowView makeConstraints:^(MASConstraintMaker *make) {

        +
        make.left.equalTo(redView.right).inset(10);
        +

        }];

        +
        1
        2
        3
        4

        ![批量约束](https://ws1.sinaimg.cn/large/006tNc79ly1fsgaz670hvj30s61iyn2l.jpg)

        **以上代码也可以简化为**
        +

        [redView makeConstraints:^(MASConstraintMaker *make) {

        +
        make.left.equalTo(blueView.right).inset(10);
        +

        }];

        +

        [yellowView makeConstraints:^(MASConstraintMaker *make) {

        +
        make.left.equalTo(redView.right).inset(10);
        +

        }];

        +
      • +
      +
      [blueView makeConstraints:^(MASConstraintMaker *make) {
      +    make.top.left.equalTo(self);
      +    make.size.equalTo(@[redView, yellowView, sizeValue]);
      +}];
       
      -        DispatchQueue.global().async {
      -            semaphore.wait()
      -            for i in 0...200 {
      -                print("request -- 2")
      -            }
      -            semaphore.signal()
      -        }
      +
      1
      2
      3
      4
      5
      	![](https://ws2.sinaimg.cn/large/006tNc79ly1fsgbhz8lw5j30s61iyte5.jpg)

      ## Snapkit 使用

      - Swift 中`edgesForExtendedLayout:UIRectEdge` 扩展布局的边缘是一个属性,默认是`All`
      +self.edgesForExtendedLayout = [] +
      1
      2
      3
      4

      在Swift中闭包参数可以使用$0、$1、$2 一次代表第一个,第二个,第三个参数

      - 使用$0 来代替闭包参数
      - DispatchQueue.global().async { - semaphore.wait() - for i in 0...160 { - print("request -- 3") - } - } - } +nullDataImageView.snp.makeConstraints { + $0.top.equalTo(view).offset(44) + $0.centerX.equalTo(view) + $0.width.height.equalTo(200) +} +
      1
      2
      3
      4
      5
      6
      7
      	
      - `deactivate` 和 `activate` 使用

      - `activate` 激活指定的约束
      - `deactivate` 移除指定的约束

      添加约束
      + +nullDataImageView.snp.makeConstraints { + nullDataViewTopConstraint = $0.top.equalTo(view).offset(80).constraint + nullDataViewLeftConstraint = $0.left.equalTo(view).inset(10).constraint + $0.width.height.equalTo(200) +} +
      1
      2

      移除对应约束,设置约束再激活约束
      + +nullDataViewLeftConstraint?.deactivate() +nullDataViewLeftConstraint?.update(inset: 100) +nullDataViewLeftConstraint?.activate() +UIView.animate(withDuration: 1.5) { + self.view.layoutIfNeeded() +} +
      1
      2
      3
      4

      - 更新约束

      - 添加约束
      + +nullDataImageView.snp.makeConstraints { + nullDataViewTopConstraint = $0.top.equalTo(view).offset(64).constraint + $0.centerX.equalTo(view) + $0.width.height.equalTo(200) +} +
      1
      2

      - 更新约束
      + +nullDataViewTopConstraint?.update(inset: 84) +UIView.animate(withDuration: 0.5, delay: 0, options: .curveEaseOut, animations: { + self.view.layoutIfNeeded() +}) { (finished) in + print("动画执行完毕") +} +
      1
      2
      	
      - 添加`debug`模式中的 `key`使用`labeled()`方法
      + +nullDataImageView.snp.makeConstraints { + nullDataViewTopConstraint = $0.top.equalTo(view).offset(80).constraint + nullDataViewLeftConstraint = $0.left.equalTo(view).inset(10).constraint + $0.width.height.equalTo(200).labeled("width和height约束") +} +``` +
        +
      • 其他注意点都和Masnory相同
      • +
      -

    @@ -307,7 +483,136 @@

    13

    + + +
    + + + +
    + +
    + + +
    + +

    flutter 常用组件

    +

    组件

      +
    • StatefulWidget
        +
      • 具有可变状态的窗口部件,使用应用时可随时变化,例如进度条
      • +
      +
    • +
    • StatelessWidget
        +
      • 不可变窗口部件
      • +
      +
    • +
    +

    Text 组件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    //  Text 组件
    class MyText extends StatelessWidget{
    @override
    Widget build(BuildContext context){
    return Text('reacctNative、 flutter 学习之路是非常坚信不疑的,作为一个程序员,因该对于技术有着追求,不断的提高自己才是王道,可是道理都懂依然过不好这一生',
    maxLines: 2,
    overflow: TextOverflow.fade,
    textAlign: TextAlign.center,
    style: TextStyle(
    fontSize: 20.0,
    color: Colors.deepOrange,
    decoration: TextDecoration.underline,
    decorationColor: Colors.redAccent,
    decorationStyle: TextDecorationStyle.dotted
    ),
    );
    }
    }
    +

    COntainer 组件

    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

    // Container容器组件
    class MyContainer extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
    return Container(
    child: Text('Container 容器中有文字',
    style: TextStyle(
    backgroundColor: Colors.cyan,
    ),
    ),
    alignment: Alignment.center, // 容器中内容对其方式
    width: 300.0,
    height: 200.0,
    padding: const EdgeInsets.all(10), // 容器内边距
    margin: const EdgeInsets.fromLTRB(10, 5, 10, 5), // container 和外部控件的边距
    decoration: new BoxDecoration(
    gradient: const LinearGradient(
    colors: [Colors.lightBlue, Colors.greenAccent, Colors.yellow]
    ),

    // 设置 border 的宽度
    border: Border.all(width: 5, color: Colors.black)

    ),
    );
    }
    }
    +

    Image 组件

    fit属性

      +
    • BoxFit.fill:全图显示,图片会被拉伸,并充满父容器。
    • +
    • BoxFit.contain:全图显示,显示原比例,可能会有空隙。
    • +
    • BoxFit.cover:显示可能拉伸,可能裁切,充满(图片要充满整个容器,还不变形)。
    • +
    • BoxFit.fitWidth:宽度充满(横向充满),显示可能拉伸,可能裁切。
    • +
    • BoxFit.fitHeight :高度充满(竖向充满),显示可能拉伸,可能裁切。
    • +
    • BoxFit.scaleDown:效果和contain差不多,但是此属性不允许显示超过源图片大小,可小不可大。
    • +
    +

    图片的混合模式

      +
    • color:是要混合的颜色,如果你只设置color是没有意义的。
    • +
    • colorBlendMode: 是混合模式。
    • +
    +

    repeat 图片填充模式

      +
    • ImageRepeat.repeat : 横向和纵向都进行重复,直到铺满整个画布。
    • +
    • ImageRepeat.repeatX: 横向重复,纵向不重复。
    • +
    • ImageRepeat.repeatY:纵向重复,横向不重复
    • +
    +
    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
    class MyImage extends StatelessWidget {
    @override
    Widget build(BuildContext context){
    return Container(
    child: new Image.network(
    // 'https://images.xiaozhuanlan.com/photo/2019/28ed76123023e46483fcf0ae5c2ba11b.png',
    'https://p.ampmake.com/mall/product/9666f00e-f02a-46ce-9b7c-090bf6aba9aa.png',

    // 1. 设置填充模式
    // fit: BoxFit.scaleDown,
    // fit: BoxFit.none,
    // fit: BoxFit.fill,
    // fit: BoxFit.fitHeight,
    // fit: BoxFit.fitWidth,

    // 保持原图比例
    // fit: BoxFit.contain,

    // 充满整个容器,图片被裁切
    // fit: BoxFit.cover,
    // scale: 1.0,

    // 2. 图片混合
    color: Colors.red,
    colorBlendMode: BlendMode.overlay,

    // 3. reap
    repeat: ImageRepeat.repeat,
    ),
    width: 300.0,
    height: 200.0,
    color: Colors.lightBlue,
    );
    }
    }
    +

    ListView 列表组件

    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
    class MyListView extends StatelessWidget{
    @override
    Widget build(BuildContext contex){

    return ListView(
    children: <Widget>[
    new ListTile(
    leading: new Icon(Icons.camera),
    title: Text('相册'),
    ),

    new ListTile(
    leading: new Icon(Icons.camera_alt),
    title: Text('相机'),
    ),

    new ListTile(
    leading: new Icon(Icons.camera_enhance),
    title: Text('相机1'),
    ),

    new ListTile(
    leading: new Icon(Icons.camera_front),
    title: Text('相机2'),
    ),

    new ListTile(
    leading: new Icon(Icons.camera_rear),
    title: Text('相机3'),
    ),
    ],
    );
    }
    }
    +

    ListView.builder 适合列表项比较多(或者无限)的情况

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class MyListViewBuilder extends StatelessWidget {

    @override
    Widget build(BuildContext context){
    return ListView.builder(
    itemCount: 100, // 数量
    itemExtent: 40, // Tile 高度
    itemBuilder: (BuildContext context, int index){
    return ListTile(title: Text("$index"));
    }
    );
    }
    }
    +

    横向列表

    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
    class MyHorizontalListView extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
    return Container(
    height: 100,
    child: ListView(
    // 设置滚动方式
    scrollDirection: Axis.horizontal,
    children: <Widget>[
    new Container(
    width: 180.0,
    color: Colors.green,
    ),
    new Container(
    width: 180.0,
    color: Colors.yellow,
    ),
    new Container(
    width: 180.0,
    color: Colors.blue,
    ),
    new Container(
    width: 180.0,
    color: Colors.cyan,
    ),
    new Container(
    width: 180.0,
    color: Colors.purple,
    ),
    ],
    ),
    );
    }
    }
    +

    动态列表和构造方法的创建

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    class MyDynamicListView extends StatelessWidget {
    // 声明参数
    final List<String> items;
    // 构造方法
    MyDynamicListView({Key key, @required this.items}):super(key:key);
    @override
    Widget build(BuildContext context) {
    return Container(
    child: new ListView.builder(
    itemCount: items.length,
    itemBuilder: (context, index){
    return ListTile(
    title: new Text('通过构造方法创建的动态列表${items[index]}'),
    );
    },
    ),
    );
    }
    }
    +

    GridView 网格列表的使用

    + +
    + +
    + +
    + + + +
    + +
    + + +
    + +

    GCD信号量用法

    +

    对于异步网络请求相互依赖问题一般用三种方式解决:

    +
      +
    • 网络请求嵌套
    • +
    • 使用 BlockOperation
    • +
    • GCD DispatchGroup
    • +
    • GCD semaphore(信号量)
    • +
    +

    GCD 信号量

      +
    • semaphore 值 <= 0 阻塞
    • +
    • semaphore semaphore.signal() 使信号量值增加
    • +
    • semaphore.wait() 等待,可设置时长
    • +
    +

    request – 3 依赖 request – 1 和 request – 2,1 和 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
    private func gcdSemaphore() {
    let semaphore = DispatchSemaphore(value: 0)
    DispatchQueue.global().async {
    for i in 0...300 {
    print("request -- 1")
    }
    semaphore.signal()
    }

    DispatchQueue.global().async {
    for i in 0...200 {
    print("request -- 2")
    }
    semaphore.signal()
    }


    DispatchQueue.global().async {
    semaphore.wait()
    semaphore.wait()
    for i in 0...160 {
    print("request -- 3")
    }
    }
    }
    +

    request – 3 ,request – 2 和 request – 1,依次相互依赖

    +
    private func gcdSemaphore() {
    +        let semaphore = DispatchSemaphore(value: 0)
    +        DispatchQueue.global().async {
    +            for i in 0...300 {
    +                print("request -- 1")
    +            }
    +            semaphore.signal()
    +        }
    +
    +        DispatchQueue.global().async {
    +            semaphore.wait()
    +            for i in 0...200 {
    +                print("request -- 2")
    +            }
    +            semaphore.signal()
    +        }
    +
    +
    +        DispatchQueue.global().async {
    +            semaphore.wait()
    +            for i in 0...160 {
    +                print("request -- 3")
    +            }
    +        }
    +    }
    +
    +
    + +
    + @@ -415,7 +720,7 @@

    - Share + Share @@ -455,7 +760,7 @@

    - Share + Share @@ -484,7 +789,7 @@

    - Share + Share @@ -524,249 +829,7 @@

    - - -
    - - - -
    - -
    - - -
    - - -

    - Block学习探究 -

    - - -
    - -
    - -

    代码及结果

      -
    • 普通局部变量

      -
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      - (void)block1 {

      NSInteger num = 100;

      // 定义一个block
      dispatch_block_t block = ^ {
      NSLog(@"block1 -- numer = %zd", num);
      };

      num = 200;

      block();
      }
      -
    • -
    • 使用__block修饰变量

      -
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      - (void)block2 {

      __block NSInteger num = 100;

      // 定义一个block
      dispatch_block_t block = ^ {
      NSLog(@"block2 -- numer = %zd", num);
      };

      num = 200;

      block();
      }
      -
    • -
    • 全局变量

      -
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      - (void)block3 {


      // 定义一个block
      dispatch_block_t block = ^ {
      NSLog(@"block3 -- numer = %zd", blockNum);
      };

      blockNum = 200;

      block();
      }
      -
    • -
    • 静态常量

      -
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      - (void)block4 {

      static NSInteger num = 100;

      // 定义一个block
      dispatch_block_t block = ^ {
      NSLog(@"block4 -- numer = %zd", num);
      };

      num = 200;

      block();
      }
      -
    • -
    -

    运行结果

    2018-01-19 00:04:13.759416+0800 CycleRetain[91251:2382380] block1 -- numer = 100
    -2018-01-19 00:04:13.760206+0800 CycleRetain[91251:2382380] block2 -- numer = 200
    -2018-01-19 00:04:13.760473+0800 CycleRetain[91251:2382380] block3 -- numer = 200
    -2018-01-19 00:04:13.760603+0800 CycleRetain[91251:2382380] block4 -- numer = 200
    -

    原理及本质

    block 根据创建位置不同,共有三种:栈block,堆block,全局block

    -

    使用block本质就是为了保证栈上和堆上block内访问和修改的是同一个变量,具体的实现是将block 修饰的变动自动封装成一个结构体,让他在堆上创建
    基于OC 底层的runtime 机制
    block 在内部会有一个指向结构体的指针,当调用block的时候其实就是让block找出对应的指针所指的函数地址进行调用。并传入了block自己本身

    - - -
    - -
    - -
    - - - -
    - -
    - - -
    - - -

    - 面试中常见问题总结(二) -

    - - -
    - -
    - -

    非正式协议和正式协议

    非正式协议:凡是给NSObject或者其子类添加的类别都是非正式协议
    正式协议:使用 @protocol 方式声明的方法列表,有 @require 和 @optional 两种类型

    -

    KVC 键值编码

    之所以可以使用就是因为给 NObject 增加了 NSKeyValueCoding 非正式协议,NSObject 实现了协议,OC中几乎所有的对象都支持KVC,获取值的方法是不通过属性的setter、getter 方法,而是通过属性名称的key间接访问了实例变量,可以访问私有变量

    -
    1
    2
    3
    4
     // 官方注释
    /* Send -setObject:forKey: to the receiver, unless the value is nil, in which case send -removeObjectForKey:.
    */
    - (void)setValue:(nullable ObjectType)value forKey:(NSString *)key;
    -

    如果为nil, 就会自动过滤掉, 此方法常用在字典中过滤空值

    -

    // forKeyPath: 用于符合属性,能够通过层层访问内部属性,相当于调用属性点语法, 拥有forKey 的所有功能

    -
    1
    - (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;
    -
      -
    • 实现原理
        -
      • 利用 setValue: forKeyPath: 赋值
      • -
      -
        -
      1. 底层实现还是使用 runtime 给对象发消息,先去查找属性是否有setter方法,存在setter 就会通过 setter 方法复制,
      2. -
      3. 如果没有setter方法,接着查找下是否划线的成员变量,如果有直接给赋值
      4. -
      5. 如果没有下划线的成员变量,查找是否存在和key值相同的属性,如果有就直接给属性赋值
      6. -
      7. 如果没有和key值相同的属性,就会执行 setValue: forUndefinedKey: 抛出一个异常程序奔溃,这也是字典转模型中需要实现这个方法的原因
      8. -
      -
    • -
    -

    KVO 键值监听

    利用一个key值来找到某个属性并监听某个属性的值的变化发送给观察者,可以理解为简单的观察者模式

    -
      -
    • KVO 使用

      -
        -
      1. 为目标对象的属性添加观察者

        -
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        - (void)testKVO {
        Person *p = [[Person alloc] init];
        self.person = p;
        p.name = @"哈哈";

        [p addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];

        p.name = @"我不是原来的我!你能监听到我 ???";
        p.name = @"看我七十二变";
        }
        -
      2. -
      -
    • -
    -
    2. 实现监听的方法
    -
    -
    1
    2
    3
    4
    5
    #pragma mark - kvo 回调方法
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {

    NSLog(@"KVO监听到的对象是 = %@, 值 = %@ , change = %@", [object class], keyPath, change);
    }
    - -3. 移除观察者 - -
    1
    2
    3
    4
    5
    - (void)dealloc {

    [self removeObserver:self forKeyPath:@"name"];
    NSLog(@"-----控制器销毁-----");
    }
    -
      -
    • KVO原理

      -

      类的属性被添加了观察者后,OC底层就会通过Runtime 动态的创建一个这个类的派生类,当前类内部的isa 指针指向了这个派生类,在派生类中重写基类被观察属性的 setter 方法,调用前后会调用 willChangeValueForKey, setValueForKey, didChangeValue 方法, 从而达到监听属性值的方法

      -
    • -
    - - -
    - -
    - -
    - - - -
    - -
    - - -
    - - -

    - 面试中常见问题总结 -

    - - -
    - -
    - -

    OC 的动态性

    OC是基于C语言的,C语言在编译阶段就会确定了调用具体的那个函数,OC它的底层是通过runtime 运行时机制来调用函数,在OC中成为消息发送,具体来说就是在编译阶段OC可以调用任何函数,即时这个函数是没有实现的,只有在运行的时候才会根据isa指针和已经注册的方法列表中找到 IMP(函数指针)真正要调用的那个函数,我们平时写的OC代码,在运行的时候也都是转换成了runtime 的方式进行的,不管是什么方法本质都是发送一个消息

    -

    runtime

      -
    • 消息机制原理: 根据方法编号列表 SEL 去映射表中查找对应方法的实现
    • -
    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    Person *p = [Person allo] init];

    // 底层的写法
    // alloc
    Person *p = objc_msgSend(objc_getClass("Person"), sel_registerName("alloc"),);

    // init
    p = objc_msgSend(p, sel-registerName("init"));

    // 调用对象方法本质就是:让对象发消息
    objc_msgSend(p, @selector(eat));

    // 调用类方法本质: 让类发消息
    objc_msgSend([Person class], @selector(run:), 20);

    // alloc init 也可以理解为:

    id objc = objc_msgSend([NSObject class], @selector(alloc));

    objc = objc_msgSend(objc, @selector(alloc));
    -
      -
    • runtime 消息机制调用流程

      -

      每个对象内部都有一个isa指针,指向他自己真实的类来确定调用的是那个类的方法; 每个方法sel都有一个IMP指针(指向现实函数的指针),不管是对象方法还是类方法,都会有一个方法列表

      -
        -
      1. 发消息时根据对象内部的isa 指针去该类所对应的方法类表中查找方法,如果没有去父类中查找
      2. -
      3. 注册每个方法编号,为了以后快速查找调用
      4. -
      5. 根据方法编号查找对应方法
      6. -
      7. 通过isa指针和IMP指针确定最终调用的函数
      8. -
      -
    • -
    • runtime 常见使用

      -
        -
      1. 运行是动态添加方法
      2. -
      3. 给分类添加属性,(关联属性)
      4. -
      5. 字典转模型中从先遍历模型属性再从字典中查找相关值实现模型属性赋值
      6. -
      7. NSCoding 自动归档解档时对象属性过多时 encodeWithCoder:initWithCoder 两个方法实现
      8. -
      -
    • -
    -

    Runloop

      -
    • 概念

      -

      NSRunloop 是封装 CFRunloopRef 一套纯C的函数,

      -
    • -
    • 作用

      -
        -
      1. 保持程序持续运行
      2. -
      3. 处理App中各种事件,具体的有:toches事件, NSTimer 事件, Selector 事件, PerformSelector 事件,source 源等等
      4. -
      5. 有事件触发时就运行,没有就休眠
      6. -
      7. 渲染app 中唯一一个主线程上所有UI界面的更新
      8. -
      -
    • -
    • 使用和要点

      -
        -
      1. Runloop 在程序入口 main 函数中就会开启,并且开启的是主线程不会销毁除非退出程序,保证了程序一直运行并能响应事件和交互
      2. -
      3. 每一条线程都是唯一对应一个Runloop
      4. -
      5. 主线程不需要创建,因为在程序启动的时候就已经创建了,而子线程的需要手动创建 直接获取currentRunloop 方式(本质是使用了懒加载)
      6. -
      7. Runloop 对象是利用字典进行存储,因为一一对应,所有key 和value 就是线程和 他所对应的runloop

        -
      8. -
      9. Runloop 中 NSDefaultRunLoopMode 一般情况下主线程运行的 Mode

        -
      10. -
      11. UIInitializationRunLoopMode 初始化mode,通常不用
      12. -
      13. UITrackingRunLoopMode 界面跟踪, 用于scrollView 追踪触摸滑动,保证界面不受其他mode影响
      14. -
      15. GSEventReceiveRunLoopMode 用于接收系统事件内部 mode, 通常不用

        -
      16. -
      17. NSRunLoopCommonModes, 并不是某种具体的 mode ,开发中使用最多,他是一个组合 mode,组合了 NSDefaultRunLoopMode和 NSRunLoopCommonModes

        -
      18. -
      -
    • -
    • Runloop 相关类

      -
        -
      1. Source 事件源/输入源可以理解为 激发源
      2. -
      3. Timer 基于事件触发,CADisplayLink,NSTimer 都是加到了 Runloop,受 Mode 的影响,GCD 定时器不受Runloop 影响
      4. -
      5. Observer 相当于runloop 循环中的监听器,时刻监听当前Runloop的运行状态
      6. -
      7. 休眠没事情做的时候可以停下来,处于休眠状态
      8. -
      -
    • -
    • 使用场景

      -
        -
      1. 创建NSTimer
      2. -
      3. PersforSelector方法
      4. -
      5. 常驻线程:(创建后处于等待状态,有事件时响应事件)
          -
        • 实现后台收集用户停留时间或者是某个按钮的点击次数,如果使用主线程会不方便,使用runloop解决
        • -
        -
      6. -
      7. AutoreleasePool 自动释放池
      8. -
      9. UI更新
      10. -
      -
    • -
    - - -
    - @@ -780,7 +843,7 @@

    Runlo @@ -826,23 +889,23 @@

    Recent Posts

    diff --git a/page/6/index.html b/page/6/index.html index 278e08d..0d0d9f0 100644 --- a/page/6/index.html +++ b/page/6/index.html @@ -62,6 +62,248 @@

    +
    + +
    + + +
    + + +

    + Block学习探究 +

    + + +
    + +
    + +

    代码及结果

      +
    • 普通局部变量

      +
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      - (void)block1 {

      NSInteger num = 100;

      // 定义一个block
      dispatch_block_t block = ^ {
      NSLog(@"block1 -- numer = %zd", num);
      };

      num = 200;

      block();
      }
      +
    • +
    • 使用__block修饰变量

      +
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      - (void)block2 {

      __block NSInteger num = 100;

      // 定义一个block
      dispatch_block_t block = ^ {
      NSLog(@"block2 -- numer = %zd", num);
      };

      num = 200;

      block();
      }
      +
    • +
    • 全局变量

      +
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      - (void)block3 {


      // 定义一个block
      dispatch_block_t block = ^ {
      NSLog(@"block3 -- numer = %zd", blockNum);
      };

      blockNum = 200;

      block();
      }
      +
    • +
    • 静态常量

      +
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      - (void)block4 {

      static NSInteger num = 100;

      // 定义一个block
      dispatch_block_t block = ^ {
      NSLog(@"block4 -- numer = %zd", num);
      };

      num = 200;

      block();
      }
      +
    • +
    +

    运行结果

    2018-01-19 00:04:13.759416+0800 CycleRetain[91251:2382380] block1 -- numer = 100
    +2018-01-19 00:04:13.760206+0800 CycleRetain[91251:2382380] block2 -- numer = 200
    +2018-01-19 00:04:13.760473+0800 CycleRetain[91251:2382380] block3 -- numer = 200
    +2018-01-19 00:04:13.760603+0800 CycleRetain[91251:2382380] block4 -- numer = 200
    +

    原理及本质

    block 根据创建位置不同,共有三种:栈block,堆block,全局block

    +

    使用block本质就是为了保证栈上和堆上block内访问和修改的是同一个变量,具体的实现是将block 修饰的变动自动封装成一个结构体,让他在堆上创建
    基于OC 底层的runtime 机制
    block 在内部会有一个指向结构体的指针,当调用block的时候其实就是让block找出对应的指针所指的函数地址进行调用。并传入了block自己本身

    + + +
    + +
    + +
    + + + +
    + +
    + + +
    + + +

    + 面试中常见问题总结(二) +

    + + +
    + +
    + +

    非正式协议和正式协议

    非正式协议:凡是给NSObject或者其子类添加的类别都是非正式协议
    正式协议:使用 @protocol 方式声明的方法列表,有 @require 和 @optional 两种类型

    +

    KVC 键值编码

    之所以可以使用就是因为给 NObject 增加了 NSKeyValueCoding 非正式协议,NSObject 实现了协议,OC中几乎所有的对象都支持KVC,获取值的方法是不通过属性的setter、getter 方法,而是通过属性名称的key间接访问了实例变量,可以访问私有变量

    +
    1
    2
    3
    4
     // 官方注释
    /* Send -setObject:forKey: to the receiver, unless the value is nil, in which case send -removeObjectForKey:.
    */
    - (void)setValue:(nullable ObjectType)value forKey:(NSString *)key;
    +

    如果为nil, 就会自动过滤掉, 此方法常用在字典中过滤空值

    +

    // forKeyPath: 用于符合属性,能够通过层层访问内部属性,相当于调用属性点语法, 拥有forKey 的所有功能

    +
    1
    - (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;
    +
      +
    • 实现原理
        +
      • 利用 setValue: forKeyPath: 赋值
      • +
      +
        +
      1. 底层实现还是使用 runtime 给对象发消息,先去查找属性是否有setter方法,存在setter 就会通过 setter 方法复制,
      2. +
      3. 如果没有setter方法,接着查找下是否划线的成员变量,如果有直接给赋值
      4. +
      5. 如果没有下划线的成员变量,查找是否存在和key值相同的属性,如果有就直接给属性赋值
      6. +
      7. 如果没有和key值相同的属性,就会执行 setValue: forUndefinedKey: 抛出一个异常程序奔溃,这也是字典转模型中需要实现这个方法的原因
      8. +
      +
    • +
    +

    KVO 键值监听

    利用一个key值来找到某个属性并监听某个属性的值的变化发送给观察者,可以理解为简单的观察者模式

    +
      +
    • KVO 使用

      +
        +
      1. 为目标对象的属性添加观察者

        +
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        - (void)testKVO {
        Person *p = [[Person alloc] init];
        self.person = p;
        p.name = @"哈哈";

        [p addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];

        p.name = @"我不是原来的我!你能监听到我 ???";
        p.name = @"看我七十二变";
        }
        +
      2. +
      +
    • +
    +
    2. 实现监听的方法
    +
    +
    1
    2
    3
    4
    5
    #pragma mark - kvo 回调方法
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {

    NSLog(@"KVO监听到的对象是 = %@, 值 = %@ , change = %@", [object class], keyPath, change);
    }
    + +3. 移除观察者 + +
    1
    2
    3
    4
    5
    - (void)dealloc {

    [self removeObserver:self forKeyPath:@"name"];
    NSLog(@"-----控制器销毁-----");
    }
    +
      +
    • KVO原理

      +

      类的属性被添加了观察者后,OC底层就会通过Runtime 动态的创建一个这个类的派生类,当前类内部的isa 指针指向了这个派生类,在派生类中重写基类被观察属性的 setter 方法,调用前后会调用 willChangeValueForKey, setValueForKey, didChangeValue 方法, 从而达到监听属性值的方法

      +
    • +
    + + +
    + +
    + +
    + + + +
    + +
    + + +
    + + +

    + 面试中常见问题总结 +

    + + +
    + +
    + +

    OC 的动态性

    OC是基于C语言的,C语言在编译阶段就会确定了调用具体的那个函数,OC它的底层是通过runtime 运行时机制来调用函数,在OC中成为消息发送,具体来说就是在编译阶段OC可以调用任何函数,即时这个函数是没有实现的,只有在运行的时候才会根据isa指针和已经注册的方法列表中找到 IMP(函数指针)真正要调用的那个函数,我们平时写的OC代码,在运行的时候也都是转换成了runtime 的方式进行的,不管是什么方法本质都是发送一个消息

    +

    runtime

      +
    • 消息机制原理: 根据方法编号列表 SEL 去映射表中查找对应方法的实现
    • +
    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    Person *p = [Person allo] init];

    // 底层的写法
    // alloc
    Person *p = objc_msgSend(objc_getClass("Person"), sel_registerName("alloc"),);

    // init
    p = objc_msgSend(p, sel-registerName("init"));

    // 调用对象方法本质就是:让对象发消息
    objc_msgSend(p, @selector(eat));

    // 调用类方法本质: 让类发消息
    objc_msgSend([Person class], @selector(run:), 20);

    // alloc init 也可以理解为:

    id objc = objc_msgSend([NSObject class], @selector(alloc));

    objc = objc_msgSend(objc, @selector(alloc));
    +
      +
    • runtime 消息机制调用流程

      +

      每个对象内部都有一个isa指针,指向他自己真实的类来确定调用的是那个类的方法; 每个方法sel都有一个IMP指针(指向现实函数的指针),不管是对象方法还是类方法,都会有一个方法列表

      +
        +
      1. 发消息时根据对象内部的isa 指针去该类所对应的方法类表中查找方法,如果没有去父类中查找
      2. +
      3. 注册每个方法编号,为了以后快速查找调用
      4. +
      5. 根据方法编号查找对应方法
      6. +
      7. 通过isa指针和IMP指针确定最终调用的函数
      8. +
      +
    • +
    • runtime 常见使用

      +
        +
      1. 运行是动态添加方法
      2. +
      3. 给分类添加属性,(关联属性)
      4. +
      5. 字典转模型中从先遍历模型属性再从字典中查找相关值实现模型属性赋值
      6. +
      7. NSCoding 自动归档解档时对象属性过多时 encodeWithCoder:initWithCoder 两个方法实现
      8. +
      +
    • +
    +

    Runloop

      +
    • 概念

      +

      NSRunloop 是封装 CFRunloopRef 一套纯C的函数,

      +
    • +
    • 作用

      +
        +
      1. 保持程序持续运行
      2. +
      3. 处理App中各种事件,具体的有:toches事件, NSTimer 事件, Selector 事件, PerformSelector 事件,source 源等等
      4. +
      5. 有事件触发时就运行,没有就休眠
      6. +
      7. 渲染app 中唯一一个主线程上所有UI界面的更新
      8. +
      +
    • +
    • 使用和要点

      +
        +
      1. Runloop 在程序入口 main 函数中就会开启,并且开启的是主线程不会销毁除非退出程序,保证了程序一直运行并能响应事件和交互
      2. +
      3. 每一条线程都是唯一对应一个Runloop
      4. +
      5. 主线程不需要创建,因为在程序启动的时候就已经创建了,而子线程的需要手动创建 直接获取currentRunloop 方式(本质是使用了懒加载)
      6. +
      7. Runloop 对象是利用字典进行存储,因为一一对应,所有key 和value 就是线程和 他所对应的runloop

        +
      8. +
      9. Runloop 中 NSDefaultRunLoopMode 一般情况下主线程运行的 Mode

        +
      10. +
      11. UIInitializationRunLoopMode 初始化mode,通常不用
      12. +
      13. UITrackingRunLoopMode 界面跟踪, 用于scrollView 追踪触摸滑动,保证界面不受其他mode影响
      14. +
      15. GSEventReceiveRunLoopMode 用于接收系统事件内部 mode, 通常不用

        +
      16. +
      17. NSRunLoopCommonModes, 并不是某种具体的 mode ,开发中使用最多,他是一个组合 mode,组合了 NSDefaultRunLoopMode和 NSRunLoopCommonModes

        +
      18. +
      +
    • +
    • Runloop 相关类

      +
        +
      1. Source 事件源/输入源可以理解为 激发源
      2. +
      3. Timer 基于事件触发,CADisplayLink,NSTimer 都是加到了 Runloop,受 Mode 的影响,GCD 定时器不受Runloop 影响
      4. +
      5. Observer 相当于runloop 循环中的监听器,时刻监听当前Runloop的运行状态
      6. +
      7. 休眠没事情做的时候可以停下来,处于休眠状态
      8. +
      +
    • +
    • 使用场景

      +
        +
      1. 创建NSTimer
      2. +
      3. PersforSelector方法
      4. +
      5. 常驻线程:(创建后处于等待状态,有事件时响应事件)
          +
        • 实现后台收集用户停留时间或者是某个按钮的点击次数,如果使用主线程会不方便,使用runloop解决
        • +
        +
      6. +
      7. AutoreleasePool 自动释放池
      8. +
      9. UI更新
      10. +
      +
    • +
    + + +
    + +
    + +
    + + +
    - - - -
    - -
    - - -
    - - -

    - Swift3.0学习(三) -

    - - -
    - -
    - -

    面向对象–必选属性

    -

    构造函数时是分段构造,先构造子类,给’必选属性’设置初始值,然后再构造父类,
    定义全局变量(属性)时,需要在构造函数中设置初始化变量值
    super.init()是一个类构造结束
    重写: override 函数重写(覆盖),如果在重写的方法中 没有super, 父类相关的一些操作就不会被执行
    重载:函数名相同, 参数的类型 或者参数的个数不同 就形成了函数的重载
    注意:重载构造函数,并且父类默认的构造函数 init()不重写, 父类默认的构造函数就不能够被访问,不能够确保必选属性设置初始值,只能调用重载的构造函数
    面向对象–可选属性

    -

    使用KVC给对象设置值
    注意: 在swift中使用KVC 基本数据类型不能声明为可选项,必须设置为必选项给定初始值

    -

    KVC实现的流程
    遍历字典的键值 给对象发送 setValue: forKey消息
    如果key 对象的属性不存在就将消息转发给 setVale: forUndefinedKey:
    如果存在就直接设置值
    注意:setVale: forUndefinedKey: 默认抛出异常 不能够super,如果super 相当于没有实现此方法

    -

    面向对象–便利构造函数

    -

    作用:方便快捷的创建对象
    场景:
    可检查参数是否正确来实例化
    实例化控件
    特点
    必须以self来调用构造函数
    指定的构造函数不能被重写,也不能被super调用
    便利构造函数可被子类继承
    可以构造失败,返回nil
    面向对象 – 描述信息

    -

    重写 description 方法,返回值是string类型
    使用kvc讲对象属性转换为字典
    将字典转化为string类型

    -
    override var description: String {
    -        let keys = ["name","age","number","sex"]
    -
    -        // 对象转为字典
    -        let dict = self.dictionaryWithValuesForKeys(keys)
    -
    -        // 每个对象都有描述属性
    -        return dict.description
    -    }
    -

    面向对象 – 存储属性

    -

    面向对象 – 计算属性(readOnly)

    -

    只有getter,没有setter
    只能取值不能赋值
    每次调用都会被执行,消耗cpu
    不占内存空间,依赖其他的属性来计算
    面向对象 – didset 属性设置监察器

    -

    能获取 oldValue 和 newValue 的值
    通常用来重写setter方法,实现视图绑定模型
    在一个属性的didset中计算其他属性

    -

    缺点:消耗内存,被计算的属性占内存
    优点:减少了cpu消耗
    懒加载

    - - -
    - -
    - -
    - - - -
    - -
    - - -
    - - -

    - Swift3.0学习(二) -

    - - -
    - -
    - -

    字符串

    -

    字符串遍历
    字符串字节长度
    字符串长度

    -

    let string = “我是switf,语言”

    -

    // 字符串遍历
    for s in string.characters {
    print(s)
    }

    -

    // 字符串的长度
    let length = string.characters.count;
    print(length)

    -

    // 字符串字节长度 汉字对应3个字节,英文是1个字节
    let characterLength = string.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)
    print(characterLength)

    -

    字符串拼接

    -
    let str1 = "你好"
    -   let str2 = "不好"
    -   let a = 20
    -
    -   // 第一种拼接
    -   print("\(str1)\(str2)\(a)")
    -
    -   // 第二种拼接
    -   let str = str1 + str2
    -   let strA = str1 + String(a)
    -   print(str)
    -   print(strA)
    -

    截取子串 as 的使用

    -
    let subString = str1.substringToIndex("小程生生世世".endIndex);
    -   print(subString)
    -
    -   let subStr = str1.substringFromIndex("听说".startIndex)
    -   print(subStr)
    -
    -   let range = str1.rangeOfString("小程序")
    -   let subStr1 = str1.substringWithRange(range!)
    -
    -   print(subStr1)
    -
    -   // 截取到某一个位置
    -   let subStr2 = str1.substringFromIndex(str1.endIndex.advancedBy(-2))
    -   print(subStr2)
    -
    -   // 限制截取几个字符
    -   let subStr3 = str1.substringToIndex(str1.startIndex.advancedBy(5, limit: str1.characters.endIndex))
    -   print(subStr3)
    -String 和 NSString 之间的 使用 as 无缝转换
    -let helloString = "我们一起飞"
    -(helloString as NSString).substringWithRange(NSMakeRange(2, 3))
    -数组
    -

    可以放不同类型的元素
    let不可变数组,var可变数组

    -
    // 初始化一个空数组 [String]声明是一个装有字符串的数组类型
    -   var emptyArray: [String] = [String]()
    -
    -// 可变数组
    -   var array = ["哈哈","呵呵","嘿嘿"]
    -   array.append("咯咯")
    -   print("array = \(array)")
    -
    -   // 不可变数组
    -   let array2 = ["可可","噗噗"]
    -
    -   // 数组拼接
    -   let arr = array + array2
    -   print("arr = \(arr)")
    -
    -   array += array2
    -   print("array = \(array)")
    -

    数组遍历

    -
    // 1. 
    -   for i in 0 ..< tempArray.count {
    -       print(tempArray[i])
    -   }
    -
    -
    -   // 2. 
    -   for obj in tempArray {
    -       print("第二种方法:\(obj)")
    -   }
    -
    -
    -   // 3. 快速便利
    -   for (index,value) in tempArray.enumerate() {
    -       print("index = \(index), value = \(value)")
    -   }
    -

    字典和OC类似

    -

    函数调用

    -

    必须在函数声明的下面调用内部函数

    -

    可以定义函数的外部参数和内部参数

    -

    可以在参数前面加上 ‘# ’ 表明函数的外部参数和内部参数名称一样 swift3.0 以后废弃

    -

    无参无返回值
    三种写法

    -
    func demo9() -> Void {
    -       print("无参无返回值第一种写法")
    -   }
    -
    -
    -   func demo_9() -> () {
    -       print("无参无返回值第二种写法")
    -   }
    -
    -   func demo_99() {
    -       print("无参无返回值第三种写法")
    -   }
    -

    有参有返回值

    -
    func demo10(width a: Int, height b: Int) -> Int {
    -   return a * b
    -

    }

    -

    闭包

    -

    和OC中block类似,
    和在函数中调用一个内部函数原理相同
    可当作参数传递
    在需要时执行闭包实现回调

    -
    // 定义闭包
    -let closure = {() -> () in
    -       print("闭包实现")
    -   }
    -
    -   // 执行闭包
    -   closure()
    -

    注意循环引用

    -

    只有两个对象相互强引用或者三个对象相互强引用形成闭环时才会形成循环引用

    -

    weak 和 unsafe_unretained的区别

    -

    __weak iOS5.0 推出,当对象被系统回收时,对象的地址会自动指向nil

    -

    ____unsafe_unretained iOS4.0 推出,当对象被系统回收时,对象的地址不会自动指向nil,会造成野指针访问
    解决办法:(官方推荐)weak - strong -dance 解决循环引用,AFNetworking中

    -

    尾随闭包

    -

    函数的最后一个参数时闭包的时候,函数的参数 ‘()’可以提前关闭,闭包写在 ‘()’后面,当作尾随闭包来使用

    -

    swift 和 OC 区别

    -

    swift 和 OC语法的快速的对比

    -

    XXX.init(xxx) ==> XXX.(xxx)
    selector 类型 ==> ‘函数名’
    对象方法的调用 是 ‘.’ self是可以省略的
    枚举 枚举名 + 枚举值的名 => .枚举值的名
    常量 和变量

    -

    let 声明常量 var 声明变量
    变量/常量的类型是自动推到的
    不同类型之间不能够直接运算 需要手动转换数据类型
    可选项
    ‘?’ 表示表示可选项 可能有值 可能为 nil
    可选项会自动带上 Optional 字样
    可选项不能够直接参与运算 需要强制解包
    ‘!’ 表示强制解包 获取可选项中具体的值
    ‘??’ 快速判断可选项是否为nil 如果为 nil 就去取 ‘??’后面的默认值
    控制流
    if 没有非零即真的概念 必须制定明确的条件
    if let 快速赋值 并且判断赋值对象是否为 nil 如果不为nil 就进入分之执行相关逻辑代码
    guard let 作用和 if let 相反 好处: 可以减少一层分支嵌套
    where 多重判断 注意 和 && || 不一样的
    循环

    -

    for in
    0..<10
    0…10
    字符串

    -

    更加轻量级 更加高效 是 结构体
    支持快速遍历
    长度
    拼接
    截取 as
    集合 let 声明不可变的集合 var 声明可变的集合

    -

    声明 []
    声明一个 空的集合
    增删改查
    合并
    遍历
    函数

    -

    func 函数名称(外部参数1 内部参数1: 类型, 外部参数2 内部参数2: 类型) -> Int {执行的代码}
    函数没有返回值的三种方式
    函数内部函数
    闭包 () -> () 没有参数没有返回值的闭包类型

    -

    提前准备好的一段可以执行代码块
    可以当做参数传递
    在需要的时候执行闭包产生回调的效果
    在闭包中使用self 需要考虑循环引用
    函数是一种特殊的闭包

    -

    block 的循环引用的解除
    weak 和 weak关键字作用类似 当属性对象被回收是 会自动指向nil
    _
    unsafeunretained 和 assgin关键字作用类似 当属性对象被回收是 不会自动指向nil, 会造成野指针访问 weak -strong - dance

    - - -
    - -
    - -
    - - -
    @@ -1142,23 +1116,23 @@

    Recent Posts

    diff --git a/page/7/index.html b/page/7/index.html index 6834392..8b7ad50 100644 --- a/page/7/index.html +++ b/page/7/index.html @@ -62,6 +62,274 @@

    +
    + +
    + + +
    + + +

    + 知识点总结 +

    + + +
    + +
    + +

    开发中的知识点总结

    +

    textField

    +

    使用textField自定义搜索框,textField及leftView等属性
    使用leftView 时需要设置model为始终显示
    可以设置leftView的frame 调整图片大小
    IBInsepectable IBDesignable 的使用

    +

    给属性检查器面板中添加属性设置选项
    IBDesignalbe 实时显示UI控件,需要讲对应的xib和对应类绑定

    +

    增加分类

    +

    新建一个swift 文件,

    +

    使用extension,重写 get set方法
    使用newValue 时需要判断,注意返回值的类型是否匹配
    试图切换

    +

    一个控制器中加载不同视图,可以在loadView 方法中自定义根视图

    +

    loadView 使用注意事项
    如果实现loadView 未调用 super loadView ,sb/xib实效
    loadView调用时,如果根视图是nil(本质是未调用super loadView),会自动调用loadView 方法初始化根视图,形成递归调用
    判断当前登陆标识的状态确定是调用 super loadView 还是 把创建自定义的根视图赋值给 view
    VFL 的使用

    +

    使用之前需要先 关闭frame 的布局才能手动的添加约束 translatesAutoresizingMaskIntoConstraints = false
    基本动画使用

    + + +
    + +
    + +
    + + + +
    + +
    + + +
    + + +

    + Swift3.0学习(三) +

    + + +
    + +
    + +

    面向对象–必选属性

    +

    构造函数时是分段构造,先构造子类,给’必选属性’设置初始值,然后再构造父类,
    定义全局变量(属性)时,需要在构造函数中设置初始化变量值
    super.init()是一个类构造结束
    重写: override 函数重写(覆盖),如果在重写的方法中 没有super, 父类相关的一些操作就不会被执行
    重载:函数名相同, 参数的类型 或者参数的个数不同 就形成了函数的重载
    注意:重载构造函数,并且父类默认的构造函数 init()不重写, 父类默认的构造函数就不能够被访问,不能够确保必选属性设置初始值,只能调用重载的构造函数
    面向对象–可选属性

    +

    使用KVC给对象设置值
    注意: 在swift中使用KVC 基本数据类型不能声明为可选项,必须设置为必选项给定初始值

    +

    KVC实现的流程
    遍历字典的键值 给对象发送 setValue: forKey消息
    如果key 对象的属性不存在就将消息转发给 setVale: forUndefinedKey:
    如果存在就直接设置值
    注意:setVale: forUndefinedKey: 默认抛出异常 不能够super,如果super 相当于没有实现此方法

    +

    面向对象–便利构造函数

    +

    作用:方便快捷的创建对象
    场景:
    可检查参数是否正确来实例化
    实例化控件
    特点
    必须以self来调用构造函数
    指定的构造函数不能被重写,也不能被super调用
    便利构造函数可被子类继承
    可以构造失败,返回nil
    面向对象 – 描述信息

    +

    重写 description 方法,返回值是string类型
    使用kvc讲对象属性转换为字典
    将字典转化为string类型

    +
    override var description: String {
    +        let keys = ["name","age","number","sex"]
    +
    +        // 对象转为字典
    +        let dict = self.dictionaryWithValuesForKeys(keys)
    +
    +        // 每个对象都有描述属性
    +        return dict.description
    +    }
    +

    面向对象 – 存储属性

    +

    面向对象 – 计算属性(readOnly)

    +

    只有getter,没有setter
    只能取值不能赋值
    每次调用都会被执行,消耗cpu
    不占内存空间,依赖其他的属性来计算
    面向对象 – didset 属性设置监察器

    +

    能获取 oldValue 和 newValue 的值
    通常用来重写setter方法,实现视图绑定模型
    在一个属性的didset中计算其他属性

    +

    缺点:消耗内存,被计算的属性占内存
    优点:减少了cpu消耗
    懒加载

    + + +
    + +
    + +
    + + + +
    + +
    + + +
    + + +

    + Swift3.0学习(二) +

    + + +
    + +
    + +

    字符串

    +

    字符串遍历
    字符串字节长度
    字符串长度

    +

    let string = “我是switf,语言”

    +

    // 字符串遍历
    for s in string.characters {
    print(s)
    }

    +

    // 字符串的长度
    let length = string.characters.count;
    print(length)

    +

    // 字符串字节长度 汉字对应3个字节,英文是1个字节
    let characterLength = string.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)
    print(characterLength)

    +

    字符串拼接

    +
    let str1 = "你好"
    +   let str2 = "不好"
    +   let a = 20
    +
    +   // 第一种拼接
    +   print("\(str1)\(str2)\(a)")
    +
    +   // 第二种拼接
    +   let str = str1 + str2
    +   let strA = str1 + String(a)
    +   print(str)
    +   print(strA)
    +

    截取子串 as 的使用

    +
    let subString = str1.substringToIndex("小程生生世世".endIndex);
    +   print(subString)
    +
    +   let subStr = str1.substringFromIndex("听说".startIndex)
    +   print(subStr)
    +
    +   let range = str1.rangeOfString("小程序")
    +   let subStr1 = str1.substringWithRange(range!)
    +
    +   print(subStr1)
    +
    +   // 截取到某一个位置
    +   let subStr2 = str1.substringFromIndex(str1.endIndex.advancedBy(-2))
    +   print(subStr2)
    +
    +   // 限制截取几个字符
    +   let subStr3 = str1.substringToIndex(str1.startIndex.advancedBy(5, limit: str1.characters.endIndex))
    +   print(subStr3)
    +String 和 NSString 之间的 使用 as 无缝转换
    +let helloString = "我们一起飞"
    +(helloString as NSString).substringWithRange(NSMakeRange(2, 3))
    +数组
    +

    可以放不同类型的元素
    let不可变数组,var可变数组

    +
    // 初始化一个空数组 [String]声明是一个装有字符串的数组类型
    +   var emptyArray: [String] = [String]()
    +
    +// 可变数组
    +   var array = ["哈哈","呵呵","嘿嘿"]
    +   array.append("咯咯")
    +   print("array = \(array)")
    +
    +   // 不可变数组
    +   let array2 = ["可可","噗噗"]
    +
    +   // 数组拼接
    +   let arr = array + array2
    +   print("arr = \(arr)")
    +
    +   array += array2
    +   print("array = \(array)")
    +

    数组遍历

    +
    // 1. 
    +   for i in 0 ..< tempArray.count {
    +       print(tempArray[i])
    +   }
    +
    +
    +   // 2. 
    +   for obj in tempArray {
    +       print("第二种方法:\(obj)")
    +   }
    +
    +
    +   // 3. 快速便利
    +   for (index,value) in tempArray.enumerate() {
    +       print("index = \(index), value = \(value)")
    +   }
    +

    字典和OC类似

    +

    函数调用

    +

    必须在函数声明的下面调用内部函数

    +

    可以定义函数的外部参数和内部参数

    +

    可以在参数前面加上 ‘# ’ 表明函数的外部参数和内部参数名称一样 swift3.0 以后废弃

    +

    无参无返回值
    三种写法

    +
    func demo9() -> Void {
    +       print("无参无返回值第一种写法")
    +   }
    +
    +
    +   func demo_9() -> () {
    +       print("无参无返回值第二种写法")
    +   }
    +
    +   func demo_99() {
    +       print("无参无返回值第三种写法")
    +   }
    +

    有参有返回值

    +
    func demo10(width a: Int, height b: Int) -> Int {
    +   return a * b
    +

    }

    +

    闭包

    +

    和OC中block类似,
    和在函数中调用一个内部函数原理相同
    可当作参数传递
    在需要时执行闭包实现回调

    +
    // 定义闭包
    +let closure = {() -> () in
    +       print("闭包实现")
    +   }
    +
    +   // 执行闭包
    +   closure()
    +

    注意循环引用

    +

    只有两个对象相互强引用或者三个对象相互强引用形成闭环时才会形成循环引用

    +

    weak 和 unsafe_unretained的区别

    +

    __weak iOS5.0 推出,当对象被系统回收时,对象的地址会自动指向nil

    +

    ____unsafe_unretained iOS4.0 推出,当对象被系统回收时,对象的地址不会自动指向nil,会造成野指针访问
    解决办法:(官方推荐)weak - strong -dance 解决循环引用,AFNetworking中

    +

    尾随闭包

    +

    函数的最后一个参数时闭包的时候,函数的参数 ‘()’可以提前关闭,闭包写在 ‘()’后面,当作尾随闭包来使用

    +

    swift 和 OC 区别

    +

    swift 和 OC语法的快速的对比

    +

    XXX.init(xxx) ==> XXX.(xxx)
    selector 类型 ==> ‘函数名’
    对象方法的调用 是 ‘.’ self是可以省略的
    枚举 枚举名 + 枚举值的名 => .枚举值的名
    常量 和变量

    +

    let 声明常量 var 声明变量
    变量/常量的类型是自动推到的
    不同类型之间不能够直接运算 需要手动转换数据类型
    可选项
    ‘?’ 表示表示可选项 可能有值 可能为 nil
    可选项会自动带上 Optional 字样
    可选项不能够直接参与运算 需要强制解包
    ‘!’ 表示强制解包 获取可选项中具体的值
    ‘??’ 快速判断可选项是否为nil 如果为 nil 就去取 ‘??’后面的默认值
    控制流
    if 没有非零即真的概念 必须制定明确的条件
    if let 快速赋值 并且判断赋值对象是否为 nil 如果不为nil 就进入分之执行相关逻辑代码
    guard let 作用和 if let 相反 好处: 可以减少一层分支嵌套
    where 多重判断 注意 和 && || 不一样的
    循环

    +

    for in
    0..<10
    0…10
    字符串

    +

    更加轻量级 更加高效 是 结构体
    支持快速遍历
    长度
    拼接
    截取 as
    集合 let 声明不可变的集合 var 声明可变的集合

    +

    声明 []
    声明一个 空的集合
    增删改查
    合并
    遍历
    函数

    +

    func 函数名称(外部参数1 内部参数1: 类型, 外部参数2 内部参数2: 类型) -> Int {执行的代码}
    函数没有返回值的三种方式
    函数内部函数
    闭包 () -> () 没有参数没有返回值的闭包类型

    +

    提前准备好的一段可以执行代码块
    可以当做参数传递
    在需要的时候执行闭包产生回调的效果
    在闭包中使用self 需要考虑循环引用
    函数是一种特殊的闭包

    +

    block 的循环引用的解除
    weak 和 weak关键字作用类似 当属性对象被回收是 会自动指向nil
    _
    unsafeunretained 和 assgin关键字作用类似 当属性对象被回收是 不会自动指向nil, 会造成野指针访问 weak -strong - dance

    + + +
    + +
    + +
    + + +
    - Share + Share @@ -394,7 +662,7 @@

    - Share + Share @@ -480,7 +748,7 @@

    - Share + Share @@ -530,7 +798,7 @@

    - Share + Share @@ -615,7 +883,7 @@

    - Share + Share @@ -627,357 +895,11 @@

    - -
    - - -
    - - -

    - Quartz2D绘制曲线 -

    - - -
    - -
    - -

    使用函数绘制曲线

    -

    Quartz 2D提供了CGContextAddCurveToPoint() 函数和CGContextAddQuadCurveToPoint()两个函数来向当前上下文添加曲线,前者用于添加贝塞尔曲线,后者用于添加二次曲线。
    确定一条贝塞尔曲线需要4个点:开始点、第一个控制点、第二个控制点和结束点。

    -

    确定一条二次曲线需要三个点:开始点、控制点和结束点。

    -

    添加贝塞尔曲线

    -
    CGContextAddCurveToPoint()
    -
    -CGContextRef ctx = UIGraphicsGetCurrentContext();
    -
    -// 添加曲线路径
    -// 设置起点
    -CGContextMoveToPoint(ctx, 0, 0);
    -
      -
    • 参数1: 上下文对象
    • -
    • 参数2: 控制点1X坐标
    • -
    • 参数3: 控制点1Y坐标
    • -
    • 参数4: 控制点2X坐标
    • -
    • 参数5: 控制点2Y坐标
    • -
    • 参数6: 终点x坐标
    • -
    • 参数7: 终点Y坐标
    • -
    -

    code:

    -
    CGContextAddCurveToPoint(ctx, 30, 200, 300, 20, 300, 300);
    -
    -// 设置颜色
    -[[UIColor magentaColor] setStroke];
    -
    -// 渲染
    -CGContextStrokePath(ctx);
    -
    -// 关闭图形上下文
    -CGContextClosePath(ctx);
    -添加二次曲线
    -CGContextAddQuadCurveToPoint()
    -
    -// 获取图形上下文
    -CGContextRef ctx = UIGraphicsGetCurrentContext();
    -
    -// 起始点
    -CGContextMoveToPoint(ctx, 20, 10);
    -
      -
    • 参数: 图形上下文
    • -
    • 参数: 控制点坐标
    • -
    • 参数: 结束点坐标
    • -
    -

    添加二次曲线

    -
    CGContextAddQuadCurveToPoint(ctx, 30, 200, 200, 40);
    -
    -[[UIColor cyanColor] setStroke];
    -CGContextStrokePath(ctx);
    -// 关闭图形上下文
    -CGContextClosePath(ctx);
    -

    绘制形状

    -

    使用绘制二次曲线的函数绘制花瓣

    -

    程序中CGContextAddFlower代码分别添加5瓣花朵路径、6瓣花朵路径、7瓣花朵路径,然后使用不同的颜色来填充这些路径。注意到上面的程序并未在每次添加花朵路径后立即关闭,这也是允许的,而且每次填充路径时并不会再次填充前一次已经填充过的路径。这是因为只用程序绘制了CGContextRef当前所包含的路径,系统会自动清除已经绘制的路径。

    -

    注意:每次绘制完成后,CGContextRef会自动清除已经绘制完成的路径

    -

    该方法负责绘制花朵。
    n:该参数控制花朵的花瓣数;dx、dy:控制花朵的位置;size:控制花朵的大小;
    length:控制花瓣的长度

    -
    void CGContextAddFlower(CGContextRef c , NSInteger n
    -                        , CGFloat dx , CGFloat dy , CGFloat size , CGFloat length)
    -{
    -    CGContextMoveToPoint(c , dx , dy + size);  // 移动到指定点
    -    CGFloat dig = 2 * M_PI / n;
    -    // 采用循环添加n段二次曲线路径
    -    for(int i = 1; i < n + 1 ; i++)
    -    {
    -        // 计算控制点坐标
    -        CGFloat ctrlX = sin((i - 0.5) * dig) * length + dx;
    -        CGFloat ctrlY= cos((i - 0.5 ) * dig) * length + dy;
    -        // 计算结束点的坐标
    -        CGFloat x = sin(i * dig) * size + dx;
    -        CGFloat y =cos(i * dig) * size + dy;
    -        // 添加二次曲线路径
    -        CGContextAddQuadCurveToPoint(c, ctrlX , ctrlY , x , y);
    -    }
    -}
    -

    绘制花瓣

    -
    - (void)drawStar
    -{
    -    CGContextRef ctx = UIGraphicsGetCurrentContext();  // 获取绘图的CGContextRef
    -    CGContextBeginPath(ctx);  // 开始添加路径
    -    CGContextAddFlower(ctx , 5 , 50 , 100 , 30 , 80);  // 添加5瓣花朵的路径
    -    CGContextSetRGBFillColor(ctx, 1, 0, 0, 1);  // 设置填充颜色
    -    CGContextFillPath(ctx);
    -    CGContextAddFlower(ctx , 6 , 160 , 100 , 30 , 80);  // 添加6瓣花朵的路径
    -    CGContextSetRGBFillColor(ctx, 1, 1, 0, 1);  // 设置填充颜色
    -    CGContextFillPath(ctx);
    -    CGContextAddFlower(ctx , 7 , 270 , 100 , 30 , 80);  // 添加7瓣花朵的路径
    -    CGContextSetRGBFillColor(ctx, 1, 0, 1, 1);  // 设置填充颜色
    -    CGContextFillPath(ctx);
    -    CGContextClosePath(ctx);  // 关闭路径
    -}
    -
    - -
    - -
    - -

    - - - -
    - -
    - - -
    - - -

    - Quartz2D 初识 -

    - - -
    - -
    - -

    Quartz2D 二维绘图引擎,支持iOS和Mac系统么,可以实现的功能有

    -
      -
    • 绘制图形: 线条,三角形,矩形,圆,弧等
    • -
    • 绘制文字
    • -
    • 绘制图像或者图片
    • -
    • 读取生成PDF
    • -
    • 截图/裁剪图片
    • -
    • 自定义UI控件
    • -
    • 手势解锁
    • -
    • 图形上下文
    • -
    -

    保存绘图信息和状态

    确定输出目标(PDF,Bitmap或者显示器)

    -
    Bitmap Graphics Context
    -PDF Granhics Context
    -Window Graphics Context
    -Layer Graphics Context
    -

    绘制线段

    -
    - (void)drawLine {
    -    // 获取图形上下文
    -    // 所用的是UIGraphics上下文
    -    CGContextRef ctx = UIGraphicsGetCurrentContext();
    -
    -    // 创建描述路径
    -    CGMutablePathRef path = CGPathCreateMutable();
    -    // 设置起点
    -    // path : 表示给那个路径设置起点
    -    CGPathMoveToPoint(path, NULL, 10, 10);
    -    CGPathAddLineToPoint(path, NULL, 80, 80);
    -
    -    // 添加路径到上下文
    -    CGContextAddPath(ctx, path);
    -
    -    // 渲染
    -    CGContextStrokePath(ctx);
    -}
    -

    系统底层自动将路径添加到图形上下文

    -
    - (void)drawLine2 {
    -
    -    CGContextRef ctx = UIGraphicsGetCurrentContext();
    -
    -    // 描述路径
    -    // 此方法底层会自动将路径添加到图形上下文
    -    CGContextMoveToPoint(ctx, 50, 50);
    -    CGContextAddLineToPoint(ctx, 100, 100);
    -
    -    CGContextSetLineWidth(ctx, 5);
    -
    -    [[UIColor redColor] set];
    -    // 渲染
    -    CGContextStrokePath(ctx);
    -}
    -

    使用C语言函数的封装

    -
    - (void)drawLine3 {
    -    // 使用UIKit 已经封装的功能,面向对象
    -    // 贝塞尔路径
    -
    -    UIBezierPath *path = [UIBezierPath bezierPath];
    -    [path moveToPoint:CGPointMake(10, 10)];
    -    [path addLineToPoint:CGPointMake(50, 50)];
    -
    -    // 绘制路径
    -    [path stroke];
    -}
    -

    绘制多条线段

    -
      -
    • 绘制多条线,状态不好管理
    • -
    • 默认下一条线的起点是上一条线的终点
    • -
    • 绘制多天不连接的线时需要重新设置起点
    • -
    • 为了方便管理,应该让每一条线对应一条路径
    • -
    • 使用UIBeizerPath
    • -
    -

    code:

    -
    - (void)drawMoreLine {
    -    CGContextRef ctx = UIGraphicsGetCurrentContext();
    -
    -    // 描述路径
    -    CGContextMoveToPoint(ctx, 20, 20);
    -    CGContextAddLineToPoint(ctx, 100, 100);
    -
    -    // 设置第二条线的起点
    -    CGContextMoveToPoint(ctx, 20, 30);
    -    CGContextAddLineToPoint(ctx, 150, 100);
    -
    -    // 设置绘图状态要在渲染之前,都是给上下文设置
    -    // 描边颜色
    -    [[UIColor greenColor] setStroke];
    -    // 线宽
    -    CGContextSetLineWidth(ctx, 10);
    -    // 连接样式
    -    // kCGLineJoinMiter,    斜接
    -    // kCGLineJoinRound,    圆角
    -    // kCGLineJoinBevel     平切
    -    CGContextSetLineJoin(ctx, kCGLineJoinBevel);
    -    // 顶角样式,线的顶端效果
    -    CGContextSetLineCap(ctx, kCGLineCapRound);
    -
    -    // 渲染
    -    CGContextStrokePath(ctx);
    -}
    -

    使用UIBezierPath 绘制多条直线并设置不同状态

    -
    - (void)drawUIBezierPahtState {
    -
    -    // 第一条线
    -    UIBezierPath *path = [UIBezierPath bezierPath];
    -    [path moveToPoint:CGPointMake(10, 10)];
    -    [path addLineToPoint:CGPointMake(80, 80)];
    -
    -    path.lineWidth = 20;
    -    path.lineJoinStyle = kCGLineJoinRound;
    -    path.lineCapStyle = kCGLineCapRound;
    -    [[UIColor redColor] setStroke];
    -
    -    [path stroke];
    -
    -    // 第二条线
    -    {
    -        UIBezierPath *path = [UIBezierPath bezierPath];
    -        [path moveToPoint:CGPointMake(40, 70)];
    -        [path addLineToPoint:CGPointMake(150, 40)];
    -
    -        path.lineWidth = 20;
    -        path.lineJoinStyle = kCGLineJoinRound;
    -        path.lineCapStyle = kCGLineCapRound;
    -        [[UIColor greenColor] setStroke];
    -
    -        [path stroke];
    -    }
    -}
    -
    - -
    - -
    - -
    - - - -
    - -
    - - -
    - - -

    - OC中运行时 -

    - - -
    - -
    - -

    RunTime
    对于C语言,函数的调用在编译的时候会决定调用哪个函数,而对于面向对象的语言对于[调用某个对象的方法或函数]就叫「消息传递」。所以在 Java,C++ 这些语言里[调用某个对象的方法或函数]和 Objective-C 里[向某个对象发送消息]在概念上就是一样的事情,

    -

    RunTime 简介

      -
    • RunTime 简称运行时,OC中通常叫运行时机制,主要的是消息机制
    • -
    • 消息机制:在OC中,属于动态调用,在编译的时候并不能决定真正调用哪个函数,只有在真正运行的时候才会根据函数的名称找到对应的函数来调用
    • -
    -

    RunTime 作用

      -
    • 发送消息:让对象发送消息
    • -
    • 交换方法
    • -
    • 动态添加方法
    • -
    • 动态获取属性和方法
    • -
    • 字典转模型
    • -
    • 给分类添加属性
    • -
    -

    原理:给分类添加一个属性,重写了setter、getter方法,但是没有生成 _成员变量,需要通过运行时关联到分类中, 其本质上就是给这个类添加关联,存值和取值
    code

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    self.oldurlPath = urlPath;

    - (void)setOldurlPath:(NSString *)oldurlPath {

    // RunTime 关联
    // 第一个参数:给哪个对象添加关联
    // 第二个参数:关联的key,通过这个key获取
    // 第三个参数:关联的value
    // 第四个参数:关联的策略
    objc_setAssociatedObject(self, key, oldurlPath, OBJC_ASSOCIATION_COPY_NONATOMIC);
    }

    - (NSString *)oldurlPath {
    return objc_getAssociatedObject(self, key);
    }
    - -
    - -
    - -
    - - - @@ -1023,23 +945,23 @@

    Recent Posts

    diff --git a/page/8/index.html b/page/8/index.html index 6011340..7be7f4b 100644 --- a/page/8/index.html +++ b/page/8/index.html @@ -62,6 +62,352 @@

    +
    + +
    + + +
    + + +

    + Quartz2D绘制曲线 +

    + + +
    + +
    + +

    使用函数绘制曲线

    +

    Quartz 2D提供了CGContextAddCurveToPoint() 函数和CGContextAddQuadCurveToPoint()两个函数来向当前上下文添加曲线,前者用于添加贝塞尔曲线,后者用于添加二次曲线。
    确定一条贝塞尔曲线需要4个点:开始点、第一个控制点、第二个控制点和结束点。

    +

    确定一条二次曲线需要三个点:开始点、控制点和结束点。

    +

    添加贝塞尔曲线

    +
    CGContextAddCurveToPoint()
    +
    +CGContextRef ctx = UIGraphicsGetCurrentContext();
    +
    +// 添加曲线路径
    +// 设置起点
    +CGContextMoveToPoint(ctx, 0, 0);
    +
      +
    • 参数1: 上下文对象
    • +
    • 参数2: 控制点1X坐标
    • +
    • 参数3: 控制点1Y坐标
    • +
    • 参数4: 控制点2X坐标
    • +
    • 参数5: 控制点2Y坐标
    • +
    • 参数6: 终点x坐标
    • +
    • 参数7: 终点Y坐标
    • +
    +

    code:

    +
    CGContextAddCurveToPoint(ctx, 30, 200, 300, 20, 300, 300);
    +
    +// 设置颜色
    +[[UIColor magentaColor] setStroke];
    +
    +// 渲染
    +CGContextStrokePath(ctx);
    +
    +// 关闭图形上下文
    +CGContextClosePath(ctx);
    +添加二次曲线
    +CGContextAddQuadCurveToPoint()
    +
    +// 获取图形上下文
    +CGContextRef ctx = UIGraphicsGetCurrentContext();
    +
    +// 起始点
    +CGContextMoveToPoint(ctx, 20, 10);
    +
      +
    • 参数: 图形上下文
    • +
    • 参数: 控制点坐标
    • +
    • 参数: 结束点坐标
    • +
    +

    添加二次曲线

    +
    CGContextAddQuadCurveToPoint(ctx, 30, 200, 200, 40);
    +
    +[[UIColor cyanColor] setStroke];
    +CGContextStrokePath(ctx);
    +// 关闭图形上下文
    +CGContextClosePath(ctx);
    +

    绘制形状

    +

    使用绘制二次曲线的函数绘制花瓣

    +

    程序中CGContextAddFlower代码分别添加5瓣花朵路径、6瓣花朵路径、7瓣花朵路径,然后使用不同的颜色来填充这些路径。注意到上面的程序并未在每次添加花朵路径后立即关闭,这也是允许的,而且每次填充路径时并不会再次填充前一次已经填充过的路径。这是因为只用程序绘制了CGContextRef当前所包含的路径,系统会自动清除已经绘制的路径。

    +

    注意:每次绘制完成后,CGContextRef会自动清除已经绘制完成的路径

    +

    该方法负责绘制花朵。
    n:该参数控制花朵的花瓣数;dx、dy:控制花朵的位置;size:控制花朵的大小;
    length:控制花瓣的长度

    +
    void CGContextAddFlower(CGContextRef c , NSInteger n
    +                        , CGFloat dx , CGFloat dy , CGFloat size , CGFloat length)
    +{
    +    CGContextMoveToPoint(c , dx , dy + size);  // 移动到指定点
    +    CGFloat dig = 2 * M_PI / n;
    +    // 采用循环添加n段二次曲线路径
    +    for(int i = 1; i < n + 1 ; i++)
    +    {
    +        // 计算控制点坐标
    +        CGFloat ctrlX = sin((i - 0.5) * dig) * length + dx;
    +        CGFloat ctrlY= cos((i - 0.5 ) * dig) * length + dy;
    +        // 计算结束点的坐标
    +        CGFloat x = sin(i * dig) * size + dx;
    +        CGFloat y =cos(i * dig) * size + dy;
    +        // 添加二次曲线路径
    +        CGContextAddQuadCurveToPoint(c, ctrlX , ctrlY , x , y);
    +    }
    +}
    +

    绘制花瓣

    +
    - (void)drawStar
    +{
    +    CGContextRef ctx = UIGraphicsGetCurrentContext();  // 获取绘图的CGContextRef
    +    CGContextBeginPath(ctx);  // 开始添加路径
    +    CGContextAddFlower(ctx , 5 , 50 , 100 , 30 , 80);  // 添加5瓣花朵的路径
    +    CGContextSetRGBFillColor(ctx, 1, 0, 0, 1);  // 设置填充颜色
    +    CGContextFillPath(ctx);
    +    CGContextAddFlower(ctx , 6 , 160 , 100 , 30 , 80);  // 添加6瓣花朵的路径
    +    CGContextSetRGBFillColor(ctx, 1, 1, 0, 1);  // 设置填充颜色
    +    CGContextFillPath(ctx);
    +    CGContextAddFlower(ctx , 7 , 270 , 100 , 30 , 80);  // 添加7瓣花朵的路径
    +    CGContextSetRGBFillColor(ctx, 1, 0, 1, 1);  // 设置填充颜色
    +    CGContextFillPath(ctx);
    +    CGContextClosePath(ctx);  // 关闭路径
    +}
    +
    + +
    + +
    + +
    + + + +
    + +
    + + +
    + + +

    + Quartz2D 初识 +

    + + +
    + +
    + +

    Quartz2D 二维绘图引擎,支持iOS和Mac系统么,可以实现的功能有

    +
      +
    • 绘制图形: 线条,三角形,矩形,圆,弧等
    • +
    • 绘制文字
    • +
    • 绘制图像或者图片
    • +
    • 读取生成PDF
    • +
    • 截图/裁剪图片
    • +
    • 自定义UI控件
    • +
    • 手势解锁
    • +
    • 图形上下文
    • +
    +

    保存绘图信息和状态

    确定输出目标(PDF,Bitmap或者显示器)

    +
    Bitmap Graphics Context
    +PDF Granhics Context
    +Window Graphics Context
    +Layer Graphics Context
    +

    绘制线段

    +
    - (void)drawLine {
    +    // 获取图形上下文
    +    // 所用的是UIGraphics上下文
    +    CGContextRef ctx = UIGraphicsGetCurrentContext();
    +
    +    // 创建描述路径
    +    CGMutablePathRef path = CGPathCreateMutable();
    +    // 设置起点
    +    // path : 表示给那个路径设置起点
    +    CGPathMoveToPoint(path, NULL, 10, 10);
    +    CGPathAddLineToPoint(path, NULL, 80, 80);
    +
    +    // 添加路径到上下文
    +    CGContextAddPath(ctx, path);
    +
    +    // 渲染
    +    CGContextStrokePath(ctx);
    +}
    +

    系统底层自动将路径添加到图形上下文

    +
    - (void)drawLine2 {
    +
    +    CGContextRef ctx = UIGraphicsGetCurrentContext();
    +
    +    // 描述路径
    +    // 此方法底层会自动将路径添加到图形上下文
    +    CGContextMoveToPoint(ctx, 50, 50);
    +    CGContextAddLineToPoint(ctx, 100, 100);
    +
    +    CGContextSetLineWidth(ctx, 5);
    +
    +    [[UIColor redColor] set];
    +    // 渲染
    +    CGContextStrokePath(ctx);
    +}
    +

    使用C语言函数的封装

    +
    - (void)drawLine3 {
    +    // 使用UIKit 已经封装的功能,面向对象
    +    // 贝塞尔路径
    +
    +    UIBezierPath *path = [UIBezierPath bezierPath];
    +    [path moveToPoint:CGPointMake(10, 10)];
    +    [path addLineToPoint:CGPointMake(50, 50)];
    +
    +    // 绘制路径
    +    [path stroke];
    +}
    +

    绘制多条线段

    +
      +
    • 绘制多条线,状态不好管理
    • +
    • 默认下一条线的起点是上一条线的终点
    • +
    • 绘制多天不连接的线时需要重新设置起点
    • +
    • 为了方便管理,应该让每一条线对应一条路径
    • +
    • 使用UIBeizerPath
    • +
    +

    code:

    +
    - (void)drawMoreLine {
    +    CGContextRef ctx = UIGraphicsGetCurrentContext();
    +
    +    // 描述路径
    +    CGContextMoveToPoint(ctx, 20, 20);
    +    CGContextAddLineToPoint(ctx, 100, 100);
    +
    +    // 设置第二条线的起点
    +    CGContextMoveToPoint(ctx, 20, 30);
    +    CGContextAddLineToPoint(ctx, 150, 100);
    +
    +    // 设置绘图状态要在渲染之前,都是给上下文设置
    +    // 描边颜色
    +    [[UIColor greenColor] setStroke];
    +    // 线宽
    +    CGContextSetLineWidth(ctx, 10);
    +    // 连接样式
    +    // kCGLineJoinMiter,    斜接
    +    // kCGLineJoinRound,    圆角
    +    // kCGLineJoinBevel     平切
    +    CGContextSetLineJoin(ctx, kCGLineJoinBevel);
    +    // 顶角样式,线的顶端效果
    +    CGContextSetLineCap(ctx, kCGLineCapRound);
    +
    +    // 渲染
    +    CGContextStrokePath(ctx);
    +}
    +

    使用UIBezierPath 绘制多条直线并设置不同状态

    +
    - (void)drawUIBezierPahtState {
    +
    +    // 第一条线
    +    UIBezierPath *path = [UIBezierPath bezierPath];
    +    [path moveToPoint:CGPointMake(10, 10)];
    +    [path addLineToPoint:CGPointMake(80, 80)];
    +
    +    path.lineWidth = 20;
    +    path.lineJoinStyle = kCGLineJoinRound;
    +    path.lineCapStyle = kCGLineCapRound;
    +    [[UIColor redColor] setStroke];
    +
    +    [path stroke];
    +
    +    // 第二条线
    +    {
    +        UIBezierPath *path = [UIBezierPath bezierPath];
    +        [path moveToPoint:CGPointMake(40, 70)];
    +        [path addLineToPoint:CGPointMake(150, 40)];
    +
    +        path.lineWidth = 20;
    +        path.lineJoinStyle = kCGLineJoinRound;
    +        path.lineCapStyle = kCGLineCapRound;
    +        [[UIColor greenColor] setStroke];
    +
    +        [path stroke];
    +    }
    +}
    +
    + +
    + +
    + +
    + + + +
    + +
    + + +
    + + +

    + OC中运行时 +

    + + +
    + +
    + +

    RunTime
    对于C语言,函数的调用在编译的时候会决定调用哪个函数,而对于面向对象的语言对于[调用某个对象的方法或函数]就叫「消息传递」。所以在 Java,C++ 这些语言里[调用某个对象的方法或函数]和 Objective-C 里[向某个对象发送消息]在概念上就是一样的事情,

    +

    RunTime 简介

      +
    • RunTime 简称运行时,OC中通常叫运行时机制,主要的是消息机制
    • +
    • 消息机制:在OC中,属于动态调用,在编译的时候并不能决定真正调用哪个函数,只有在真正运行的时候才会根据函数的名称找到对应的函数来调用
    • +
    +

    RunTime 作用

      +
    • 发送消息:让对象发送消息
    • +
    • 交换方法
    • +
    • 动态添加方法
    • +
    • 动态获取属性和方法
    • +
    • 字典转模型
    • +
    • 给分类添加属性
    • +
    +

    原理:给分类添加一个属性,重写了setter、getter方法,但是没有生成 _成员变量,需要通过运行时关联到分类中, 其本质上就是给这个类添加关联,存值和取值
    code

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    self.oldurlPath = urlPath;

    - (void)setOldurlPath:(NSString *)oldurlPath {

    // RunTime 关联
    // 第一个参数:给哪个对象添加关联
    // 第二个参数:关联的key,通过这个key获取
    // 第三个参数:关联的value
    // 第四个参数:关联的策略
    objc_setAssociatedObject(self, key, oldurlPath, OBJC_ASSOCIATION_COPY_NONATOMIC);
    }

    - (NSString *)oldurlPath {
    return objc_getAssociatedObject(self, key);
    }
    + +
    + +
    + +
    + + +
    - - - -
    - -
    - - -
    - - -

    - 本地缓存 -

    - - -
    - -
    - -

    保存图片到沙盒

    1
    2
    3
    4
    5
    6
    - (void)saveImage:(UIImage *)image InSanBoxWithIndexPath:(NSInteger)indexPath {

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
    NSString *filePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:[NSString stringWithFormat:@"pic_%ld.png", indexPath]]; // 保存文件的名称
    [UIImagePNGRepresentation(image)writeToFile:filePath atomically:YES];
    }
    -

    获取沙盒中的图片

    1
    2
    3
    4
    5
    6
    7
    - (UIImage *)fetchImageFromSanBoxWithIndexPath:(NSInteger)indexPath
    {
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
    NSString *filePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:[NSString stringWithFormat:@"pic_%ld.png", indexPath]]; // 保存文件的名称
    UIImage *img = [UIImage imageWithContentsOfFile:filePath];
    return img;
    }
    -

    删除沙盒中的图片

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    - (BOOL)deleteImageAtIndexPath:(NSInteger)index;
    {

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
    NSString *filePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:[NSString stringWithFormat:@"pic_%ld.png", index]];

    if([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
    return [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
    }

    return NO;
    }
    -

    修改图片

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    - (UIImage *)fixOrientation:(UIImage *)aImage {

    // No-op if the orientation is already correct
    if (aImage.imageOrientation == UIImageOrientationUp)
    return aImage;

    // We need to calculate the proper transformation to make the image upright.
    // We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored.
    CGAffineTransform transform = CGAffineTransformIdentity;

    switch (aImage.imageOrientation) {
    case UIImageOrientationDown:
    case UIImageOrientationDownMirrored:
    transform = CGAffineTransformTranslate(transform, aImage.size.width, aImage.size.height);
    transform = CGAffineTransformRotate(transform, M_PI);
    break;

    case UIImageOrientationLeft:
    case UIImageOrientationLeftMirrored:
    transform = CGAffineTransformTranslate(transform, aImage.size.width, 0);
    transform = CGAffineTransformRotate(transform, M_PI_2);
    break;

    case UIImageOrientationRight:
    case UIImageOrientationRightMirrored:
    transform = CGAffineTransformTranslate(transform, 0, aImage.size.height);
    transform = CGAffineTransformRotate(transform, -M_PI_2);
    break;
    default:
    break;
    }

    switch (aImage.imageOrientation) {
    case UIImageOrientationUpMirrored:
    case UIImageOrientationDownMirrored:
    transform = CGAffineTransformTranslate(transform, aImage.size.width, 0);
    transform = CGAffineTransformScale(transform, -1, 1);
    break;

    case UIImageOrientationLeftMirrored:
    case UIImageOrientationRightMirrored:
    transform = CGAffineTransformTranslate(transform, aImage.size.height, 0);
    transform = CGAffineTransformScale(transform, -1, 1);
    break;
    default:
    break;
    }

    // Now we draw the underlying CGImage into a new context, applying the transform
    // calculated above.
    CGContextRef ctx = CGBitmapContextCreate(NULL, aImage.size.width, aImage.size.height,
    CGImageGetBitsPerComponent(aImage.CGImage), 0,
    CGImageGetColorSpace(aImage.CGImage),
    CGImageGetBitmapInfo(aImage.CGImage));
    CGContextConcatCTM(ctx, transform);
    switch (aImage.imageOrientation) {
    case UIImageOrientationLeft:
    case UIImageOrientationLeftMirrored:
    case UIImageOrientationRight:
    case UIImageOrientationRightMirrored:
    // Grr...
    CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.height,aImage.size.width), aImage.CGImage);
    break;

    default:
    CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.width,aImage.size.height), aImage.CGImage);
    break;
    }
    CGImageRef cgimg = CGBitmapContextCreateImage(ctx);
    UIImage *img = [UIImage imageWithCGImage:cgimg];
    CGContextRelease(ctx);
    CGImageRelease(cgimg);
    return img;
    }
    - -
    - -
    - -
    - - - -
    - -
    - - -
    - - -

    - SQL 使用 -

    - - -
    - -
    - -

    常用SQL 语句

    创建一张表

    -
    create table if not exists t_product(productId integer, productName text, productPrice real);
    -

    删除一张表

    -
    drop table if exists t_product;
    -

    插入一条记录

    -

    如果不写某个字段和对应值就会为NUll

    -
    insert into t_product(productId, productName, productPrice) values (0001, 'iPhone7', 6100.8);
    -

    修改记录

    -
    update t_product set productId = 20002 where productName = 'iPhone5s';
    -

    删除

    -
    delete from t_product where productName = 'iphone5s';
    -

    注意:条件语句不能单独存在,只能在修改/删除/查询之后

    -

    简单查询

    准备数据生成sql语句文件

    -
    NSMutableString *mutableString = [NSMutableString string];
    -int productIdValue = arc4random_uniform(1000);
    -NSString *productNameValue = [NSString stringWithFormat:@"iPhone_%d",1 + arc4random_uniform(8)];
    -CGFloat productPriceValue = 3000 + arc4random_uniform(1000);
    -
    -for (NSInteger i = 0; i < 1000; i++) {
    -    [mutableString appendFormat:@"insert into t_product(productId, productName, productPrice) values (%d, %@, %f); \r\n", productIdValue, productNameValue, productPriceValue];
    -}
    -
    -// 写入文件
    -[mutableString writeToFile:@"/Users/Apeng/work/Demo/Sqlite/products.sql" atomically:YES encoding:NSUTF8StringEncoding error:NULL];
    -

    生成的sql语句文件

    -

    -

    SQL 查询语句

    -
    select *(表示字段)from t_product(表名)where(条件语句) productId > 100 (字段满足的条件) ;
    -
    -select * from t_product where productPrice < 5000; 
    -


    or: 或

    -

    -

    and: 且

    -

    -

    分页查询

    -

    limit 0,5 ( limit 索引,每页取得数据条数 )

    -

    第一页 limit 0,5;

    -

    第二页 limit 5,5;

    -

    第三页 limit 10,5;

    -

    第四页 limit 15,5;

    -

    第N页 limit (N - 1)* 5

    -
    select * from t_product where productName = 'iPhone_8' and productPrice < 3300 order by productPrice limit 5,5;
    -

    -

    排序查询

    -

    order by 默认是从小到大,一般是在某一个结果之后

    -

    默认升序

    -
    // 默认升序
    -select * from t_product where productName = 'iPhone_8' and productPrice < 3300 order by productPrice;
    -
    -// 降序
    -select * from t_product where productName = 'iPhone_8' and productPrice < 3300 order by productPrice desc;
    -

    -

    模糊查询

    -

    关键字搜索 ,% 是指通配符

    -

    SQL 顺序

    -
    select * from 表名 条件语句(模糊匹配)排序语句 分页语句
    -
    -select * from t_product where productName like '%_4' order by productPrice limit 0,5;
    -

    -

    主键

    -

    数据库的约定俗称,自增长,建议创建表时增加主键,也可以之后再设计表

    -
    CREATE TABLE IF NOT EXISTS t_class (id integer PRIMARY KEY, className text, classNO integer); 
    -

    外键约束

    -

    当两张表有关联时,需要使用外键约束,一张表中的字段名所对应的值只能来自于另一张表中

    -

    -

    添加外键约束之后的变化

    -

    -

    多表查询

    -

    t_department(部门表)和 t_employee(员工表)两张表

    -

    需求:在 t_employee 表中查出部门研发部的人员

    -

    1 嵌套查询:

    -

    先查根据部门名称查出部ID,

    -

    -

    然后再用部门ID查出员工表中的员工

    -

    以上两步的代码可合并

    -

    根据部门名称查出部门ID

    -
    SELECT id from t_department WHERE departmentName = '研发部';
    -

    根据部门id查出这个部门的人员

    -
    SELECT * from t_employee WHERE departmentID = 1;
    -

    合并后为

    -
    select * from t_employee where departmentID = (SELECT id from t_department WHERE departmentName = '研发部'); 
    -

    结果为:

    -

    -
      -
    1. 链接查询
    2. -
    -

    给表起别名,给字段起别名

    -
    select employee.*, depart.departmentName deptN from t_department depart, t_employee employee;
    -

    /*多表查询之链接查询/

    -
    select t_employee.*, t_department.departmentName FROM t_department, t_employee;
    -

    -

    消除笛卡尔积

    -
    select employee.*, depart.departmentName deptN from t_department depart, t_employee employee where employee.departmentID = depart.id and employee.departmentID = 1;
    -

    - - -
    - -
    - -
    - - -
    @@ -1022,23 +1136,23 @@

    Recent Posts

    diff --git a/page/9/index.html b/page/9/index.html new file mode 100644 index 0000000..88ea010 --- /dev/null +++ b/page/9/index.html @@ -0,0 +1,406 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
    +
    + +
    +
    + +
    + +
    + + +
    + + +

    + iOS 弹框视图 +

    + + +
    + +
    + +

    UIActionSheet UIAlertView UIAlertViewController 的区别

    +

    UIActionSheet

      +
    • iOS 8.3 之后过期
    • +
    • 模态弹出,显示在试图的底部 类似菜单提供选择
    • +
    +
    1
    2
    UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"title" delegate:self cancelButtonTitle:@"取消" destructiveButtonTitle:nil otherButtonTitles:@"按钮1", @"按钮2", nil];
    [actionSheet showInView:self.view];
    +

    UIAlertView

      +
    • iOS 9.0 之后过期
    • +
    • 模态弹出,显示在试图的中间位置 类似一个对话框提供选择
    • +
    +
    1
    2
    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"考勤方式" message:nil delegate:self cancelButtonTitle:@"取消" otherButtonTitles:faceBtn, fingerPrintBtn, nil];
    [alertView show];
    +

    UIAlertViewController

      +
    • iOS 8.0 起可以使用
    • +
    • style : 默认样式, 类似 UIActionSheet 列表形式
    • +
    • style 为:UIAlertControllerStyleAlert 对话框形式
    • +
    • 设置按钮的样式
    • +
    +

    如果修改 UIAlertAction 中stye 为:

    +

    UIAlertActionStyleCancel
    取消按钮会和其他按钮分开

    +

    如果修改 UIAlertAction 中stye 为:

    +

    UIAlertActionStyleDestructive
    取消按钮会以红色警告的方式显示, 但是按钮之间不会分开

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"考勤方式" message:nil preferredStyle:UIAlertControllerStyleActionSheet];
    UIAlertAction *faceBtn = [UIAlertAction actionWithTitle:@"人脸识别" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { }];

    UIAlertAction *fingerPrintBtn = [UIAlertAction actionWithTitle:@"指纹认证" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {

    }];

    UIAlertAction *cancelBtn = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {

    }];

    [alert addAction:faceBtn];
    [alert addAction:fingerPrintBtn];
    [alert addAction:cancelBtn];
    [self presentViewController:alert animated:YES completion:nil];
    + +
    + +
    + +
    + + + +
    + +
    + + +
    + + +

    + 本地缓存 +

    + + +
    + +
    + +

    保存图片到沙盒

    1
    2
    3
    4
    5
    6
    - (void)saveImage:(UIImage *)image InSanBoxWithIndexPath:(NSInteger)indexPath {

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
    NSString *filePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:[NSString stringWithFormat:@"pic_%ld.png", indexPath]]; // 保存文件的名称
    [UIImagePNGRepresentation(image)writeToFile:filePath atomically:YES];
    }
    +

    获取沙盒中的图片

    1
    2
    3
    4
    5
    6
    7
    - (UIImage *)fetchImageFromSanBoxWithIndexPath:(NSInteger)indexPath
    {
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
    NSString *filePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:[NSString stringWithFormat:@"pic_%ld.png", indexPath]]; // 保存文件的名称
    UIImage *img = [UIImage imageWithContentsOfFile:filePath];
    return img;
    }
    +

    删除沙盒中的图片

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    - (BOOL)deleteImageAtIndexPath:(NSInteger)index;
    {

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
    NSString *filePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:[NSString stringWithFormat:@"pic_%ld.png", index]];

    if([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
    return [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
    }

    return NO;
    }
    +

    修改图片

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    - (UIImage *)fixOrientation:(UIImage *)aImage {

    // No-op if the orientation is already correct
    if (aImage.imageOrientation == UIImageOrientationUp)
    return aImage;

    // We need to calculate the proper transformation to make the image upright.
    // We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored.
    CGAffineTransform transform = CGAffineTransformIdentity;

    switch (aImage.imageOrientation) {
    case UIImageOrientationDown:
    case UIImageOrientationDownMirrored:
    transform = CGAffineTransformTranslate(transform, aImage.size.width, aImage.size.height);
    transform = CGAffineTransformRotate(transform, M_PI);
    break;

    case UIImageOrientationLeft:
    case UIImageOrientationLeftMirrored:
    transform = CGAffineTransformTranslate(transform, aImage.size.width, 0);
    transform = CGAffineTransformRotate(transform, M_PI_2);
    break;

    case UIImageOrientationRight:
    case UIImageOrientationRightMirrored:
    transform = CGAffineTransformTranslate(transform, 0, aImage.size.height);
    transform = CGAffineTransformRotate(transform, -M_PI_2);
    break;
    default:
    break;
    }

    switch (aImage.imageOrientation) {
    case UIImageOrientationUpMirrored:
    case UIImageOrientationDownMirrored:
    transform = CGAffineTransformTranslate(transform, aImage.size.width, 0);
    transform = CGAffineTransformScale(transform, -1, 1);
    break;

    case UIImageOrientationLeftMirrored:
    case UIImageOrientationRightMirrored:
    transform = CGAffineTransformTranslate(transform, aImage.size.height, 0);
    transform = CGAffineTransformScale(transform, -1, 1);
    break;
    default:
    break;
    }

    // Now we draw the underlying CGImage into a new context, applying the transform
    // calculated above.
    CGContextRef ctx = CGBitmapContextCreate(NULL, aImage.size.width, aImage.size.height,
    CGImageGetBitsPerComponent(aImage.CGImage), 0,
    CGImageGetColorSpace(aImage.CGImage),
    CGImageGetBitmapInfo(aImage.CGImage));
    CGContextConcatCTM(ctx, transform);
    switch (aImage.imageOrientation) {
    case UIImageOrientationLeft:
    case UIImageOrientationLeftMirrored:
    case UIImageOrientationRight:
    case UIImageOrientationRightMirrored:
    // Grr...
    CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.height,aImage.size.width), aImage.CGImage);
    break;

    default:
    CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.width,aImage.size.height), aImage.CGImage);
    break;
    }
    CGImageRef cgimg = CGBitmapContextCreateImage(ctx);
    UIImage *img = [UIImage imageWithCGImage:cgimg];
    CGContextRelease(ctx);
    CGImageRelease(cgimg);
    return img;
    }
    + +
    + +
    + +
    + + + +
    + +
    + + +
    + + +

    + SQL 使用 +

    + + +
    + +
    + +

    常用SQL 语句

    创建一张表

    +
    create table if not exists t_product(productId integer, productName text, productPrice real);
    +

    删除一张表

    +
    drop table if exists t_product;
    +

    插入一条记录

    +

    如果不写某个字段和对应值就会为NUll

    +
    insert into t_product(productId, productName, productPrice) values (0001, 'iPhone7', 6100.8);
    +

    修改记录

    +
    update t_product set productId = 20002 where productName = 'iPhone5s';
    +

    删除

    +
    delete from t_product where productName = 'iphone5s';
    +

    注意:条件语句不能单独存在,只能在修改/删除/查询之后

    +

    简单查询

    准备数据生成sql语句文件

    +
    NSMutableString *mutableString = [NSMutableString string];
    +int productIdValue = arc4random_uniform(1000);
    +NSString *productNameValue = [NSString stringWithFormat:@"iPhone_%d",1 + arc4random_uniform(8)];
    +CGFloat productPriceValue = 3000 + arc4random_uniform(1000);
    +
    +for (NSInteger i = 0; i < 1000; i++) {
    +    [mutableString appendFormat:@"insert into t_product(productId, productName, productPrice) values (%d, %@, %f); \r\n", productIdValue, productNameValue, productPriceValue];
    +}
    +
    +// 写入文件
    +[mutableString writeToFile:@"/Users/Apeng/work/Demo/Sqlite/products.sql" atomically:YES encoding:NSUTF8StringEncoding error:NULL];
    +

    生成的sql语句文件

    +

    +

    SQL 查询语句

    +
    select *(表示字段)from t_product(表名)where(条件语句) productId > 100 (字段满足的条件) ;
    +
    +select * from t_product where productPrice < 5000; 
    +


    or: 或

    +

    +

    and: 且

    +

    +

    分页查询

    +

    limit 0,5 ( limit 索引,每页取得数据条数 )

    +

    第一页 limit 0,5;

    +

    第二页 limit 5,5;

    +

    第三页 limit 10,5;

    +

    第四页 limit 15,5;

    +

    第N页 limit (N - 1)* 5

    +
    select * from t_product where productName = 'iPhone_8' and productPrice < 3300 order by productPrice limit 5,5;
    +

    +

    排序查询

    +

    order by 默认是从小到大,一般是在某一个结果之后

    +

    默认升序

    +
    // 默认升序
    +select * from t_product where productName = 'iPhone_8' and productPrice < 3300 order by productPrice;
    +
    +// 降序
    +select * from t_product where productName = 'iPhone_8' and productPrice < 3300 order by productPrice desc;
    +

    +

    模糊查询

    +

    关键字搜索 ,% 是指通配符

    +

    SQL 顺序

    +
    select * from 表名 条件语句(模糊匹配)排序语句 分页语句
    +
    +select * from t_product where productName like '%_4' order by productPrice limit 0,5;
    +

    +

    主键

    +

    数据库的约定俗称,自增长,建议创建表时增加主键,也可以之后再设计表

    +
    CREATE TABLE IF NOT EXISTS t_class (id integer PRIMARY KEY, className text, classNO integer); 
    +

    外键约束

    +

    当两张表有关联时,需要使用外键约束,一张表中的字段名所对应的值只能来自于另一张表中

    +

    +

    添加外键约束之后的变化

    +

    +

    多表查询

    +

    t_department(部门表)和 t_employee(员工表)两张表

    +

    需求:在 t_employee 表中查出部门研发部的人员

    +

    1 嵌套查询:

    +

    先查根据部门名称查出部ID,

    +

    +

    然后再用部门ID查出员工表中的员工

    +

    以上两步的代码可合并

    +

    根据部门名称查出部门ID

    +
    SELECT id from t_department WHERE departmentName = '研发部';
    +

    根据部门id查出这个部门的人员

    +
    SELECT * from t_employee WHERE departmentID = 1;
    +

    合并后为

    +
    select * from t_employee where departmentID = (SELECT id from t_department WHERE departmentName = '研发部'); 
    +

    结果为:

    +

    +
      +
    1. 链接查询
    2. +
    +

    给表起别名,给字段起别名

    +
    select employee.*, depart.departmentName deptN from t_department depart, t_employee employee;
    +

    /*多表查询之链接查询/

    +
    select t_employee.*, t_department.departmentName FROM t_department, t_employee;
    +

    +

    消除笛卡尔积

    +
    select employee.*, depart.departmentName deptN from t_department depart, t_employee employee where employee.departmentID = depart.id and employee.departmentID = 1;
    +

    + + +
    + +
    + +
    + + + + + + + +
    + + + +
    +
    + +
    + +
    +
    +
    + + + + + + + + + + + + + + +
    + + \ No newline at end of file diff --git a/search.xml b/search.xml index f484b9e..64eee28 100644 --- a/search.xml +++ b/search.xml @@ -3,12 +3,51 @@ + + + + /2019/06/23/Flutter%E6%89%93%E5%8C%85/ + + Flutter 打包

    app 图标替换

    Android 图标更换

    • 替换图中路径的图片资源即可,
    • 注意图片名字需要是 ic_launcher
    • 每个尺寸对应不同的文件大小
    • 如果要修改启动图片名称需要先在主配置文件中修改

    iOS 图标更换

    修改app图标名称

    • 找到 Android 主配置文件
    • 修改 android:label=”app_flutter” 中名称

    修改app 的名称

    打包

    生成key

    ]]> + + + + + + + + + + + /2019/06/23/Flutter%E4%B8%AD%E6%9C%AC%E5%9C%B0%E5%9B%BE%E7%89%87%E8%B5%84%E6%BA%90%E4%BD%BF%E7%94%A8/ + + Flutter中本地图片资源使用

    新建图片文件目录

    添加图片到项目

    手动投入图片到新建的文件目录

    声明图片资源

    找到项目中 pubspec.yaml 文件,找到 assets: 选项 按照注释代码格式声明图片路径

    1
    2
    assets:
    - lib/assets/ideaOne.png

    使用图片资源

    1
    Image.asset('lib/assets/ideaOne.png')
    ]]> + + + +
    + + + + + + + /2019/06/20/Flutter%E9%A1%B5%E9%9D%A2%E5%AF%BC%E8%88%AA/ + + Flutter页面导航

    导航栏的使用

    程序主入口

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    class MyApp extends StatelessWidget {

    // 声明参数
    final List<String> items;

    // 构造函数
    MyApp({Key key, @required this.items}):super(key: key); // 调用父类的方法


    // 重写
    @override

    Widget build(BuildContext context) {

    // 返回一窗口
    return MaterialApp(
    title:'Flutter --',
    // 基础组件学习
    // home: MyLearningFlutterHome(),

    home: FirstScreen(),
    );
    }
    }

    导航栏页面跳转参数传递

    商品类

    1
    2
    3
    4
    5
    6
    class Product {
    final String title; // 定义商品标题
    final String description;
    // 定义构造方法
    Product(this.title, this.description);
    }

    第一级页面

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    class FirstScreen extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
    return new Scaffold(
    appBar: AppBar(
    title: new Text('第一个页面'),
    ),

    body: Center(
    child: RaisedButton(
    child: new Text('点击跳转详情页'),
    onPressed: (){
    Navigator.push(context, new MaterialPageRoute(
    builder: (context) => (new SecondScreen())
    // builder: (context){
    // new SecondScreen();
    // }
    ));
    },
    ),
    ),
    );
    }
    }

    详情页面

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    class SecondScreen extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
    return Scaffold(
    appBar: AppBar(
    title: Text('详情页面')
    ),

    body: Center(
    child: RaisedButton(
    child: Text('返回上一级页面'),

    onPressed: () => (Navigator.pop(context)),

    // onPressed: (){
    // Navigator.pop(context);
    // },
    ),
    ),
    );
    }
    }

    导航栏页面返回反向参数传递

    使用异步请求和等待

    商品列表页面

    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
    class ProductListView extends StatelessWidget {
    // 定义变量,里面是一个对象
    final List<Product> products;

    // 使用构造方法 函数的方式接受参数
    ProductListView({Key key, @required this.products}):super(key:key); // 设置必参,调用父类方法

    @override
    Widget build(BuildContext context) {
    return Scaffold(
    appBar: AppBar(
    title: Text('商品列表'),
    ),

    // 使用 ListView.builder 动态构建
    body: ListView.builder(
    itemCount: products.length,
    itemBuilder: (context, index) {
    return ListTile(
    title: Text(products[index].title),
    onLongPress: (){
    print("长按事件响应");
    },


    // 点击时传入参数
    onTap: (){
    print('点击事件');
    Navigator.push(
    context,
    MaterialPageRoute(
    builder: (context) => ProducterDetailView(product:products[index])
    )
    );
    },
    );
    },
    ),
    );
    }
    }

    商品详情页面点击按钮返回上一级页面,并带有参数

    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
    class ProducterDetailView extends StatelessWidget {
    final Product product;

    ProducterDetailView({Key key, @required this.product}):super(key:key);
    @override
    Widget build(BuildContext context) {
    return Scaffold(
    appBar: AppBar(
    title: Text('Product ${product.title} 详情页面')
    ),

    body: Center(
    // 有多控件
    child: Column(
    children: <Widget>[
    Text('Product ${product.title} desc ${product.description}'),
    RaisedButton(
    child: Text('back Product List'),
    color: Colors.blue,
    onPressed: (){
    Navigator.pop(context, 'selected Id = ${product.title}');
    },
    )
    ],
    ),
    ),
    );
    }
    }

    ]]>
    + + + +
    + + + /2019/06/19/Flutter%E4%B8%AD%E5%B8%83%E5%B1%80/ - Flutter中布局

    线性布局

    Row 水平方向布局组件

    • 非自适应布局:根据元素本身大小来布局
    • 自适应布局:使用 Expanded 布局

    非自适应布局

    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
    class MyRowNoChangeLayout extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
    return new Container(
    child: Row(
    children: <Widget>[
    new RaisedButton(
    onPressed: (){
    print('点击了---- 红色');
    },

    color: Colors.red,
    child: new Text('按钮一红色'),

    ),

    new RaisedButton(
    onPressed: (){
    print('点击了---- 黄色');
    },

    color: Colors.yellow,
    child: new Text('按钮一黄色'),

    ),
    new RaisedButton(
    onPressed: (){
    print('点击了---- 绿色');
    },

    color: Colors.green,
    child: new Text('按钮一绿色'),

    )
    ],
    ),
    );
    }
    }

    自适应布局

    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
    class MyRowAutoLayout extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
    return new Container(
    child: Row(
    children: <Widget>[
    Expanded(
    child: new RaisedButton(
    onPressed: (){
    print('点击了---- 红色');
    },

    color: Colors.red,
    child: new Text('按钮一红色'),
    ),
    ),

    Expanded(
    child: new RaisedButton(
    onPressed: (){
    print('点击了---- 黄色');
    },

    color: Colors.yellow,
    child: new Text('按钮一黄色'),

    )
    ),

    Expanded(
    child: new RaisedButton(
    onPressed: (){
    print('点击了---- 绿色');
    },

    color: Colors.green,
    child: new Text('按钮一绿色'),

    )
    )
    ],
    ),
    );
    }
    }

    非自动布局和自动布局混合使用

    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
    class MyRowLayout extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
    return new Container(
    child: Row(
    children: <Widget>[
    new RaisedButton(
    onPressed: (){
    print('点击了---- 红色');
    },

    color: Colors.red,
    child: new Text('按钮一红色'),
    ),

    Expanded(
    child: new RaisedButton(
    onPressed: (){
    print('点击了---- 黄色');
    },

    color: Colors.yellow,
    child: new Text('按钮一黄色'),

    )
    ),

    new RaisedButton(
    onPressed: (){
    print('点击了---- 绿色');
    },

    color: Colors.green,
    child: new Text('按钮一绿色'),

    )
    ],
    ),
    );
    }
    }

    Column 垂直方向布局组件

    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
    class MyColunmLayout extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
    return new Container(
    color: Colors.cyan,
    child: Column(
    crossAxisAlignment: CrossAxisAlignment.stretch,
    mainAxisSize: MainAxisSize.max,
    // 设置主轴的对齐方式
    mainAxisAlignment: MainAxisAlignment.center,
    // 设置横轴的对齐方式
    crossAxisAlignment: CrossAxisAlignment.start,
    children: <Widget>[
    new RaisedButton (
    onPressed: (){
    print('点击了---- 红色');
    },

    color: Colors.red,
    child: new Text('按钮一红色'),
    ),

    new RaisedButton(
    onPressed: (){
    print('点击了---- 绿色');
    },
    color: Colors.green,
    child: new Text('按钮一绿色'),
    ),

    new Text('文字内容',
    textAlign: TextAlign.center,
    ),
    new Text('测试文字',
    textAlign: TextAlign.right,
    )
    ],
    ),
    );
    }
    }
    ]]>
    + Flutter中布局

    线性布局

    主轴、副轴

    • main轴:如果用column组件,垂直就是主轴,如果用Row组件,那水平就是主轴

    • cross轴:幅轴,是和主轴垂直的方向。用Row组件,那垂直就是幅轴,Column组件,水平方向就是副轴

    Row 水平方向布局组件

    • 非自适应布局:根据元素本身大小来布局
    • 自适应布局:使用 Expanded 布局

    非自适应布局

    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
    class MyRowNoChangeLayout extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
    return new Container(
    child: Row(
    children: <Widget>[
    new RaisedButton(
    onPressed: (){
    print('点击了---- 红色');
    },

    color: Colors.red,
    child: new Text('按钮一红色'),

    ),

    new RaisedButton(
    onPressed: (){
    print('点击了---- 黄色');
    },

    color: Colors.yellow,
    child: new Text('按钮一黄色'),

    ),
    new RaisedButton(
    onPressed: (){
    print('点击了---- 绿色');
    },

    color: Colors.green,
    child: new Text('按钮一绿色'),

    )
    ],
    ),
    );
    }
    }

    自适应布局

    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
    class MyRowAutoLayout extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
    return new Container(
    child: Row(
    children: <Widget>[
    Expanded(
    child: new RaisedButton(
    onPressed: (){
    print('点击了---- 红色');
    },

    color: Colors.red,
    child: new Text('按钮一红色'),
    ),
    ),

    Expanded(
    child: new RaisedButton(
    onPressed: (){
    print('点击了---- 黄色');
    },

    color: Colors.yellow,
    child: new Text('按钮一黄色'),

    )
    ),

    Expanded(
    child: new RaisedButton(
    onPressed: (){
    print('点击了---- 绿色');
    },

    color: Colors.green,
    child: new Text('按钮一绿色'),

    )
    )
    ],
    ),
    );
    }
    }

    非自动布局和自动布局混合使用

    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
    class MyRowLayout extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
    return new Container(
    child: Row(
    children: <Widget>[
    new RaisedButton(
    onPressed: (){
    print('点击了---- 红色');
    },

    color: Colors.red,
    child: new Text('按钮一红色'),
    ),

    Expanded(
    child: new RaisedButton(
    onPressed: (){
    print('点击了---- 黄色');
    },

    color: Colors.yellow,
    child: new Text('按钮一黄色'),

    )
    ),

    new RaisedButton(
    onPressed: (){
    print('点击了---- 绿色');
    },

    color: Colors.green,
    child: new Text('按钮一绿色'),

    )
    ],
    ),
    );
    }
    }

    Column 垂直方向布局组件

    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
    class MyColunmLayout extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
    return new Container(
    color: Colors.cyan,
    child: Column(
    crossAxisAlignment: CrossAxisAlignment.stretch,
    mainAxisSize: MainAxisSize.max,
    // 设置主轴的对齐方式
    mainAxisAlignment: MainAxisAlignment.center,
    // 设置横轴的对齐方式
    crossAxisAlignment: CrossAxisAlignment.start,
    children: <Widget>[
    new RaisedButton (
    onPressed: (){
    print('点击了---- 红色');
    },

    color: Colors.red,
    child: new Text('按钮一红色'),
    ),

    new RaisedButton(
    onPressed: (){
    print('点击了---- 绿色');
    },
    color: Colors.green,
    child: new Text('按钮一绿色'),
    ),

    new Text('文字内容',
    textAlign: TextAlign.center,
    ),
    new Text('测试文字',
    textAlign: TextAlign.right,
    )
    ],
    ),
    );
    }
    }

    层叠布局

    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
    // stack层叠布局

    class MyStackLayout extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
    // 返回一个 Stack 布局
    return new Stack(
    // 设置位置
    alignment: const FractionalOffset(0.5, .9),
    // 圆角组件
    children: <Widget>[
    new CircleAvatar(
    backgroundImage: new NetworkImage('https://p.ampmake.com/mall/product/26153760-b964-45d5-b811-7f2bf5844102.jpg'),
    // backgroundImage: new Image.network('https://p.ampmake.com/mall/product/26153760-b964-45d5-b811-7f2bf5844102.jpg'),
    radius: 100.0,
    ),

    // 容器组件
    new Container(
    // 设置容器掩饰
    decoration: BoxDecoration(
    color: Colors.blue,
    // border
    border: Border.all(
    color: Colors.red,
    width: 3,
    ),
    ),

    padding: const EdgeInsets.all(10.0),

    child: new Text('stack 布局',
    textAlign: TextAlign.center,
    maxLines: 1,
    // 文字样式
    style: TextStyle(
    color: Colors.yellow,
    textBaseline: TextBaseline.ideographic,
    // 下划线
    decoration: TextDecoration.underline,
    // 下划线颜色
    decorationColor: Colors.orange,
    // 下划线样式
    decorationStyle: TextDecorationStyle.double,
    ),
    ),
    )
    ],

    );
    }
    }

    Positioned组件

    • bottom: 距离层叠组件下边的距离
    • left:距离层叠组件左边的距离
    • top:距离层叠组件上边的距离
    • right:距离层叠组件右边的距离
    • width: 层叠定位组件的宽度
    • height: 层叠定位组件的高度
    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
    class MyMoreStackLayout extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
    return new Stack(
    children: <Widget>[
    new CircleAvatar(
    backgroundImage: NetworkImage('https://p.ampmake.com/mall/product/26153760-b964-45d5-b811-7f2bf5844102.jpg'),
    radius: 100.0,
    ),

    new Positioned(
    top: 5,
    left: 50,
    // right: 5,
    width: 100,
    child: new RaisedButton(
    onPressed: (){
    print("多个组件层叠布局");
    },

    color: Colors.deepOrange,
    child: Text('测试按钮'),
    ),
    ),

    new Positioned(
    bottom: 10,
    right: 10,
    left: 10,
    child: new Text('flutter 多个试图 stack布局',
    maxLines: 1,
    textAlign: TextAlign.center,
    style: TextStyle(
    color: Colors.lightGreen,
    fontSize: 20,
    fontStyle: FontStyle.italic
    ),
    )
    )
    ],
    );
    }
    }

    Card 布局

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    class MyCardLayout extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
    return new Card(
    child: Column(
    children: <Widget>[
    new ListTile(
    title: new Text('card 布局1'),
    subtitle: new Text('子标题1',
    textAlign: TextAlign.right,
    style: TextStyle(
    color: Colors.lightGreen
    ),
    ),
    leading: new Icon(Icons.account_balance, color: Colors.purple)
    ),

    new Divider(
    color: Colors.cyan
    ),

    new ListTile(
    title: new Text('card 布局2'),
    subtitle: new Text('子标题2',
    textAlign: TextAlign.right,
    style: TextStyle(
    color: Colors.lightGreen
    ),
    ),
    leading: new Icon(Icons.account_box, color: Colors.yellow)
    ),

    new Divider(
    color: Colors.red,
    ),

    new ListTile(
    title: new Text('card 布局3'),
    subtitle: new Text('子标题3',
    textAlign: TextAlign.right,
    style: TextStyle(
    color: Colors.lightGreen
    ),
    ),
    leading: new Icon(Icons.account_circle, color: Colors.red)
    ),

    new Divider(
    color: Colors.orangeAccent,
    ),

    new ListTile(
    title: new Text('card 布局4'),
    subtitle: new Text('子标题4',
    textAlign: TextAlign.right,
    style: TextStyle(
    color: Colors.lightGreen
    ),
    ),
    leading: new Icon(Icons.camera_enhance, color: Colors.brown)
    )
    ],
    ),
    );
    }
    }
    ]]>
    @@ -50,10 +89,10 @@ - - /2019/06/18/%E7%AC%AC%E5%8D%81%E7%AB%A0%E7%BD%91%E7%BB%9C%E7%9B%B8%E5%85%B3/ + + /2019/06/18/%E7%AC%AC%E5%9B%9B%E7%AB%A0OC%E8%AF%AD%E8%A8%80%E7%89%B9%E6%80%A7%E9%9D%A2%E8%AF%95/ - 第十章 网络请求相关

    HTTP协议

    超文本传输协议

    请求/响应报文

    请求报文:

    • 请求行
      方法 URL 协议版本(1.1版本) CRLF(回车)

    • 首部字段区域
      首部字段名(key) :值(value )CRLF
      首部字段名(key) :值(value )CRLF

    • 实体主体
      get 请求没有
      post 有实体主体

    响应报文:

    • 响应行
      版本 状态码 状态码描述(短语) CRLF

    • 首部字段区

    • 响应实体主体

    连接建立流程

    三次握手和四次挥手

    三次握手

    • 客户端 发送 syn 同步报文到Server,
    • server 端收到syn报文后会,链接建立,server 端会返回一个 syn,ack 报文给客户端,
    • 客户端收到 server 端的ask 报文后,会再次回应一个确认ack 的报文

    数据请求

    • 接下来在已经建立的tcp 通道上进行http 的请求报文
    • server 端回复给客户端一个http响应报文

    四次挥手

    • 客户端发送 fin 终止报文到server 端
    • server 端收到报文后会回回复一个ack 报文到客户端
    • 客户端到server 端的tcp的链接已经断开,但是 server 端到客户端的链接还会传输数据
    • server 端向客户端发送一个fin ack 报文 ,然后客户端回应给server 一个ack 报文,释放链接

    HTTP 的特点

    • 无连接
      非持久链接 需要多次断开/建立链接,就会经历 三次握手,四次挥手

      HTTP 的持久链接: 多个http请求在同一个tcp 通道上, 一定时间内不会断开tcp通道,提高了网络请求的效率

      头部字段:

      coonection : keep-alive 客户端是否采用持久链接time :20 多长时间有效max:10 最多发生多少个http请求和响应对
    • 无状态
      同一个用户多次发送http请求,server 端是不知道是同一个用户的,server 端通过 Cookie/Session 规避这个问题

      • cookie
        cookie 主要是用来记录用户状态,区分用户,cookie由server端返回给客户端,cookie 状态保存在客户端

        保证cookie 的安全

        • cookie 加密处理
        • 只在https 上携带Cookie
        • 设置Cookie为 httpOnly,防止跨站脚本攻击
      • session 用来记录用户的状态,区分用户,状态存放在服务端

    HTTPS 与网络安全

    HTTPS 都使用了那些加密手段,为什么?

    • 建立链接过程使用非对称加密,因为耗时
    • 后续通信数据传输过程中使用对称性加密

    对称性加密

    TCP: 传输控制协议

    • 面向链接
      TCP 是全双工的所以数据传输之前需要三次握手,传输完毕需要四次挥手,
    • 可靠传输

      • 无差错
      • 按序到达
    • 面向字节流

      发送方发送数据时不管一次性提交给TCP的缓冲是多大的数据,TCP本身会根据实际情况来划分数据包再发送给接受数据方

    • 流量控制

      滑动窗口协议

    UDP : 用户数据协议

    • 无连接
      发送数据时不需要建立链接,传输完毕也不需要释放链接

    • 尽最大努力交付
      是不保证可靠传输的,

    • 面向报文
      既不合并,也不拆分

    问题

    GET 和post 请求的区别

    • GET 获取资源的

      • 安全的: 不因该引起Server 端的任何状态变化的
      • 幂等的:同一个请求方法执行多次和执行一次的效果完全相同
      • 可缓存的:请求是可以缓存的,代理服务器可以缓存
      • 请求参数以? 分割拼接到URL 后面
    • post 请求处理资源

      • 不安全的:
      • 非幂等的
      • 不可缓存的

    GET 请求参数有长度限制,是2048 个字符,post 一般没有限制

    HTTPS 链接建立流程

    客户端会发送服务端一个支持的加密算法列表
    包括 TLS 的版本号 + 随机数C,
    服务端再回给客户端一个证书
    包括 商定的加密算法 后续首先通过非对称加密进行对称性加密的密钥传输,http的网络就通过被非对称密钥保护的对称密钥访问和传输数据

    TCP 和 UDP的区别

    tcp是面向链接的,并且支持可靠的传输,支持面向字节流, tcp 提供了流浪上的控制和拥塞的控制
    UDP 提供了简单的复用/分用及差错检测的传输层的功能,

    状态吗有哪些

    • 1XX
    • 2XX:成功
    • 3XX:重定向
    • 4XX:客户端请求错误
    • 5XX: 服务器端请求错误

    为什么需要三次握手,两次行不行?

    解决同步请求链接建立时超时的情况,如果只有两次握手,

    持久链接怎么判断一个请求是否结束

    • 请求/响应报文的头部字段

    响应报文 content-length: 1024 头部字段是由server 端返回数据大小,客户端根据所接收的数据的字节数是否达到了头部字段 cotent-length 的大小判断

    • 通过post请求时server 端返回数据有可能需要多次响应才能返回,此时需要根据chunked 字段名 是否为空来判断

    Charels 抓包原理是什么?

    中间人攻击

    正常步骤是 客户端 —- server

    抓包:发送请求和响应请求都可以通过中间人修改再返回给某一端

    客户端 —中间人—- server

    DNS 解析

    域名到IP地址的映射,DNS解析请求采用UDP数据报,且明文

    NDS 解析存在的常见问题

    • DNS 劫持问题
      钓鱼网站
    • DNS 解析转发问题

    DNS 劫持和HTTP 的关系时怎么样的

    没有关系,

    • DNS 解析发在HTTP 建立连接之前
    • DNS 解析请求使用UDP数据报, 端口是53,跟HTTP没有任何关系

    解决DNS 劫持问题

    • httpDNS
    • 长连接
    ]]>
    + OC 语言特性

    分类

    原理

    由运行时来决议的,不同分类当中含有同名方法 谁最终生效取决于谁最终参与编译,最后参编译的同名分类方法会最终生效,假如分类方法中添加的方法和宿主类中的某一个方法名相同,分类中的方法会覆盖宿主类中的同名方法,覆盖是指在消息传递过程中优先查找数组靠前的元素,如果查找到了同名方法就直接调用,实际上宿主类的同名方法实现仍然是存在的,我们可可以通过一些手段调用到原有类的同名方法的实现

    使用场景

    • 申明私有方法,分类的 .m 文件中申明私有方法啊,对外不暴露
    • 对类中的代码进行抽取分解
    • 把Framework的私有方法公开化

    特点

    • 运行时决议,运行时通过runtime 才把分类中的方法添加到了宿主类上
    • 可以为系统类添加方法
    • 有多个分类中存在同名方法时,最后编译的方法会最先生效
    • 分类中有和宿主类同名方法时,底层中通过内存copy将分类方法“覆盖”宿主类方法
    • 名字相同的分类编译会报错

    分类中可以添加的内容

    • 实例方法
    • 类方法
    • 协议
    • 属性,实际上只是声明了 getter / setter 方法,并没有添加成员变量

    关联对象技术

    关联对象由 AssocicationsManager 管理并在 AssociationsHashMap 上存储
    所有对象的关联内容都在同一个全局容器中

    • 给分类添加成员变量,通过关联对象的方式

    扩展

    使用场景

    • 声明私有属性
    • 声明私有方法
    • 声明私有成员变量

    特点

    • 编译时决议
    • 只以声明的形式存在,多数情况下寄生在宿主类的.m中
    • 不能为系统添加扩展

    代理

    • 代理设计模式,传递方式是一对一
    • 使用weak 避免循环引用

    通知

    使用观察者模式来实现的用于跨层传递消息的机制

    KVO

    KVO 是系统对观察者模式的又一实现,使用isa 混写技术(isa - swizzling)来动态运行时为某一个类添加一个子类重写了它的setter 方法,同时将原有类的isa指针指向了新创建的类上

    • KVO 是OC对观察者模式的又一实现
    • apple 使用isa 混写技术(isa - swizzling)来实现KVO
      • 给我A类注册一个观察者时本质上是调用系统的 Observer for keyPath 方法,系统会创建一个NSKVONotifiying_A 的类
    • 使用 setter 方法设置值KVO才能生效
    • 使用setValue:forKey: 改变值KVO才能生效
    • 使用下划线的成员变量直接修改值需要手动添加KVO才能生效(增加 willChangeValueForKey 和 didChangeValueForKey 两个方法)

    KVC

    apple提供的键值编码技术

    • (nullable id)valueForKey:(NSString *)key;
    • (void)setValue:(nullable id)value forKey:(NSString *)key;

      以上两个方法中的key是没有任何限制的,只要知道对应的成员变量名称,就可以对私有的成员变量设置和操作,这违背了面向对象的编程思想

    (void)setValue:(nullable id)value forKey:(NSString *)key; 方法调用流程

    • 先判断是否有跟key 相关的setter 方法,如果有就直接调用,结束调用
    • 如果没有再判断是否存在实例变量,如果存在直接给赋值,结束调用
    • 如果说实例变量不存在,会去调用 - (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key; 抛出异常

    属性关键字

    读写权限

    • readonly

    • readwrite 系统默认

    原子性

    • atomic (系统默认)保证赋值和获取(对成员属性的直接获取和赋值)时线程安全,并不代表操作和访问,如果atomic修饰的一个数组,只能保证独具改数组的读取和赋值时线程安全,并不能保证对数据操作(增加/删除数组元素)时是安全的
    • nonatomic

    引用技术

    • assign
      • 修饰基本数据类型,
      • 修饰对象时不改变其引用计数,
      • 会产生悬垂指针
    • weak
      • 不改变被修饰对象的引用计数,
      • 所指对象被释放之后指针自动置为nil

    两者区别:

    • weak可以修饰对象,而assign 既可以修饰对象也可以修饰基本数据类型
    • assign 修饰的对象,对象释放后指针仍然指向原对象的内存地址,而weak 修饰的对象,释放后会自动置为nil

    问题1

    weak 修饰的对象为什么释放后会自动置为nil

    问题2

    @property (copy) NSMutableArray *array; 有什么问题

    • 被copy修饰后,如果赋值过来的是NSMutableArray,copy之后就是NSArray,如果赋值过来的是NSAarray,copy 之后仍然是NSArray,有可能会调用array的添加元素方法会导致crash

    copy 关键词

    • 浅拷贝:内存地址的复制,让目标对象指针和源对象指向同一块内存空间,
      • 会增加对象的引用计数
      • 并没有一个新的内存分配
    • 深拷贝:目标对象指针和源对象指针分别指向两块内容相同的内存空间
      • 不会增加源对象的引用计数
      • 有新对象的内存分配

    MRC 重写 retain修饰的变量的setter 方法

    1
    2
    3
    4
    5
    6
    7
    8

    - (void)setObj:(id)obj {
    if (_obj != obj) {
    [_obj release];
    }
    _obj = [obj retain];

    }

    判断是为了防止异常处理,如果不做 if 判断,当传入的 obj 对象正好是原来的_obj 对象,对原对象尽行releas 操作,实际上也会对传入的对象进行releas 操作进行释放,此时如果再通过obj指针访问废弃的对象时就会导致carsh

    ]]>
    @@ -63,10 +102,10 @@ - - /2019/06/18/%E7%AC%AC%E5%9B%9B%E7%AB%A0OC%E8%AF%AD%E8%A8%80%E7%89%B9%E6%80%A7%E9%9D%A2%E8%AF%95/ + + /2019/06/18/%E7%AC%AC%E5%8D%81%E7%AB%A0%E7%BD%91%E7%BB%9C%E7%9B%B8%E5%85%B3/ - OC 语言特性

    分类

    原理

    由运行时来决议的,不同分类当中含有同名方法 谁最终生效取决于谁最终参与编译,最后参编译的同名分类方法会最终生效,假如分类方法中添加的方法和宿主类中的某一个方法名相同,分类中的方法会覆盖宿主类中的同名方法,覆盖是指在消息传递过程中优先查找数组靠前的元素,如果查找到了同名方法就直接调用,实际上宿主类的同名方法实现仍然是存在的,我们可可以通过一些手段调用到原有类的同名方法的实现

    使用场景

    • 申明私有方法,分类的 .m 文件中申明私有方法啊,对外不暴露
    • 对类中的代码进行抽取分解
    • 把Framework的私有方法公开化

    特点

    • 运行时决议,运行时通过runtime 才把分类中的方法添加到了宿主类上
    • 可以为系统类添加方法
    • 有多个分类中存在同名方法时,最后编译的方法会最先生效
    • 分类中有和宿主类同名方法时,底层中通过内存copy将分类方法“覆盖”宿主类方法
    • 名字相同的分类编译会报错

    分类中可以添加的内容

    • 实例方法
    • 类方法
    • 协议
    • 属性,实际上只是声明了 getter / setter 方法,并没有添加成员变量

    关联对象技术

    关联对象由 AssocicationsManager 管理并在 AssociationsHashMap 上存储
    所有对象的关联内容都在同一个全局容器中

    • 给分类添加成员变量,通过关联对象的方式

    扩展

    使用场景

    • 声明私有属性
    • 声明私有方法
    • 声明私有成员变量

    特点

    • 编译时决议
    • 只以声明的形式存在,多数情况下寄生在宿主类的.m中
    • 不能为系统添加扩展

    代理

    • 代理设计模式,传递方式是一对一
    • 使用weak 避免循环引用

    通知

    使用观察者模式来实现的用于跨层传递消息的机制

    KVO

    KVO 是系统对观察者模式的又一实现,使用isa 混写技术(isa - swizzling)来动态运行时为某一个类添加一个子类重写了它的setter 方法,同时将原有类的isa指针指向了新创建的类上

    • KVO 是OC对观察者模式的又一实现
    • apple 使用isa 混写技术(isa - swizzling)来实现KVO
      • 给我A类注册一个观察者时本质上是调用系统的 Observer for keyPath 方法,系统会创建一个NSKVONotifiying_A 的类
    • 使用 setter 方法设置值KVO才能生效
    • 使用setValue:forKey: 改变值KVO才能生效
    • 使用下划线的成员变量直接修改值需要手动添加KVO才能生效(增加 willChangeValueForKey 和 didChangeValueForKey 两个方法)

    KVC

    apple提供的键值编码技术

    • (nullable id)valueForKey:(NSString *)key;
    • (void)setValue:(nullable id)value forKey:(NSString *)key;

      以上两个方法中的key是没有任何限制的,只要知道对应的成员变量名称,就可以对私有的成员变量设置和操作,这违背了面向对象的编程思想

    (void)setValue:(nullable id)value forKey:(NSString *)key; 方法调用流程

    • 先判断是否有跟key 相关的setter 方法,如果有就直接调用,结束调用
    • 如果没有再判断是否存在实例变量,如果存在直接给赋值,结束调用
    • 如果说实例变量不存在,会去调用 - (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key; 抛出异常

    属性关键字

    读写权限

    • readonly

    • readwrite 系统默认

    原子性

    • atomic (系统默认)保证赋值和获取(对成员属性的直接获取和赋值)时线程安全,并不代表操作和访问,如果atomic修饰的一个数组,只能保证独具改数组的读取和赋值时线程安全,并不能保证对数据操作(增加/删除数组元素)时是安全的
    • nonatomic

    引用技术

    • assign
      • 修饰基本数据类型,
      • 修饰对象时不改变其引用计数,
      • 会产生悬垂指针
    • weak
      • 不改变被修饰对象的引用计数,
      • 所指对象被释放之后指针自动置为nil

    两者区别:

    • weak可以修饰对象,而assign 既可以修饰对象也可以修饰基本数据类型
    • assign 修饰的对象,对象释放后指针仍然指向原对象的内存地址,而weak 修饰的对象,释放后会自动置为nil

    问题1

    weak 修饰的对象为什么释放后会自动置为nil

    问题2

    @property (copy) NSMutableArray *array; 有什么问题

    • 被copy修饰后,如果赋值过来的是NSMutableArray,copy之后就是NSArray,如果赋值过来的是NSAarray,copy 之后仍然是NSArray,有可能会调用array的添加元素方法会导致crash

    copy 关键词

    • 浅拷贝:内存地址的复制,让目标对象指针和源对象指向同一块内存空间,
      • 会增加对象的引用计数
      • 并没有一个新的内存分配
    • 深拷贝:目标对象指针和源对象指针分别指向两块内容相同的内存空间
      • 不会增加源对象的引用计数
      • 有新对象的内存分配

    MRC 重写 retain修饰的变量的setter 方法

    1
    2
    3
    4
    5
    6
    7
    8

    - (void)setObj:(id)obj {
    if (_obj != obj) {
    [_obj release];
    }
    _obj = [obj retain];

    }

    判断是为了防止异常处理,如果不做 if 判断,当传入的 obj 对象正好是原来的_obj 对象,对原对象尽行releas 操作,实际上也会对传入的对象进行releas 操作进行释放,此时如果再通过obj指针访问废弃的对象时就会导致carsh

    ]]>
    + 第十章 网络请求相关

    HTTP协议

    超文本传输协议

    请求/响应报文

    请求报文:

    • 请求行
      方法 URL 协议版本(1.1版本) CRLF(回车)

    • 首部字段区域
      首部字段名(key) :值(value )CRLF
      首部字段名(key) :值(value )CRLF

    • 实体主体
      get 请求没有
      post 有实体主体

    响应报文:

    • 响应行
      版本 状态码 状态码描述(短语) CRLF

    • 首部字段区

    • 响应实体主体

    连接建立流程

    三次握手和四次挥手

    三次握手

    • 客户端 发送 syn 同步报文到Server,
    • server 端收到syn报文后会,链接建立,server 端会返回一个 syn,ack 报文给客户端,
    • 客户端收到 server 端的ask 报文后,会再次回应一个确认ack 的报文

    数据请求

    • 接下来在已经建立的tcp 通道上进行http 的请求报文
    • server 端回复给客户端一个http响应报文

    四次挥手

    • 客户端发送 fin 终止报文到server 端
    • server 端收到报文后会回回复一个ack 报文到客户端
    • 客户端到server 端的tcp的链接已经断开,但是 server 端到客户端的链接还会传输数据
    • server 端向客户端发送一个fin ack 报文 ,然后客户端回应给server 一个ack 报文,释放链接

    HTTP 的特点

    • 无连接
      非持久链接 需要多次断开/建立链接,就会经历 三次握手,四次挥手

      HTTP 的持久链接: 多个http请求在同一个tcp 通道上, 一定时间内不会断开tcp通道,提高了网络请求的效率

      头部字段:

      coonection : keep-alive 客户端是否采用持久链接time :20 多长时间有效max:10 最多发生多少个http请求和响应对
    • 无状态
      同一个用户多次发送http请求,server 端是不知道是同一个用户的,server 端通过 Cookie/Session 规避这个问题

      • cookie
        cookie 主要是用来记录用户状态,区分用户,cookie由server端返回给客户端,cookie 状态保存在客户端

        保证cookie 的安全

        • cookie 加密处理
        • 只在https 上携带Cookie
        • 设置Cookie为 httpOnly,防止跨站脚本攻击
      • session 用来记录用户的状态,区分用户,状态存放在服务端

    HTTPS 与网络安全

    HTTPS 都使用了那些加密手段,为什么?

    • 建立链接过程使用非对称加密,因为耗时
    • 后续通信数据传输过程中使用对称性加密

    对称性加密

    TCP: 传输控制协议

    • 面向链接
      TCP 是全双工的所以数据传输之前需要三次握手,传输完毕需要四次挥手,
    • 可靠传输

      • 无差错
      • 按序到达
    • 面向字节流

      发送方发送数据时不管一次性提交给TCP的缓冲是多大的数据,TCP本身会根据实际情况来划分数据包再发送给接受数据方

    • 流量控制

      滑动窗口协议

    UDP : 用户数据协议

    • 无连接
      发送数据时不需要建立链接,传输完毕也不需要释放链接

    • 尽最大努力交付
      是不保证可靠传输的,

    • 面向报文
      既不合并,也不拆分

    问题

    GET 和post 请求的区别

    • GET 获取资源的

      • 安全的: 不因该引起Server 端的任何状态变化的
      • 幂等的:同一个请求方法执行多次和执行一次的效果完全相同
      • 可缓存的:请求是可以缓存的,代理服务器可以缓存
      • 请求参数以? 分割拼接到URL 后面
    • post 请求处理资源

      • 不安全的:
      • 非幂等的
      • 不可缓存的

    GET 请求参数有长度限制,是2048 个字符,post 一般没有限制

    HTTPS 链接建立流程

    客户端会发送服务端一个支持的加密算法列表
    包括 TLS 的版本号 + 随机数C,
    服务端再回给客户端一个证书
    包括 商定的加密算法 后续首先通过非对称加密进行对称性加密的密钥传输,http的网络就通过被非对称密钥保护的对称密钥访问和传输数据

    TCP 和 UDP的区别

    tcp是面向链接的,并且支持可靠的传输,支持面向字节流, tcp 提供了流浪上的控制和拥塞的控制
    UDP 提供了简单的复用/分用及差错检测的传输层的功能,

    状态吗有哪些

    • 1XX
    • 2XX:成功
    • 3XX:重定向
    • 4XX:客户端请求错误
    • 5XX: 服务器端请求错误

    为什么需要三次握手,两次行不行?

    解决同步请求链接建立时超时的情况,如果只有两次握手,

    持久链接怎么判断一个请求是否结束

    • 请求/响应报文的头部字段

    响应报文 content-length: 1024 头部字段是由server 端返回数据大小,客户端根据所接收的数据的字节数是否达到了头部字段 cotent-length 的大小判断

    • 通过post请求时server 端返回数据有可能需要多次响应才能返回,此时需要根据chunked 字段名 是否为空来判断

    Charels 抓包原理是什么?

    中间人攻击

    正常步骤是 客户端 —- server

    抓包:发送请求和响应请求都可以通过中间人修改再返回给某一端

    客户端 —中间人—- server

    DNS 解析

    域名到IP地址的映射,DNS解析请求采用UDP数据报,且明文

    NDS 解析存在的常见问题

    • DNS 劫持问题
      钓鱼网站
    • DNS 解析转发问题

    DNS 劫持和HTTP 的关系时怎么样的

    没有关系,

    • DNS 解析发在HTTP 建立连接之前
    • DNS 解析请求使用UDP数据报, 端口是53,跟HTTP没有任何关系

    解决DNS 劫持问题

    • httpDNS
    • 长连接
    ]]>
    @@ -76,10 +115,10 @@ - - /2019/06/18/%E7%AC%AC%E5%8D%81%E4%B8%89%E7%AB%A0%E7%AE%97%E6%B3%95%E9%9D%A2%E8%AF%95/ + + /2019/06/18/%E7%AC%AC%E5%8D%81%E4%B8%80%E7%AB%A0%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E9%9D%A2%E8%AF%95/ - 第十三章算法

    ]]>
    + 第十一章 设计模式

    六大设计原则

    单一职责原则:一个类只负责一件事

    开闭原则: 对修改关闭,对扩展开放

    接口隔离原则:使用多个专门的协议,而不是一个庞大臃肿的协议,iOS系统的UITableView的数据和代理协议分开

    依赖倒置原则:抽象不应该依赖于具体实现,具体实现可以依赖于抽象

    里氏替换原则:父类可以被子类无缝替换,且原有功能不受任何影响,iOS系统 的 KVO 机制遵守了这一原则,当调用 addObserver 方法时,因为系统在动态运行时为我们创建了一个子类,直观感受是仍然使用的是父类,实际上系统已经替换为创建的子类,所以KVO 机制 不但使用了管擦这模式,并且遵从了 里氏替换原则

    迪米特法则: 一个对象应对其他对象有尽可能少的了解,平时所说的高内聚,低耦合

    常用设计模式

    责任链

    有三个业务分别为A/B/C,以前的调用顺序为 A -> B -> C ,现在顺序有调整,

    定义某一个类,有一个成员变量他的类型跟原有类类型一致,组成一个责任链,让处理具体业务的责任者去处理

    桥接模式

    业务解耦的问题:同一个列表和多套数据显示,多套数据通过后端来控制是共存,

    类构成

    抽象类A ——> class B

    适配器模式

    一个现有类需要适应变化的问题, 年代比较久远的类或者对象

    类构成

    • 对象适配
    • 类适配

    单例模式

    命令模式

    行为参数化

    降低代码重合度

    ]]>
    @@ -89,10 +128,10 @@ - - /2019/06/18/%E7%AC%AC%E5%8D%81%E4%B8%80%E7%AB%A0%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E9%9D%A2%E8%AF%95/ + + /2019/06/18/%E7%AC%AC%E5%8D%81%E4%BA%8C%E7%AB%A0%E6%9E%B6%E6%9E%84%E5%92%8C%E6%A1%86%E6%9E%B6%E9%9D%A2%E8%AF%95/ - 第十一章 设计模式

    六大设计原则

    单一职责原则:一个类只负责一件事

    开闭原则: 对修改关闭,对扩展开放

    接口隔离原则:使用多个专门的协议,而不是一个庞大臃肿的协议,iOS系统的UITableView的数据和代理协议分开

    依赖倒置原则:抽象不应该依赖于具体实现,具体实现可以依赖于抽象

    里氏替换原则:父类可以被子类无缝替换,且原有功能不受任何影响,iOS系统 的 KVO 机制遵守了这一原则,当调用 addObserver 方法时,因为系统在动态运行时为我们创建了一个子类,直观感受是仍然使用的是父类,实际上系统已经替换为创建的子类,所以KVO 机制 不但使用了管擦这模式,并且遵从了 里氏替换原则

    迪米特法则: 一个对象应对其他对象有尽可能少的了解,平时所说的高内聚,低耦合

    常用设计模式

    责任链

    有三个业务分别为A/B/C,以前的调用顺序为 A -> B -> C ,现在顺序有调整,

    定义某一个类,有一个成员变量他的类型跟原有类类型一致,组成一个责任链,让处理具体业务的责任者去处理

    桥接模式

    业务解耦的问题:同一个列表和多套数据显示,多套数据通过后端来控制是共存,

    类构成

    抽象类A ——> class B

    适配器模式

    一个现有类需要适应变化的问题, 年代比较久远的类或者对象

    类构成

    • 对象适配
    • 类适配

    单例模式

    命令模式

    行为参数化

    降低代码重合度

    ]]>
    + 第十二章 架构和框架

    目的

    • 实现模块化
    • 分层
    • 解耦

    图片缓存

    图片缓存框架设计方案?

    图片缓存时内存设计上需要考虑的问题

    • 内存存储的size

      • 10kb的图片开辟的内存空间 50 张
      • 100kb图片的 20 张
      • 100kb 以上的 10 张
        通过队列的方式存储,先进先出的方式淘汰
    • 淘汰策略

      • 以队列先进先出的方式淘汰
      • 已LRU 算法 (如 30 min 之内是否使用过)

    磁盘设计考虑的问题

    • 存储方式
    • 大小限制
    • 淘汰策略(超过 7 天)

    网络部分的设计需要考虑的问题

    • 图片请求最大并发量
    • 请求超时策略,一旦超时了,可以重试,如果重试2次失败,是否还下载
    • 请求优先级,下载或者缓存的图片是否紧急需要

    图片通过什么方式进行读写,过程是怎样的?

    • 以图片的url 的单向哈希值作为key 存储
    • 读取过程

    图片解码

    • 对于不同格式的图片,解码采用什么方式来做?

      • 应用策略模式对于不同图片格式进行解码
    • 在那个阶段做图片解码处理?

      • 再磁盘读取后未解码或者网络请求返回后,解码后再放到内存中,可以减轻主线程的压力,解码在主线程中

    线程处理

    阅读时长统计

    怎样设计一个时长统计框架?

    为何要有不同类型的记录器,处于什么考虑?

    基于不同分类场景提供的关于记录的封装、适配

    记录的数据由于程序杀死或者断电,关机等丢失,你是怎样处理的?

    • 定时写磁盘
    • 限定内存缓存条数,超过该条数就写如磁盘

    记录器上传 中延时上传的具体场景有哪些?上传时机如何把握?

    上传时机

    • 立刻上传
    • 延时上传
    • 定时上传

    延时上传场景

    统计结果立即上传会重复调用接口导致资源浪费

    • 前后台切换
    • 从无网到有网的变化时
    • 通过其他的接口捎带着上传

    复杂页面的架构

    MVVM 框架思想

    RN 的数据流思想

    任何一个子节点是没有权力做自己的变化更新的,它必须把这个消息传递给根结点,根结点通过自顶向下的遍历查找需要更新的节点

    系统UIView 更新机制的思想

    AsyncDisplayKit 关于预排版的设计思想

    客户端整体架构

    需要独立于APP 的通用层
    通用的业务层
    中间层
    业务层

    业务之间的解耦通信方式

    • OpenURL
    • 依赖注入方式, 在中间层通过代理 的方式实现业务A 和业务B之间的通信
    ]]>
    @@ -102,10 +141,10 @@ - - /2019/06/18/%E7%AC%AC%E5%8D%81%E4%BA%8C%E7%AB%A0%E6%9E%B6%E6%9E%84%E5%92%8C%E6%A1%86%E6%9E%B6%E9%9D%A2%E8%AF%95/ + + /2019/06/18/%E7%AC%AC%E5%8D%81%E4%B8%89%E7%AB%A0%E7%AE%97%E6%B3%95%E9%9D%A2%E8%AF%95/ - 第十二章 架构和框架

    目的

    • 实现模块化
    • 分层
    • 解耦

    图片缓存

    图片缓存框架设计方案?

    图片缓存时内存设计上需要考虑的问题

    • 内存存储的size

      • 10kb的图片开辟的内存空间 50 张
      • 100kb图片的 20 张
      • 100kb 以上的 10 张
        通过队列的方式存储,先进先出的方式淘汰
    • 淘汰策略

      • 以队列先进先出的方式淘汰
      • 已LRU 算法 (如 30 min 之内是否使用过)

    磁盘设计考虑的问题

    • 存储方式
    • 大小限制
    • 淘汰策略(超过 7 天)

    网络部分的设计需要考虑的问题

    • 图片请求最大并发量
    • 请求超时策略,一旦超时了,可以重试,如果重试2次失败,是否还下载
    • 请求优先级,下载或者缓存的图片是否紧急需要

    图片通过什么方式进行读写,过程是怎样的?

    • 以图片的url 的单向哈希值作为key 存储
    • 读取过程

    图片解码

    • 对于不同格式的图片,解码采用什么方式来做?

      • 应用策略模式对于不同图片格式进行解码
    • 在那个阶段做图片解码处理?

      • 再磁盘读取后未解码或者网络请求返回后,解码后再放到内存中,可以减轻主线程的压力,解码在主线程中

    线程处理

    阅读时长统计

    怎样设计一个时长统计框架?

    为何要有不同类型的记录器,处于什么考虑?

    基于不同分类场景提供的关于记录的封装、适配

    记录的数据由于程序杀死或者断电,关机等丢失,你是怎样处理的?

    • 定时写磁盘
    • 限定内存缓存条数,超过该条数就写如磁盘

    记录器上传 中延时上传的具体场景有哪些?上传时机如何把握?

    上传时机

    • 立刻上传
    • 延时上传
    • 定时上传

    延时上传场景

    统计结果立即上传会重复调用接口导致资源浪费

    • 前后台切换
    • 从无网到有网的变化时
    • 通过其他的接口捎带着上传

    复杂页面的架构

    MVVM 框架思想

    RN 的数据流思想

    任何一个子节点是没有权力做自己的变化更新的,它必须把这个消息传递给根结点,根结点通过自顶向下的遍历查找需要更新的节点

    系统UIView 更新机制的思想

    AsyncDisplayKit 关于预排版的设计思想

    客户端整体架构

    需要独立于APP 的通用层
    通用的业务层
    中间层
    业务层

    业务之间的解耦通信方式

    • OpenURL
    • 依赖注入方式, 在中间层通过代理 的方式实现业务A 和业务B之间的通信
    ]]>
    + 第十三章算法

    ]]>
    @@ -141,10 +180,10 @@ - - /2019/06/18/%E7%AC%AC%E4%B9%9D%E7%AB%A0Runloop%E9%9D%A2%E8%AF%95/ + + /2019/06/18/%E7%AC%AC%E4%BA%94%E7%AB%A0Runtime%E7%9B%B8%E5%85%B3%E9%9D%A2%E8%AF%95/ - RunLop

    ]]>
    + Runtime 相关问题

    数据结构

    objc_object 结构体

    objc_class

    对象、类对象、元类对象的区别

    • 类对象存储实例方法列表等信息
    • 元类对象存锤类方法列表等信息

    消息传递

    void objc_msgSend(void /* id self, SEL op, ... */)

    经过编译器转变后
    [self class] <==> objc_msgSend(self, @selector(class))
    第一个参数是消息传递的接收者self, 第二个参数是传递的消息名称(选择器)

    void objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */)
    第一个参数是一个结构体指针

    //
    struuch objc_super {
    __unsafe_unretained id receiver; // 就是当前的对象
    }

    [superclass] <==> objc_msgSendSuper(super, @selector(class))

    无论调用[super class] 还是 [self class] 最终这条消息的接收者都是当前对象

    图1

    消息传递过程

    图2

    问题1

    打印结果都是 Phone

    原因:

    • 无论调用[super class] 还是 [self class] 最终这条消息的接收者都是当前对象
    • [self class] 在Phone的类对象中查找class 方法,没有去父类查找,父类也没有,如图1顺次往上查找到根类,调用根类中class 的具体实现
    • [super class]只是跨越了当前类,直接从当前类的父类中查找 class 方法,父类中没有class 方法,如图1所示只能继续往上查找到根类对象,在NSObject 中有class 方法实现,所以接受者任然是当前的对象

    缓存查找

    根据给定的SEL(方法选择器)通过一个函数来映射出bucket_t 在数组当中的位置,本质上是哈希查找

    当前类中查找

    当前类中存在着对应的方法列表

    • 对于已经排序好的列表,采用二分查找算法查找对应的执行函数
    • 对于没有排序的列表,采用一般遍历查找方法查找对应的执行函数

    父类逐级查找

    图3

    • 最关键是通过当前类的 superclass 成员变量去查找父类,或者访问父类
    • curClass 的父类是否为 nil?如果是NSObject 类,他的父类为空,所以会结束查找
    • 如果当前类有父类就要去该父类的缓存中查找,如果在缓存中命中,那么就结束查找,如果缓存中没有,就需要遍历当前类的父类的方法列表,如果没有就遍历父类的父类查找,沿着superclass指针逐级向上查找,直到查找到NSObject,再去Superclass。如果还是没有为nil时就结束父类逐级查找

    消息转发

    resolveInstanceMethod:

    • 该方法是类方法不是实例方法,参数是SEL 方法选择器类型
    • 返回值是一个BOOL值
    • 告诉系统是否要解决当前实例方法的实现
    • 如果返回YES,结束消息转发
    • 如果返回NO,系统会给第二次机会处理这个消息,会回掉forwardingTargetForSelector:

    forwardingTargetForSelector:

    • 参数是 SEL 方法选择器
    • 返回值是 id 类型,说明有哪个对象来处理,转发的对象是谁,如果返回了转发目标就会结束当前调用
    • 如果返回为nil,系统会调用 methodSignatureForSelector:方法,最后一次机会处理这个消息

    methodSignatureForSelector:

    • 参数是 SEL 方法选择器
    • 返回值是一个对象,实际上这个对象是对methodSignatureForSelector 方法的参数,参数个数,参数类型和返回值的包装
    • 如果返回一个方法签名的话,就会调用 forwardInvocation: 方法
    • 如果返回为nil,标记为消息无法处理,程序crash

    forwardInvocation:

    • 如果此方法不能处理消息,程序crash

    代码示例

    .h 文件

    1
    2
    3
    4
    @interface RunTimeObject : NSObject
    // 只声明,不实现,验证消息转发机制
    - (void)test;
    @end

    .m 文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    @implementation RunTimeObject

    // 实现消息转发流程 中的方法
    /**
    是否要解决当前实例方法的实现
    */
    + (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(test)) {
    NSLog(@"resolveInstanceMethod:");
    // 如果返回YES,消息转发就会结束
    // return YES;

    return NO;
    } else {

    NSLog(@"super resolveInstanceMethod:");
    // 返回父类的默认调用
    return [super resolveInstanceMethod:sel];
    }
    }


    /**
    - 参数是 SEL 方法选择器
    - 返回值是 id 类型,说明有哪个对象来处理,转发的对象是谁,如果返回了转发目标就会结束当前调用
    - 如果返回为nil,系统会调用 methodSignatureForSelector:方法,最后一次机会处理这个消息
    */
    - (id)forwardingTargetForSelector:(SEL)aSelector {
    NSLog(@" forwardingTargetForSelector: ");
    return nil;
    }

    /**
    - 参数是 SEL 方法选择器
    - 返回值是一个对象,实际上这个对象是对`methodSignatureForSelector` 方法的参数,参数个数,参数类型和返回值的包装
    - 如果返回一个方法签名的话,就会调用 forwardInvocation: 方法
    - 如果返回为nil,标记为消息无法处理,程序crash

    */
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    if (aSelector == @selector(test)) {
    NSLog(@"methodSignatureForSelector:");
    /**
    v: 表示这个方法返回值是 void

    固定参数@: 表示参数类型是 id, 即self
    固定参数`:` : 表示参数类型是选择器类型,即 @selector(test)
    */
    return [NSMethodSignature signatureWithObjCTypes:"v@:"];
    } else {

    NSLog(@"super methodSignatureForSelector:");
    // 返回父类的默认调用
    return [super methodSignatureForSelector:aSelector];
    }
    }


    - (void)forwardInvocation:(NSInvocation *)anInvocation {
    NSLog(@"forwardInvocation:");
    }

    @end

    调用

    1
    2
    3
    4
    - (void)runTiemTest {
    RunTimeObject *obj = [[RunTimeObject alloc] init];
    [obj test];
    }

    结果:

    1
    2
    3
    4
    5
    resolveInstanceMethod:
    forwardingTargetForSelector:
    methodSignatureForSelector:
    super resolveInstanceMethod:
    forwardInvocation:

    为类动态添加方法

    methond Swizzling

    使用场景

    • 页面中的进出添加统计信息,使用 methond Swizzling 替换viewillAppear 方法

    动态添加方法

    • perforSelector: 实际上是考察class_addMethod 的方法使用

    动态方法解析

    • @dynamic关键字
    • 动态运行时语言将函数决议推迟到运行时
    • 编译时不生成setter/getter 方法,而是在运行时才添加具体的执行函数

    问题

    [obj foo] 和 obj_msgSend()函数之间有什么关系?

    • [obj foo]经过编译器处理过后会变成 obj_msgSend()有两个参数,一个是obj,第二个参数是foo 选择器

    runtime 如何通过Selector找到对应的IMP地址的?

    消息传递机制

    能否向编译后的类增加实例变量

    编译之前就完成了实例变量的布局,
    runtime_r_t 表示是readonly 的,编译后的类是不能添加实例变量的,可以向动态添加的类中添加实例变量

    ]]>
    @@ -154,10 +193,10 @@ - - /2019/06/18/%E7%AC%AC%E4%BA%94%E7%AB%A0Runtime%E7%9B%B8%E5%85%B3%E9%9D%A2%E8%AF%95/ + + /2019/06/18/%E7%AC%AC%E4%B9%9D%E7%AB%A0Runloop%E9%9D%A2%E8%AF%95/ - Runtime 相关问题

    数据结构

    objc_object 结构体

    objc_class

    对象、类对象、元类对象的区别

    • 类对象存储实例方法列表等信息
    • 元类对象存锤类方法列表等信息

    消息传递

    void objc_msgSend(void /* id self, SEL op, ... */)

    经过编译器转变后
    [self class] <==> objc_msgSend(self, @selector(class))
    第一个参数是消息传递的接收者self, 第二个参数是传递的消息名称(选择器)

    void objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */)
    第一个参数是一个结构体指针

    //
    struuch objc_super {
    __unsafe_unretained id receiver; // 就是当前的对象
    }

    [superclass] <==> objc_msgSendSuper(super, @selector(class))

    无论调用[super class] 还是 [self class] 最终这条消息的接收者都是当前对象

    图1

    消息传递过程

    图2

    问题1

    打印结果都是 Phone

    原因:

    • 无论调用[super class] 还是 [self class] 最终这条消息的接收者都是当前对象
    • [self class] 在Phone的类对象中查找class 方法,没有去父类查找,父类也没有,如图1顺次往上查找到根类,调用根类中class 的具体实现
    • [super class]只是跨越了当前类,直接从当前类的父类中查找 class 方法,父类中没有class 方法,如图1所示只能继续往上查找到根类对象,在NSObject 中有class 方法实现,所以接受者任然是当前的对象

    缓存查找

    根据给定的SEL(方法选择器)通过一个函数来映射出bucket_t 在数组当中的位置,本质上是哈希查找

    当前类中查找

    当前类中存在着对应的方法列表

    • 对于已经排序好的列表,采用二分查找算法查找对应的执行函数
    • 对于没有排序的列表,采用一般遍历查找方法查找对应的执行函数

    父类逐级查找

    图3

    • 最关键是通过当前类的 superclass 成员变量去查找父类,或者访问父类
    • curClass 的父类是否为 nil?如果是NSObject 类,他的父类为空,所以会结束查找
    • 如果当前类有父类就要去该父类的缓存中查找,如果在缓存中命中,那么就结束查找,如果缓存中没有,就需要遍历当前类的父类的方法列表,如果没有就遍历父类的父类查找,沿着superclass指针逐级向上查找,直到查找到NSObject,再去Superclass。如果还是没有为nil时就结束父类逐级查找

    消息转发

    resolveInstanceMethod:

    • 该方法是类方法不是实例方法,参数是SEL 方法选择器类型
    • 返回值是一个BOOL值
    • 告诉系统是否要解决当前实例方法的实现
    • 如果返回YES,结束消息转发
    • 如果返回NO,系统会给第二次机会处理这个消息,会回掉forwardingTargetForSelector:

    forwardingTargetForSelector:

    • 参数是 SEL 方法选择器
    • 返回值是 id 类型,说明有哪个对象来处理,转发的对象是谁,如果返回了转发目标就会结束当前调用
    • 如果返回为nil,系统会调用 methodSignatureForSelector:方法,最后一次机会处理这个消息

    methodSignatureForSelector:

    • 参数是 SEL 方法选择器
    • 返回值是一个对象,实际上这个对象是对methodSignatureForSelector 方法的参数,参数个数,参数类型和返回值的包装
    • 如果返回一个方法签名的话,就会调用 forwardInvocation: 方法
    • 如果返回为nil,标记为消息无法处理,程序crash

    forwardInvocation:

    • 如果此方法不能处理消息,程序crash

    代码示例

    .h 文件

    1
    2
    3
    4
    @interface RunTimeObject : NSObject
    // 只声明,不实现,验证消息转发机制
    - (void)test;
    @end

    .m 文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    @implementation RunTimeObject

    // 实现消息转发流程 中的方法
    /**
    是否要解决当前实例方法的实现
    */
    + (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(test)) {
    NSLog(@"resolveInstanceMethod:");
    // 如果返回YES,消息转发就会结束
    // return YES;

    return NO;
    } else {

    NSLog(@"super resolveInstanceMethod:");
    // 返回父类的默认调用
    return [super resolveInstanceMethod:sel];
    }
    }


    /**
    - 参数是 SEL 方法选择器
    - 返回值是 id 类型,说明有哪个对象来处理,转发的对象是谁,如果返回了转发目标就会结束当前调用
    - 如果返回为nil,系统会调用 methodSignatureForSelector:方法,最后一次机会处理这个消息
    */
    - (id)forwardingTargetForSelector:(SEL)aSelector {
    NSLog(@" forwardingTargetForSelector: ");
    return nil;
    }

    /**
    - 参数是 SEL 方法选择器
    - 返回值是一个对象,实际上这个对象是对`methodSignatureForSelector` 方法的参数,参数个数,参数类型和返回值的包装
    - 如果返回一个方法签名的话,就会调用 forwardInvocation: 方法
    - 如果返回为nil,标记为消息无法处理,程序crash

    */
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    if (aSelector == @selector(test)) {
    NSLog(@"methodSignatureForSelector:");
    /**
    v: 表示这个方法返回值是 void

    固定参数@: 表示参数类型是 id, 即self
    固定参数`:` : 表示参数类型是选择器类型,即 @selector(test)
    */
    return [NSMethodSignature signatureWithObjCTypes:"v@:"];
    } else {

    NSLog(@"super methodSignatureForSelector:");
    // 返回父类的默认调用
    return [super methodSignatureForSelector:aSelector];
    }
    }


    - (void)forwardInvocation:(NSInvocation *)anInvocation {
    NSLog(@"forwardInvocation:");
    }

    @end

    调用

    1
    2
    3
    4
    - (void)runTiemTest {
    RunTimeObject *obj = [[RunTimeObject alloc] init];
    [obj test];
    }

    结果:

    1
    2
    3
    4
    5
    resolveInstanceMethod:
    forwardingTargetForSelector:
    methodSignatureForSelector:
    super resolveInstanceMethod:
    forwardInvocation:

    为类动态添加方法

    methond Swizzling

    使用场景

    • 页面中的进出添加统计信息,使用 methond Swizzling 替换viewillAppear 方法

    动态添加方法

    • perforSelector: 实际上是考察class_addMethod 的方法使用

    动态方法解析

    • @dynamic关键字
    • 动态运行时语言将函数决议推迟到运行时
    • 编译时不生成setter/getter 方法,而是在运行时才添加具体的执行函数

    问题

    [obj foo] 和 obj_msgSend()函数之间有什么关系?

    • [obj foo]经过编译器处理过后会变成 obj_msgSend()有两个参数,一个是obj,第二个参数是foo 选择器

    runtime 如何通过Selector找到对应的IMP地址的?

    消息传递机制

    能否向编译后的类增加实例变量

    编译之前就完成了实例变量的布局,
    runtime_r_t 表示是readonly 的,编译后的类是不能添加实例变量的,可以向动态添加的类中添加实例变量

    ]]>
    + RunLop

    ]]>
    @@ -167,10 +206,10 @@ - - /2019/06/18/%E7%AC%AC%E4%B8%89%E7%AB%A0UI%E8%A7%86%E5%9B%BE%E5%93%8D%E5%BA%94%E9%93%BE%E7%A6%BB%E5%B1%8F%E6%B8%B2%E6%9F%93%E9%9D%A2%E8%AF%95/ + + /2019/06/18/%E7%AC%AC%E4%B8%83%E7%AB%A0Blocks%E9%9D%A2%E8%AF%95/ - UITableView 相关
    • 重用机制
    • 并发访问,数据拷贝问题:主线程删除了一条数据,子线程数据由删除之前的数据源copy而来,子线程网络请求,数据解析,预排版等操作后,回到主线程刷新时已经删除的数据又会重现

      • 主线程数据源删除时对数据进行记录,子线程的数据回来后再进行一次同步删除操作,然后再刷新数据

      • 使用GCD中的串行队列,将数据删除和数据解析在此队列中进行

    UIView 和CALayer 的关系

    • UIView 为 CALayer 提供内容,负责处理触摸事件,参与响应链
    • CALayer 负责显示内容,分工明确,UIView 的Layer 实际上就是CALayer, backgroundColor 等属性本质是对CALayer 做了一层包装

    事件传递

    • hitTest 方法是返回响应时间的试图
    • pointInside withEvent 方法判断点击位置是否在当前视图范围内
    • 倒序遍历最终响应事件的试图,最后添加的试图会最优先被遍历到

    需求案例

    只让正方形的view中圆形区域内可以响应事件,四个角的位置不响应事件

    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
    class CircularAreaResponseButton: UIButton {

    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
    if self.isHidden || !self.isUserInteractionEnabled || self.alpha <= 0.01 {
    return nil
    }

    var hitView: UIView?
    if self.point(inside: point, with: event) {
    // 遍历当前视图的子试图
    for subView in subviews {
    // 坐标转换
    let convertPoint = self.convert(point, to: subView)
    if let hitTestView = subView.hitTest(convertPoint, with: event) {
    // 找到了最终响应事件的试图
    hitView = hitTestView
    break;
    }
    }

    if hitView != nil {
    return hitView
    }
    return self
    }
    return nil
    }


    override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
    let x1 = point.x
    let y1 = point.y

    let x2 = self.frame.width / 2.0
    let y2 = self.frame.height / 2.0
    // 使用平面内两点见距离公式计算距离, 并且判断是否 <= 圆的半径
    return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)) <= x2
    }
    }

    视图响应

    UIView 都继承于 UIResponder,都有以下方法

    1
    2
    3
    4
    func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
    func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?)
    func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?)
    func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?)

    如果事件沿着视图响应链一直传递到 UIApplicationDelegate 仍然没有任何一个视图处理事件,程序将会发生什么?

    • 忽略掉了这个事件当作什么都没有发生

    图像显示原理

    CALayer 的 contents 是位图

    • CPU的工作

      Layout | Display | Prepare | Commite |
      ———|———–|————|———|
      UI布局 | 绘制 | 图片编解码 | 提交位图 |
      文本计算 |

    • GPU渲染管线

      定点着色 | 图元转配 | 光栅化 | 片段着色 | 片段处理 |
      ——–|——–|——-|———|———|

    提交到帧缓冲区(frameBuffer)

    UI卡顿掉帧的原因

    1/60 ms 时间内, 由cpu 和GPU 协同产生最终的数据,当 CPU UI布局,文本计算所用的时间较长时,留给GPU渲染,提交到帧缓冲区的时间就会变短,这样以来就会发生掉帧情况

    UIView 的绘制原理

    CPU优化方案

    • 对象的创建、调整、销毁放在子线程,节省cpu的一部分时间
    • 预排版(布局计算,文本计算)放在子线程中
    • 预渲染(文本等异步绘制,图片编解码)等

    GPU优化方案

    在屏渲染

    指GPU的渲染操作是在当前用于显示的屏幕缓冲区域中进行

    离屏渲染

    指GPU在当前屏幕缓冲区域外新开劈了一个缓冲区域进行渲染操作
    指定了UI视图图层的某些属性,标记为视图在未预合成之前不能用于在当前屏幕上直接显示的时候就会发生离屏渲染,例如图层的圆角设置和蒙层设置

    • 离屏渲染何时触发

      • 设置layer 的圆角并且和 maskToBounds 一起使用时
      • 图层蒙版
      • 阴影
      • 光栅化
    • 为何要避免离屏渲染

      • 离屏渲染,会增加 GPU 的工作量,GPU工作量的增加就很有可能导致CPU和GPU的总耗时超过1/60s,导致UI卡顿和掉帧
    • 避免离屏渲染

    ###异步绘制

    ]]>
    + Blocks

    Block 是什么?

    Blocks 是 C 语言的扩充功能,将函数及其执行上下文封装起来的对象,带有自动变量(局部变量)的匿名函数

    • 因为block 底层C++ 结构体中存在着 isa 指针,所以是一个对象
    • 因为block 底层C++ 结构体中存在着一个函数指针 FuncPtr,所以是一个函数

    什么是Block 调用

    Block 的调用就是函数的调用
    在终端使用 clang 编译之后查看文件内容可得出以下结论:

    • 对其进行一个强制转换,转换之后取出成员变量 FuncPtr

    截获变量

    以下代码运行结果是 12

    原因:block 对基本数据类型的局部变量会截获其值,在定义 block 时以值的方式传到了block 对应的结构体当中,而调用block 时直接使用的是block对应结构体当中已经传过来的那个值,所以值为12
    • 局部变量截获

      • 基本数据类型的局部变量截获其值
      • 对象类型的局部变量连同所有权修饰符一起截获
    • 局部静态变量: 已指针形式截获

    • 全局变量:不截获

    • 静态全局变量: 不截获

    以下代码运行结果是 8

    原因:block 对局部静态变量会以指针形式截获,所以值为8

    __block 修饰符

    • __block 修饰后的变量最后变成了对象,底层c++ 结构体中存在isa指针
    • 一般情况下,对被截获变量进行赋值操作需要添加block 修饰符,而操作是不需要添加block 修饰符的
    • 不需要__block 修饰符,全局变量和静态全局变量 block 是不截获的,所以不需要修饰,静态局部变量是通过指针截获的,修改外部的变量,也不需要添加
      • 静态局部变量
      • 全局变量
      • 静态全局变量

    不需要添加__Block 修饰符

    1
    2
    3
    4
    5
    6
    7
    8
    - (void)test {
    NSMutableArray *array = [NSMutableArray array];
    void(^block)(void) = ^{
    [array addObject:@1234];
    };

    block();
    }

    需要添加__Block 修饰符

    1
    2
    3
    4
    5
    6
    7
    8
    - (void)test {
    __block NSMutableArray *tempArray = nil;
    void(^block)(void) = ^{
    tempArray = [NSMutableArray array];
    };

    block();
    }

    以下代码运行结果是 8

    1
    2
    3
    4
    5
    6
    7
    8
    - (void)test__Block3 {
    __block int multiplier = 6;
    int(^Block)(int) = ^int(int num){
    return multiplier * num;
    };
    multiplier = 4;
    NSLog(@"__block--test__Block3---result is %d", Block(2));
    }

    原因

    栈上Block 的block 变量中的 forwarding 指针指向的是自身

    Block 的内存管理

    Block 的 内存分布

    Block 的 Copy 操作

    • 全局block
    • 堆block
    • 栈block

    Block 循环引用

    • 如果当前block对当前对象的某一变量进行截获的话,block 会对对应变量有一个强引用,当前block因为当前的对象对其有个强引用,产生了自循环引用,通过声明为__weak变量来解决
    • 如果定义了__block 修饰符的话也会产生循环引用,在ARC 下会产生循环引用,而在MRC 下不会,在ARC 下通过打破环来消除,但是存在弊端时当block 没有机会执行时,

    循环引用1

    解决方法:

    大环循环引用2

    以上代码,在MRC 下,不会产生循环引用,在ARC 下,会产生循环应用,引起内存泄露

    解决方法

    如果block 没有机会执行,那么循环引用将不会被打破

    ]]>
    @@ -180,10 +219,10 @@ - - /2019/06/18/%E7%AC%AC%E4%B8%83%E7%AB%A0Blocks%E9%9D%A2%E8%AF%95/ + + /2019/06/18/%E7%AC%AC%E4%B8%89%E7%AB%A0UI%E8%A7%86%E5%9B%BE%E5%93%8D%E5%BA%94%E9%93%BE%E7%A6%BB%E5%B1%8F%E6%B8%B2%E6%9F%93%E9%9D%A2%E8%AF%95/ - Blocks

    Block 是什么?

    Blocks 是 C 语言的扩充功能,将函数及其执行上下文封装起来的对象,带有自动变量(局部变量)的匿名函数

    • 因为block 底层C++ 结构体中存在着 isa 指针,所以是一个对象
    • 因为block 底层C++ 结构体中存在着一个函数指针 FuncPtr,所以是一个函数

    什么是Block 调用

    Block 的调用就是函数的调用
    在终端使用 clang 编译之后查看文件内容可得出以下结论:

    • 对其进行一个强制转换,转换之后取出成员变量 FuncPtr

    截获变量

    以下代码运行结果是 12

    原因:block 对基本数据类型的局部变量会截获其值,在定义 block 时以值的方式传到了block 对应的结构体当中,而调用block 时直接使用的是block对应结构体当中已经传过来的那个值,所以值为12
    • 局部变量截获

      • 基本数据类型的局部变量截获其值
      • 对象类型的局部变量连同所有权修饰符一起截获
    • 局部静态变量: 已指针形式截获

    • 全局变量:不截获

    • 静态全局变量: 不截获

    以下代码运行结果是 8

    原因:block 对局部静态变量会以指针形式截获,所以值为8

    __block 修饰符

    • __block 修饰后的变量最后变成了对象,底层c++ 结构体中存在isa指针
    • 一般情况下,对被截获变量进行赋值操作需要添加block 修饰符,而操作是不需要添加block 修饰符的
    • 不需要__block 修饰符,全局变量和静态全局变量 block 是不截获的,所以不需要修饰,静态局部变量是通过指针截获的,修改外部的变量,也不需要添加
      • 静态局部变量
      • 全局变量
      • 静态全局变量

    不需要添加__Block 修饰符

    1
    2
    3
    4
    5
    6
    7
    8
    - (void)test {
    NSMutableArray *array = [NSMutableArray array];
    void(^block)(void) = ^{
    [array addObject:@1234];
    };

    block();
    }

    需要添加__Block 修饰符

    1
    2
    3
    4
    5
    6
    7
    8
    - (void)test {
    __block NSMutableArray *tempArray = nil;
    void(^block)(void) = ^{
    tempArray = [NSMutableArray array];
    };

    block();
    }

    以下代码运行结果是 8

    1
    2
    3
    4
    5
    6
    7
    8
    - (void)test__Block3 {
    __block int multiplier = 6;
    int(^Block)(int) = ^int(int num){
    return multiplier * num;
    };
    multiplier = 4;
    NSLog(@"__block--test__Block3---result is %d", Block(2));
    }

    原因

    栈上Block 的block 变量中的 forwarding 指针指向的是自身

    Block 的内存管理

    Block 的 内存分布

    Block 的 Copy 操作

    • 全局block
    • 堆block
    • 栈block

    Block 循环引用

    • 如果当前block对当前对象的某一变量进行截获的话,block 会对对应变量有一个强引用,当前block因为当前的对象对其有个强引用,产生了自循环引用,通过声明为__weak变量来解决
    • 如果定义了__block 修饰符的话也会产生循环引用,在ARC 下会产生循环引用,而在MRC 下不会,在ARC 下通过打破环来消除,但是存在弊端时当block 没有机会执行时,

    循环引用1

    解决方法:

    大环循环引用2

    以上代码,在MRC 下,不会产生循环引用,在ARC 下,会产生循环应用,引起内存泄露

    解决方法

    如果block 没有机会执行,那么循环引用将不会被打破

    ]]>
    + UITableView 相关
    • 重用机制
    • 并发访问,数据拷贝问题:主线程删除了一条数据,子线程数据由删除之前的数据源copy而来,子线程网络请求,数据解析,预排版等操作后,回到主线程刷新时已经删除的数据又会重现

      • 主线程数据源删除时对数据进行记录,子线程的数据回来后再进行一次同步删除操作,然后再刷新数据

      • 使用GCD中的串行队列,将数据删除和数据解析在此队列中进行

    UIView 和CALayer 的关系

    • UIView 为 CALayer 提供内容,负责处理触摸事件,参与响应链
    • CALayer 负责显示内容,分工明确,UIView 的Layer 实际上就是CALayer, backgroundColor 等属性本质是对CALayer 做了一层包装

    事件传递

    • hitTest 方法是返回响应时间的试图
    • pointInside withEvent 方法判断点击位置是否在当前视图范围内
    • 倒序遍历最终响应事件的试图,最后添加的试图会最优先被遍历到

    需求案例

    只让正方形的view中圆形区域内可以响应事件,四个角的位置不响应事件

    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
    class CircularAreaResponseButton: UIButton {

    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
    if self.isHidden || !self.isUserInteractionEnabled || self.alpha <= 0.01 {
    return nil
    }

    var hitView: UIView?
    if self.point(inside: point, with: event) {
    // 遍历当前视图的子试图
    for subView in subviews {
    // 坐标转换
    let convertPoint = self.convert(point, to: subView)
    if let hitTestView = subView.hitTest(convertPoint, with: event) {
    // 找到了最终响应事件的试图
    hitView = hitTestView
    break;
    }
    }

    if hitView != nil {
    return hitView
    }
    return self
    }
    return nil
    }


    override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
    let x1 = point.x
    let y1 = point.y

    let x2 = self.frame.width / 2.0
    let y2 = self.frame.height / 2.0
    // 使用平面内两点见距离公式计算距离, 并且判断是否 <= 圆的半径
    return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)) <= x2
    }
    }

    视图响应

    UIView 都继承于 UIResponder,都有以下方法

    1
    2
    3
    4
    func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
    func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?)
    func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?)
    func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?)

    如果事件沿着视图响应链一直传递到 UIApplicationDelegate 仍然没有任何一个视图处理事件,程序将会发生什么?

    • 忽略掉了这个事件当作什么都没有发生

    图像显示原理

    CALayer 的 contents 是位图

    • CPU的工作

      Layout | Display | Prepare | Commite |
      ———|———–|————|———|
      UI布局 | 绘制 | 图片编解码 | 提交位图 |
      文本计算 |

    • GPU渲染管线

      定点着色 | 图元转配 | 光栅化 | 片段着色 | 片段处理 |
      ——–|——–|——-|———|———|

    提交到帧缓冲区(frameBuffer)

    UI卡顿掉帧的原因

    1/60 ms 时间内, 由cpu 和GPU 协同产生最终的数据,当 CPU UI布局,文本计算所用的时间较长时,留给GPU渲染,提交到帧缓冲区的时间就会变短,这样以来就会发生掉帧情况

    UIView 的绘制原理

    CPU优化方案

    • 对象的创建、调整、销毁放在子线程,节省cpu的一部分时间
    • 预排版(布局计算,文本计算)放在子线程中
    • 预渲染(文本等异步绘制,图片编解码)等

    GPU优化方案

    在屏渲染

    指GPU的渲染操作是在当前用于显示的屏幕缓冲区域中进行

    离屏渲染

    指GPU在当前屏幕缓冲区域外新开劈了一个缓冲区域进行渲染操作
    指定了UI视图图层的某些属性,标记为视图在未预合成之前不能用于在当前屏幕上直接显示的时候就会发生离屏渲染,例如图层的圆角设置和蒙层设置

    • 离屏渲染何时触发

      • 设置layer 的圆角并且和 maskToBounds 一起使用时
      • 图层蒙版
      • 阴影
      • 光栅化
    • 为何要避免离屏渲染

      • 离屏渲染,会增加 GPU 的工作量,GPU工作量的增加就很有可能导致CPU和GPU的总耗时超过1/60s,导致UI卡顿和掉帧
    • 避免离屏渲染

    ###异步绘制

    ]]>
    @@ -362,10 +401,10 @@ - - /2019/06/18/Swift%E6%B3%9B%E5%9E%8B/ + + /2019/06/18/Swift%E9%94%99%E8%AF%AF%E5%A4%84%E7%90%86/ - Swift 泛型

    泛型定义–wikipedia

    1. 在程序编码中一些包含类型参数的类型,也就是说泛型的参数只可以代表类,不能代表个别对象。(这是当今较常见的定义)
    2. 在程序编码中一些包含参数的类。其参数可以代表类或对象等等。(现在人们大多把这称作模板)

    泛型的用处

    泛型可以实现传入一个指定类型后做一些操作然后返回后还是原来指定的类型,你可以指定特定的类型(特定是指约束)

    泛型和 Any 的区别

    区别:Any能代表任何类型,但是不能返回某个具体的类型,都需要使用 as 去转化,而泛型正好相反

    • Any 可以代表任何类型,除了class之外还可以代表struct,enum,
    • AnyObject 可以代码任何 class 的类型, swift 中基本类型, ArrayDictionary 都是 struct 类型,都不能使用 AnyObject 来接受

    Swift中Any 和 AnyObject用法

    泛型函数和泛型参数

    swapTwoValues是一个泛型函数,可以接收或者返回任何类型

    1
    2
    3
    4
    5
    func swapTwoValues<T>(inout a: T, inout b: T) {
    let temporaryA = a
    a = b
    b = temporaryA
    }
    • swapTwoValues 函数名后面的 <T> 表示这个 T 类型是函数所定义的一个类型, T 只是一个类型的占位符,不用关心具体类型

    • (inout a: T, inout b: T) 中a和b两个参数类型T是泛型参数

    类型约束

    两个泛型参数的泛型函数

    1
    2
    3
    func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
    // function body goes here
    }
    • 泛型参数T约束条件:必须是SomeClass类的子类
    • 泛型参数U约束条件:必须遵守SomeProtocol协议
    • 使用where指定约束
    ]]>
    + Swift 中错误处理

    assert 断言

    只在debug 模式下程序会自动退出

    1
    public func assert(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)

    assertionFailure 断言错误

    只在debug 模式下程序会自动退出, 没有条件

    1
    public func assertionFailure(_ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)

    precondition

    releas 模式程序也会退出

    1
    public func precondition(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)

    throw

    ]]>
    @@ -375,10 +414,10 @@ - - /2019/06/18/Swift%E9%94%99%E8%AF%AF%E5%A4%84%E7%90%86/ + + /2019/06/18/Swift%E6%B3%9B%E5%9E%8B/ - Swift 中错误处理

    assert 断言

    只在debug 模式下程序会自动退出

    1
    public func assert(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)

    assertionFailure 断言错误

    只在debug 模式下程序会自动退出, 没有条件

    1
    public func assertionFailure(_ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)

    precondition

    releas 模式程序也会退出

    1
    public func precondition(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)

    throw

    ]]>
    + Swift 泛型

    泛型定义–wikipedia

    1. 在程序编码中一些包含类型参数的类型,也就是说泛型的参数只可以代表类,不能代表个别对象。(这是当今较常见的定义)
    2. 在程序编码中一些包含参数的类。其参数可以代表类或对象等等。(现在人们大多把这称作模板)

    泛型的用处

    泛型可以实现传入一个指定类型后做一些操作然后返回后还是原来指定的类型,你可以指定特定的类型(特定是指约束)

    泛型和 Any 的区别

    区别:Any能代表任何类型,但是不能返回某个具体的类型,都需要使用 as 去转化,而泛型正好相反

    • Any 可以代表任何类型,除了class之外还可以代表struct,enum,
    • AnyObject 可以代码任何 class 的类型, swift 中基本类型, ArrayDictionary 都是 struct 类型,都不能使用 AnyObject 来接受

    Swift中Any 和 AnyObject用法

    泛型函数和泛型参数

    swapTwoValues是一个泛型函数,可以接收或者返回任何类型

    1
    2
    3
    4
    5
    func swapTwoValues<T>(inout a: T, inout b: T) {
    let temporaryA = a
    a = b
    b = temporaryA
    }
    • swapTwoValues 函数名后面的 <T> 表示这个 T 类型是函数所定义的一个类型, T 只是一个类型的占位符,不用关心具体类型

    • (inout a: T, inout b: T) 中a和b两个参数类型T是泛型参数

    类型约束

    两个泛型参数的泛型函数

    1
    2
    3
    func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
    // function body goes here
    }
    • 泛型参数T约束条件:必须是SomeClass类的子类
    • 泛型参数U约束条件:必须遵守SomeProtocol协议
    • 使用where指定约束
    ]]>
    @@ -466,10 +505,10 @@ - - /2019/06/18/ReactNative%E4%B8%ADref%E4%BD%BF%E7%94%A8/ + + /2019/06/18/RN%E6%8A%A5%E9%94%99/ - Reat Native ref 使用

    State 状态的使用

    在父控件中指定,整个生命周期讲不再改变
    需求:一段闪烁的文字, Props 属性显示文字内容创建时初始化,State控制随时间变化是否显示,在父控件中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    class Blink extends Component{
    // 构造
    constructor(props) {
    super(props);
    // 初始状态
    this.state = {showWithText:true};
    this.state = {}
    setInterval(() => {
    this.setState(previousState => {
    return {showWithText: !previousState.showWithText}
    });
    }, 100);
    }
    render() {
    let display= this.state.showWithText ? this.props.text : ' ';
    return(
    <Text>{display}</Text>
    );
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    export default class App extends Component<Props> {
    render() {
    return(
    <View style={styles.container}>
    <Blink
    text='I love to blink'
    />

    <Blink
    text='猜猜猜'
    />

    <Blink
    text='Blink is great'
    />
    </View>
    );
    }
    }

    Props 属性的使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 定义一个组件
    class Greeting extends Component {
    render() {
    return(
    <Text> Hello {this.props.name}!
    </Text>
    );
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    export default class App extends Component<Props> {
    render() {
    return(
    <View style={styles.container}>
    <Image source={{uri:'https://upload.wikimedia.org/wikipedia/commons/d/de/Bananavarieties.jpg'}}
    style={{width: 190, height: 110}}
    />
    <Text style={styles.instructions}>
    使用Props属性
    </Text>
    <View>
    <Greeting name='React-Native'
    />
    <Greeting name='iOS'
    />
    <Greeting name='Swift'
    />
    </View>

    </View>
    );
    }
    }

    ref的使用

    • 可以理解为组件渲染后指向组件的一个应用,可以通过ref获取到真实的组件(类似指针?)

    定义

    1
    ref='scrollView'

    获取

    1
    2
    let scrollView = this.refs.scrollView
    let scrollView = this.refs['scrollView']
    • 可以通过ref访问组件的属性和方法

    ES6 定时器

    • 开启定时器
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    let scrollView = this.refs.scrollView;
    // 2.4timer 定时器的写法
    this.timer = setInterval(
    ()=>{
    var tempPage = 0;
    // 修改banner索引
    if ((this.state.currentPage+1) >= ImageData.data.length) {
    tempPage = 0;
    } else {
    tempPage = this.state.currentPage+1;
    }

    // 更新状态
    this.setState({
    currentPage: tempPage
    })

    let offSet_x = width * tempPage
    scrollView.scrollResponderScrollTo({x:offSet_x, y:0, animated: true})
    },
    this.state.duration
    );
    • 暂停定时器
    1
    this.timer && clearInterval(this.timer)
    ]]>
    + RN学习中遇到的错误总结

    执行 npm install 时报错

    错误1. Unexpected end of JSON input while parsing near '...er":"0.4.0"},"bin":{"'

    本地有package.json文件,执行 npm install 报错,或者clone 的项目执行npm install 也报同样的错误, 可能是因为缓存导致,

    解决办法:

    1. 设置镜像源,以前可能大部分都是用的淘宝

      1
      npm set registry https://registry.npmjs.org/
    2. 清除缓存

      1
      npm cache clean –-force
    3. 如果提示失败,使用sudo

      1
      sudo npm cache clean --force

    错误2. xcrun: error: unable to find utility "simctl", not a developer tool or in PATH

    可能是因为 Xcode 版本问题引起,XCode 偏好设置中 Command line Tools 中为选择版本问题导致(我的情况是安装了Xcode 10 beta版导致的)

    解决办法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    The following build commands failed:
    CompileC /Users/liepin/LiePinWorkspace/RNDemo/GitHubPopular/ios/build/Build/Intermediates.noindex/RCTWebSocket.build/Debug-iphonesimulator/RCTWebSocket.build/Objects-normal/x86_64/RCTSRWebSocket.o RCTSRWebSocket.m normal x86_64 objective-c com.apple.compilers.llvm.clang.1_0.compiler
    (1 failure)
    Installing build/Build/Products/Debug-iphonesimulator/GitHubPopular.app
    An error was encountered processing the command (domain=NSPOSIXErrorDomain, code=2):
    Failed to install the requested application
    An application bundle was not found at the provided path.
    Provide a valid path to the desired application bundle.
    Print: Entry, ":CFBundleIdentifier", Does Not Exist

    Command failed: /usr/libexec/PlistBuddy -c Print:CFBundleIdentifier build/Build/Products/Debug-iphonesimulator/GitHubPopular.app/Info.plist
    Print: Entry, ":CFBundleIdentifier", Does Not Exist

    错误3. Error: Cannot find module '../lib/utils/unsupported.js'

    安装的node 版本不是稳定的版本,需要删除后重新安装

    1
    2
    sudo rm -rf /usr/local/lib/node_modules/npm
    brew reinstall node

    错误4. Couldn't find preset "module:metro-react-native-babel-preset" when running jest

    解决方法

    已有项目集成RN

    报错1. 文件路径错误,查看你的node_modules 文件夹是在当前目录还是上级目录

    [!] No podspec found for `React` in `../node_modules/react-native
    • ../ 是指父级目录

    • ./ 是指当前目录

    报错 2. yoga Y 大小写为问题

    [!] The name of the given podspec `yoga` doesn't match the expected one `Yoga`_modules/react-native/ReactCommon/yoga"

    pod install 之后

    报错 1.

    解决办法: pod ‘React’, :subspecs 中加入 ‘CxxBridge’, ‘DevSupport’

    pod 'React', :path => './node_modules/react-native', :subspecs => ['Core','RCTText','RCTNetwork','RCTWebSocket', # 这个模块是用于调试功能的'CxxBridge', # Include this for RN >= 0.47'DevSupport', # Include this to enable In-App Devmenu if RN >= 0.43

    ]

    报错 2.

    RCTAnimation/RCTValueAnimatedNode.h' file not found

    解决方法:

    1
    #import <RCTAnimation/RCTValueAnimatedNode.h>

    替换为

    1
    #import "RCTValueAnimatedNode.h"

    修改头文件导入方式

    报错3 版本不匹配 react native version mismatch javascript version 0.54.3 native 0.57.2

    • 关闭所有的 terminal,或者是集成开发环境的命令行窗口

      1
      2
      watchman watch-del-all
      react-native start --reset-cache
    • 重新打开一个terminal窗口

      1
      react-native run-ios

      问题描述和解决1

      问题描述和解决2

    报错4 Unable to resolve module "schedule/tracking"

    缺少一些开发依赖的库

    npm i schedule@0.4.0 --save-dev

    问题描述和解决

    ]]>
    @@ -479,10 +518,10 @@ - - /2019/06/18/RN%E6%8A%A5%E9%94%99/ + + /2019/06/18/React-Native%E5%BC%80%E5%8F%91iOS%E6%89%93%E5%8C%85/ - RN学习中遇到的错误总结

    执行 npm install 时报错

    错误1. Unexpected end of JSON input while parsing near '...er":"0.4.0"},"bin":{"'

    本地有package.json文件,执行 npm install 报错,或者clone 的项目执行npm install 也报同样的错误, 可能是因为缓存导致,

    解决办法:

    1. 设置镜像源,以前可能大部分都是用的淘宝

      1
      npm set registry https://registry.npmjs.org/
    2. 清除缓存

      1
      npm cache clean –-force
    3. 如果提示失败,使用sudo

      1
      sudo npm cache clean --force

    错误2. xcrun: error: unable to find utility "simctl", not a developer tool or in PATH

    可能是因为 Xcode 版本问题引起,XCode 偏好设置中 Command line Tools 中为选择版本问题导致(我的情况是安装了Xcode 10 beta版导致的)

    解决办法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    The following build commands failed:
    CompileC /Users/liepin/LiePinWorkspace/RNDemo/GitHubPopular/ios/build/Build/Intermediates.noindex/RCTWebSocket.build/Debug-iphonesimulator/RCTWebSocket.build/Objects-normal/x86_64/RCTSRWebSocket.o RCTSRWebSocket.m normal x86_64 objective-c com.apple.compilers.llvm.clang.1_0.compiler
    (1 failure)
    Installing build/Build/Products/Debug-iphonesimulator/GitHubPopular.app
    An error was encountered processing the command (domain=NSPOSIXErrorDomain, code=2):
    Failed to install the requested application
    An application bundle was not found at the provided path.
    Provide a valid path to the desired application bundle.
    Print: Entry, ":CFBundleIdentifier", Does Not Exist

    Command failed: /usr/libexec/PlistBuddy -c Print:CFBundleIdentifier build/Build/Products/Debug-iphonesimulator/GitHubPopular.app/Info.plist
    Print: Entry, ":CFBundleIdentifier", Does Not Exist

    错误3. Error: Cannot find module '../lib/utils/unsupported.js'

    安装的node 版本不是稳定的版本,需要删除后重新安装

    1
    2
    sudo rm -rf /usr/local/lib/node_modules/npm
    brew reinstall node

    错误4. Couldn't find preset "module:metro-react-native-babel-preset" when running jest

    解决方法

    已有项目集成RN

    报错1. 文件路径错误,查看你的node_modules 文件夹是在当前目录还是上级目录

    [!] No podspec found for `React` in `../node_modules/react-native
    • ../ 是指父级目录

    • ./ 是指当前目录

    报错 2. yoga Y 大小写为问题

    [!] The name of the given podspec `yoga` doesn't match the expected one `Yoga`_modules/react-native/ReactCommon/yoga"

    pod install 之后

    报错 1.

    解决办法: pod ‘React’, :subspecs 中加入 ‘CxxBridge’, ‘DevSupport’

    pod 'React', :path => './node_modules/react-native', :subspecs => ['Core','RCTText','RCTNetwork','RCTWebSocket', # 这个模块是用于调试功能的'CxxBridge', # Include this for RN >= 0.47'DevSupport', # Include this to enable In-App Devmenu if RN >= 0.43

    ]

    报错 2.

    RCTAnimation/RCTValueAnimatedNode.h' file not found

    解决方法:

    1
    #import <RCTAnimation/RCTValueAnimatedNode.h>

    替换为

    1
    #import "RCTValueAnimatedNode.h"

    修改头文件导入方式

    报错3 版本不匹配 react native version mismatch javascript version 0.54.3 native 0.57.2

    • 关闭所有的 terminal,或者是集成开发环境的命令行窗口

      1
      2
      watchman watch-del-all
      react-native start --reset-cache
    • 重新打开一个terminal窗口

      1
      react-native run-ios

      问题描述和解决1

      问题描述和解决2

    报错4 Unable to resolve module "schedule/tracking"

    缺少一些开发依赖的库

    npm i schedule@0.4.0 --save-dev

    问题描述和解决

    ]]>
    + React-Native开发iOS打包

    打jsbudnle包

    创建打包后存放文件目录

    • cd 到 React-Native 项目根目录下 并且 进入 ios 文件目录下
    • 创建 bundle 文件夹

      1
      mkdir bundle

    配置 React-Native 打包命令

    • 打包命令

      1
      react-native bundle
    • 在React Native项目的根目录下执行命令

      1
      react-native bundle --entry-file index.js --platform ios --dev false --bundle-output ./ios/bundle/index.jsbundle --assets-dest ./ios/bundle
    - `--entry-file`: ios或者android入口的js名称,比如 `index.js`- `--platform`: 平台名称(ios或者android)- `--dev`: 设置为`false`的时候将会对`JavaScript`代码进行优化处理。- `--bundle-output`: 生成的jsbundle文件的名称和路径,比如 `./ios/bundle/index.jsbundle`- `--assets-dest`: 图片以及其他资源存放的目录

    使用打包命令

    • 将打包命令添加到 packger.json

      1
      2
      3
      4
      5
      "scripts": {
      "start": "node node_modules/react-native/local-cli/cli.js start",
      "test": "jest",
      "bundle-ios": "node node_modules/react-native/local-cli/cli.js bundle --entry-file index.js --platform ios --dev false --bundle-output ./ios/bundle/index.jsbundle --assets-dest ./ios/bundle"
      },
    • 再次打包只需要 React Native项目的根目录下执行命令,不用再次输入类似上面的命令

      1
      npm run bundle-ios

    集成到Xcode(iOS原生项目中)

    • 添加已经打好的.jsbundle 离线包

    • iOS 项目代码配置

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      private func loadReactNativeTest() {
      // debug 测试加载本地文件
      let url = URL(string: "http://localhost:8081/index.bundle?platform=ios")!

      // 加载已经加入的离线包
      let url2 = Bundle.main.url(forResource: "index", withExtension: "jsbundle")

      let rootView = RCTRootView(
      bundleURL: url,
      moduleName: "MyApp", //这里的名字要和index.js中相同
      initialProperties: nil,
      launchOptions: nil
      )
      view = rootView
      }
    ]]>
    @@ -492,10 +531,10 @@ - - /2019/06/18/React-Native%E5%BC%80%E5%8F%91iOS%E6%89%93%E5%8C%85/ + + /2019/06/18/ReactNative%E4%B8%ADref%E4%BD%BF%E7%94%A8/ - React-Native开发iOS打包

    打jsbudnle包

    创建打包后存放文件目录

    • cd 到 React-Native 项目根目录下 并且 进入 ios 文件目录下
    • 创建 bundle 文件夹

      1
      mkdir bundle

    配置 React-Native 打包命令

    • 打包命令

      1
      react-native bundle
    • 在React Native项目的根目录下执行命令

      1
      react-native bundle --entry-file index.js --platform ios --dev false --bundle-output ./ios/bundle/index.jsbundle --assets-dest ./ios/bundle
    - `--entry-file`: ios或者android入口的js名称,比如 `index.js`- `--platform`: 平台名称(ios或者android)- `--dev`: 设置为`false`的时候将会对`JavaScript`代码进行优化处理。- `--bundle-output`: 生成的jsbundle文件的名称和路径,比如 `./ios/bundle/index.jsbundle`- `--assets-dest`: 图片以及其他资源存放的目录

    使用打包命令

    • 将打包命令添加到 packger.json

      1
      2
      3
      4
      5
      "scripts": {
      "start": "node node_modules/react-native/local-cli/cli.js start",
      "test": "jest",
      "bundle-ios": "node node_modules/react-native/local-cli/cli.js bundle --entry-file index.js --platform ios --dev false --bundle-output ./ios/bundle/index.jsbundle --assets-dest ./ios/bundle"
      },
    • 再次打包只需要 React Native项目的根目录下执行命令,不用再次输入类似上面的命令

      1
      npm run bundle-ios

    集成到Xcode(iOS原生项目中)

    • 添加已经打好的.jsbundle 离线包

    • iOS 项目代码配置

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      private func loadReactNativeTest() {
      // debug 测试加载本地文件
      let url = URL(string: "http://localhost:8081/index.bundle?platform=ios")!

      // 加载已经加入的离线包
      let url2 = Bundle.main.url(forResource: "index", withExtension: "jsbundle")

      let rootView = RCTRootView(
      bundleURL: url,
      moduleName: "MyApp", //这里的名字要和index.js中相同
      initialProperties: nil,
      launchOptions: nil
      )
      view = rootView
      }
    ]]>
    + Reat Native ref 使用

    State 状态的使用

    在父控件中指定,整个生命周期讲不再改变
    需求:一段闪烁的文字, Props 属性显示文字内容创建时初始化,State控制随时间变化是否显示,在父控件中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    class Blink extends Component{
    // 构造
    constructor(props) {
    super(props);
    // 初始状态
    this.state = {showWithText:true};
    this.state = {}
    setInterval(() => {
    this.setState(previousState => {
    return {showWithText: !previousState.showWithText}
    });
    }, 100);
    }
    render() {
    let display= this.state.showWithText ? this.props.text : ' ';
    return(
    <Text>{display}</Text>
    );
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    export default class App extends Component<Props> {
    render() {
    return(
    <View style={styles.container}>
    <Blink
    text='I love to blink'
    />

    <Blink
    text='猜猜猜'
    />

    <Blink
    text='Blink is great'
    />
    </View>
    );
    }
    }

    Props 属性的使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 定义一个组件
    class Greeting extends Component {
    render() {
    return(
    <Text> Hello {this.props.name}!
    </Text>
    );
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    export default class App extends Component<Props> {
    render() {
    return(
    <View style={styles.container}>
    <Image source={{uri:'https://upload.wikimedia.org/wikipedia/commons/d/de/Bananavarieties.jpg'}}
    style={{width: 190, height: 110}}
    />
    <Text style={styles.instructions}>
    使用Props属性
    </Text>
    <View>
    <Greeting name='React-Native'
    />
    <Greeting name='iOS'
    />
    <Greeting name='Swift'
    />
    </View>

    </View>
    );
    }
    }

    ref的使用

    • 可以理解为组件渲染后指向组件的一个应用,可以通过ref获取到真实的组件(类似指针?)

    定义

    1
    ref='scrollView'

    获取

    1
    2
    let scrollView = this.refs.scrollView
    let scrollView = this.refs['scrollView']
    • 可以通过ref访问组件的属性和方法

    ES6 定时器

    • 开启定时器
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    let scrollView = this.refs.scrollView;
    // 2.4timer 定时器的写法
    this.timer = setInterval(
    ()=>{
    var tempPage = 0;
    // 修改banner索引
    if ((this.state.currentPage+1) >= ImageData.data.length) {
    tempPage = 0;
    } else {
    tempPage = this.state.currentPage+1;
    }

    // 更新状态
    this.setState({
    currentPage: tempPage
    })

    let offSet_x = width * tempPage
    scrollView.scrollResponderScrollTo({x:offSet_x, y:0, animated: true})
    },
    this.state.duration
    );
    • 暂停定时器
    1
    this.timer && clearInterval(this.timer)
    ]]>
    @@ -531,10 +570,10 @@ - - /2019/06/18/Flutter%E5%B8%B8%E7%94%A8%E7%BB%84%E4%BB%B6/ + + /2019/06/18/ES6%E8%AF%AD%E6%B3%95/ - flutter 常用组件

    组件

    • StatefulWidget
      • 具有可变状态的窗口部件,使用应用时可随时变化,例如进度条
    • StatelessWidget
      • 不可变窗口部件

    Text 组件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    //  Text 组件
    class MyText extends StatelessWidget{
    @override
    Widget build(BuildContext context){
    return Text('reacctNative、 flutter 学习之路是非常坚信不疑的,作为一个程序员,因该对于技术有着追求,不断的提高自己才是王道,可是道理都懂依然过不好这一生',
    maxLines: 2,
    overflow: TextOverflow.fade,
    textAlign: TextAlign.center,
    style: TextStyle(
    fontSize: 20.0,
    color: Colors.deepOrange,
    decoration: TextDecoration.underline,
    decorationColor: Colors.redAccent,
    decorationStyle: TextDecorationStyle.dotted
    ),
    );
    }
    }

    COntainer 组件

    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

    // Container容器组件
    class MyContainer extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
    return Container(
    child: Text('Container 容器中有文字',
    style: TextStyle(
    backgroundColor: Colors.cyan,
    ),
    ),
    alignment: Alignment.center, // 容器中内容对其方式
    width: 300.0,
    height: 200.0,
    padding: const EdgeInsets.all(10), // 容器内边距
    margin: const EdgeInsets.fromLTRB(10, 5, 10, 5), // container 和外部控件的边距
    decoration: new BoxDecoration(
    gradient: const LinearGradient(
    colors: [Colors.lightBlue, Colors.greenAccent, Colors.yellow]
    ),

    // 设置 border 的宽度
    border: Border.all(width: 5, color: Colors.black)

    ),
    );
    }
    }

    Image 组件

    fit属性

    • BoxFit.fill:全图显示,图片会被拉伸,并充满父容器。
    • BoxFit.contain:全图显示,显示原比例,可能会有空隙。
    • BoxFit.cover:显示可能拉伸,可能裁切,充满(图片要充满整个容器,还不变形)。
    • BoxFit.fitWidth:宽度充满(横向充满),显示可能拉伸,可能裁切。
    • BoxFit.fitHeight :高度充满(竖向充满),显示可能拉伸,可能裁切。
    • BoxFit.scaleDown:效果和contain差不多,但是此属性不允许显示超过源图片大小,可小不可大。

    图片的混合模式

    • color:是要混合的颜色,如果你只设置color是没有意义的。
    • colorBlendMode: 是混合模式。

    repeat 图片填充模式

    • ImageRepeat.repeat : 横向和纵向都进行重复,直到铺满整个画布。
    • ImageRepeat.repeatX: 横向重复,纵向不重复。
    • ImageRepeat.repeatY:纵向重复,横向不重复
    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
    class MyImage extends StatelessWidget {
    @override
    Widget build(BuildContext context){
    return Container(
    child: new Image.network(
    // 'https://images.xiaozhuanlan.com/photo/2019/28ed76123023e46483fcf0ae5c2ba11b.png',
    'https://p.ampmake.com/mall/product/9666f00e-f02a-46ce-9b7c-090bf6aba9aa.png',

    // 1. 设置填充模式
    // fit: BoxFit.scaleDown,
    // fit: BoxFit.none,
    // fit: BoxFit.fill,
    // fit: BoxFit.fitHeight,
    // fit: BoxFit.fitWidth,

    // 保持原图比例
    // fit: BoxFit.contain,

    // 充满整个容器,图片被裁切
    // fit: BoxFit.cover,
    // scale: 1.0,

    // 2. 图片混合
    color: Colors.red,
    colorBlendMode: BlendMode.overlay,

    // 3. reap
    repeat: ImageRepeat.repeat,
    ),
    width: 300.0,
    height: 200.0,
    color: Colors.lightBlue,
    );
    }
    }

    ListView 列表组件

    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
    class MyListView extends StatelessWidget{
    @override
    Widget build(BuildContext contex){

    return ListView(
    children: <Widget>[
    new ListTile(
    leading: new Icon(Icons.camera),
    title: Text('相册'),
    ),

    new ListTile(
    leading: new Icon(Icons.camera_alt),
    title: Text('相机'),
    ),

    new ListTile(
    leading: new Icon(Icons.camera_enhance),
    title: Text('相机1'),
    ),

    new ListTile(
    leading: new Icon(Icons.camera_front),
    title: Text('相机2'),
    ),

    new ListTile(
    leading: new Icon(Icons.camera_rear),
    title: Text('相机3'),
    ),
    ],
    );
    }
    }

    ListView.builder 适合列表项比较多(或者无限)的情况

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class MyListViewBuilder extends StatelessWidget {

    @override
    Widget build(BuildContext context){
    return ListView.builder(
    itemCount: 100, // 数量
    itemExtent: 40, // Tile 高度
    itemBuilder: (BuildContext context, int index){
    return ListTile(title: Text("$index"));
    }
    );
    }
    }

    横向列表

    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
    class MyHorizontalListView extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
    return Container(
    height: 100,
    child: ListView(
    // 设置滚动方式
    scrollDirection: Axis.horizontal,
    children: <Widget>[
    new Container(
    width: 180.0,
    color: Colors.green,
    ),
    new Container(
    width: 180.0,
    color: Colors.yellow,
    ),
    new Container(
    width: 180.0,
    color: Colors.blue,
    ),
    new Container(
    width: 180.0,
    color: Colors.cyan,
    ),
    new Container(
    width: 180.0,
    color: Colors.purple,
    ),
    ],
    ),
    );
    }
    }

    动态列表和构造方法的创建

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    class MyDynamicListView extends StatelessWidget {
    // 声明参数
    final List<String> items;
    // 构造方法
    MyDynamicListView({Key key, @required this.items}):super(key:key);
    @override
    Widget build(BuildContext context) {
    return Container(
    child: new ListView.builder(
    itemCount: items.length,
    itemBuilder: (context, index){
    return ListTile(
    title: new Text('通过构造方法创建的动态列表${items[index]}'),
    );
    },
    ),
    );
    }
    }

    GridView 网格列表的使用

    ]]>
    + JS ES6 语法

    1. let var 变量,作用域

    var a = 10;if (a > 10) {    let b = 5; // let 只能在代码块内被访问    console.log(b);}// 访问 b 报错console.log(b);console.log(a);

    2. const 只能分配一次值

    {
    const const_a = 1;
    console.log(const_a)

    // 报错const_a = 2;// 声明一个常量数组const const_array = [];const_array.push(1);const_array.push(2);// 再次赋值时会报错const_array = [3];

    }

    3. 解构语法

    {
    // 1.解构数组
    function getArray() {
    return [10, 20, 30];
    }

    let [a,b,c] = getArray()console.log(a, b, c)// 函数返回一个对象function getObj() {    return {dessert: '面包', drink: '水', play: 'Game'};}// 2.解构对象let {des: dessert,  dri: drink, pla: play} = getObj()console.log(des, dri, pla);

    }

    4. 模版字符串

    {
    let name = ‘姓名:’, password = ‘密码:’
    let str_4 = name + ‘李老师’ + ‘—-‘ + password + ‘北京太热。。。’
    console.log(str_4)

    // 使用 `${}` 包裹会生成字符串let str_41 = `${name}李老师----${password}北京太热`console.log(str_41)// 多行显示时直接回车换行let str_42 = `${name}李老师----${password}北京太热`console.log(str_42)

    }

    5. 带标签的模版字符串

    {
    let name = ‘姓名:’, password = ‘密码:’
    // 多行显示时直接回车换行
    let str_5 = strTest${name}李老师 ---- ${password}北京太热
    console.log(str_5)

    // strings 字符中包含的被插值分割的子字符串已数组形式展现// values 字符串中的插值数组function strTest(strings, ...values) {    console.log(strings)    console.log(values)}function strTest1(strings, ...values) {    let result = '';    for (let i = 0; i < values.length; i++) {        result += strings[i]        result += values[i]    }    return result}

    }

    6. 判断字符串是否包含其他字符

    {
    let name = ‘姓名:’, password = ‘密码:’
    let str_6 = ${name}李老师---${password}北京真热
    console.log(str_6.startsWith(${name}))
    console.log(str_6.endsWith(${name}))
    console.log(str_6.includes(‘李老师’))
    }

    7. 函数默认参数

    {
    function funcDefaultParame(param1 = ‘a’, param2 = ‘b’, param3 = ‘c’) {
    return ${param1}--${param2}--${param3}
    }
    console.log(funcDefaultParame(1, 2,3))
    }

    8. “…”展开操作符

    {
    let a = [1,2,3];
    console.log(a);
    console.log(…a)

    let b = [...a, 4, 5];console.log(a);console.log(b)

    }

    9. ‘… ‘剩余操作符, 一般用在函数参数, 可以看作是一个可以展开的参数

    //  ... param3表示函数可以接收除了param1, param2,两个参数外,多个参数,都会放在param3中function operator(param1, param2, ...param3) {    console.log(param1, param2, param3)}operator('a','b','c','d','e','f','g')

    10. 解构对象解构函数

    // 当函数参数是对象是可以按照对象解构function getObjcValue(param1, param2, {value1, value2} = {}) {    console.log(param1, param2, value1, value2)}getObjcValue(1,2,{value1: -3,value2: -4})

    11. 获取函数名称

    function getObjcValue(param1, param2, {value1, value2} = {}) {    console.log(param1, param2, value1, value2)}console.log(getObjcValue.name)let funName = function getfunctionName() {  }console.log(funName.name)

    12. 箭头函数

    // 原来普通写法var funType = function arrowfunction(param) {    return param}// 函数参数 => 函数返回值// '=>' 的左边表示箭头函数的参数,多个时表示为 (param1, param2, param3)// '=>' 的右边表示箭头函数的返回值,如果返回值是一个对象时用 { } 表示let arrowFunction = (param1, param2) => {    return `${param1}${param2}`

    }

    13

    ]]>
    @@ -544,10 +583,10 @@ - - /2019/06/18/GCD%20%E4%BF%A1%E5%8F%B7%E9%87%8F/ + + /2019/06/18/Flutter%E5%B8%B8%E7%94%A8%E7%BB%84%E4%BB%B6/ - GCD信号量用法

    对于异步网络请求相互依赖问题一般用三种方式解决:

    • 网络请求嵌套
    • 使用 BlockOperation
    • GCD DispatchGroup
    • GCD semaphore(信号量)

    GCD 信号量

    • semaphore 值 <= 0 阻塞
    • semaphore semaphore.signal() 使信号量值增加
    • semaphore.wait() 等待,可设置时长

    request – 3 依赖 request – 1 和 request – 2,1 和 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
    private func gcdSemaphore() {
    let semaphore = DispatchSemaphore(value: 0)
    DispatchQueue.global().async {
    for i in 0...300 {
    print("request -- 1")
    }
    semaphore.signal()
    }

    DispatchQueue.global().async {
    for i in 0...200 {
    print("request -- 2")
    }
    semaphore.signal()
    }


    DispatchQueue.global().async {
    semaphore.wait()
    semaphore.wait()
    for i in 0...160 {
    print("request -- 3")
    }
    }
    }

    request – 3 ,request – 2 和 request – 1,依次相互依赖

    private func gcdSemaphore() {        let semaphore = DispatchSemaphore(value: 0)        DispatchQueue.global().async {            for i in 0...300 {                print("request -- 1")            }            semaphore.signal()        }        DispatchQueue.global().async {            semaphore.wait()            for i in 0...200 {                print("request -- 2")            }            semaphore.signal()        }        DispatchQueue.global().async {            semaphore.wait()            for i in 0...160 {                print("request -- 3")            }        }    }
    ]]>
    + flutter 常用组件

    组件

    • StatefulWidget
      • 具有可变状态的窗口部件,使用应用时可随时变化,例如进度条
    • StatelessWidget
      • 不可变窗口部件

    Text 组件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    //  Text 组件
    class MyText extends StatelessWidget{
    @override
    Widget build(BuildContext context){
    return Text('reacctNative、 flutter 学习之路是非常坚信不疑的,作为一个程序员,因该对于技术有着追求,不断的提高自己才是王道,可是道理都懂依然过不好这一生',
    maxLines: 2,
    overflow: TextOverflow.fade,
    textAlign: TextAlign.center,
    style: TextStyle(
    fontSize: 20.0,
    color: Colors.deepOrange,
    decoration: TextDecoration.underline,
    decorationColor: Colors.redAccent,
    decorationStyle: TextDecorationStyle.dotted
    ),
    );
    }
    }

    COntainer 组件

    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

    // Container容器组件
    class MyContainer extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
    return Container(
    child: Text('Container 容器中有文字',
    style: TextStyle(
    backgroundColor: Colors.cyan,
    ),
    ),
    alignment: Alignment.center, // 容器中内容对其方式
    width: 300.0,
    height: 200.0,
    padding: const EdgeInsets.all(10), // 容器内边距
    margin: const EdgeInsets.fromLTRB(10, 5, 10, 5), // container 和外部控件的边距
    decoration: new BoxDecoration(
    gradient: const LinearGradient(
    colors: [Colors.lightBlue, Colors.greenAccent, Colors.yellow]
    ),

    // 设置 border 的宽度
    border: Border.all(width: 5, color: Colors.black)

    ),
    );
    }
    }

    Image 组件

    fit属性

    • BoxFit.fill:全图显示,图片会被拉伸,并充满父容器。
    • BoxFit.contain:全图显示,显示原比例,可能会有空隙。
    • BoxFit.cover:显示可能拉伸,可能裁切,充满(图片要充满整个容器,还不变形)。
    • BoxFit.fitWidth:宽度充满(横向充满),显示可能拉伸,可能裁切。
    • BoxFit.fitHeight :高度充满(竖向充满),显示可能拉伸,可能裁切。
    • BoxFit.scaleDown:效果和contain差不多,但是此属性不允许显示超过源图片大小,可小不可大。

    图片的混合模式

    • color:是要混合的颜色,如果你只设置color是没有意义的。
    • colorBlendMode: 是混合模式。

    repeat 图片填充模式

    • ImageRepeat.repeat : 横向和纵向都进行重复,直到铺满整个画布。
    • ImageRepeat.repeatX: 横向重复,纵向不重复。
    • ImageRepeat.repeatY:纵向重复,横向不重复
    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
    class MyImage extends StatelessWidget {
    @override
    Widget build(BuildContext context){
    return Container(
    child: new Image.network(
    // 'https://images.xiaozhuanlan.com/photo/2019/28ed76123023e46483fcf0ae5c2ba11b.png',
    'https://p.ampmake.com/mall/product/9666f00e-f02a-46ce-9b7c-090bf6aba9aa.png',

    // 1. 设置填充模式
    // fit: BoxFit.scaleDown,
    // fit: BoxFit.none,
    // fit: BoxFit.fill,
    // fit: BoxFit.fitHeight,
    // fit: BoxFit.fitWidth,

    // 保持原图比例
    // fit: BoxFit.contain,

    // 充满整个容器,图片被裁切
    // fit: BoxFit.cover,
    // scale: 1.0,

    // 2. 图片混合
    color: Colors.red,
    colorBlendMode: BlendMode.overlay,

    // 3. reap
    repeat: ImageRepeat.repeat,
    ),
    width: 300.0,
    height: 200.0,
    color: Colors.lightBlue,
    );
    }
    }

    ListView 列表组件

    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
    class MyListView extends StatelessWidget{
    @override
    Widget build(BuildContext contex){

    return ListView(
    children: <Widget>[
    new ListTile(
    leading: new Icon(Icons.camera),
    title: Text('相册'),
    ),

    new ListTile(
    leading: new Icon(Icons.camera_alt),
    title: Text('相机'),
    ),

    new ListTile(
    leading: new Icon(Icons.camera_enhance),
    title: Text('相机1'),
    ),

    new ListTile(
    leading: new Icon(Icons.camera_front),
    title: Text('相机2'),
    ),

    new ListTile(
    leading: new Icon(Icons.camera_rear),
    title: Text('相机3'),
    ),
    ],
    );
    }
    }

    ListView.builder 适合列表项比较多(或者无限)的情况

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class MyListViewBuilder extends StatelessWidget {

    @override
    Widget build(BuildContext context){
    return ListView.builder(
    itemCount: 100, // 数量
    itemExtent: 40, // Tile 高度
    itemBuilder: (BuildContext context, int index){
    return ListTile(title: Text("$index"));
    }
    );
    }
    }

    横向列表

    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
    class MyHorizontalListView extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
    return Container(
    height: 100,
    child: ListView(
    // 设置滚动方式
    scrollDirection: Axis.horizontal,
    children: <Widget>[
    new Container(
    width: 180.0,
    color: Colors.green,
    ),
    new Container(
    width: 180.0,
    color: Colors.yellow,
    ),
    new Container(
    width: 180.0,
    color: Colors.blue,
    ),
    new Container(
    width: 180.0,
    color: Colors.cyan,
    ),
    new Container(
    width: 180.0,
    color: Colors.purple,
    ),
    ],
    ),
    );
    }
    }

    动态列表和构造方法的创建

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    class MyDynamicListView extends StatelessWidget {
    // 声明参数
    final List<String> items;
    // 构造方法
    MyDynamicListView({Key key, @required this.items}):super(key:key);
    @override
    Widget build(BuildContext context) {
    return Container(
    child: new ListView.builder(
    itemCount: items.length,
    itemBuilder: (context, index){
    return ListTile(
    title: new Text('通过构造方法创建的动态列表${items[index]}'),
    );
    },
    ),
    );
    }
    }

    GridView 网格列表的使用

    ]]>
    @@ -557,10 +596,10 @@ - - /2019/06/18/ES6%E8%AF%AD%E6%B3%95/ + + /2019/06/18/GCD%20%E4%BF%A1%E5%8F%B7%E9%87%8F/ - JS ES6 语法

    1. let var 变量,作用域

    var a = 10;if (a > 10) {    let b = 5; // let 只能在代码块内被访问    console.log(b);}// 访问 b 报错console.log(b);console.log(a);

    2. const 只能分配一次值

    {
    const const_a = 1;
    console.log(const_a)

    // 报错const_a = 2;// 声明一个常量数组const const_array = [];const_array.push(1);const_array.push(2);// 再次赋值时会报错const_array = [3];

    }

    3. 解构语法

    {
    // 1.解构数组
    function getArray() {
    return [10, 20, 30];
    }

    let [a,b,c] = getArray()console.log(a, b, c)// 函数返回一个对象function getObj() {    return {dessert: '面包', drink: '水', play: 'Game'};}// 2.解构对象let {des: dessert,  dri: drink, pla: play} = getObj()console.log(des, dri, pla);

    }

    4. 模版字符串

    {
    let name = ‘姓名:’, password = ‘密码:’
    let str_4 = name + ‘李老师’ + ‘—-‘ + password + ‘北京太热。。。’
    console.log(str_4)

    // 使用 `${}` 包裹会生成字符串let str_41 = `${name}李老师----${password}北京太热`console.log(str_41)// 多行显示时直接回车换行let str_42 = `${name}李老师----${password}北京太热`console.log(str_42)

    }

    5. 带标签的模版字符串

    {
    let name = ‘姓名:’, password = ‘密码:’
    // 多行显示时直接回车换行
    let str_5 = strTest${name}李老师 ---- ${password}北京太热
    console.log(str_5)

    // strings 字符中包含的被插值分割的子字符串已数组形式展现// values 字符串中的插值数组function strTest(strings, ...values) {    console.log(strings)    console.log(values)}function strTest1(strings, ...values) {    let result = '';    for (let i = 0; i < values.length; i++) {        result += strings[i]        result += values[i]    }    return result}

    }

    6. 判断字符串是否包含其他字符

    {
    let name = ‘姓名:’, password = ‘密码:’
    let str_6 = ${name}李老师---${password}北京真热
    console.log(str_6.startsWith(${name}))
    console.log(str_6.endsWith(${name}))
    console.log(str_6.includes(‘李老师’))
    }

    7. 函数默认参数

    {
    function funcDefaultParame(param1 = ‘a’, param2 = ‘b’, param3 = ‘c’) {
    return ${param1}--${param2}--${param3}
    }
    console.log(funcDefaultParame(1, 2,3))
    }

    8. “…”展开操作符

    {
    let a = [1,2,3];
    console.log(a);
    console.log(…a)

    let b = [...a, 4, 5];console.log(a);console.log(b)

    }

    9. ‘… ‘剩余操作符, 一般用在函数参数, 可以看作是一个可以展开的参数

    //  ... param3表示函数可以接收除了param1, param2,两个参数外,多个参数,都会放在param3中function operator(param1, param2, ...param3) {    console.log(param1, param2, param3)}operator('a','b','c','d','e','f','g')

    10. 解构对象解构函数

    // 当函数参数是对象是可以按照对象解构function getObjcValue(param1, param2, {value1, value2} = {}) {    console.log(param1, param2, value1, value2)}getObjcValue(1,2,{value1: -3,value2: -4})

    11. 获取函数名称

    function getObjcValue(param1, param2, {value1, value2} = {}) {    console.log(param1, param2, value1, value2)}console.log(getObjcValue.name)let funName = function getfunctionName() {  }console.log(funName.name)

    12. 箭头函数

    // 原来普通写法var funType = function arrowfunction(param) {    return param}// 函数参数 => 函数返回值// '=>' 的左边表示箭头函数的参数,多个时表示为 (param1, param2, param3)// '=>' 的右边表示箭头函数的返回值,如果返回值是一个对象时用 { } 表示let arrowFunction = (param1, param2) => {    return `${param1}${param2}`

    }

    13

    ]]>
    + GCD信号量用法

    对于异步网络请求相互依赖问题一般用三种方式解决:

    • 网络请求嵌套
    • 使用 BlockOperation
    • GCD DispatchGroup
    • GCD semaphore(信号量)

    GCD 信号量

    • semaphore 值 <= 0 阻塞
    • semaphore semaphore.signal() 使信号量值增加
    • semaphore.wait() 等待,可设置时长

    request – 3 依赖 request – 1 和 request – 2,1 和 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
    private func gcdSemaphore() {
    let semaphore = DispatchSemaphore(value: 0)
    DispatchQueue.global().async {
    for i in 0...300 {
    print("request -- 1")
    }
    semaphore.signal()
    }

    DispatchQueue.global().async {
    for i in 0...200 {
    print("request -- 2")
    }
    semaphore.signal()
    }


    DispatchQueue.global().async {
    semaphore.wait()
    semaphore.wait()
    for i in 0...160 {
    print("request -- 3")
    }
    }
    }

    request – 3 ,request – 2 和 request – 1,依次相互依赖

    private func gcdSemaphore() {        let semaphore = DispatchSemaphore(value: 0)        DispatchQueue.global().async {            for i in 0...300 {                print("request -- 1")            }            semaphore.signal()        }        DispatchQueue.global().async {            semaphore.wait()            for i in 0...200 {                print("request -- 2")            }            semaphore.signal()        }        DispatchQueue.global().async {            semaphore.wait()            for i in 0...160 {                print("request -- 3")            }        }    }
    ]]>
    diff --git a/tags/AES-Base64-MD5/index.html b/tags/AES-Base64-MD5/index.html index 102bc16..9ee46dc 100644 --- a/tags/AES-Base64-MD5/index.html +++ b/tags/AES-Base64-MD5/index.html @@ -138,23 +138,23 @@

    Recent Posts

    diff --git a/tags/Alamofire/index.html b/tags/Alamofire/index.html index 33163c0..c92b00e 100644 --- a/tags/Alamofire/index.html +++ b/tags/Alamofire/index.html @@ -138,23 +138,23 @@

    Recent Posts

    diff --git a/tags/AlertView/index.html b/tags/AlertView/index.html index 9f672c3..2f2aba3 100644 --- a/tags/AlertView/index.html +++ b/tags/AlertView/index.html @@ -138,23 +138,23 @@

    Recent Posts

    diff --git a/tags/Flutter-GridView/index.html b/tags/Flutter-GridView/index.html index e1bbc52..9986914 100644 --- a/tags/Flutter-GridView/index.html +++ b/tags/Flutter-GridView/index.html @@ -138,23 +138,23 @@

    Recent Posts

    diff --git a/tags/Flutter/index.html b/tags/Flutter/index.html index 201548f..a6c973c 100644 --- a/tags/Flutter/index.html +++ b/tags/Flutter/index.html @@ -138,23 +138,23 @@

    Recent Posts

    diff --git a/tags/GCD/index.html b/tags/GCD/index.html index 1c1e4e4..6201484 100644 --- a/tags/GCD/index.html +++ b/tags/GCD/index.html @@ -138,23 +138,23 @@

    Recent Posts

    diff --git a/tags/Hexo/index.html b/tags/Hexo/index.html index b125e82..c5dc101 100644 --- a/tags/Hexo/index.html +++ b/tags/Hexo/index.html @@ -138,23 +138,23 @@

    Recent Posts

    diff --git a/tags/OC-JS/index.html b/tags/OC-JS/index.html index 1b67ea7..23696f5 100644 --- a/tags/OC-JS/index.html +++ b/tags/OC-JS/index.html @@ -138,23 +138,23 @@

    Recent Posts

    diff --git a/tags/Quartz-2D/index.html b/tags/Quartz-2D/index.html index a32b1f8..8cff448 100644 --- a/tags/Quartz-2D/index.html +++ b/tags/Quartz-2D/index.html @@ -138,23 +138,23 @@

    Recent Posts

    diff --git a/tags/Quartz2D/index.html b/tags/Quartz2D/index.html index 31f0d04..c192b19 100644 --- a/tags/Quartz2D/index.html +++ b/tags/Quartz2D/index.html @@ -157,23 +157,23 @@

    Recent Posts

    diff --git a/tags/RunTime/index.html b/tags/RunTime/index.html index 86b229b..9fcb79a 100644 --- a/tags/RunTime/index.html +++ b/tags/RunTime/index.html @@ -138,23 +138,23 @@

    Recent Posts

    diff --git a/tags/SDWebImage/index.html b/tags/SDWebImage/index.html index 6abc6a7..3ac60c6 100644 --- a/tags/SDWebImage/index.html +++ b/tags/SDWebImage/index.html @@ -138,23 +138,23 @@

    Recent Posts

    diff --git a/tags/SQL/index.html b/tags/SQL/index.html index 5295985..e3575eb 100644 --- a/tags/SQL/index.html +++ b/tags/SQL/index.html @@ -138,23 +138,23 @@

    Recent Posts

    diff --git a/tags/Swift/index.html b/tags/Swift/index.html index bbd4e22..0063989 100644 --- a/tags/Swift/index.html +++ b/tags/Swift/index.html @@ -157,23 +157,23 @@

    Recent Posts

    diff --git a/tags/TableView/index.html b/tags/TableView/index.html index 5f7988d..08e25b7 100644 --- a/tags/TableView/index.html +++ b/tags/TableView/index.html @@ -138,23 +138,23 @@

    Recent Posts

    diff --git a/tags/WKWebView/index.html b/tags/WKWebView/index.html index 73cdaaf..7d04844 100644 --- a/tags/WKWebView/index.html +++ b/tags/WKWebView/index.html @@ -224,23 +224,23 @@

    Recent Posts

    diff --git a/tags/git/index.html b/tags/git/index.html index d7d9788..fe82bd3 100644 --- a/tags/git/index.html +++ b/tags/git/index.html @@ -138,23 +138,23 @@

    Recent Posts

    diff --git "a/tags/\345\274\202\346\255\245/index.html" "b/tags/\345\274\202\346\255\245/index.html" index 4318814..e2d7f99 100644 --- "a/tags/\345\274\202\346\255\245/index.html" +++ "b/tags/\345\274\202\346\255\245/index.html" @@ -138,23 +138,23 @@

    Recent Posts

    diff --git "a/tags/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" "b/tags/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" index e21c426..25c6afc 100644 --- "a/tags/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" +++ "b/tags/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" @@ -138,23 +138,23 @@

    Recent Posts