<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Duke Wu&#39;s Blog</title>
    <link>https://blog.devwu.com/</link>
    <description>Recent content on Duke Wu&#39;s Blog</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <lastBuildDate>Sun, 20 Mar 2022 00:00:00 +0000</lastBuildDate><atom:link href="https://blog.devwu.com/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>3 个鲜为人知的 Postman 小技巧，助你成为卓有成效的工程师</title>
      <link>https://blog.devwu.com/2022/03/20/3-tips-of-postman-to-make-you-more-effective/</link>
      <pubDate>Sun, 20 Mar 2022 00:00:00 +0000</pubDate>
      
      <guid>https://blog.devwu.com/2022/03/20/3-tips-of-postman-to-make-you-more-effective/</guid>
      <description>问题 Postman 想必大家已经很熟悉了，本文不做过多介绍。在文章开始之前，请允许我提出以下几个问题。如果这些问题您都了然，请迅速关闭这个页面并立马去做更重要的事情。
 Postman 中如何设置路径参数？  例： GET /posts/{id}，如何为 id 字段赋值。   Postman 中的变量的作用域有哪些？ Postman 中如何修改 HTTP 请求报文？  例1：对请求参数进行重排序并生成签名 例2：对请求参数进行加密   Postman 中如何完成链式调用？  例1：一键操作，串行调用xx列表接口、xx详情接口。 例2：在调用业务接口之前，先从公共接口中获取 token 字段。   Postman 中如何校验和遴选 HTTP 响应报文？  例1：如何对响应报文进行校验 例2：如何使用可视化的方式，高亮显示关键数据？    如果你对上述问题抱有好奇，那好，我们今天便来聊聊 Postman 中一些被不为人知却非常有用的功能。
 利益不相关声明：
本文采用「和风天气API」与「心知天气API」进行讲解示范。
在互联网愈加封闭的今天，很庆幸仍有这些不忘初心的企业为我们提供如此简单易得的接口服务。
 正文 1. PathParam 路径参数 RESTful 形式的接口通常会有一些参数被放置在 URL 中。例如：GET /posts/1，其中的 1 表示 post 的 ID。
Postman 虽未提供明显的 UI 界面来设置路径参数，但我们可以在 URL 输入栏中以:params的形式来添加路径参数，如下所示。</description>
    </item>
    
    <item>
      <title>Kotlin中多态对象的JSON序列化</title>
      <link>https://blog.devwu.com/2022/03/13/polymorphic-json-convert-in-kotlin/</link>
      <pubDate>Sun, 13 Mar 2022 00:00:00 +0000</pubDate>
      
      <guid>https://blog.devwu.com/2022/03/13/polymorphic-json-convert-in-kotlin/</guid>
      <description>前言 在最近的项目中，我同时用到了 Moshi 和 kotlinx-serialization 这两个 JSON 库，两者的 API 都很简洁且实用。
与 Gson 的反射机制不同，Moshi 和 kotlinx-serialization 都提供了预编译机制，可以在编译期间分别生成 Adapter 和 Serializer，从而能够以类型安全、更高效的方式完成 JSON 的序列化和反序列化。
 Moshi  源于 Square ，与 Retrofit 的集成度较高，对 Android 平台的开发者比较友好 可以借助 kapt/ksp 在编译期生成 XxxJsonAdapter.kt 文件   kotlinx-serialization  源于 JetBrains，属于官方推出的扩展包，能够很方便的集成到 Ktor 中 基于 kotlin compiler plugin，在编译期生成字节码文件(Xxx$$serializer.class) 支持 KMP，能够跨平台使用。  例如：定义一套 DTO，同时在 Android端、iOS端、前端、桌面端、服务端复用。   官方支持 JSON、Protobuf、CBOR、Hocon、Properties 等格式 有大量的三方扩展，支持 TOML、XML、YAML、BSON、NBT、SharePreference、Bundle 等格式    如果你在使用 kotlin 进行日常开发工作，非常推荐你去体验和使用这两个 JSON 库。</description>
    </item>
    
    <item>
      <title>Java Exception 历史回顾</title>
      <link>https://blog.devwu.com/2022/01/10/exception-historical-in-java/</link>
      <pubDate>Mon, 10 Jan 2022 00:00:00 +0000</pubDate>
      
      <guid>https://blog.devwu.com/2022/01/10/exception-historical-in-java/</guid>
      <description>Java Exception 历史回顾 前言  本文从 Exception 的起源讲起，试图帮开发者建立对 Java、Kotlin 编程语言异常处理机制的感性认知。文章内容出自个人理解，如有纰漏，还请各位看官斧正。
 1990 年前后， Sun 公司的工程师团队在项目开发中经历了 C++ 的诸多痛点，对此 Sun 公司内部成立了 Stealth 计划小组（后改名为 Green 计划小组）来解决这些痛点问题。小组起初进行了多种尝试：有切换编程语言到 NexT 的；也有尝试改进和扩展 C++ 的，但都宣告失败。最终小组内的 James Gosling ( Java 之父) 决定创造一个新的编程语言来解决问题。他以办公室外的橡树为名，把这一编程语言称为 Oak，随后因商标原因将其更名为 Java。 Java 语言的开发过程中广泛借鉴了 C/C++ 的特性，C++ 中的异常处理机制也被沿用了下来。
C++ 语言中的异常  异常：表示程序中意料之外，情理之中的一类错误。
 C++ 中的异常处理是一种条件转移机制，当程序在运行过程中遇到 throw 关键字时，会中止当前的程序代码块并跳转到最近的 catch 代码块执行后续操作。这一行为对 C 语言中的 goto 机制，以及汇编语言中的转移指令（如：jmp）。
   C++ with Exception C/C++ with Goto          在 C 语言体系中，goto 因过于灵活和难以调试，常被诟病为邪恶的。常见的规避手段是使用 int 类型的返回值表示函数的执行状态，通常用 0 表示执行成功，非0表示执行失败。但上述做法违背了函数返回值的用途，导致大量库函数不得不增加额外的形参作为真实的返回值，如下所示：</description>
    </item>
    
    <item>
      <title>使用 Android 原生命令开启 WiFI ADB</title>
      <link>https://blog.devwu.com/2021/11/26/Use-buildin-command-to-open-WiFiAdb/</link>
      <pubDate>Fri, 26 Nov 2021 00:00:00 +0000</pubDate>
      
      <guid>https://blog.devwu.com/2021/11/26/Use-buildin-command-to-open-WiFiAdb/</guid>
      <description>Android 设备需要提前 root
 命令方式 使用 PC/Mac  需要 USB 连接 Android 设备后执行以下命令
 1  adb tcpip 5555   使用 Termux  无需 USB 线 , Android 设备需安装 termux app
在 Termux 中执行以下命令
 开启 WiFi ADB 1 2 3 4 5  # 设置 ADB 服务的端口为默认端口 su -c &amp;#39;setprop service.adb.tcp.port 5555&amp;#39; # 重启 ADB 守护进程以便 prop 生效 su -c &amp;#39;stop adbd&amp;#39; su -c &amp;#39;start adbd&amp;#39;   关闭 WiFi ADB 1 2 3 4 5  # 重置 ADB 服务的端口 su -c &amp;#39;setprop service.</description>
    </item>
    
    <item>
      <title>ICS-OpenVPN 编译小记</title>
      <link>https://blog.devwu.com/2021/11/23/ICS-OpenVPN-%E7%BC%96%E8%AF%91%E5%B0%8F%E8%AE%B0/</link>
      <pubDate>Tue, 23 Nov 2021 00:00:00 +0000</pubDate>
      
      <guid>https://blog.devwu.com/2021/11/23/ICS-OpenVPN-%E7%BC%96%E8%AF%91%E5%B0%8F%E8%AE%B0/</guid>
      <description>编译环境    Key Value      CPU 架构 arm64    CPU 型号 M1 Pro    操作系统 macOS Monterey 12.0.1    Android Studio arm64 Preview, 2020.3.1 Patch3    NDK 21.4.7075529    Swig@3 3.0.12    CMAKE 3.10          编译步骤  克隆或下载 ics-openvpn，并使用 Android Studio 打开 在 Android Studio 中安装最新的 cmake 和 ndk 在终端中进入 ics-openvpn 项目根目录，执行 git submodule init 、git submodule update  使用 homebrew 安装 swig@3： brew install swig@3 将 swig 命令添加到 PATH 中：  export PATH=&amp;quot;/opt/homebrew/opt/swig@3/bin:$PATH&amp;quot;   在 ics-openvpn 项目根目录执行 gradle build  </description>
    </item>
    
    <item>
      <title>聊聊分支管理</title>
      <link>https://blog.devwu.com/2019/11/09/Git-Branch-Management/</link>
      <pubDate>Sat, 09 Nov 2019 00:00:00 +0000</pubDate>
      
      <guid>https://blog.devwu.com/2019/11/09/Git-Branch-Management/</guid>
      <description>前言 背景 大多时候，开发人员仅需使用少量的分支，甚至只需 master 和 develop 两个分支即可完成日常工作。这足以应付小型项目或小规模团队的开发工作，但随着协作人员的增多和项目周期的延长，各样的挑战便会纷至沓来&amp;hellip;
代码版本控制的挑战  并行开发：如何开始一个 feature 的开发，而不影响别的 feature ？ 代码回溯：如何了解每次提交做了哪些工作，如何让提交记录承载软件文档的功能？ 代码回溯：随着时间的流逝，如何快速了解每个分支都做了什么？ 分支管理：由于新分支的创建是廉价的，分支多了之后该如何管理？ 发布管理：如何进行发布管理？发布时如何冻结 Feature 的合并？发布过程中如何修复线上 bug？发布过程中如何并行开发新功能？ 修复管理：线上代码出 Bug 后如何快速修复？修复后的代码如何安全、优雅的合并到所有协作者的工作分支中？ 代码维护：如何在多人员的并行开发过程中保证代码质量？  面临这些挑战，本文试图提出一种简洁的、清晰的、可执行的分支管理方案，以期能够规避一些常见的版本控制陷阱，提高协作效率，增强知识共享，降低维护成本。
本分支规范的目的  提高协作效率：基于 git 的分布式实现，发挥并行开发的优势 增强知识共享：提交记录即文档，使其成为知识的载体之一，使得项目变更具有回溯性 降低维护成本：通过清晰的分支划分，简化代码集成过程、问题修复过程，使得项目的集成更可控  代码提交规范  好的提交记录是分支管理的基石，在讲述分支管理之前，务必先了解代码提交规范。
 git commit 规范及原则   单一职责原则：细化提交粒度，每次提交内容应少而精，职责清晰，任务单一，拒绝将多个改动汇聚到同一个提交记录中
  基于第一点，每次提交日志应尽可能的准确，详细阐述本次提交的改动内容，必要时可详细阐述改动的原因并给出相关链接。下附提交日志格式，以供参考。
第一行，简述变更内容，对于简单改动，仅填写此行信息即可。控制在 72 个半角字符以内，避免 Web 端自动换行 第二行，保持空行 第三行，详细变更 1，必要时需说明变更原因 第四行，详细变更 2，必要时需说明变更原因 第五行，相关链接（Wiki、RFC、技术博客等）...   拒绝空日志、重复日志、无意义的提交记录
  避免不完整的提交，确保推送至远程分支的代码都是可运行的
 如有某次提交不完整的情况，可在推送至远端前采用git commit --amend 的方式完善上次提交的内容    避免过于琐碎的无意义提交，尽量保持提交记录清晰可读</description>
    </item>
    
    <item>
      <title>自定义 Gradle 插件开发(一)</title>
      <link>https://blog.devwu.com/2019/09/07/Developing-Gradle-Plugin-1/</link>
      <pubDate>Sat, 07 Sep 2019 00:00:00 +0000</pubDate>
      
      <guid>https://blog.devwu.com/2019/09/07/Developing-Gradle-Plugin-1/</guid>
      <description>Gradle 插件简介 Gradle 插件是一个能够将 Gradle 的构建逻辑（build logic）和构建任务（build task）打包到一起，以便在多个项目的构建脚本（build.gradle）中应用（apply）的工具。
例如，build.gradle 构建脚本文件内 apply plugin: &#39;java&#39; 、apply plugin: &#39;com.android.application&#39; 中的 java、com.android.application 就是官方提供的 Gradle 插件，通过应用这些插件，可以丰富项目的构建任务与构建逻辑。
除官方提供的插件外，Gradle 还允许开发者定义自己的 Gradle 插件。开发者可以根据实际需求定义自己的构建逻辑和构建任务，将其打包为 Gradle 插件，从而在多个项目的构建脚本中复用。此外，还可以将自定义的 Gradle 插件发布到 plugin portal 或其他仓库中，更为方便的分享给他人使用。
Gradle 插件对编程语言没有太多限制，只要是能够被编译为 JVM 字节码的编程语言，都能用来编写 Gradle 插件。Gradle-API 的被设计为对 Groovy、Java、Koltin 友好的，通常情况下，使用 Java 或 Kotlin 这类静态类型语言实现的 Gradle 插件的性能要比使用 Groovy 实现的相同常见的性能更好。
开始之前 Gradle 作为一个普通的构建工具，本身并不依赖任何可视化 GUI 工具。为简化步骤，本文将采用命令行方式来完成自定义 Gradle 插件的演示。在开始之前，需先将 gradle 命令添加到系统环境变量中。
若读者在 IDEA / Android Studio 使用过 gradle ，则可在当前用户目录下找到 gradle 命令，具体路径如下
 Mac：/Users/当前用户名/.gradle/wrapper/dists/gradle版本/沙盒路径/gradle版本/bin Win：C:\Users\当前用户名\.</description>
    </item>
    
    <item>
      <title>Kotlin/Everywhere BeijingGDG 见闻录</title>
      <link>https://blog.devwu.com/2019/08/31/Kotlin-Everywhere-Notes/</link>
      <pubDate>Sat, 31 Aug 2019 00:00:00 +0000</pubDate>
      
      <guid>https://blog.devwu.com/2019/08/31/Kotlin-Everywhere-Notes/</guid>
      <description>Kotlin-Everywhere 在今年五月份的 Google I/O 大会上，Google 宣布了 Android 应用开发中 Kotlin 优先的战略，此后 Google 提供的 API（例如 Jetpack 工具库）将以 Kotlin 优先。大会结束后不久，Google 便联合 Jetbrains 在全球范围内推出了 Koltin/Everywhere 计划。Kotlin/Everywhere 计划 是由各地社区主导的一系列活动，旨在关注 Kotlin 在多平台上的潜力，并为学习 Kotlin 的基本要素与最佳实践提供帮助，包括但不限于 Android、服务端、前端和其他平台。
Kotlin-Everywhere Beijing 笔者有幸和一位朋友一起参加了北京 GDG 与 Kotlin 中文社区共同主办的 Kotlin/Everywhere Beijing ，这场活动的嘉宾阵容空前强大，台下观众高朋满座，各路大咖云集，可谓之国内规格最高的一场 Kotlin/Everywhere 活动。会议的全程录像可在 大咖说 回放。
日程安排
   时间 主题 嘉宾     9:00-10:00 签到入场 -   10:00-10:10 开场介绍 韩国恺   10:10-10:50 What&amp;rsquo;s New in Kotlin Svetlana Isakova   10:50-11:30 携程 Kotlin Multiplatform 工程实践 陈琦   11:30-12:10 潜力无限：Kotlin 用于服务端与 WebAssembly 贾彦伟   12:10-13:40 中午午休 -   13:40-14:20 Boost Your Productivity with Kotlin 陈龙博   14:20-15:00 Be Friends with kotlinc 段建华   15:00-15:20 下午茶歇 -   15:20-16:00 Kotlin High-Performance Programming 朱涛3   16:00-16:40 Kotlin Coroutine 朱凯   16:40-17:20 Kotlin 函数式编程 乔禹昂    上午 我和朋友于九点半到达活动会场，会场签到处有 kotlin 主题的挎包、徽章、冰箱贴、贴纸等伴手礼，签到进场后发现观众席已落座 300 余人，便匆忙在一个靠中间的位置坐下，会场大屏播放着 Kotlin 相关的视频，视频中世界各地的开发者在表达着对 Kotlin 语言的喜爱，现场观众席也充斥着对 Kotlin 的探讨之声。</description>
    </item>
    
    <item>
      <title>MultiType 源码学习小结</title>
      <link>https://blog.devwu.com/2019/06/24/Insight-into-MultiType/</link>
      <pubDate>Mon, 24 Jun 2019 00:00:00 +0000</pubDate>
      
      <guid>https://blog.devwu.com/2019/06/24/Insight-into-MultiType/</guid>
      <description>MultiType 是一个为 RecycleView 创建多种 Item 的 Android 库，它的设计简洁优雅，源码阅读体验也很好，本文记录了笔者研读此项目源码的感悟。
MultiType 的简单使用 开始之前，先来看下 MultiType 的总体类图
图中左侧 MultiTypeAdapter 是供外部访问的主要类，它的使用方式如下。
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  class SampleActivity : AppCompatActivity() {   private lateinit var adapter: MultiTypeAdapter  private lateinit var items: MutableList&amp;lt;Any&amp;gt;   override fun onCreate(savedInstanceState: Bundle?</description>
    </item>
    
    <item>
      <title>使用 Android Studio 阅读 AOSP 源码</title>
      <link>https://blog.devwu.com/2019/06/23/how-to-reading-AOSP-with-Android-Studio/</link>
      <pubDate>Sun, 23 Jun 2019 00:00:00 +0000</pubDate>
      
      <guid>https://blog.devwu.com/2019/06/23/how-to-reading-AOSP-with-Android-Studio/</guid>
      <description>上一篇文章介绍了 AOSP 9 在 macOS 10.14 环境下的编译方式，本文就 Andorid Studio 环境下阅读 AOSP 源码的方式进行记录。
阅读前 AOSP 的准备 开始之前我们先为 AOSP 生成 IDEA 工程文件，便于稍后在 Android Studio 中载入。
1. 为 AOSP 生成 Android Studio 工程配置文件 1 2 3 4 5 6 7 8  cd /Volumes/AOSP # 设置 AOSP 编译所需的环境变量 source build/envsetup.sh # 使用 idegen.sh 脚本生成 IDEA 工程文件 development/tools/idegen/idegen.sh ------------------------------- Read excludes: 4ms Traversed tree: 68826ms   稍等片刻，即可在 AOSP 目录下看到生成的 android.iml 和 android.ipr，其中 iml 文件 表示 information of modules, 用来描述 AOSP 的模块信息。ipr 文件 表示 IDEA project configuration ，用来描述 IDEA 的工程配置信息，双击此文件时系统将直接使用 Andorid Studio 打开此项目。</description>
    </item>
    
    <item>
      <title>使用 macOS 10.14 编译 Android 9.0</title>
      <link>https://blog.devwu.com/2019/06/10/build-android-pie-in-macOS-Mojave/</link>
      <pubDate>Mon, 10 Jun 2019 00:00:00 +0000</pubDate>
      
      <guid>https://blog.devwu.com/2019/06/10/build-android-pie-in-macOS-Mojave/</guid>
      <description>随着移动端设备增量的放缓和市场上各行各业 App 的饱和，连笔者的手机都很久未安装过新 App，更不用提更换手机的频率。与此同时，移动开发也由上半场拼点子拼速度的快捷玩法，进入到下半场拼质量拼生态的高阶玩法。作为一名一线开发人员，大环境的走向与业务形态的发展虽由不得我们控制，但核心技术的掌握则是我们可以身体力行的( 笑~)。
AOSP 是 Android 系统的代码，囊括了 Andorid 系统的全部内容，从应用开发 Framework 层到 Android 虚拟机 ART/Dalvik 层再到 Linux 内核层，所有的疑惑都能在源码中找到答案，其源码的重要性不言而喻。本文作为系列的开篇，主要对 AOSP 的下载、编译过程进行阐述，同时记录下过程中遇到的问题及解决方案。
笔者的系统版本为 macOS 10.14.5，截止文章发布时仍是最新的操作系统，AOSP-android-9.0.0_r42 的源码加编译产物需要 200G 的磁盘空间，请提前预留好磁盘空间。若主机磁盘空间不足，可使用外置移动硬盘来解决。
1. Xcode 的安装 Xcode 包含了编译过程所需的各种工具，如 git、clang、make 等，在编译之前，我们先处理好 Xcode 相关的问题。
截止到2019年6月，AOSP 仍不支持使用 Xcode 10 编译。虽可在 AOSP 配置文件增加 10.14 字段绕过该限制，但后续编译时仍会出现莫名其妙的错误。因此，建议直接使用 Xcode9.4 来避免此类问题。Xcode9.4 的下载地址为Apple Developer Download，登录苹果账号后选中 Xcode 9.4 条目即可看到下载按钮。
若之前已安装 Xcode10 或更高版本，建议在 Xcode9.4 下载成功后进行以下操作：
 将原有 Xcode.app 更名为 Xcode10.app 或其他 解压缩 Xcode 9.4 对应的 Xcode.xip 文件，随后将解压缩出的 Xcode.</description>
    </item>
    
    <item>
      <title>导出七牛云存储的数据</title>
      <link>https://blog.devwu.com/2019/03/19/export-data-of-qiniu/</link>
      <pubDate>Tue, 19 Mar 2019 00:00:00 +0000</pubDate>
      
      <guid>https://blog.devwu.com/2019/03/19/export-data-of-qiniu/</guid>
      <description>前不久，七牛收回了其测试域名，导致先前创建的外链失效，关联的图片也因此而无法加载。继续使用七牛云的话，需要绑定一个已备案的域名。我没有域名，只能选择将七牛中的数据迁出，本文记录了我的迁出方案，仅供参考。
七牛控制台 登录七牛控制台，看到之前上传的文件都还在，不由松了一口气。
但因七牛提供的测试域名已被收回，这些文件无法直接下载。七牛官方有提供命令行工具 qshell 用来管理七牛云存储中的数据，下面便尝试用 qshell 来取回七牛云存储中的数据。
qshell 操作步骤  下载qshell 设置七牛密钥,AccessKey与SecretKey可在个人中心查看，username即登录名  1  $ ./qshell account $AccessKey $SecretKey $username   查看七牛中的所有的存储空间  1 2  $ ./qshell buckets bucket1 bucket2   查看任意bucket下的所有文件,bucket1即存储空间名称，可在第三步获得  1 2 3 4 5 6 7 8 9 10 11 12  $ ./qshell listbucket bucket1 20180607152835781571470.png	146467	FkomvVSnuHSTqOb5DTP2P-tMfvhu	15283578247522637	image/g&amp;gt; 0	20180607152835853736115.png	275169	FjNQ_IsQ2O1uAJ8GUIWnf4EI_ria	15283585599550264	image/g&amp;gt; 0	20180607152835873357855.png	301775	Fkf66UnqTOE_2qk9JLaK7CmBYGaz	15283587418650741	image/g&amp;gt; 0	20180607152835873635379.</description>
    </item>
    
    <item>
      <title>Android中同时finish多个Activity - 一次内存泄露的小记</title>
      <link>https://blog.devwu.com/2018/07/17/Android-ActivityUtil/</link>
      <pubDate>Tue, 17 Jul 2018 00:00:00 +0000</pubDate>
      
      <guid>https://blog.devwu.com/2018/07/17/Android-ActivityUtil/</guid>
      <description>Android开发中，有时需要同时finish多个Activity，避免用户点击后退按钮时回退到不该呈现的Activity。对于这样的问题，常见的方式是维护一个单例ActivityList，将多个Activity置入此单例列表，需要关闭多个Activity时可遍历该List依次执行finish操作。 代码如下所示：
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  public class ActivityUtil {  private static List&amp;lt;Activity&amp;gt; activityList;  private static ActivityUtil instance;  private ActivityUtil() {  }  public static ActivityUtil getInstance() {  return null == instance ?</description>
    </item>
    
    <item>
      <title>Android MVP架构解析</title>
      <link>https://blog.devwu.com/2018/07/08/Understand-Android-MVP-Architecture/</link>
      <pubDate>Sun, 08 Jul 2018 00:00:00 +0000</pubDate>
      
      <guid>https://blog.devwu.com/2018/07/08/Understand-Android-MVP-Architecture/</guid>
      <description>前言 Android Architecture Blueprints 是谷歌官方开源的一组 Android 架构方案，项目以多个分支分别采用 MVP，MVVM 的概念以及 dagger、rxjava、databinding、livedata 等工具库，演示了一个简单的 TodoApp 在不同架构模式下的代码组织方式。这个项目中的代码非常规范，架构模式也非常有借鉴意义，是一个很有价值的学习素材。
本文将通过 项目组织、架构组织与通信、设计原则 这三个层次对项目中的todo-mvp分支进行解析，从而达到学习的目的。
项目概览 在分析之前，我们先通过截图来了解一下TodoApp的功能。
   TodoApp      用来跳转列表页与统计页的抽屉窗口 统计页       列表页        详情页 编辑页        知晓了项目功能后，再来看项目的组织。
项目组织 在开始之前推荐大家先了解一下IDEA符号表，熟悉符号表对理解项目组织有很大帮助。
总览 app/src 除了 androidTest、main、test 这三个常见的模板目录外，还有androidTestMock、mock与prod 目录。androidTestMock 虽不常见，但通过名称可推断它是一个测试相关的目录，另外还有 mock、prod 这两个目录，我们稍后会讲到。 ## UI模块划分 app/src/main/java 下的 addedittask、statistics、taskdetail、tasks 这四个目录分别对应 App 内编辑页、统计页、详情页、列表页这四个模块，每个目录都有自己的 Activity、Fragment与Presenter，分别表示 MVP 中的 **View** 层和 **Presenter** 层，Contract 由字面意义推断为契约接口，此处暂且不表，稍后会在源码中了解其实际内涵。 ## Model层 app/src/main/java 下的 data 目录表示 **Model** 层，Task 是经final修饰的实体类模型，data/source 下有表示数据源接口的 TasksDataSource 与表示数据仓库类的 TasksRepository，data/source/local 与 data/source/remote 分别表示本地数据源与远程数据源，由ToDoDatabase及TastsDao可推断其local部分以数据库的形式实现。 最下方的 BasePresenter、BaseView 分别表示Presenter与View的基类，剩下的utils包用到时再进行查阅即可。</description>
    </item>
    
    <item>
      <title>编程马拉松 Day05 堆、二叉堆、堆排序</title>
      <link>https://blog.devwu.com/2018/06/27/coding-marathon-05/</link>
      <pubDate>Wed, 27 Jun 2018 00:00:00 +0000</pubDate>
      
      <guid>https://blog.devwu.com/2018/06/27/coding-marathon-05/</guid>
      <description>堆 堆排序需要用到二叉堆，在开始之前，我们先来了解一下什么是二叉堆。
当二叉树满足满足如下条件时，我们说这个二叉树是堆有序的：
 每一个父结点的值都比它的子结点大（称为大顶堆）或小（称为小顶堆） 子结点的大小与其左右位置无关  堆有序的二叉树，也可称为二叉堆。二叉堆是最常见的堆结构，因此也常将二叉堆直接称为堆，可以采用如下两种方式来表示二叉堆
 使用指针，二叉树的每个结点需存储三个指针，分别指向其父结点和两个子结点 使用数组，对二叉树做层序遍历，按层级顺序放入数组中，根结点在数组索引0的位置存放，其子结点分别在索引1和2的位置，1和2个子结点分别在位置3、4和5、6中存放，以此类推  就排序来讲，其所需处理的数据较为连续，没有空隙，可用完全二叉树来表示。对于完全二叉树，采用数组的表示方法也更方便些，下图展示了采用数组实现的两个二叉堆。 对于数组实现的二叉堆，索引为k的结点的父结点的索引为(k-1)/2，它的子结点的索引分别为2k+1和2k+2。
堆有序化 以大顶堆为例，有序化的过程中我们会遇到两种情况
 在堆底加入一个较大元素时，我们需要由下至上恢复堆的顺序 当将根结点替换为一个较小元素时，我们需要由上到下恢复堆的顺序  由下至上的堆有序化（上浮） 如果堆的有序状态因为某个结点变的比它的父结点更大而被打破，就需要通过将它与它的父结点交换来恢复堆有序。交换后，这个结点比它的两个子结点都大，但这个结点仍然可能比它现在的父结点更大。我们可以一遍遍的用同样的方式来将其向上移动，直到遇到一个比它更大的父结点或到达了堆的根结点，如下图所示。 上浮操作对应的代码如下
1 2 3 4 5 6  private void swim(Integer arr[], int k) {  while(k &amp;gt; 0 &amp;amp;&amp;amp; arr[(k - 1) / 2] &amp;lt; arr[k]) { //若k&amp;gt;0且索引为k的结点大于其父结点时，将该结点与其父结点交换  swap(arr, k, (k - 1) / 2);  k = (k - 1) / 2;  } }   由上至下的堆有序化（下沉） 如果堆的有序状态因为某个结点变的比它的某个子结点更小而被打破，就需要通过将它和它的子结点中较大者交换位置来恢复堆有序。交换可能会在子结点处继续打破堆的有序状态，此时可以采用相同的方式，将结点向下移动直到它的子结点都比它小或是到达了堆的底部，如下图所示。 下沉操作对应的代码如下</description>
    </item>
    
    <item>
      <title>编程马拉松 Day04 希尔排序、归并排序、快速排序</title>
      <link>https://blog.devwu.com/2018/06/24/coding-marathon-04/</link>
      <pubDate>Sun, 24 Jun 2018 00:00:00 +0000</pubDate>
      
      <guid>https://blog.devwu.com/2018/06/24/coding-marathon-04/</guid>
      <description>本文将介绍三个高级排序算法
 希尔排序 归并排序 快速排序  希尔排序 希尔排序(Shell&amp;rsquo;s Sort)的名称源于它的发明者Donald Shell，这是一种基于插入排序算法的改进。在处理大规模乱序数组时，插入排序的速度不容乐观，因为它只能一点一点的将元素从数组的一端移动到另一端。希尔排序为了加快速度，对插入排序进行了小幅的改动，开始时将数组划分为m相邻的若干个子数组，并对每一个子数组进行插入排序，然后缩小m的值再次划分并排序，循环往复直到完成m = 1时的最后一次插入排序，此时整个数组有序。
插入排序，每次将第k个元素插入前k-1个元素之间。 希尔排序示例，每次将第mk个元素插入到k,2k,3k&amp;hellip;mk这m个元素之间。 希尔排序的思路是使数组中任意间隔为m的元素都是有序的，这样的数组被称为m有序数组。一般m的初始值较大，以便我们可以将元素移动到很远的地方，随着希尔排序的进行，m会逐渐变小，当收敛到1时希尔排序完全退化为插入排序，从而完成最后的排序动作。 希尔排序高效的原因是它完全利用了插入排序的特点：
 初始时，m值较大，子数组元素个数较小（小规模数据） 初始前几次，构造出了m有序子数组（部分有序）  希尔排序的过程如下图所示 希尔排序代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22  public static void shellSort(Integer arr[]) {  long before = System.currentTimeMillis();  int m = 1;  //获取的m值，与数组长度相关  //子数组的部分有序的程度取决于m的值，而如何选取m的值是一项数学难题，在这里我们采用`1 4 13 40 121 364`这样的序列来作为希尔排序中m的值。  while (m &amp;lt; arr.</description>
    </item>
    
    <item>
      <title>编程马拉松 Day03 冒泡排序、选择排序、插入排序</title>
      <link>https://blog.devwu.com/2018/06/23/coding-marathon-03/</link>
      <pubDate>Sat, 23 Jun 2018 00:00:00 +0000</pubDate>
      
      <guid>https://blog.devwu.com/2018/06/23/coding-marathon-03/</guid>
      <description>排序是科学计算和数据处理必不可少的一个环节，今天起我们就来聊聊排序。
本文将介绍三个初级排序算法
 冒泡排序 选择排序 插入排序  先来看下图这样的一组初始数据，每一个矩形的高度都与其下方的数字成比例，数值越大则矩形的高度就越高。
假设有如下两个问题，我们该如何求解。
 找出最(小/大)值 找出第k(小/大)的值  显然，在乱序的数组中这两个问题都不太容易求解，但如果数据是有序的就会容易很多。
冒泡排序 冒泡排序是最容易想到的排序算法。以对N个元素的数组进行升序排序为例，其基本思路如下：
 从数组内的前两个元素开始，将这两个元素进行比较，如果前一个元素大于后一个元素，则交换两者的位置 接着取数组中的第2-3个元素进行比较，若第2个元素大于第3个元素，则交换两者的位置 循环往复，直到数组中的最后两个元素，此时，若第N-1个元素大于第N个元素，则交换它们的位置。经过一轮的比较与交换，我们已经得到了数组中最大的元素，并将其安置在了数组的第N位。 经过前三个步骤，我们将数组中最大的元素放到了第N位，下边只用排序数组中的前N-1个元素即可。此时我们将N的值减1，并判断新N的值，若新N&amp;gt;0，则循环1-3步骤；若新N=0，则代表我们已经完成了排序。  冒泡排序的过程(剪辑版)如下图所示，你也可以点击这里查看完整的冒泡排序过程。 我们知道，水杯中出现气泡时，越大的气泡浮力越大，上升速度也就越快，最先到达水面，冒牌排序中每轮遴选较大元素放置末尾的行为与水中气泡上升的现象十分相似，因此得名冒泡排序。
冒泡排序代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16  public static void bubbleSort(Integer arr[]) {  int compareCount = 0;//比较次数  int swapCount = 0;//交换次数  long before = System.currentTimeMillis();  for (int i = 0; i &amp;lt; arr.</description>
    </item>
    
    <item>
      <title>编程马拉松 Day02 递归</title>
      <link>https://blog.devwu.com/2018/06/21/coding-marathon-02/</link>
      <pubDate>Thu, 21 Jun 2018 00:00:00 +0000</pubDate>
      
      <guid>https://blog.devwu.com/2018/06/21/coding-marathon-02/</guid>
      <description>今天是第二天，继续我们的征程。
题目  编写代码，把字符串中的每个空格替换为%20。例如，输入&amp;quot;hello world.&amp;quot;，则输出&amp;quot;hello%20world.&amp;quot;。 编写代码，给定系数n，求1+2+3+&amp;hellip;+n的总和，即∑运算符 编写代码，观察如下数列，给定系数n，求数列中的第n个数字(tips: 斐波那契数列)。 1 1 2 3 5 8 13 21 34 55 89 ...  字符替换 本题是将空格等特殊字符变为转义字符的函数，常用于URL编码中，用来避免URL中可能存在的字符歧义。
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  /** * 判断当前字符是否为普通字符 */ public static boolean isPlainChar(char c) {  return (c &amp;gt;= &amp;#39;0&amp;#39; &amp;amp;&amp;amp; c &amp;lt;= &amp;#39;9&amp;#39;) ||  (c &amp;gt;= &amp;#39;a&amp;#39; &amp;amp;&amp;amp; c &amp;lt;= &amp;#39;z&amp;#39;) ||  (c &amp;gt;= &amp;#39;A&amp;#39; &amp;amp;&amp;amp; c &amp;lt;= &amp;#39;Z&amp;#39;) ||  c == &amp;#39;!</description>
    </item>
    
    <item>
      <title>编程马拉松 Day01 面试题小记</title>
      <link>https://blog.devwu.com/2018/06/20/coding-marathon-01/</link>
      <pubDate>Wed, 20 Jun 2018 00:00:00 +0000</pubDate>
      
      <guid>https://blog.devwu.com/2018/06/20/coding-marathon-01/</guid>
      <description>打算从今天起开始每日一练，巩固一下算法，数据结构相关的知识，废话少说，开始看题。
以上是我近期面试中遇到的一些题，其中1，3题出自百度系面试官；2，4出自Uber系面试官。
两个水杯倒出3L水的问题 先来看第一个问题，题干是要求倒腾出3L的水。我们可以从4、5两个数字入手，观察通过怎样的方式能够得到3，得到以下两种情况
 4 * 2 -5 == 3 ( 5 - 4 ) * 3 == 3  转换为文字
 第一种方式  先将4L的杯子接满水，全部倒入到5L的杯子中（tips：此时4L的杯子为空，5L的杯子中装载了4L水，还能再倒入1L） 再将4L的杯子接满水，然后向5L的杯子倒水，当5L杯子倒满时，4L杯子剩下3L水   第二种方式  将5L的杯子接满水，倒入4L的杯子中（tips: 此时5L的杯子剩下1L水），然后将4L杯子的水全部倒掉，再将5L杯子剩余的1L水倒入4L的杯子（tips: 此时4L杯子有1L水） 将5L的杯子接满水，倒入4L的杯子中（tips: 由于上一轮4L杯子已存在1L水，本次5L杯最多只能倒出3L水，倒出后5L杯剩余2L水），然后将4L杯子的水全部的倒掉，再将5L杯子剩余的2L水倒入到4L的杯子（tips: 此时4L杯子有2L水） 将5L的杯子接满水，倒入4L的杯子中（tips: 由于上一轮4L杯子已存在2L水，本次5L杯最多只能倒出2L水，倒出后5L杯正好剩余3L水）    大数四则运算 第二个问题是一个典型的大数运算问题。编程中有时会遇到数字上溢（tips: 如数值的大小超过了Long型可表示的最大数）的问题，此时便需要采用字符串替换原有的数值计算。
以10进制加法为例，解决思路如下：
 使用字符串装载参与计算的两个数值 分别取两个字符串的末尾数值 a和b，进行相加计算 sum = a+b ，如结果sum大于9，则设置进位标志 flag = 1；否则设置 flag = 0。记录sum%10，表示当前位的结果 再次取两个字符串的次末尾数值 a和b，进行相加计算 sum = a+b+flag ，如结果sum大于9，则设置进位标志 flag = 1；否则设置 flag = 0。记录sum%10，表示当前位的结果 &amp;hellip; 重复以上步骤，若两个数字字符串的长度不一致，则当其中较短字符串s1遍历完成后，只遍历另一个字符串s2，每次取出s2的末位数值与flag相加，得到新的进制标志和当前位的结果。  代码如下：</description>
    </item>
    
    <item>
      <title>IntelliJ IDEA 断点调试时查看所有变量</title>
      <link>https://blog.devwu.com/2018/06/07/IntelliJ-IDEA-%E6%96%AD%E7%82%B9%E8%B0%83%E8%AF%95%E6%97%B6%E6%9F%A5%E7%9C%8B%E6%89%80%E6%9C%89%E5%8F%98%E9%87%8F/</link>
      <pubDate>Thu, 07 Jun 2018 00:00:00 +0000</pubDate>
      
      <guid>https://blog.devwu.com/2018/06/07/IntelliJ-IDEA-%E6%96%AD%E7%82%B9%E8%B0%83%E8%AF%95%E6%97%B6%E6%9F%A5%E7%9C%8B%E6%89%80%E6%9C%89%E5%8F%98%E9%87%8F/</guid>
      <description>在看Java HashMap工作原理及实现这篇文章时，发现博主在断点模式下来观察hash冲突的效果挺好的，随后便想在IntelliJ IDEA中也试下。
我们先来看下HashMap.Node的代码，有4个成员变量。
1 2 3 4 5 6 7  static class Node&amp;lt;K,V&amp;gt; implements Map.Entry&amp;lt;K,V&amp;gt; {  final int hash;  final K key;  V value;  Node&amp;lt;K,V&amp;gt; next;  //省略后续内容 }   如下图所示，对于HashMap.Node类型的对象，IDEA默认只显示了key，value这两个成员变量。
原因在于IDEA默认将HashMap.Node视为了Map.Entry，那么如何在Debug时以HashMap.Node的类型来查看该对象其他变量呢？
方法有二：
一. 在debugger的Variables面板中，右键该对象，找到View As选项，选择其中的Object，然后就可以看到该对象的所有属性了。
二. 采用第一种方式时，每次Debug都需要手动指定其类型，较为繁琐。对于常用的类，我们也可以在View as时点击Create按钮，为其新建类型。这样的话以后每次Debug时，无需任何设置就能在Variables面板内直接看到其准确类型的所有属性。
Create过程也很简单，在Java Type Renders窗口内，设置HashMap$Node对应的完整类名即可，如下图所示。 最后看一下，在View as中Create了HashMap及HashMap.Node后的效果。 现在已经能看到类中的所有成员变量了，那么对于静态变量该如何查看呢？这个也很简单，同样在此处右键，点击Customize Data Views&amp;hellip; 然后在Static fields和Static final fields的选项前打勾即可~
以HashMap为例，在选中了Static fields和Static final fields的选项后，所有的静态变量也显示出来了 ：） </description>
    </item>
    
    <item>
      <title>objc/runtime 探索(四)</title>
      <link>https://blog.devwu.com/2014/08/19/objcruntime-explore4/</link>
      <pubDate>Tue, 19 Aug 2014 00:00:00 +0000</pubDate>
      
      <guid>https://blog.devwu.com/2014/08/19/objcruntime-explore4/</guid>
      <description>前言 在这一篇中,我们来聊一聊runtime中method
定义 先来看一下method相关的定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14  	typedef struct objc_method *Method; 	typedef struct objc_selector *SEL; 	typedef void (*IMP)(void /* id, SEL, ... */ ); 	//方法描述 	struct objc_method_description { 	SEL name; //方法名称 	char *types; //参数类型字符串 	}; 	//以下代码是 ObjC2.0 之前method的定义 	struct objc_method { 	SEL method_name; 	char *method_types; 	IMP method_imp; 	}   里边有三个类型别名,在这儿先解释一下</description>
    </item>
    
    <item>
      <title>objc/runtime 探索(三)</title>
      <link>https://blog.devwu.com/2014/08/18/objcruntime-explore3/</link>
      <pubDate>Mon, 18 Aug 2014 00:00:00 +0000</pubDate>
      
      <guid>https://blog.devwu.com/2014/08/18/objcruntime-explore3/</guid>
      <description>前言 续前,本文主要谈论类中的变量和属性
1.Ivar 1.1 Ivar的类型 typedef objc_ivar * Ivar;
1 2 3 4 5 6 7 8  	struct objc_ivar { 	char *ivar_name; //ivar名称 	char *ivar_type; //ivar类型 	int ivar_offset;	//ivar偏移量 	#ifdef __LP64__ 	int space; 	#endif 	}//ObjC2.0 已过时   Ivar是objc_ivar的指针,包含变量名称,变量类型等成员.
1.2 为类添加Ivar 运行时规定,只能在objc_allocateClassPair与objc_registerClassPair两个函数之间为类添加变量
如下所示:
1 2 3 4 5 6 7 8  	//额外空间 未知,通常设置为 0 	Class clazz = objc_allocateClassPair(父类class,类名,额外空间); 	//以NSString*为例 	//变量size sizeof(NSString) 	//对齐 指针类型的为log2(sizeof(NSString*)) 	//类型 @encode(NSString*) 	BOOL flag = class_addIvar(clazz,变量名,变量size,对齐,类型); 	objc_registerClassPair(clazz);   1.</description>
    </item>
    
    <item>
      <title>objc/runtime 探索(二)</title>
      <link>https://blog.devwu.com/2014/08/17/objcruntime-explore2/</link>
      <pubDate>Sun, 17 Aug 2014 00:00:00 +0000</pubDate>
      
      <guid>https://blog.devwu.com/2014/08/17/objcruntime-explore2/</guid>
      <description>前言 接上次的话题,继续就objc/runtime进行讨论.
经过今晚的探究,基本掌握了运行时的函数规则,总的有如下几种函数前缀.
在后边的文章中我会就一些问题再次讨论,本文权当做API的速查手册使用.
 objc_ class_ object_ method_ property_ protocol_ ivar_ ,sel_ ,imp_  1.objc_xxx 系列函数    函数名称 函数作用     objc_getClass 获取Class对象   objc_getMetaClass 获取MetaClass对象   objc_allocateClassPair 分配空间,创建类(仅在 创建之后,注册之前 能够添加成员变量)   objc_registerClassPair 注册一个类(注册后方可使用该类创建对象)   objc_disposeClassPair 注销某个类   objc_allocateProtocol 开辟空间创建协议   objc_registerProtocol 注册一个协议   objc_constructInstance 构造一个实例对象(ARC下无效)   objc_destructInstance 析构一个实例对象(ARC下无效)   objc_setAssociatedObject 为实例对象关联对象   objc_getAssociatedObje*ct 获取实例对象的关联对象   objc_removeAssociatedObjects 清空实例对象的所有关联对象   objc_msgSend 发送ObjC消息       objc_系列函数关注于宏观使用,如类与协议的空间分配,注册,注销等操作</description>
    </item>
    
    <item>
      <title>objc/runtime 探索(一)</title>
      <link>https://blog.devwu.com/2014/08/15/objcruntime-explore1/</link>
      <pubDate>Fri, 15 Aug 2014 00:00:00 +0000</pubDate>
      
      <guid>https://blog.devwu.com/2014/08/15/objcruntime-explore1/</guid>
      <description>前言 最近闲来无事,打算对objc/runtime进行一番研究,今晚把API翻了一遍,拿出了如下的一些有趣代码,本代码需事先导入部分objc/runtime中的头文件,如下所示
1 2  	#import &amp;lt;objc/runtime.h&amp;gt; 	#import &amp;lt;objc/message.h&amp;gt;   动态创建类  类的创建分为两步,添加成员变量需要在这两步操作之间,添加成员方法则无此要求
  Class objc_allocateClassPair(Class superClass,const char* className,size_t extraBytes); void objc_registerClassPair(Class cls);  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16  //开始类的定义 Class Test= objc_allocateClassPair([NSObject class], &amp;#34;Test&amp;#34;, 0); //为类添加变量 class_addIvar(Test, &amp;#34;_name&amp;#34;, sizeof(NSString*), log2(sizeof(NSString*)), @encode(NSString*)); //为类添加方法 //1.注册名为 test: 的方法 SEL s = sel_registerName(&amp;#34;test:&amp;#34;); //2.定义函数实现,此处的IMP是函数指针,原型为 typedef id (*IMP)(id, SEL, .</description>
    </item>
    
    <item>
      <title>变长参数的讨论</title>
      <link>https://blog.devwu.com/2014/08/15/variadic-param/</link>
      <pubDate>Fri, 15 Aug 2014 00:00:00 +0000</pubDate>
      
      <guid>https://blog.devwu.com/2014/08/15/variadic-param/</guid>
      <description>前言 一直以来对变长参数都比较好奇,索性花时间解解痒.
先从数组指针来看 void test1(int* arr,int count) { int sum = 0; for (int *p = arr; p &amp;lt; arr + count; p++) { sum = sum+ *p; } printf(&amp;quot;%d\n&amp;quot;,sum ); }  代码较为简单,此处不再展开.
接下来是可变参数  可变参数需要使用 &amp;lt;stdarg.h&amp;gt; 头文件,请提前导入
  什么是可变参数?  可变参数是函数或方法中形如int show(int a,...)这样的的参数   可变参数的相关规定  ... 不能单独出现,且只能放在参数列表中的最后 变长参数的类型没有限定,使用时最好统一类型,避免类型安全问题 变长参数的个数没有限定,调用时一般以0x00即 NULL/nil结尾       void test2(int a,...) { int sum = a; //声明变长参数列表vl va_list vl; //va_start :将变长参数的前一个参数的地址存入vl va_start(vl,a); int next; //va_arg :将vl向后偏移sizeof(int),并返回偏移后长度为sizeof(int)的数据 //判断 next 是否为 0x00 ,如果不为 0x00 则继续遍历 while( (next = va_arg(vl,int)) ) { sum+= next; } //释放变长参数列表vl va_end(vl); printf(&amp;quot;%d\n&amp;quot;,sum ); }  调用形式 int main(int argc,char * args[]) {	int arr[] = {1,2,3,4,5}; //数组指针 test1(arr,5); printf(&amp;quot;---------\n&amp;quot;); //变长参数 test2(1,2,3,4,5,6,NULL); return 0; }  输出结果 15 --------- 21  小结 .</description>
    </item>
    
    <item>
      <title>数据结构--栈</title>
      <link>https://blog.devwu.com/2014/07/28/stack/</link>
      <pubDate>Mon, 28 Jul 2014 00:00:00 +0000</pubDate>
      
      <guid>https://blog.devwu.com/2014/07/28/stack/</guid>
      <description>栈 栈是一种数据结构,其特点是后进先出(LIFO).计算机中有很多操作都采用了栈的数据结构,如浏览器的后退,文本编辑的撤销操作等.
栈的定义  栈的额定大小 栈的实际大小 栈的数据  栈的操作  入栈 出栈 清空栈 初始化栈 输出栈内容  栈的应用   中缀转后缀
 从左到右遍历中缀表达式: 遇到数值时,直接输出; 遇到操作符时: 如果栈为空或操作符为(,则操作符入栈 如果操作符为),则一直弹栈到与其匹配的(为止 如果栈不为空,则将栈顶操作符与当前操作符进行比较: 如果当前操作符的优先级小于等于栈顶符号的优先级,则将栈顶操作符出栈输出,然后再次拿栈顶符号与当前操作符进行比较; 如果当前操作符的优先级大于栈顶操作符的优先级,则将当前操作符入栈; 直到表达式结束    后缀表达式的计算:
 从左到右遍历表达式: 遇到数值直接进栈; 遇到操作符时,取出栈顶的两个元素进行计算,将计算结果入栈 直到表达式结束    </description>
    </item>
    
    <item>
      <title>指针与二维数组</title>
      <link>https://blog.devwu.com/2014/07/24/2d-array--pointer/</link>
      <pubDate>Thu, 24 Jul 2014 00:00:00 +0000</pubDate>
      
      <guid>https://blog.devwu.com/2014/07/24/2d-array--pointer/</guid>
      <description>通过前面的学习,已知:
int arr[3]={1,3,5}; arr[0] 可以用 *(arr+0) 表示 arr[1] 可以用 *(arr+1) 表示 arr[2] 可以用 *(arr+2) 表示  现有二维数组如下:
int arr[3][4]= { {1,2,3,4}, {5,6,7,8}, {9,10,11,12} };  则:
arr[0] ~~~ *(arr+0) arr[1] ~~~ *(arr+1) arr[2] ~~~ *(arr+2)  此处arr[0]即*(arr+0)是二维数组arr的第一个元素的值,该值是内层数组{1,2,3,4}的起始地址.
将arr[0]赋值给 temp数组
int temp0[4] = arr[0]; int temp1[4] = arr[1]; int temp2[4] = arr[2];  获取最终的值:
arr[0][0] ~~~~ 将arr[0]换为上边的temp0数组
arr[0][0] ~~~~ temp0[0] ~~~~ *(temp+0) ~~~~ *(*(arr+0)+0) arr[0][1] ~~~~ temp0[1] ~~~~ *(temp+1) ~~~~ *(*(arr+0)+1) arr[0][2] ~~~~ temp0[2] ~~~~ *(temp+2) ~~~~ *(*(arr+0)+2) arr[0][3] ~~~~ temp0[3] ~~~~ *(temp+3) ~~~~ *(*(arr+0)+3)  arr[1][0] ~~~~ 将arr[1]换为上边的temp1数组</description>
    </item>
    
    <item>
      <title>Objective-C 正则表达式的使用</title>
      <link>https://blog.devwu.com/2014/05/29/use-regex-in-objective-c/</link>
      <pubDate>Thu, 29 May 2014 00:00:00 +0000</pubDate>
      
      <guid>https://blog.devwu.com/2014/05/29/use-regex-in-objective-c/</guid>
      <description>前言: 编程中,对于字符串的处理是无处不在的.时常需要在一堆乱码中找到有用的信息.比如在如下的字符串中获取有效的 URL 或 Email
ababsdbasbdabdhttp://baidu.com &amp;lt;div&amp;gt;test@test.com&amp;lt;div&amp;gt;  诸如此类问题,我们都可以通过正则表达式来解决,正则在任意编程语言都有对应实现.
iOS4 之后,Cocoa 也提供了用于正则的 NSRegularExpression 和 NSTextCheckingResult 这两个类. 前者用于创建正则,匹配,替换;后者是一个快速匹配对象,可用于检查拼写,URL等,也可作为正则匹配的结果,包含匹配到子串所在 range 和匹配规则.
创建: NSRegularExpression * regex = [NSRegularExpression regularExpressionWithPattern:@&amp;quot;http[s]?://[A-z0-9.-]*&amp;quot; options:NSRegularExpressionCaseInsensitive error:nil];  正则匹配模式
  [] 表示集合, * 表示 出现零次或多次, ? 表示出现零次或一次, http[s]?://[A-z0-9.-]* ,即匹配以 http:// 或 https:// 开头,含有零或多个字母 - . 的字符串   option
  NSRegularExpressionCaseInsensitive //忽略大小写 NSRegularExpressionAllowCommentsAndWhitespace //忽略空格,回车,tab,注释 NSRegularExpressionIgnoreMetacharacters	//忽略 Meta NSRegularExpressionDotMatchesLineSeparators	//允许使用 . 通配任何符号,包括换行符 NSRegularExpressionAnchorsMatchLines	//允许使用 ^ $匹配行头和行尾 NSRegularExpressionUseUnixLineSeparators	//只把 \n 当做换行符 NSRegularExpressionUseUnicodeWordBoundaries   应用: NSRegularExpression 提供了对字符串 匹配 , 替换 的函数,</description>
    </item>
    
    <item>
      <title>在iOS开发中使用block</title>
      <link>https://blog.devwu.com/2014/01/13/use-block-in-iOS-dev/</link>
      <pubDate>Mon, 13 Jan 2014 00:00:00 +0000</pubDate>
      
      <guid>https://blog.devwu.com/2014/01/13/use-block-in-iOS-dev/</guid>
      <description>今天下午把启动了一周的项目重构了一下，用storyboard做界面（之前是代码+xib），block做回调（之前是delegate）。
关于storyboard和block都已经出现好几个年头了，从开发效率上来看，这俩东西再配合ARC上简直是倚天屠龙。
在之前的项目中，回调需要做以下几步：
  A类中声明ProtocolA协议及协议下的方法。
  A类中声明delegate属性。
  B类实现ProtocolA协议以及协议下的方法，并将自身对象赋值给A类对象的delegate属性。
  当需要实现回调的时候，直接通过A类对象的delegate属性调用 ProtocolA协议下的方法即可执行B类中对该协议方法的实现。
以前大伙都在这样乐呵乐呵的写，但是有了block之后，步骤变得简单了许多。
用block做回调省了哪些事儿？
  不用定义协议及协议方法。
  减少冗余，即不需要为回调而创建B并实现协议方法。
  闭包特性，使的代码更为紧凑，表达更为直观，即不需要把业务逻辑分散到多个方法中。
  废话少说，上代码。
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  #import &amp;lt;Foundation/Foundation.</description>
    </item>
    
    <item>
      <title>函数指针和类成员指针</title>
      <link>https://blog.devwu.com/2013/11/03/function-pointer-and-class-member-pointer/</link>
      <pubDate>Sun, 03 Nov 2013 00:00:00 +0000</pubDate>
      
      <guid>https://blog.devwu.com/2013/11/03/function-pointer-and-class-member-pointer/</guid>
      <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 25 26 27 28 29 30 31 32 33 34 35 36 37 38  #include &amp;lt;iostream&amp;gt;#include &amp;lt;string&amp;gt;#include &amp;lt;string.h&amp;gt;using namespace std; class Demo { public:  void show() {  cout &amp;lt;&amp;lt; &amp;#34;member method Demo::show() did call&amp;#34; &amp;lt;&amp;lt; endl;  str = &amp;#34;str from Demo::show()&amp;#34;;  }  string str; }; void show() {  cout&amp;lt;&amp;lt;&amp;#34;普通函数指针被调用&amp;#34;&amp;lt;&amp;lt;endl; } int main(int argc, char* argv[]) {  //普通函数指针  //将普通函数show的地址赋值给pPainShow,优先级原因,必须在(*pPainShow)外加小括号,  //否则编译器会将此认为是一个void* pPainShow(); 的函数声明,显然,我们不是这个意思.</description>
    </item>
    
    <item>
      <title>指针常量和常量指针</title>
      <link>https://blog.devwu.com/2013/11/03/pointer-of-const-and-const-of-pointer/</link>
      <pubDate>Sun, 03 Nov 2013 00:00:00 +0000</pubDate>
      
      <guid>https://blog.devwu.com/2013/11/03/pointer-of-const-and-const-of-pointer/</guid>
      <description>这一概念经常混淆,故写下来,以备不时之需
1 2 3 4 5 6 7 8   int a = 10;  int * const b = &amp;amp;a;//const修饰b,b本身不可变,但是*b可变;人称常量指针,即指针是常量,地址不可改变  *b = 20;//更换值  cout &amp;lt;&amp;lt; *b &amp;lt;&amp;lt; endl;  int const * bb = &amp;amp;a;//const修饰*bb,*bb不可变,但是bb可变;人称指针常量,指向常量的指针,值不可以变,地址可以改变  int c = 30;  bb = &amp;amp;c;//更换指向  cout &amp;lt;&amp;lt; *bb &amp;lt;&amp;lt; endl;   </description>
    </item>
    
  </channel>
</rss>
