<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>miniLV</title>
    <description>iOS、架构、产品感觉和长期主义的技术笔记。</description>
    <link>https://minilv.github.io/</link>
    <atom:link href="https://minilv.github.io/feed.xml" rel="self" type="application/rss+xml" />
    <pubDate>Mon, 30 Mar 2026 09:03:44 +0800</pubDate>
    <lastBuildDate>Mon, 30 Mar 2026 09:03:44 +0800</lastBuildDate>
    <generator>Jekyll v3.10.0</generator>
    
      <item>
        <title>赛博朋克摸鱼计数器(100% Vibe Coding)</title>
        <description>&lt;p&gt;💡 &lt;strong&gt;背景&lt;/strong&gt;: 有时候你会迷茫，上班的意义是什么，特别是工作多年之后。当你感觉班上不下去的时候，可能会想说自己上班的意义是什么。这时候，你需要知道自己每分钟的价值，才能有继续工作的动力。于是就有了这个工具 —— 一个实时显示你当前已赚多少钱的小玩意儿~&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h2 id=&quot;-关于-vibe-coding&quot;&gt;🤖 关于 Vibe Coding&lt;/h2&gt;

&lt;p&gt;这个项目有点特别，是一次 &lt;strong&gt;100% Vibe Coding&lt;/strong&gt; 的实践。&lt;/p&gt;

&lt;p&gt;整个开发过程中，我的角色是产品经理 —— 负责提需求、定方向、做决策。代码实现 100% 由 &lt;strong&gt;Claude Code (Opus 4.5)&lt;/strong&gt; 完成，UI 设计则是用 Figma AI 自动生成后微调。&lt;/p&gt;

&lt;p&gt;同时，这也是一次 &lt;strong&gt;Spec-Driven Development (SDD)&lt;/strong&gt; 的实操。感兴趣可以看看 &lt;a href=&quot;https://github.com/github/spec-kit&quot;&gt;github/spec-kit&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;整个过程下来效率确实高了不少，这种开发方式值得尝试。&lt;/p&gt;

&lt;h2 id=&quot;-效果展示&quot;&gt;🎬 效果展示&lt;/h2&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;💰 正常状态&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;🙈 隐私模式&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;⏰ 加班提醒&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;img src=&quot;https://raw.githubusercontent.com/miniLV/Paytick/master/docs/images/normal_state.png&quot; alt=&quot;Normal State&quot; /&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;img src=&quot;https://raw.githubusercontent.com/miniLV/Paytick/master/docs/images/privacy_mode.png&quot; alt=&quot;Privacy Mode&quot; /&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;img src=&quot;https://raw.githubusercontent.com/miniLV/Paytick/master/docs/images/overtime_alert.jpg&quot; alt=&quot;Overtime Alert&quot; /&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;em&gt;实时收入&lt;/em&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;em&gt;遮住数字&lt;/em&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;em&gt;该下班了&lt;/em&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;视觉上走的是赛博朋克风，霓虹绿主色调，CRT扫描线效果，偶尔来点Glitch故障风。&lt;/p&gt;

&lt;h2 id=&quot;-项目功能&quot;&gt;✨ 项目功能&lt;/h2&gt;

&lt;ul class=&quot;task-list&quot;&gt;
  &lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; class=&quot;task-list-item-checkbox&quot; disabled=&quot;disabled&quot; checked=&quot;checked&quot; /&gt;实时收入显示 — 菜单栏直接看，精确到分&lt;/li&gt;
  &lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; class=&quot;task-list-item-checkbox&quot; disabled=&quot;disabled&quot; checked=&quot;checked&quot; /&gt;愿望清单 — 想买啥设置啥，看进度条慢慢填满&lt;/li&gt;
  &lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; class=&quot;task-list-item-checkbox&quot; disabled=&quot;disabled&quot; checked=&quot;checked&quot; /&gt;加班提醒 — 过了下班时间界面变红，提醒你时薪正在被稀释&lt;/li&gt;
  &lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; class=&quot;task-list-item-checkbox&quot; disabled=&quot;disabled&quot; checked=&quot;checked&quot; /&gt;上下班通知 — 到点自动弹出系统通知，提醒你该上班/下班了&lt;/li&gt;
  &lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; class=&quot;task-list-item-checkbox&quot; disabled=&quot;disabled&quot; checked=&quot;checked&quot; /&gt;隐私保护 — 同事凑过来看屏幕的时候一键切换，支持快捷键
    &lt;ul&gt;
      &lt;li&gt;Emoji模式：数字变成 🍎🚀⭐&lt;/li&gt;
      &lt;li&gt;模糊模式：高斯模糊&lt;/li&gt;
      &lt;li&gt;打码模式：显示 &lt;em&gt;**&lt;/em&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; class=&quot;task-list-item-checkbox&quot; disabled=&quot;disabled&quot; checked=&quot;checked&quot; /&gt;多语言支持 — 在 Settings → Advanced 中切换语言，支持中文/英文&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/img/in-post/paytick-language-settings.png&quot; alt=&quot;Language Settings&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;️-技术栈&quot;&gt;🛠️ 技术栈&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Swift 6.0&lt;/li&gt;
  &lt;li&gt;SwiftUI（纯代码）&lt;/li&gt;
  &lt;li&gt;macOS菜单栏应用&lt;/li&gt;
  &lt;li&gt;本地数据存储&lt;/li&gt;
  &lt;li&gt;多语言本地化（中文/英文）&lt;/li&gt;
  &lt;li&gt;Apple公证签名&lt;/li&gt;
  &lt;li&gt;Figma AI 生成 UI&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;-使用方法&quot;&gt;📦 使用方法&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;去 &lt;a href=&quot;https://github.com/miniLV/Paytick/releases&quot;&gt;Releases&lt;/a&gt; 下载 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Paytick.dmg&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;拖进应用程序文件夹&lt;/li&gt;
  &lt;li&gt;输入月薪、工作天数、上下班时间&lt;/li&gt;
  &lt;li&gt;看数字跳&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;已通过Apple公证，双击就能跑。如果提示无法验证，右键选”打开”就行。&lt;/p&gt;

&lt;h2 id=&quot;-隐私说明&quot;&gt;🔒 隐私说明&lt;/h2&gt;

&lt;p&gt;所有数据存本地。不联网，不上传，不追踪。代码开源，不放心可以自己看。&lt;/p&gt;

&lt;h2 id=&quot;-写在最后&quot;&gt;💭 写在最后&lt;/h2&gt;

&lt;p&gt;老实说，上班就是拿时间换钱，没什么情怀可言。老板付钱，我出力，公平交易。&lt;/p&gt;

&lt;p&gt;但是吧，工作只是生活的一部分，不是全部。到点了就下班，该休息休息，该玩玩。给自己定个小目标，攒够多少钱，就奖励自己一顿好吃的、一个新手机、一次说走就走的旅行。&lt;/p&gt;

&lt;p&gt;这个工具的初衷很简单：&lt;strong&gt;让你知道自己每小时值多少钱，希望大家都可以准时下班，好好享受生活把～&lt;/strong&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;本文Demo：&lt;a href=&quot;https://github.com/miniLV/Paytick&quot;&gt;Paytick&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;觉得有用的话，欢迎 Star ⭐&lt;/em&gt;&lt;/p&gt;
</description>
        <pubDate>Mon, 26 Jan 2026 20:00:00 +0800</pubDate>
        <link>https://minilv.github.io/2026/01/26/Paytick/</link>
        <guid isPermaLink="true">https://minilv.github.io/2026/01/26/Paytick/</guid>
        
        <category>Swift</category>
        
        <category>macOS</category>
        
        <category>Vibe Coding</category>
        
        
      </item>
    
      <item>
        <title>Swift5 + MVVM + 文艺复兴微博(纯代码)</title>
        <description>&lt;p&gt;背景: 现在越来越多的公司会尝试用 Swift 做 native 的开发了，很多之前习惯用 Objective-C 的开发，有新项目启动的时候，也会想说用 Swift 试试。如果从 &lt;a href=&quot;https://hellogithub.com/report/tiobe/&quot;&gt;2020年编程语言排行榜&lt;/a&gt; 上看的话，Swift的热度也领先 Objective-C 10个身位了。而我们现在公司所做的项目，也是用 Swift 开发的，虽然说之前有些 OC 基础，写起 Swift 功能也是能实现，但是代码不是很优雅，不够 ‘Swift Style’。 熟练度不够的话，很多 Swift 的高级写法还得去翻文档才知道什么意思，所以就打算从0单排一个Swift的项目，而微博正好有 &lt;a href=&quot;https://open.weibo.com/&quot;&gt;开放API&lt;/a&gt; , 所以这里就选择它了。&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h2 id=&quot;主页界面&quot;&gt;主页界面&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/171bb13d7c6e6265?raw=true&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h2 id=&quot;表情界面&quot;&gt;表情界面&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/171bb1441ef0b8bf?raw=true&quot; alt=&quot;&quot; /&gt;
&lt;br /&gt;&lt;/p&gt;

&lt;h2 id=&quot;项目架构&quot;&gt;项目架构&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/171b713959fc32de?raw=true&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h2 id=&quot;项目功能&quot;&gt;项目功能&lt;/h2&gt;

&lt;ul class=&quot;task-list&quot;&gt;
  &lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; class=&quot;task-list-item-checkbox&quot; disabled=&quot;disabled&quot; checked=&quot;checked&quot; /&gt;原创微博功能&lt;/li&gt;
  &lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; class=&quot;task-list-item-checkbox&quot; disabled=&quot;disabled&quot; checked=&quot;checked&quot; /&gt;转发微博功能&lt;/li&gt;
  &lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; class=&quot;task-list-item-checkbox&quot; disabled=&quot;disabled&quot; checked=&quot;checked&quot; /&gt;Emoji表情功能&lt;/li&gt;
  &lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; class=&quot;task-list-item-checkbox&quot; disabled=&quot;disabled&quot; checked=&quot;checked&quot; /&gt;撰写微博界面&lt;/li&gt;
  &lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; class=&quot;task-list-item-checkbox&quot; disabled=&quot;disabled&quot; checked=&quot;checked&quot; /&gt;新特性功能&lt;/li&gt;
  &lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; class=&quot;task-list-item-checkbox&quot; disabled=&quot;disabled&quot; checked=&quot;checked&quot; /&gt;消息提醒功能&lt;/li&gt;
  &lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; class=&quot;task-list-item-checkbox&quot; disabled=&quot;disabled&quot; checked=&quot;checked&quot; /&gt;多图展示功能&lt;/li&gt;
  &lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; class=&quot;task-list-item-checkbox&quot; disabled=&quot;disabled&quot; checked=&quot;checked&quot; /&gt;富文本功能&lt;/li&gt;
  &lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; class=&quot;task-list-item-checkbox&quot; disabled=&quot;disabled&quot; checked=&quot;checked&quot; /&gt;多图展示功能&lt;/li&gt;
  &lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; class=&quot;task-list-item-checkbox&quot; disabled=&quot;disabled&quot; checked=&quot;checked&quot; /&gt;下滑自动加载功能&lt;/li&gt;
  &lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; class=&quot;task-list-item-checkbox&quot; disabled=&quot;disabled&quot; checked=&quot;checked&quot; /&gt;上/下拉刷新功能&lt;/li&gt;
  &lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; class=&quot;task-list-item-checkbox&quot; disabled=&quot;disabled&quot; checked=&quot;checked&quot; /&gt;OAuth 授权登录功能&lt;/li&gt;
  &lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; class=&quot;task-list-item-checkbox&quot; disabled=&quot;disabled&quot; checked=&quot;checked&quot; /&gt;已完成&lt;/li&gt;
  &lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; class=&quot;task-list-item-checkbox&quot; disabled=&quot;disabled&quot; /&gt;发布微博功能(API已不提供)&lt;/li&gt;
  &lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; class=&quot;task-list-item-checkbox&quot; disabled=&quot;disabled&quot; /&gt;图片上传功能(API已不提供)&lt;/li&gt;
  &lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; class=&quot;task-list-item-checkbox&quot; disabled=&quot;disabled&quot; /&gt;消息功能&lt;/li&gt;
  &lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; class=&quot;task-list-item-checkbox&quot; disabled=&quot;disabled&quot; /&gt;发现功能&lt;/li&gt;
  &lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; class=&quot;task-list-item-checkbox&quot; disabled=&quot;disabled&quot; /&gt;我的功能&lt;/li&gt;
  &lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; class=&quot;task-list-item-checkbox&quot; disabled=&quot;disabled&quot; /&gt;SVProgressHUD 提醒功能&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h2 id=&quot;用到的技术点&quot;&gt;用到的技术点&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;TableView高度缓存&lt;/li&gt;
  &lt;li&gt;SDWebImage圆角处理&lt;/li&gt;
  &lt;li&gt;FMDB数据缓存&lt;/li&gt;
  &lt;li&gt;OAuth授权&lt;/li&gt;
  &lt;li&gt;单张图片的高度计算&lt;/li&gt;
  &lt;li&gt;Cell滚动自动加载更多&lt;/li&gt;
  &lt;li&gt;撰写页面的动画处理&lt;/li&gt;
  &lt;li&gt;发布界面的Emoji处理&lt;/li&gt;
  &lt;li&gt;多图的展示&lt;/li&gt;
  &lt;li&gt;…&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;不足点&quot;&gt;不足点&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Weibo 的 App 包里的素材图片实在太多了，找到完全匹配的要花费超多时间，所以有的资源不是很匹配，或者不是当前最新的&lt;/li&gt;
  &lt;li&gt;该Demo使用了FMDB，其实微博这种时效性毕竟高，且有较高政治敏感度的，不适合用本地缓存, 这里只是一个 FMDB 使用的简单案例，更适合更新不用太及时，或者类似qq这种接收服务端推送消息的app.&lt;/li&gt;
  &lt;li&gt;这个文艺复兴版的微博(2016年的)，现在的微博已经有了较大的变动，且很多API接口现在已经不开放了，所以暂时先用此怀旧版本.&lt;/li&gt;
  &lt;li&gt;SVProgressHUD 在Swift5.0会crash，所以这个app里没有 toast 功能，坐等修复…&lt;/li&gt;
  &lt;li&gt;Gif图片由于现在门户网站只能传 &amp;lt; 5M 的，所以看上去毕竟模糊，实际效果会比 Gif 图片好得多，建议用真机跑一下试试~&lt;/li&gt;
  &lt;li&gt;这版本来打算用本人拙劣的英文写全篇的注释，后来由于本人 Chinelish 水平有限，毕竟复杂的逻辑/业务就用中文写了(&lt;em&gt;求轻喷&lt;/em&gt;)&lt;/li&gt;
  &lt;li&gt;…&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;使用的第三方库&quot;&gt;使用的第三方库&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;AFNetworking&lt;/li&gt;
  &lt;li&gt;SDWebImage&lt;/li&gt;
  &lt;li&gt;YYModel&lt;/li&gt;
  &lt;li&gt;SnapKit&lt;/li&gt;
  &lt;li&gt;FMDB&lt;/li&gt;
  &lt;li&gt;pop&lt;/li&gt;
  &lt;li&gt;HMPhotoViewerController&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;工具分享&quot;&gt;工具分享&lt;/h2&gt;

&lt;p&gt;图片素材获取: &lt;a href=&quot;https://github.com/steventroughtonsmith/cartool&quot;&gt;cartool&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;取色标注工具: &lt;a href=&quot;http://www.getmarkman.com/&quot;&gt;MarkMan&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;数据库工具: &lt;a href=&quot;https://www.navicat.com.cn/&quot;&gt;Navicat&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;画图工具: &lt;a href=&quot;https://mindnode.com/&quot;&gt;MindNode&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;…&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h2 id=&quot;总结&quot;&gt;总结&lt;/h2&gt;

&lt;p&gt;从事了一段时间的 Swift 开发，发现没有一定量的代码积淀(踩坑)还是不行，所以就打算自己写一个入门的纯Swift App。这个项目是本萌新的第一个发布swift项目，有些粗糙，但是如果新手作为 Swift 上手，入门的，其实应该还可以。该项目基本都是用的 “纯代码+纯Swift” 的方式写的，用的也是尽量新的 API, 萌新们想玩的话感觉还是可以上手的。&lt;/p&gt;

&lt;p&gt;最近发现，&lt;strong&gt;时间管理&lt;/strong&gt; 真的很重要，很多事情，一开始以为自己做不到，就打退堂鼓了，但是只要咬咬牙，挑战一下自己的极限，这不，这个Demo就出来了~ (为了写这破玩意，老夫都颈椎病去做针灸了，骗波 star不过分吧~ (开玩笑的，大家开心就好😂))&lt;/p&gt;

&lt;p&gt;&lt;em&gt;觉得哪里写的不好的，哪里能改进的，有时间帮忙做code review，提PR的，热烈欢迎啊，这里先谢过了~&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;本文Demo：&lt;a href=&quot;https://github.com/miniLV/MNWeibo&quot;&gt;MNWeibo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;本文感谢 新浪提供的 &lt;a href=&quot;https://open.weibo.com/&quot;&gt;API&lt;/a&gt; 支持，感谢 &lt;a href=&quot;https://github.com/liufan321&quot;&gt;天涯刀哥-傅红雪&lt;/a&gt; 的指导&lt;/em&gt;&lt;/p&gt;
</description>
        <pubDate>Mon, 27 Apr 2020 07:30:00 +0800</pubDate>
        <link>https://minilv.github.io/2020/04/27/MNWeibo/</link>
        <guid isPermaLink="true">https://minilv.github.io/2020/04/27/MNWeibo/</guid>
        
        <category>Swift</category>
        
        
      </item>
    
      <item>
        <title>萌新iOS面试官迷你厂第一视角</title>
        <description>&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/16f51fd23792e182?raw=true&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;之前做过一段时间的面试官，负责了一段时间 iOS 端的技术招聘，身边也有不少朋友咨询过关于这方面的细节。借着这个机会，以一个初级面试官第一视角介绍下招聘方的想法。&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;当时组里就我一个 iOS 开发，老大安排要扩招两个人，于是临时兼任了一段时间的面试官。&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;小公司的面试流程一般比较简单，第一轮技术面（就是本弟弟）, 第二轮我们老大面进行 (大佬后来去了阿里，所以他这轮也不算很好过)，第三轮就是和HR谈钱了，基本上半天搞定。当然，怕有的大佬看到要做笔试题扭头就跑，所以一般是没有笔试题。算法的话一般也不怎么考，一般是确定他淘汰了，才会出一题来劝退。&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;从我们发出招聘之后，第二天就开始陆续有简历收到了。第一个哥们就是来面高级岗的，接近四年经验，当时拿到简历其实心里有点虚，怕hold不住，所以那天拿到他简历之后，认真地过了一下他写的技术点（&amp;gt;.&amp;lt;）因为一般招人，是面试者写什么我们问什么，我们是要发现面试者会什么，而不是出一些只有我们自己会的很冷门的东西去刁难面试者，那样对面试者其实有点不友好。(招人，不就是想招一个能展示)&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;第二天那个小哥就来了，我先给他倒了一杯水，其实第一次有点紧张，所以我先让他做一下自我介绍，小哥介绍得特别的简短，三句话概括完。然后我事先有先去看了一下他项目，让他展示一下他做的 App，让他从最擅长的地方上手，问一些比较有技术难度的。老实说，那哥们的项目还可以，但是我问到项目中技术点的实现的时候，他的回答都是使用XXX第三方库，但是并不知道具体实现原理，他的回答都是 “这个很简单，用XXX，一行代码集成搞定“ 之类的，以我当时的面试官视角，感觉他是个比较熟悉API调用的，其他技术细节也不够深入，最后遗憾道别。&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;以一个普通开发者视角来看，我们平时遇到的需求，也不一定说全部的第三方库都能完美解决，所以我们有时候一些功能不得不自己实现，而如果招一个基本只会用第三方库的同事，想必他很难 hold 得住一个全新的项目。&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;第一次面试之后，面试起来比较得心应手了，但是这里还是要友情提示一下，HR和面试者约的是几点，面试者在那个时间点提前10分钟到就行，太早到。那时候有个哥们，约的下午1点30面试，11点多就来了，我们是12点到1点30休息，一般我中午是有午休的，那哥们这一来，虽然最后还是1：30面试，但是我一中午没睡，有点伤。还有那哥们的一个操作，也是差点把老夫秀翻，那时候正在面试ing…， 突然他电话响了，他说他接个电话，然后走开离我差不多三米的地方，和对方约第二天下午的面试。。。😆&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;其实面了三四个的时候，就有一个哥们，很合适了，我使劲浑身解数尽量往深度问，发现他都能答上来，反正技术肯定甩我好几个身位的，然后我也如实和我们老大说了，由他进行二面，但是最终这哥们还是理智地选择了大厂🤣~&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;小厂招聘有一个劣势，就是比如你钱和大厂一样，甚至稍高一点，别人也大概率会选择大厂，光环在那，但是小厂毕竟资金有限，所以其实有时候就陷入一种境地，要的人他不来，想来的又达不到要求。陆陆续续面了可能半个多月吧，发现如果不降低面试要求，就我们厂应该很难抢到别人要的人了，最后把目标锁定在 =&amp;gt; 经验丰富，能干活的就行。&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;最终有两个哥们入围了决赛圈，如果仅从短暂的面试来看，实力应该差不多，期望薪资有点差距，最终 HR 从经济的角度出发，选择了那个性价比较高的，（当然那哥们那时候的工资也远超过我🥺）但是这里确实有一个很血粼粼的现实问题出现了，当两个人水平相当的时候，确实招聘方从成本角度出发，可能会优先考虑要价低的，所以有时候你面试的时候，对方说几天内给你消息，这时候可能就是在权衡性价比🤣&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;理想目标&quot;&gt;理想目标&lt;/h2&gt;

&lt;p&gt;面试官招人要说合格的话，最理想的目标应该是符合以下几个标准：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;要基础扎实，头脑灵活聪明的&lt;/li&gt;
  &lt;li&gt;思维清晰，善于沟通的&lt;/li&gt;
  &lt;li&gt;和团队风格匹配的&lt;/li&gt;
  &lt;li&gt;喜欢钻研，对技术有追求的&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h2 id=&quot;敲黑板重点来了&quot;&gt;敲黑板！重点来了&lt;/h2&gt;

&lt;p&gt;这段时间，看了不下一百分简历，面了也有一二十个人，简历合格的其实不多，这里有几个小小的建议：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;简历可以直接用 markdown 格式的&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;接到过好多word格式的，打开排版全乱了&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;简历不要写太多，一般 2 页够了，不要从网上抄一大堆 JD，不要有错别字&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;简历实事求是，别太浮夸&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;收到过好几个哥们简历 5 年经验，面试表现不如实习生的，感觉水分可能还是有一点&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;写进简历的，都要对他负责任，做好准备
    &lt;ul&gt;
      &lt;li&gt;完全不懂的就不要写&lt;/li&gt;
      &lt;li&gt;慎用精通！&lt;/li&gt;
      &lt;li&gt;用技术亮点吸引面试官&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;要有亮点
    &lt;ul&gt;
      &lt;li&gt;大厂背景&lt;/li&gt;
      &lt;li&gt;名校背景&lt;/li&gt;
      &lt;li&gt;都没有的话，GitHub &amp;amp;&amp;amp; 技术博客很重要
        &lt;ul&gt;
          &lt;li&gt;（如果想进大厂的话，这个很重要！）&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;面试前，准备工作要先准备好
    &lt;ul&gt;
      &lt;li&gt;把要演示的 App 先安装好，面试官如果要看能直接演示的
        &lt;ul&gt;
          &lt;li&gt;遇到过不止一个被问到能否演示的时候，才去 App Store 下载的&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;如果你是iOS开发，尽量用 iPhone 手机演示
        &lt;ul&gt;
          &lt;li&gt;遇到过用安卓手机演示的，说iOS的和这个都一样，有点醉&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;来之前检查下手机电量
        &lt;ul&gt;
          &lt;li&gt;遇到过3%电的，我首页点了没两分钟，直接没电了😹&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;注意App的版本是否正确
        &lt;ul&gt;
          &lt;li&gt;遇到过一个 App 一划就crash的，最后得出的结论，是版本装错了，其实这个bug修复了~ 😹&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;回答问题简明扼要，挑重点回答
    &lt;ul&gt;
      &lt;li&gt;不要问你 who，你回答 how。 问你 where，你回答 when (英语老师饶命😝)&lt;/li&gt;
      &lt;li&gt;不要回答一大堆毫不相干的，一句话能表述的，说一两百字，但是回答不到重点的&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;要掌握主动权
    &lt;ul&gt;
      &lt;li&gt;从自我介绍开始，挑一些自己比较擅长的领域，引导面试官来问你&lt;/li&gt;
      &lt;li&gt;如果面试官进入到自己熟悉的领域，由浅入深慢慢引导其继续发问
        &lt;ul&gt;
          &lt;li&gt;🌰: KVO -&amp;gt; isa混写 -&amp;gt; 底层原理 -&amp;gt;。。。&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;如果正好问到知识盲区，虚心请教，让面试官给点提示引导下~&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;其实面试过没过，面试结束的时候，凭自己的感觉，也能猜到个八分了，如果全程无障碍沟通，对方问的问题也都能答得上七七八八，准备接offer吧兄弟~&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;面试考点根据简历由浅入深提问&quot;&gt;面试考点（根据简历由浅入深提问）&lt;/h2&gt;

&lt;h3 id=&quot;初级&quot;&gt;初级&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;copy 和 mutablecopy 的区别
    &lt;ul&gt;
      &lt;li&gt;NSarry 用 copy 和 mutablecopy 修饰的不同&lt;/li&gt;
      &lt;li&gt;NSMutableArry 用 copy 和  mutablecopy 修饰的不同&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;介绍一下 TableView 的代理和数据源方法
    &lt;ul&gt;
      &lt;li&gt;讲一下他们的调用顺序&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;介绍控制器的声明周期&lt;/li&gt;
  &lt;li&gt;介绍下通知，代理 和 block，使用的场景分别是什么&lt;/li&gt;
  &lt;li&gt;代理修饰为什么用weak，
    &lt;ul&gt;
      &lt;li&gt;扩展：知道什么是循环引用吗&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;属性的关键字都有哪些，默认的是哪些&lt;/li&gt;
  &lt;li&gt;使用 #import 跟 #include 、@class 的区别是什么，分别在什么情况下使用
    &lt;ul&gt;
      &lt;li&gt;扩展：在.h 文件中一般使用哪个，为什么&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;原子(atomic)跟非原子(non-atomic)属性有什么区别?
    &lt;ul&gt;
      &lt;li&gt;atomic一定是线程安全的吗？&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;浅复制和深复制的区别? 可以举个🌰吗&lt;/li&gt;
  &lt;li&gt;category 和 extensions 的区别，在项目中使用过吗
    &lt;ul&gt;
      &lt;li&gt;category 可以添加属性吗&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;了解KVO 和 KVC 吗？&lt;/li&gt;
  &lt;li&gt;对于单例的理解&lt;/li&gt;
  &lt;li&gt;frame和bounds有什么不同&lt;/li&gt;
  &lt;li&gt;iOS 中的多线程有哪些
    &lt;ul&gt;
      &lt;li&gt;GCD的一些常用的函数（group，barrier，信号量，线程同步）&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;实现数据持久化的几个方案
    &lt;ul&gt;
      &lt;li&gt;介绍一下沙盒模型，存储的位置&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;用过 UICollectionView 吗？如何实现一个流水布局
    &lt;ul&gt;
      &lt;li&gt;一般是根据项目问&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;了解 svn, git 等版本控制工具么
    &lt;ul&gt;
      &lt;li&gt;如果有写git，可以拓展一下 git 的一些指令问题（merge 和 rebase 区别等等）&lt;/li&gt;
      &lt;li&gt;分支管理&lt;/li&gt;
      &lt;li&gt;一次发版的流程&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;简述内存分区情况&lt;/li&gt;
  &lt;li&gt;队列和栈有什么区别&lt;/li&gt;
  &lt;li&gt;Http请求了解吗
    &lt;ul&gt;
      &lt;li&gt;知道什么是Restful API 吗&lt;/li&gt;
      &lt;li&gt;GET 和 POST 请求的区别&lt;/li&gt;
      &lt;li&gt;特殊的状态码，比如401、503，201的意义
        &lt;ul&gt;
          &lt;li&gt;或者直接问 4XX 是哪端的错误&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;中级&quot;&gt;中级&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;讲述类和结构体的区别，了解什么是 isa指针吗&lt;/li&gt;
  &lt;li&gt;消息查找的顺序
    &lt;ul&gt;
      &lt;li&gt;子类调用父类的对象方法，执行的流程是如何的？&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;如何实现消息转发&lt;/li&gt;
  &lt;li&gt;给一个nil对象发消息会crash吗&lt;/li&gt;
  &lt;li&gt;有使用过分类吗
    &lt;ul&gt;
      &lt;li&gt;分类会”覆盖” 原来的方法吗&lt;/li&gt;
      &lt;li&gt;分类可以添加属性吗，了解关联对象吗&lt;/li&gt;
      &lt;li&gt;分类的内部结构是什么&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;各种Block
    &lt;ul&gt;
      &lt;li&gt;有遇到过内存泄漏吗&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
  &lt;li&gt;Block的几种类型
    &lt;ul&gt;
      &lt;li&gt;Block的变量捕捉&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;__block中的 _ forwarding 指针
    &lt;ul&gt;
      &lt;li&gt;ARC下和MRC下，__block的区别&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;KVO &amp;amp; KVC 细问&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;什么是 isa 混写&lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;如何手动触发一个 value 的 KVO&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;KVC修改属性是否会触发KVO&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;为什么 delegate 设置为 weak 就不会有循环引用
    &lt;ul&gt;
      &lt;li&gt;了解什么是哈希表吗&lt;/li&gt;
      &lt;li&gt;弱引用表做了什么&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;对UITableView有做过什么优化吗
    &lt;ul&gt;
      &lt;li&gt;UITableView 为什么会卡顿&lt;/li&gt;
      &lt;li&gt;如何避免卡顿&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;最熟悉的第三方库是什么
    &lt;ul&gt;
      &lt;li&gt;99.9%是回答 SDWebImage
        &lt;ul&gt;
          &lt;li&gt;问一下 SDWebImage 的缓存机制&lt;/li&gt;
          &lt;li&gt;如果设计一个图片下载框架&lt;/li&gt;
          &lt;li&gt;引导对方说出 LRU 算法&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;万一回答 AFNetworking
        &lt;ul&gt;
          &lt;li&gt;问下 AFNetworing 有遇到内存泄漏吗&lt;/li&gt;
          &lt;li&gt;有没有对 AFNetworing 进行二次封装过&lt;/li&gt;
          &lt;li&gt;有没有对 AFNetworing 中的常驻现场有了解&lt;/li&gt;
          &lt;li&gt;有办法自己写一个常驻线程吗&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;如果回答 Masonry
        &lt;ul&gt;
          &lt;li&gt;问一些自动布局相关的&lt;/li&gt;
          &lt;li&gt;两个label相互挤压拉伸的&lt;/li&gt;
          &lt;li&gt;让几个控件平等均匀分布的&lt;/li&gt;
          &lt;li&gt;原生的autolayout了解吗&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;RunLoop （除非对方有写，不然不建议问！！）&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;
        &lt;p&gt;介绍下主线程的 RunLoop&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;RunLoop有几种mode&lt;/li&gt;
      &lt;li&gt;为什么 ScrollView 滚动的时候图片轮播器会停止
        &lt;ul&gt;
          &lt;li&gt;如果面试者项目中有这个情况&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;内存管理可以问一问(如果他有写)&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;定时器target的内存泄漏&lt;/li&gt;
      &lt;li&gt;autorelease的原理&lt;/li&gt;
      &lt;li&gt;weak 和 assign 的原理&lt;/li&gt;
      &lt;li&gt;引用计数器的存储&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;高级&quot;&gt;高级&lt;/h3&gt;

&lt;p&gt;没有😹（本人水平有限，距离高级距离太远，就不嫌丑了）&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;闲聊&quot;&gt;闲聊&lt;/h2&gt;

&lt;p&gt;沟通能力，理解能力其实都算软实力，在面试过程中如果对面试官的问题理解不过来的，可以多和面试官多沟通，一定要等真正get到考点的时候再答，不要乱答一气。(其实很多题目就是让你回答不上来的，面试官有时候并不是真的要考你某个很深的知识点，是想看你思维能力，能否在他引导之下给出他想要的答案，哪怕是错的，有很多时候其实也过了~)&lt;/p&gt;

&lt;p&gt;有没有开源精神其实在面试中也是很加分的。不过确实很多面试者没有写博客的习惯，也不少人不了解 GitHub 是啥。如果是大厂，对这方面要求会比较高，然后问下&lt;/p&gt;

&lt;p&gt;当然和team风格是否合拍也特别重要，如果你在面试过程中觉得特别不舒服，一般这个团队就不适合你，毕竟面试也算双向选择(虽然企业相对来说处于领先的位置)。&lt;/p&gt;

&lt;p&gt;老实说，一个人面试者过来合不合适，聊个5~10分钟其实在面试官心里就能看个七七八八了。一个人还没来面试前，从他的简历上其实也很多时候能看个五五六六了😹，所以简历和第一印象真的非常重要哈~&lt;/p&gt;

&lt;p&gt;总的来说，今年的大环境确实不太好，但是还是希望大家可以找到一个心仪的工作~&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;打个广告，如果是厦门地区的，有打算换工作的，Ringcentral了解一下，955榜上有名的，简历可以发我内推，成功之后请你吃大餐😹 &lt;strong&gt;（2020年Q1前能入职的，送air pod pro！！）&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
</description>
        <pubDate>Sun, 29 Dec 2019 20:00:00 +0800</pubDate>
        <link>https://minilv.github.io/2019/12/29/interviewer/</link>
        <guid isPermaLink="true">https://minilv.github.io/2019/12/29/interviewer/</guid>
        
        <category>面试</category>
        
        
      </item>
    
      <item>
        <title>iOS初中级开发简历指北</title>
        <description>&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/16a874425b62387e?raw=true&quot; alt=&quot;伪装成首页~&quot; /&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;之前在老东家当过一阵子的 iOS 面试官，看过的简历不下于 100 份，感觉能合格的简历大概只有 30% 左右，很多同学在简历这一关就被刷掉了。这里给经验比较不足的同学们，来一波基础的简历指导~&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;简历的作用&quot;&gt;简历的作用&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;面试的敲门砖&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;最大的作用：争取面试机会的时候&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;引导面试官问问题的方向&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;好的简历，面试官看完之后，大致就能确定你就是他们需要的人了!!&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;hr简历关&quot;&gt;HR简历关&lt;/h2&gt;

&lt;p&gt;HR一般是你投完简历之后的第一关，但是其实很多HR可能只做初筛，比如看你的工作年限，学历是否符合要求，然后最终拍板是否邀请你来面试的还是技术，所以对HR的那套其实不怎么吃香，写出技术面试官感兴趣的其实更为重要&lt;/p&gt;

&lt;p&gt;大多数的开发都是从初级到中级，一步一步往上走的，基本上每个公司的开发者，也都是涵盖了各个层次的开发者，刚毕业的时候都是一步一步慢慢走，先找到一份工作，慢慢学习，慢慢成长&lt;/p&gt;

&lt;p&gt;如果刚毕业的，写个3年经验，侥幸面试遇到不懂技术的面试官，过了，但是对方对你的期望是3年经验的能独当一面的开发者，后来发现你的能力和对方所期望的严重不符，最后其实对双方都没有好处&lt;/p&gt;

&lt;p&gt;拒绝虚假简历，不要因为招聘需求需要精通，就全部写精通&lt;/p&gt;

&lt;h3 id=&quot;ios简历格式常见错误&quot;&gt;iOS简历格式常见错误&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Object-C，OC&lt;/li&gt;
  &lt;li&gt;Foudation&lt;/li&gt;
  &lt;li&gt;ios / IOS / Ios&lt;/li&gt;
  &lt;li&gt;iphone / Iphone/ IPone&lt;/li&gt;
  &lt;li&gt;…&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;简历闪光点&quot;&gt;简历闪光点&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;大厂经历 (BAT等)&lt;/li&gt;
  &lt;li&gt;知名App开发经验 (说出去有人知道的)&lt;/li&gt;
  &lt;li&gt;Blog&lt;/li&gt;
  &lt;li&gt;GitHub&lt;/li&gt;
  &lt;li&gt;对技术的态度，追求与探索&lt;/li&gt;
  &lt;li&gt;…&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;对blog的看法&quot;&gt;对Blog的看法&lt;/h4&gt;

&lt;p&gt;不要太功利化，不要随便什么东西都丢到 GitHub 上&lt;/p&gt;

&lt;p&gt;Blog 可以看做加分项，是非一线大厂 or 非名牌大学背景的开发者们，进入一二线大厂的加分项，很重要，但是不是全部，不过可以慢慢积累&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;最好的写博客的时间是三年前，其次是现在&lt;/p&gt;

  &lt;p&gt;​                                 - 鲁迅&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;切记 - Blog &amp;amp;&amp;amp; GitHub一定不要造假！买星星的行为感觉不是很好( &lt;a href=&quot;https://github.com/yulingtianxia/FuckFakeGitHubStars&quot;&gt;FuckFakeGitHubStars&lt;/a&gt; 了解一下~)&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;无大厂经验无重点大学背景的简历投递&quot;&gt;无大厂经验、无重点大学背景的简历投递&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;前面说的，Blog那些是加分项，但是如果你的公司和学校都比较一般，不是那些有比较有名头的，其实Blog就变成了比较重要的部分了&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;因为，大部分人肯定都是想进大厂，或者至少是比现在更大的厂，而我们知道，越好的公司，越多的人竞争，比如某BAT大厂要招一个iOS开发，可能一个HC，就能收到上百份简历，如果我们手里没有加分项的情况下，很难脱颖而出，简历投出去很可能石沉大海&lt;/p&gt;

&lt;p&gt;那如果目前你想找工作，但是简历上可能体现不出亮点的话，怎么办呢？&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;建议，不要直接投一线大厂！先找个二线的厂镀镀银，做跳板。&lt;/p&gt;

  &lt;p&gt;举个🌰：如果你在一家50人的公司，想直接进一家万人大厂可能比较难，但是你可以争取先进一家500人的，500人的厂其实一般业内也是有点名气了，然后如果目标一直很坚定，比如瞄准BAT的话，就在这家500的人厂做准备，看你的目标厂需求是什么，你就在工作之余以他们的需求为目标，不断学习，可能一年后，你就能成长为他们所需要的人才了~&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;简历细节处理&quot;&gt;简历细节处理&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;英文中文之间，加一个半角空格
    &lt;ul&gt;
      &lt;li&gt;
        &lt;p&gt;使用 iOS 开发 ✅&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;使用iOS开发   ❎&lt;/p&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;英文命名最好统一&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;比如前文使用 ‘app’，后面就不要用 ‘App’ 或者 ‘APP’ 之类的&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;不要使用“精通”
    &lt;ul&gt;
      &lt;li&gt;精通Objective-C、C、C++ 。。。
        &lt;ul&gt;
          &lt;li&gt;基本宣布gg&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;不要直接抄招聘要求&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;第三方框架最好不要写，特别是只用简单 API 调用的话&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;除非一线知名大厂或者知名 App，不然很多项目其实面试官是不知道的，所以如果上架AppStore了，建议加个App Store链接&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;会OC、会 Runtime、Block、只这样写是不够的&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;最好连实际操作具体描述下，类似AOP打点上传逻辑（不要太空泛，最好实际举🌰）&lt;/li&gt;
      &lt;li&gt;例如 熟悉 &lt;a href=&quot;https://minilv.github.io/2019/03/17/Runtime-%E6%B6%88%E6%81%AF%E6%9C%BA%E5%88%B6%E5%9C%9F%E5%91%B3%E8%AE%B2%E8%A7%A3/&quot;&gt;Runtime&lt;/a&gt;，在项目中使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Method Swizzling&lt;/code&gt; 技术实现AOP打点上传功能；&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;简历最好2-3页，1页太少了，看不出东西，也不用写太多，8000字作文警告也是对简历阅读者的一大考验&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;不需要自我评价&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;不需要贴自拍&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;不需要过多无用信息&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;排版一定不要错乱，强烈建议使用 PD F格式，不要用 word 格式&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;如果有比较需要理解的技术点，最好有自己对该知识点理解的blog链接&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;每次用 markdown 写完准备投递的时候，一定要先生成 PDF 文件先自己 review 下，不要有错别字，尽量不要出现内容模块被划分的情况&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;
        &lt;p&gt;比如你在介绍你的某一段工作经历，尽量不要，第一页写3点，然后剩下的内容被划分到下一页，最好是每页的结尾，都把某个部分的内容介绍完毕，下一页开始新的介绍&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;如果某个工作经历的出现了一共划分成两页的5个点，第一页结尾3点，第二页开头2点，尽量想办法，弄成要不从第二页开始介绍全部5点，要么在第一页的格式上，看能否全写在第一页结尾&lt;/p&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;每页要尽量写满，比如你写了3页，最后一页只有不到三分之一，就浓缩成2页，不要留下大面积空白&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;如果工作经历真的特别多的，但是技术点有很重复的，没必要每个都写，挑重要的，有技术的写，尽量不要出现多个项目，描述使用相同的技术&lt;/li&gt;
  &lt;li&gt;简历的两个闪光点
    &lt;ul&gt;
      &lt;li&gt;开源项目 / GitHb &amp;amp; Blog&lt;/li&gt;
      &lt;li&gt;技能清单&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;如果没有开源项目，技能清单一定认真写，写出亮点出来&lt;/strong&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;最最最重要的一点，简历写的是自己，要为简历上的每个字负责！不能说一顿乱写，跟写小说一样，最后主人公不是自己（手动🐶）&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;简历模板&quot;&gt;简历模板&lt;/h2&gt;

&lt;p&gt;如果自己投递的话，不要用什么模板，直接 markdown 搞定一切~&lt;/p&gt;

&lt;p&gt;&lt;em&gt;推荐软件&lt;/em&gt;  &lt;a href=&quot;https://typora.io/&quot;&gt;typora&lt;/a&gt;(&lt;strong&gt;免费&lt;/strong&gt;，非广告🐶)&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/miniLV/MNResume&quot;&gt;小蠢驴脱敏简历&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;总结&quot;&gt;总结&lt;/h2&gt;

&lt;p&gt;最近的金三银四确实好像比往年差一些，iOS开发没人要的新闻，也已经听了很多年了，看到论坛上，很多人再说找了三个月还没面试邀请，这时候，可能就是真的简历写的不够好，可以写一份，投递几家，没面试邀请，复盘下，看有哪里可以改进优化的。老夫作为一个108本无任何大厂背景的3年iOSer，以上面的 &lt;a href=&quot;https://github.com/miniLV/MNResume&quot;&gt;模板&lt;/a&gt; 基本投出去的简历都有接到面试，所以说 &lt;strong&gt;简历是面试的敲门砖~&lt;/strong&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;打个隐藏广告，&lt;a href=&quot;https://github.com/miniLV/Interview-series&quot;&gt;面试驱动技术系列&lt;/a&gt; 了解一下！面试的时候其实很能派上用场(本人亲测)!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;还有些技术点，比如 多线程、 RunLoop、 内存管理的之后会继续补上&lt;/em&gt;&lt;/p&gt;

</description>
        <pubDate>Mon, 06 May 2019 00:00:00 +0800</pubDate>
        <link>https://minilv.github.io/2019/05/06/iOS%E5%88%9D%E4%B8%AD%E7%BA%A7%E5%BC%80%E5%8F%91%E7%AE%80%E5%8E%86%E6%8C%87%E5%8C%97/</link>
        <guid isPermaLink="true">https://minilv.github.io/2019/05/06/iOS%E5%88%9D%E4%B8%AD%E7%BA%A7%E5%BC%80%E5%8F%91%E7%AE%80%E5%8E%86%E6%8C%87%E5%8C%97/</guid>
        
        <category>面试</category>
        
        
      </item>
    
      <item>
        <title>一道高级iOS面试题(runtime方向)</title>
        <description>&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;面试驱动技术合集（初中级iOS开发），关注仓库，及时获取更新&lt;/em&gt; &lt;a href=&quot;https://github.com/miniLV/Interview-series&quot;&gt;Interview-series&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/169d447aa55fb28f?raw=true&quot; alt=&quot;photo-1521120413309-42e7eada0334&quot; /&gt;&lt;/p&gt;

&lt;p&gt;说到iOS，要么公司规模比较小，&amp;lt;=3人，不需要面试。&lt;/p&gt;

&lt;p&gt;其他的，大概率要让你刀枪棍棒十八般武艺都拿出来耍耍。&lt;/p&gt;

&lt;p&gt;而其中，但凡敌军阵营中有iOSer的，又极大概率会考到 Runtime 的知识点。&lt;/p&gt;

&lt;p&gt;以下，是一题 &lt;a href=&quot;https://github.com/sunnyxx&quot;&gt;sunnyxx&lt;/a&gt;的一道 &lt;a href=&quot;https://blog.sunnyxx.com/2014/11/06/runtime-nuts/&quot;&gt;runtime 考题&lt;/a&gt;，给大伙练练手，如果掌握了，Runtime层面的初中级问题应该都不在话下~&lt;/p&gt;

&lt;h4 id=&quot;题目来袭&quot;&gt;题目来袭：&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;//MNPerson
@interface MNPerson : NSObject

@property (nonatomic, copy)NSString *name;

- (void)print;

@end

@implementation MNPerson

- (void)print{
    NSLog(@&quot;self.name = %@&quot;,self.name);
}

@end

---------------------------------------------------

@implementation ViewController

- (void)viewDidLoad {

    [super viewDidLoad];
    
    id cls = [MNPerson class];
    
    void *obj = &amp;amp;cls;
    
    [(__bridge id)obj print];
    
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;问输出结果是啥，会不会崩溃。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/169d44968ff6bab7?raw=true&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;最终结果：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;self.name = &amp;lt;ViewController: 0x7fe667608ae0&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;what？&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;问题1：print 是实例方法，但是并没有哪里调用了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[MNPerson alloc]init]&lt;/code&gt; ??&lt;/li&gt;
  &lt;li&gt;问题2: 为啥打印了 viewController？&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;当前内存地址结构 - 与正常的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt; [person print]&lt;/code&gt; 对比&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/169d447a7c8cacd3?raw=true&quot; alt=&quot;image-20190320211003867&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;person变量的指针，执行 MNPerson 实例对象&lt;/li&gt;
  &lt;li&gt;实例对象的本身是个结构体，之前指向他，等价于执行结构体的第一个成员&lt;/li&gt;
  &lt;li&gt;结构体的第一个成员是isa，所以可以理解为，person-&amp;gt;isa&lt;/li&gt;
  &lt;li&gt;所以两个print，其实内存结构一致
    &lt;ul&gt;
      &lt;li&gt;obj -&amp;gt; cls -&amp;gt; [MNPerson Class]&lt;/li&gt;
      &lt;li&gt;person -&amp;gt; isa -&amp;gt; [MNPerson Class]&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;调用print 方法，不需要关心有没有成员变量 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_name&lt;/code&gt;，所以可以理解为，cls == isa&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
  &lt;li&gt;函数调用，是通过查找isa，其实本质，是查找结构体的前八个字节;&lt;/li&gt;
  &lt;li&gt;前八个字节正好是isa，所以这里可以理解为 cls == isa，这么理解的话，cls其实等于isa;&lt;/li&gt;
  &lt;li&gt;所以可以找得到 MNPerson 类，就可以找到MNPerson 类内部的方法，从而调用 `  print` 函数&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;问题2：为啥里面打印的是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ViewController&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;这就需要了解到iOS的内存分配相关知识&lt;/p&gt;

&lt;h4 id=&quot;内存分配&quot;&gt;内存分配&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;void test(){
    int a = 4;
    int b = 5;
    int c = 6;
    
    NSLog(@&quot;a = %p,b = %p,c = %p&quot;,&amp;amp;a,&amp;amp;b,&amp;amp;c);
}
---------------------------
a = 0x7ffee87e9fdc,
b = 0x7ffee87e9fd8,
c = 0x7ffee87e9fd4
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;局部变量是在栈空间&lt;/li&gt;
  &lt;li&gt;上图可以发现，a先定义，a的地址比b高，得出结论：&lt;strong&gt;栈的内存分配是从高地址到低地址&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;栈的内存是连续的&lt;/strong&gt; (这点也很重要！！)&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;OC方法的本质，其实是函数调用, 底层就是调用 objc_msgSend() 函数发送消息。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;- (void)viewDidLoad {

    [super viewDidLoad];
    
    NSString  *test = @&quot;666&quot;;
    
    id cls = [MNPerson class];
    
    void *obj = &amp;amp;cls;
    
    [(__bridge id)obj print];
    
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上述代码为例，三个变量 - test、cls、obj，都是局部变量，所以都在栈空间&lt;/p&gt;

&lt;p&gt;栈空间是从高地址到低地址分配，所以test是最高地址，而obj是最低地址&lt;/p&gt;

&lt;p&gt;MNPerson底层结构&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;struct MNPerson_IMPL{
    Class isa;
    NSString *_name;
}

- (void)print{
    NSLog(@&quot;self.name = %@&quot;,self-&amp;gt;_name);
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ol&gt;
  &lt;li&gt;要打印的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_name&lt;/code&gt; 成员变量，其实是通过&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt; self -&amp;gt; &lt;/code&gt; 去查找；&lt;/li&gt;
  &lt;li&gt;这里的 self，就是函数调用者；&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[(__bridge id)obj print];&lt;/code&gt;  即通过 obj 开始找；&lt;/li&gt;
  &lt;li&gt;而找 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_name&lt;/code&gt; ，是通过指针地址查找，找得&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt; MNPerson_IMPL&lt;/code&gt; 结构体&lt;/li&gt;
  &lt;li&gt;因为这里的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MNPerson_IMPL&lt;/code&gt; 里面就两个变量，所以这里查找 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_name&lt;/code&gt;，就是通过 ` isa&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;  的地址，跳过8个字节，找到 &lt;/code&gt;_name`&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/169d447a9390b02a?raw=true&quot; alt=&quot;image-20190320214425257&quot; /&gt;&lt;/p&gt;

&lt;p&gt;而前面又说过，cls = isa，而_name 的地址 = isa往下偏移 8 个字节，所以上面的图可以转成&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/169d447a93b646e2?raw=true&quot; alt=&quot;image-20190320214534204&quot; /&gt;&lt;/p&gt;

&lt;p&gt;_name的本质，先找到 isa，然后跳过 isa 的八个字节，就找到 _name这个变量&lt;/p&gt;

&lt;p&gt;所以上图输出&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;self.name = 666
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;最早没有 test变量的时候呢&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;- (void)viewDidLoad {

    [super viewDidLoad];
    
    id cls = [MNPerson class];
    
    void *obj = &amp;amp;cls;
    
    [(__bridge id)obj print];
    
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;super-viewdidload做了什么&quot;&gt;[super viewDidLoad];做了什么&lt;/h4&gt;

&lt;p&gt;底层 - objc_msgSendSuper&lt;/p&gt;

&lt;p&gt;` objc_msgSendSuper({ self, [ViewController class] },@selector(ViewDidLoad)),`&lt;/p&gt;

&lt;p&gt;等价于：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;struct temp = {
    self,
    [ViewController class] 
}

objc_msgSendSuper(temp, @selector(ViewDidLoad))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;所以等于有个局部变量 - 结构体 temp，&lt;/p&gt;

&lt;p&gt;结构体的地址 = 他的第一个成员，这里的第一个成员是self&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/169d447a938981ee?raw=true&quot; alt=&quot;image-20190320215517076&quot; /&gt;&lt;/p&gt;

&lt;p&gt;所以等价于 _name = self = 当前ViewController，所以最后输出&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;self.name = &amp;lt;ViewController: 0x7fc6e5f14970&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;话外篇-super-的本质&quot;&gt;话外篇 super 的本质&lt;/h4&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/169d447a94ceae1a?raw=true&quot; alt=&quot;image-20190320220159663&quot; /&gt;&lt;/p&gt;

&lt;p&gt;**其实super的本质，不是 objc_msgSendSuper({self,[super class],@selector(xxx)}) **&lt;/p&gt;

&lt;p&gt;而是&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;objc_msgSendSuper2(
{self,
[current class]//当前类
},
@selector(xxx)})
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;函数内部逻辑，拿到第二个成员 - 当前类，通过superClass指针找到他的父类，从superClass开始搜索，最终结果是差不多的~&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;友情演出:&lt;a href=&quot;https://github.com/CoderMJLee&quot;&gt;小马哥MJ&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;题目来源:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://blog.sunnyxx.com/2014/11/06/runtime-nuts/&quot;&gt;神经病院入学考试&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://minilv.github.io/2019/03/17/Runtime-%E6%B6%88%E6%81%AF%E6%9C%BA%E5%88%B6%E5%9C%9F%E5%91%B3%E8%AE%B2%E8%A7%A3/&quot;&gt;runtime消息机制理解&lt;/a&gt;&lt;/p&gt;

</description>
        <pubDate>Thu, 28 Mar 2019 07:00:00 +0800</pubDate>
        <link>https://minilv.github.io/2019/03/28/%E4%B8%80%E9%81%93%E9%AB%98%E7%BA%A7iOS%E9%9D%A2%E8%AF%95%E9%A2%98(runtime%E6%96%B9%E5%90%91)/</link>
        <guid isPermaLink="true">https://minilv.github.io/2019/03/28/%E4%B8%80%E9%81%93%E9%AB%98%E7%BA%A7iOS%E9%9D%A2%E8%AF%95%E9%A2%98(runtime%E6%96%B9%E5%90%91)/</guid>
        
        <category>面试</category>
        
        
      </item>
    
      <item>
        <title>isa详解 &amp; class 内部结构</title>
        <description>&lt;blockquote&gt;
  &lt;p&gt;面试驱动技术合集（初中级iOS开发），关注仓库，及时获取更新 &lt;a href=&quot;https://github.com/miniLV/Interview-series&quot;&gt;Interview-series&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;https://ws2.sinaimg.cn/large/006tKfTcgy1g17aa0lmmhj31a80u0wie.jpg&quot; alt=&quot;patrick-ward-503767-unsplash&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;isa详解&quot;&gt;isa详解&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;arm64架构之后，isa进行了优化，变成了一个&lt;strong&gt;共用体( union)&lt;/strong&gt;结构，使用了&lt;strong&gt;位域&lt;/strong&gt;的技术，可以存储更多的信息&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  #define MNWhiteMask (1&amp;lt;&amp;lt;0) //Ox0000 0001
  #define MNRichMask (1&amp;lt;&amp;lt;1) //Ox0000 0010
  #define MNBeautyMask (1&amp;lt;&amp;lt;2) //Ox0000 0100
  
  
  @interface MNObject ()
  {
      //位域
      struct {
          char white : 1;
          char rich : 1;
          char beauty : 1;
      }_whiteRichBeauty;
  }
  @end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;番外篇 - 假设一个程序员都喜欢对象，有白富美三个属性，能否用一个字节去存储呢？&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;具体实现&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  @implementation MNGirl
  
  - (void)setWhite:(BOOL)white{
      _whiteRichBeauty.white = white;
  }
  - (void)setRich:(BOOL)rich{
      _whiteRichBeauty.rich = rich;
  }
  - (void)setBeauty:(BOOL)beauty{
      _whiteRichBeauty.beauty = beauty;
  }
  
  - (BOOL)white{
      return _whiteRichBeauty.white;
  }
  - (BOOL)rich{
      return _whiteRichBeauty.rich;
  }
  - (BOOL)beauty{
      return _whiteRichBeauty.beauty;
  }
  
  @end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;使用情况&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  MNGirl *girl = [MNGirl new];
  [girl setWhite:NO];
  [girl setRich:YES];
  [girl setBeauty:NO];
  
  NSLog(@&quot;white = %d, rich = %d, beauty = %d&quot;, girl.white, girl.rich, girl.beauty);
  
  ------------------------------------------------------------------
  输出：
  white = 0, rich = -1, beauty = 0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;white 和 beauty的值都是正确的，为啥rich 出错了？&lt;/p&gt;

&lt;p&gt;涉及到符号位覆盖的问题！&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  (lldb) p/x girl-&amp;gt;_whiteRichBeauty
  ((anonymous struct)) $1 = (white = 0x00, rich = 0x01, beauty = 0x00)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;赋值的时候，其实还是正确的，rich = 1，取值的时候，怎么出问题了呢?&lt;/p&gt;

&lt;p&gt;这里假设结构体 _whiteRichBeauty 占8位，假设是0x 0000 0000&lt;/p&gt;

&lt;p&gt;rich = 1，占1位，就是 0x1&lt;/p&gt;

&lt;p&gt;要把 0x1 赋值到 0x 0000 0000，就要涉及到拷贝&lt;/p&gt;

&lt;p&gt;0x的第一位是1，所以将1拷贝过去，最后变成 0x 1111 1111&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/1696d63a9073b8fd?raw=true&quot; alt=&quot;image-20190311230151798&quot; /&gt;&lt;/p&gt;

&lt;p&gt;全是1，8个1 = 255 = 0xff&lt;/p&gt;

&lt;p&gt;0xff 有符号 = 255，无符号 = -1，&lt;/p&gt;

&lt;p&gt;所以这里输出-1 ==&amp;gt; 错误输出！&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;解决办法：&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  {
      //位域
      struct {
          char white : 2;
          char rich : 2;
          char beauty : 2;
      }_whiteRichBeauty;
  }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;一个占2位，三个属性合起来也才6位，1个字节还是可以放得下&lt;/p&gt;

&lt;p&gt;然后现在 rich = 1，占2位，就是 0x01&lt;/p&gt;

&lt;p&gt;rich的第一位是0，第二位才是1，所以将第一位0拷贝过去，最后变成 0x 0000 0001 = 结果！&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/1696d63a906d3ed2?raw=true&quot; alt=&quot;image-20190311230354335&quot; /&gt;&lt;/p&gt;

&lt;p&gt;苹果的做法 -&amp;gt; 共用体：&lt;/p&gt;

&lt;p&gt;里面的成员共用一块内存，和结构体不一样，存储采取位运算，结构体只是增加可读性&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  @interface MNGirl ()
  {
      //共用体
      union {
          //存放所有数据
          char bits;
          
          //位域:增加可读性，可以省略
          struct {
              char white : 1;
              char rich : 1;
              char beauty : 1;
          };
      }_whiteRichBeauty;
      
  }
  @end
  
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  //共用体
  union isa_t {
      isa_t() { }
      isa_t(uintptr_t value) : bits(value) { }
  
      Class cls;
      uintptr_t bits;
  #if defined(ISA_BITFIELD)
      struct {
          ISA_BITFIELD;  // defined in isa.h
      };
  #endif
  };
  ISA_BITFIELD = struct{
        uintptr_t nonpointer        : 1;                                       \
        uintptr_t has_assoc         : 1;                                       \
        uintptr_t has_cxx_dtor      : 1;                                       \
        uintptr_t shiftcls          : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
        uintptr_t magic             : 6;                                       \
        uintptr_t weakly_referenced : 1;                                       \
        uintptr_t deallocating      : 1;                                       \
        uintptr_t has_sidetable_rc  : 1;                                       \
        uintptr_t extra_rc          : 19
  }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;arm64架构下，isa不是直接指向class的地址，而且要&amp;amp;上一个MASK&lt;/p&gt;

&lt;p&gt;shiftcls: class存放的地址，通过 &amp;amp; ISA_MASK ==&amp;gt; 取出表示 class地址的44位&lt;/p&gt;

&lt;p&gt;这样其他位就还能存储数据了&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/169720d3b6bebaab?raw=true&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;释放更快的源码 - objc_destructInstance&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  void *objc_destructInstance(id obj) 
  {
      if (obj) {
          // Read all of the flags at once for performance.
          //如果没有C++析构函数
          bool cxx = obj-&amp;gt;hasCxxDtor();
          //没有设置过关联对象
          bool assoc = obj-&amp;gt;hasAssociatedObjects();
  
          // This order is important.
          //移除操作
          if (cxx) object_cxxDestruct(obj);
          if (assoc) _object_remove_assocations(obj);
          obj-&amp;gt;clearDeallocating();
      }
  
      return obj;
  }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;class内部结构&quot;&gt;Class内部结构&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/169721a57dcc2d98?raw=true&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/1697232afbb4eebd?raw=true&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;class_rw_t&quot;&gt;class_rw_t&lt;/h3&gt;
&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;rw –&amp;gt; readwrite&lt;/em&gt; 可读可写，可以修改&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  struct class_rw_t {
  
      method_array_t methods;//方法列表
      property_array_t properties;//属性列表
      protocol_array_t protocols;//协议列表
      }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;class_rw_t  中的 methods、properties、protocols 都是 二维数组 ，以methods为例&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;methods 里面是一组一组的 method_list_t，可以理解为一个一个的分类，每个method_list_t里面是该分类下的一个一个method_t 组成的方法列表&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;对于分类想了解得更深一点的，怎么拼接组装的，底层实现的，可以看下&lt;a href=&quot;https://minilv.github.io/2019/02/27/category/&quot;&gt;Category相关知识&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h3 id=&quot;class_ro_t&quot;&gt;class_ro_t&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;ro –&amp;gt; readonly&lt;/em&gt;，只读，不可修改，保留类最初始的属性&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  struct class_ro_t {
  
      method_list_t * baseMethodList;//原方法列表
      protocol_list_t * baseProtocols;
      const ivar_list_t * ivars;//成员变量列表
      }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;class_ro_t 里面baseMethodList 是一维数组，因为原始的类对象只有一个，就是宿主类，不包含分类信息，所以不需要二维数组，直接 baseMethodList 里面直接放 method_t就行了&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  struct method_t {
      SEL name;//函数名
      const char *types;//返回值类型&amp;amp;参数
      MethodListIMP imp;//函数地址 
      }
  
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;type-encodings&quot;&gt;Type Encodings&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/169778d87e7d81b6?raw=true&quot; alt=&quot;方法缓存3&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  // &quot;i24@0:8i16f20&quot;
  // 0id 8SEL 16int 20float  == 24
  - (int)test:(int)age height:(float)height
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;每一个方法都有两个默认参数&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;self&lt;/code&gt;和&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_cmd&lt;/code&gt; 我们可以查到&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;id&lt;/code&gt;类型为&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@&lt;/code&gt;，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SEL&lt;/code&gt;类型为&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;1、第一个参数&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i&lt;/code&gt;返回值&lt;/li&gt;
  &lt;li&gt;2、第二个参数&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@&lt;/code&gt; 是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;id 类型的self&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;3、第三个参数&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:&lt;/code&gt;是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SEL 类型的_cmd&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;4、第四个参数&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i&lt;/code&gt; 是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Int age&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;5、第五个参数&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;f&lt;/code&gt; 是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;float height&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;其中加载的数字其实是跟所占字节有关&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;1、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;24&lt;/code&gt; 总共占有多少字节&lt;/li&gt;
  &lt;li&gt;2、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@0&lt;/code&gt; 是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;id 类型的self&lt;/code&gt;的起始位置为0&lt;/li&gt;
  &lt;li&gt;3、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:8&lt;/code&gt; 是因为&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;id 类型的self&lt;/code&gt;占字节为8，所以SEL 类型的_cmd`的起始位置为8&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;这里的数字都可以省略 i24@0:8i16f20 ==&amp;gt; i@:if&lt;/p&gt;
&lt;/blockquote&gt;

</description>
        <pubDate>Tue, 19 Mar 2019 06:49:00 +0800</pubDate>
        <link>https://minilv.github.io/2019/03/19/isa%E8%AF%A6%E8%A7%A3-&&-class%E5%86%85%E9%83%A8%E7%BB%93%E6%9E%84/</link>
        <guid isPermaLink="true">https://minilv.github.io/2019/03/19/isa%E8%AF%A6%E8%A7%A3-&&-class%E5%86%85%E9%83%A8%E7%BB%93%E6%9E%84/</guid>
        
        <category>Runtime</category>
        
        
      </item>
    
      <item>
        <title>Runtime - 消息机制详解</title>
        <description>&lt;blockquote&gt;
  &lt;p&gt;面试驱动技术合集（初中级iOS开发），关注仓库，及时获取更新 &lt;a href=&quot;https://github.com/miniLV/Interview-series&quot;&gt;Interview-series&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/16fd036d6b3a08c0?raw=true&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;class-结构详解&quot;&gt;Class 结构详解&lt;/h1&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;struct objc_class : objc_object {
    Class isa;
    Class superclass;
    cache_t cache;--&amp;gt; 方法缓存      
    class_data_bits_t bits;  
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;struct cache_t {
    struct bucket_t *_buckets;//散列表
    mask_t _mask;//散列表长度-1
    mask_t _occupied;//已经缓存的方法数量
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;struct bucket_t {
    cache_key_t _key;//@selecter(xxx) 作为key
    MethodCacheIMP _imp;//函数的执行地址
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;buckets&lt;/code&gt; 散列表，是一个数组，数组里面的每一个元素就是一个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bucket_t&lt;/code&gt;,&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bucket_t&lt;/code&gt;里面存放两个
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_key&lt;/code&gt; SEL作为key&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_imp&lt;/code&gt; 函数的内存地址&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_mask&lt;/code&gt; 散列表的长度&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_occupied&lt;/code&gt;已经缓存的方法数量&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/16fd036d6bdc0668?raw=true&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;函数调用底层走的是objc_msgSend&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/16fd036d6c63210f?raw=true&quot; alt=&quot;image-20190313222359416&quot; /&gt;&lt;/p&gt;

&lt;p&gt;正常的流程：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;对象通过isa，找到函数所在的类对象&lt;/li&gt;
  &lt;li&gt;这时候先做缓存查找，如果缓存的函数列表中没找到该方法&lt;/li&gt;
  &lt;li&gt;就去类的class_rw中的methods中找，如果找到了，调用并缓存该方法&lt;/li&gt;
  &lt;li&gt;如果类的class_rw中没找到该方法，通过superclass到父类中，走的逻辑还是先查缓存，缓存没有查类里面的方法。&lt;/li&gt;
  &lt;li&gt;最终如果在父类中调用到了，会将方法缓存到&lt;strong&gt;当前类&lt;/strong&gt;的方法缓存列表中&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;方法缓存&quot;&gt;方法缓存&lt;/h2&gt;

&lt;p&gt;如何进行缓存查找-&amp;gt;使用散列表(&lt;em&gt;散列表 - 空间换时间&lt;/em&gt;)&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/16fd036d6cf4aa6a?raw=true&quot; alt=&quot;image-20190317205913318&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/16fd036d6cec48e1?raw=true&quot; alt=&quot;image-20190313220800705&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;MNGirl *girl = [[MNGirl alloc]init];
mj_objc_class *girlClass = (__bridge mj_objc_class *)[MNGirl class];

[girl beauty];
[girl rich];

//遍历缓存(散列表长度 = mask + 1)
cache_t cache = girlClass-&amp;gt;cache;
bucket_t *buckets = cache._buckets;

for (int i = 0; i &amp;lt; cache._mask + 1; i++) {
    
    bucket_t bucket = buckets[i];
    
    NSLog(@&quot;%s %p&quot;, bucket,bucket._imp);
}

----------------------------------------
2019-03-13 22:11:42.911494+0800 rich 0x100000be0
2019-03-13 22:11:42.912946+0800 beauty 0x100000c10
2019-03-13 22:11:42.912970+0800 (null) 0x0
2019-03-13 22:11:42.913002+0800 init 0x7fff4f98ff4d
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;发现缓存中已经有三个方法了，分别是初始化调用的init，第一次调用的beauty和第二次调用的rich&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;散列表取方法&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[girl beauty];
[girl rich];

//遍历缓存(散列表长度 = mask + 1)
cache_t cache = girlClass-&amp;gt;cache;
bucket_t *buckets = cache._buckets;

bucket_t bucket = buckets[(long long)@selector(beauty) &amp;amp; cache._mask];

NSLog(@&quot;%s %p&quot;, bucket,bucket._imp);

-----------------------------------------
2019-03-13 22:15:00 beauty 0x100000c60
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;确实是取方法的时候，不用遍历，通过@selector( ) &amp;amp; mask = index索引，数组同index就&lt;/p&gt;

&lt;p&gt;&lt;em&gt;注意，不一定每次都能准确的index索引，算出来的index取出来的内容不一定是想要的，但是经常是比较接近，最差的情况下，也只是一边的循环遍历&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;索引散列表效率远高于数组！&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/16fd036d6d1370df?raw=true&quot; alt=&quot;image-20190313223112407&quot; /&gt;&lt;/p&gt;

&lt;p&gt;方法查找的源码:  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bucket_t * cache_t::find(cache_key_t k, id receiver)&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;bucket_t * cache_t::find(cache_key_t k, id receiver)
{
assert(k != 0);

bucket_t *b = buckets();
mask_t m = mask();
mask_t begin = cache_hash(k, m);
mask_t i = begin;
do {
if (b[i].key() == 0  ||  b[i].key() == k) {
return &amp;amp;b[i];
}
} while ((i = cache_next(i, m)) != begin);

// hack
Class cls = (Class)((uintptr_t)this - offsetof(objc_class, cache));
cache_t::bad_cache(receiver, (SEL)k, cls);
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;索引值 Index 的计算&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;static inline mask_t cache_hash(cache_key_t key, mask_t mask) 
{
    return (mask_t)(key &amp;amp; mask);
}

mask_t begin = cache_hash(k, m);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;走的是 key &amp;amp; mask的方法， A &amp;amp; B 一定是小于 A的&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; 1111 0010
&amp;amp;0011 1111
----------
 0011 0010 &amp;lt;= 原来的值
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;哈希表的算法也有用求余的，和&amp;amp;类似&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;实现如下:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/16fd036d9ad68f3f?raw=true&quot; alt=&quot;image-20190313223858753&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;(i = cache_next(i, m)) != begin
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;查找流程梳理： 比如起始下标是4， 总长度是6，目标不在列表中&lt;/p&gt;
  &lt;ol&gt;
    &lt;li&gt;取出index = 4的值，发现不是想要的，i - - 变成3&lt;/li&gt;
    &lt;li&gt;3 依次 - - 到0，然后mask长度开始 = 6继续&lt;/li&gt;
    &lt;li&gt;当6 又 - - 到起始index = 4的时候，说明已经遍历一圈了，还是没找到，方法缓存查找结束&lt;/li&gt;
  &lt;/ol&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;oc的消息机制&quot;&gt;OC的消息机制&lt;/h1&gt;

&lt;p&gt;三个阶段&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;消息发送&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;动态方法解析&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;消息转发&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;消息发送&quot;&gt;消息发送&lt;/h2&gt;

&lt;h4 id=&quot;当前类查找顺序&quot;&gt;当前类查找顺序&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;排序好的列表，采用&lt;strong&gt;二分查找&lt;/strong&gt;算法查找对应的执行函数&lt;/li&gt;
  &lt;li&gt;未排序的列表，采用一般遍历的方法查找对象执行函数&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;父类逐级查找&quot;&gt;父类逐级查找&lt;/h4&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/16fd036da02f2033?raw=true&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/16fd036da833b25d?raw=true&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;动态方法解析&quot;&gt;动态方法解析&lt;/h2&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@interface IOSer : NSObject

- (void)interview;

@end

@implementation IOSer

- (void)test{
    
    NSLog(@&quot;%s&quot;,__func__);
    
}

+ (BOOL)resolveInstanceMethod:(SEL)sel{
    if (sel == @selector(interview)) {
        
        Method method = class_getInstanceMethod(self, @selector(test));
        
        //动态添加interview方法
        class_addMethod(self, sel, method_getImplementation(method), method_getTypeEncoding(method));
        
        return YES;
        
    }
    return [super resolveInstanceMethod:sel];
}

@end

----------------------------------------------

//调用
IOSer *ios = [[IOSer alloc]init];
[ios interview];


---------------------------------------------
结果，不会crash，进入了动态添加的方法了
2019-03-17 21:33:51.475717+0800 Runtime-TriedResolverDemo[11419:9277997] -[IOSer test]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/16fd036dad3fef13?raw=true&quot; alt=&quot;image-20190317214712857&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;消息转发流程&quot;&gt;消息转发流程&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;消息转发流程1：forwardingTargetForSelector&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@implementation IOSer

- (void)interview{
    
    NSLog(@&quot;%s&quot;,__func__);
}
@end

@interface Forwarding : NSObject

- (void)interview;

@end

@implementation Forwarding

- (id)forwardingTargetForSelector:(SEL)aSelector{
    if (aSelector == @selector(interview)) {
    
        //objc_msgSend([[IOSer alloc]init],aSelector)
        //由IOSer作为消息转发的接收者
        return [[IOSer alloc]init];
    }
    return [super forwardingTargetForSelector:aSelector];
}

@end

---------------------------------------------------------------
调用
Forwarding *obj = [[Forwarding alloc]init];
[obj interview];


---------------------------------------------
结果，不会crash，进入了动态添加的方法了
2019-03-17 22:57:45.130805+0800 Runtime-TriedResolverDemo[13776:9355195] -[IOSer interview]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;消息转发流程2：forwardingTargetForSelector&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@implementation Forwarding

//返回方法签名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    if (aSelector == @selector(interview)) {

        //v16@0:8 = void xxx (self,_cmd)
        return [NSMethodSignature signatureWithObjCTypes:&quot;v16@0:8&quot;];
    }
    return [super methodSignatureForSelector:aSelector];
}

//NSInvocation - 方法调用
- (void)forwardInvocation:(NSInvocation *)anInvocation{
    //设置方法调用者
    [anInvocation invokeWithTarget:[[IOSer alloc]init]];
}

@end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;NSInvocation 其实封装了一个方法调用，包括：
    &lt;ul&gt;
      &lt;li&gt;方法名  - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;anInvocation.selector&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;方法调用 - ` anInvocation.target`&lt;/li&gt;
      &lt;li&gt;方法参数 - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;anInvocation getArgument: atIndex:&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/16fd036db9987c88?raw=true&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h4 id=&quot;冷门知识补充&quot;&gt;冷门知识补充&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;//类方法的消息转发
[Forwarding test]；
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;类方法也可以实现消息转发，但是用的是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;+ (id)forwardingTargetForSelector:(SEL)aSelector&lt;/code&gt;函数&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;因为__forwarding底层，是用receiver去发送 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;forwardingTargetForSelector&lt;/code&gt;消息，如果是类方法，receiver是类对象，所以要调用的是 “+” 方法&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;小tips：默认是没有&lt;/em&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;+ (id)forwardingTargetForSelector:(SEL)aSelector&lt;/code&gt;方法，可以先打&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;- (id)forwardingTargetForSelector:(SEL)aSelector&lt;/code&gt;，“-” 替换成“+”，完成~&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;友情演出:&lt;a href=&quot;https://github.com/CoderMJLee&quot;&gt;小马哥MJ&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;参考资料：&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://blog.zhengdong.me/2013/07/18/a-look-under-the-hood-of-objc-msgsend/&quot;&gt;objc-msgsend&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.gnu.org/software/libc/&quot;&gt;gun&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://opensource.apple.com/tarballs/libmalloc/&quot;&gt;libmalloc&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://opensource.apple.com/tarballs/objc4/&quot;&gt;objc4&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://yulingtianxia.com/blog/2016/06/15/Objective-C-Message-Sending-and-Forwarding/&quot;&gt;Objective-C-Message-Sending-and-Forwarding&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://minilv.github.io/2019/03/18/isa%E8%AF%A6%E8%A7%A3-&amp;amp;&amp;amp;-class%E5%86%85%E9%83%A8%E7%BB%93%E6%9E%84/&quot;&gt;Type Encodings&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Mon, 18 Mar 2019 07:49:00 +0800</pubDate>
        <link>https://minilv.github.io/2019/03/18/Runtime-%E6%B6%88%E6%81%AF%E6%9C%BA%E5%88%B6%E5%9C%9F%E5%91%B3%E8%AE%B2%E8%A7%A3/</link>
        <guid isPermaLink="true">https://minilv.github.io/2019/03/18/Runtime-%E6%B6%88%E6%81%AF%E6%9C%BA%E5%88%B6%E5%9C%9F%E5%91%B3%E8%AE%B2%E8%A7%A3/</guid>
        
        <category>Runtime</category>
        
        
      </item>
    
      <item>
        <title>面试驱动技术 - Block详解</title>
        <description>&lt;blockquote&gt;
  &lt;p&gt;面试驱动技术合集（初中级iOS开发），关注仓库，及时获取更新 &lt;a href=&quot;https://github.com/miniLV/Interview-series&quot;&gt;Interview-series&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/16fd041b499553bd?raw=true&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Block 在 iOS 算比较常见常用且常考的了，现在面试中，要么没面试题，有面试题的，基本都会考到 block 的点。&lt;strong&gt;本文特别干！&lt;/strong&gt;(但是初中级iOSer应该能有所收获~)&lt;/p&gt;

&lt;p&gt;先来个面试题热热身，题目: &lt;strong&gt;手撕代码 - 用Block实现两个数的求和&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(这题如果会的，block基础知识可以跳过了，直接到&lt;/em&gt; Block原理探究）&lt;/p&gt;

&lt;h4 id=&quot;简单介绍block入门级用法&quot;&gt;简单介绍block入门级用法&lt;/h4&gt;

&lt;p&gt;Block结构比较复杂，一般用 typedef 定义，直接调用的感觉比较简单、清晰易懂&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;//typedef block的时候有提示
typedef void(^MNBlock)(int);

@interface ViewController ()

@property (nonatomic, copy) MNBlock block;

@end


@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    //直接用self.block调用
    self.block = ^(int a) {
        //dosomething...
    };
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;参数解释:&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;typedef &amp;lt;#returnType#&amp;gt;(^&amp;lt;#name#&amp;gt;)(&amp;lt;#arguments#&amp;gt;);&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/16fd041b4c958749?raw=true&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;题目: 手撕代码 - 用Block实现两个数的求和&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;日常开发中，block声明一般写的比较多，实现一般是靠Xcode自动补全提示出现的，手撕代码的情况下，等号右侧的block实现要怎么写？&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;声明:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;typedef int(^MNBlock)(int a, int b);

@interface ViewController ()

@property (nonatomic, copy) MNBlock sum;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Vip补全功能:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/16fd041b4cafb073?raw=true&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;纸上按Enter没用啊兄弟！看来还是需要了解一下Block右边的东西~&lt;/p&gt;

&lt;p&gt;先在 Xcode上按下 Enter，了解下再撕
&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/16fd041b4f258093?raw=true&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;^int(int a, int b) {
    //Control reaches end of non-void block    
    因为返回值是int类型，所以这里需要返回
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/16fd041b5070781f?raw=true&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;int(^Sum)(int, int) = ^(int a, int b){
    return a + b;
};
int result = Sum(5, 10);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/16fd041b5526ab31?raw=true&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;block的坑出现新手可能会写错的地方&quot;&gt;Block的坑出现！新手可能会写错的地方&lt;/h4&gt;

&lt;p&gt;1.声明出错 - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;void ^(testBlock)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/16fd041b79c2d244?raw=true&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;修正版：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;void (^testBlock)() = ^{
    
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;block的声明，^ 和 blockName 都是在小括号里面！！&lt;/p&gt;

&lt;p&gt;2.block各种实现的参数问题&lt;/p&gt;

&lt;p&gt;声明&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;typedef int(^MNBlock)(int, int);&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/16fd041b7ee9131a?raw=true&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    self.sum = ^int(int a, int b) {
        return a + b;
    };
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里要注意，block声明里面只有参数类型，没有实际参数的话，Xcode提示也只有参数，这里涉及到形参和实参的问题&lt;/p&gt;

&lt;p&gt;声明是形参，可以不写参数，但是使用的时候，必须有实际参数，才可以进行使用，所以这里需要实参，可以在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;^int(int , int)&lt;/code&gt; 中手动添加实参&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;^int(int a, int b)&lt;/code&gt;，就可以让a 和 b 参与运算&lt;/p&gt;

&lt;p&gt;小tips：实际开发中，建议声明的时候，如果需要带参数，最好形参也声明下，这样使用Xcode提示的时候，会把参数带进去，方便得多~(踩过坑的自然懂！)&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;省略void导致看不懂block结构的 &lt;em&gt;(正常是两个void导致局面混乱)&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;//声明
typedef void(^MNBlock)(void);

//实现
self.sum = ^{
    //dosomething...
};

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这种情况下，能知道怎么省略的，声明里两个void，能知道怎么对应的吗？&lt;/p&gt;

&lt;p&gt;这个其实比较简单，block不管声明 or 实现，最后一个小括号，里面都是参数，而参数是可以省略的！&lt;/p&gt;

&lt;p&gt;而为了把声明的两个void区分开，返回值 or 参数区分开，其实就ok了&lt;/p&gt;

&lt;p&gt;参数非void的例子&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;//声明非void的参数
typedef void(^MNBlock)(int a);

//实现就必须带参数，不可省略！
self.sum = ^(int a) {
    
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;参数void的例子 ==&amp;gt; 参数可以省略&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;typedef int(^MNBlock)(void);

self.sum = ^{
    //声明的返回值类型是int，所以一定要return；
    return 5;
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;其实-返回值是void的，也可以不省略&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;typedef void(^MNBlock)(void);

//实现的返回值不省略
self.sum = ^void () {
    
};

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;参数是void的省略:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;typedef int(^MNBlock)();

//实现里面，没有参数，就可以不写()
self.sum = ^int{
    return 5;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;注意！！ 声明里面的返回值void是不可以省略的！！&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/16fd041b916500ee?raw=true&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;小箭头^混乱的问题,到底放小括号内还是小括号外&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;声明是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;int(^MNBlock)(int a , int b)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;实现是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;^int(int a, int b)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;注意，这里箭头之后的，不管是多写() 还是少写，都会出错&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/16fd041b900d0685?raw=true&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/16fd041b93188229?raw=true&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;所以这里还不能死记，比如不管声明还是实现,死记 (^ xxx) 是没问题的 or 死记 ^…… xxx 不加括号是没问题的,在这里都行不通，只能靠脑记了&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这时候，就需要用到巧记了！&lt;/p&gt;

&lt;p&gt;&lt;em&gt;^ 和小括号组合的，一共有三种情况&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;一种是声明的，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;void(^MNBlock)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;一种是实现的，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;^int(int a,)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;还一种 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;^(int a)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;兄弟，看到这你还不乱吗！！&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/16fd041b9be707fe?raw=true&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;怎么记看这里，&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;手写分为两个部分，block等号左边 or 等号右边的，左边为声明，右边为实现区分开&lt;/li&gt;
  &lt;li&gt;声明记住：^后面跟blockName，他们需要包起来！ (^blockName),只有声明会用到blockName，先记住一点，如果有blockName，要和^一起，用小括号包起来&lt;/li&gt;
  &lt;li&gt;实现又分为两种：
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;^int&lt;/code&gt;:^后面跟的是返回值类型
        &lt;ul&gt;
          &lt;li&gt;^ 直接跟类型，不用加”( )” ==&amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;^int&lt;/code&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;^(int a)&lt;/code&gt;:^后面直接跟参数 &lt;em&gt;(返回值是void)&lt;/em&gt;。
        &lt;ul&gt;
          &lt;li&gt;参数都是要用”( )”包起来的，如果^后面跟参数，就得用”( )” ==&amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;^(int a)&lt;/code&gt;,&lt;/li&gt;
          &lt;li&gt;实现里，肯定有实际参数，这时候，参数类型和实参，就得用( )包起来&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;与小括号纠缠的总结&quot;&gt;^与小括号纠缠的总结&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;^ 后面仅跟类型，不需要小括号，==&amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;^int&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;^ 后面跟参数，参数需要小括号 ==&amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;^(int a)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;^ 后面跟block名称，^和blockName需要小括号 ==&amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;void (^MNBlock)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h2 id=&quot;block原理探究&quot;&gt;Block原理探究&lt;/h2&gt;

&lt;pre&gt;&lt;code class=&quot;language-objective-c&quot;&gt;void (^MNBlock)(void) = ^(void){
    NSLog(@&quot;this is a Block~ rua~&quot;);
};
MNBlock();
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xcrun  -sdk  iphoneos  clang  -arch  arm64  -rewrite-objc main.m&lt;/code&gt; 转成 C++ 代码, 查看底层结构&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-objective-c&quot;&gt;//对应上面的 MNBlock声明
void (*MNBlock)(void) = (&amp;amp;__main_block_impl_0(__main_block_func_0,
                                                      &amp;amp;__main_block_desc_0_DATA));
        
//对应上面的 MNblock() 调用
MNBlock-&amp;gt;FuncPtr(MNBlock);
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code class=&quot;language-objective-c&quot;&gt;//block声明调用的 - __main_block_impl_0
struct __main_block_impl_0 {
  //结构体内的参数
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  
  //c++中的构造函数，类似于 OC 的 init 方法，返回一个结构体对象
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
    impl.isa = &amp;amp;_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这里的block封装的函数调用解释&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MNBlock-&amp;gt;FuncPtr(MNBlock);&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;MNBlock 其实内部结构是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__main_block_impl_0&lt;/code&gt;，&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;struct __main_block_impl_0 {

  //函数调用地址在这个结构体内
  struct __block_impl impl;

  struct __main_block_desc_0* Desc;
  }
  
  struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  //函数调用地址在这里
  void *FuncPtr;
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;内部只有两个参数，一个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;impl&lt;/code&gt;，一个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Desc&lt;/code&gt;，而函数的调用地址 - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FuncPtr&lt;/code&gt;是再&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;impl&lt;/code&gt;中的，为什么这里能直接这样写呢？&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;因为，__main_block_impl_0 结构的地址和他的第一个成员一样，第一个成员的地址是__block_impl，所以__main_block_impl_0 和 __block_impl 的地址其实是同一个，通过格式强制转换，将 main_block_impl_0 转成 block_impl 就可以直接拿到他内部的 FuncPtr 函数地址，然后进行调用！&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/16fd041bb7b89526?raw=true&quot; alt=&quot;image-20190307213258239&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;可见- block本质上是OC对象，内部有一个isa指针&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;block是封装了函数调用以及函数调用的oc对象&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;block面试题抛砖引玉&quot;&gt;Block面试题抛砖引玉~&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;开胃菜先来一下，以下结果输出什么&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;int a = 10;
void (^MNBlock)(void) = ^{
    NSLog(@&quot;a = %d&quot;,a);
};
a += 20;

MNBlock();
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;调用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MNBlock();&lt;/code&gt; 之前，a 已经 + 20了，输出30？ 太天真了兄弟，这里涉及到capture的概念，即变量捕获&lt;/p&gt;

&lt;h3 id=&quot;block捕获变量capture&quot;&gt;Block捕获变量(capture)&lt;/h3&gt;

&lt;p&gt;捕获：Block内部会新增一个成员，来存储传进来的变量&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/16fd041bbe9bc3a3?raw=true&quot; alt=&quot;image-20190307214010613&quot; /&gt;&lt;/p&gt;

&lt;p&gt;block 内部直接捕获了传进去的这个变量a(10)&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/16fd041bc8aec909?raw=true&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;创建block的时候，已经将变量a=10 捕获到 block内部，之后再怎么修改，不会影响block 内部的  a&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;auto 和 static的区别&lt;/strong&gt;:以下会输出什么~&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;static int b = 10;
void (^MNBlock)(void) = ^{
    NSLog(@&quot;a = %d, b = %d&quot;,a,b);
};
a = 20;
b = 20;

MNBlock();
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;输出&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;2019-03-07 21:49:49 Block-Demo a = 10, b = 20
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;why?&lt;/p&gt;

&lt;p&gt;查看原因:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;auto int a = 10;
static int b = 10;
void (*MNBlock)(void) = (&amp;amp;__main_block_impl_0(__main_block_func_0,
                                              &amp;amp;__main_block_desc_0_DATA,
                                              a,
                                              &amp;amp;b));
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;发现：两种变量，都有捕获到block内部。&lt;/p&gt;

&lt;p&gt;a 是auto变量，走的是值传递，&lt;/p&gt;

&lt;p&gt;b 是 static 变量，走的是地址传递，所以会影响(指针指向同一块内存，修改的等于是同个对象)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;总结&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;只有局部变量才需要捕获，&lt;/li&gt;
  &lt;li&gt;全局变量不需要捕获，因为在哪都可以访问&lt;/li&gt;
  &lt;li&gt;需不需要捕获，其实主要是看作用域问题&lt;/li&gt;
  &lt;li&gt;auto局部变量 ==&amp;gt;值传递-&amp;gt;因为会销毁&lt;/li&gt;
  &lt;li&gt;static局部变量==&amp;gt;不会销毁==&amp;gt;所以地址传递&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;看图就行~&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/16fd041bca774905?raw=true&quot; alt=&quot;image-20190307220857223&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;进阶考题 - self 会被捕获到 block 内部吗&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;void (^MNBlock)(void) = ^{
    NSLog(@&quot;p = %p&quot;,self);
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;模拟看官作答：不会，因为整个类里，都能调用self，应该是全局的，全局变量不会捕获到block中&lt;/p&gt;

&lt;p&gt;哈哈哈哈！中计了！其实 self 是参数(局部变量)&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;struct __MNDemo__test_block_impl_0 {
  struct __block_impl impl;
  struct __MNDemo__test_block_desc_0* Desc;
  MNDemo *self; ==&amp;gt; 捕捉到了兄弟
  }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;解释原因：&lt;/p&gt;

  &lt;ul&gt;
    &lt;li&gt;每个OC函数，其实默认有两个参数，一个self，一个_cmd，只是他们倆兄弟默认是隐藏的&lt;/li&gt;
    &lt;li&gt;而由于他们是参数，所以是局部变量，局部变量就要被 block 捕获&lt;/li&gt;
    &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;- (void)test(self, SEL _cmd){XXX}&lt;/code&gt; 默认的OC方法里面其实有这两个隐藏的参数！所以上题的答案，self是会被block捕获的！&lt;strong&gt;（能听懂掌声！）&lt;/strong&gt;&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;进进阶考题 - 成员变量_name 会被捕获到 block 内部吗&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;void (^MNBlock)(void) = ^{
    NSLog(@&quot;==%@&quot;,_name);
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;模拟看官作答：呵呵，老子都中了这么多次技了，这题学会了！！ 因为_name是成员变量，全局的，也没有self，所以不需要捕获整个类就都可以随便访问它！&lt;/p&gt;

&lt;p&gt;哎，兄弟，还是太年轻了！！&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;void (^MNBlock)(void) = ^{
    NSLog(@&quot;==%@&quot;,self-&amp;gt;_name);
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;看图说话：&lt;/p&gt;

&lt;h2 id=&quot;block的类型&quot;&gt;Block的类型&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__NSGlobalBlock__&lt;/code&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__NSStackBlock__&lt;/code&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__NSMallocBlock__&lt;/code&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;MRC环境下&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;void (^global)() = ^{
    NSLog(@&quot;globalValue = %d&quot;,globalValue);
};

void (^autoBlock)() = ^{
    NSLog(@&quot;this is a Block~ rua~ = %d&quot;,a);
};

void (^copyAuto)() = [autoBlock copy];

--------------------------------------------
print class
2019-03-08 17:40:43 Block-Demo

 global class = __NSGlobalBlock__ 
 autoBlock class = __NSStackBlock__ 
 copyAuto = __NSMallocBlock__
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;总结:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/16fd041bcb790659?raw=true&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;内存分配示意图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/16fd041bcc135a3a?raw=true&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;栈上的内存系统会自动回收&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;栈空间的block 不会对 对象进行强引用&lt;/li&gt;
  &lt;li&gt;堆空间的block 可能会对对象产生强引用：
    &lt;ul&gt;
      &lt;li&gt;如果是weak指针，不会强引用&lt;/li&gt;
      &lt;li&gt;如果是strong指针，会强引用&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;堆上的内存是由程序员控制，所以一般将block 拷贝到堆上，让程序员控制他与内部变量的生命周期&lt;/p&gt;

&lt;p&gt;题目：&lt;strong&gt;以下输出的顺序是什么(ARC环境下)&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@implementation MNPerson

- (void)dealloc{
    NSLog(@&quot;MNPerson - dealloc&quot;);
}

@end

--------------------------------------

MNPerson *person = [[MNPerson alloc]init];

__weak MNPerson *weakPerson = person;

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    
    NSLog(@&quot;1-----%@&quot;,person);
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@&quot;2------%@&quot;,weakPerson);
    });
    
});

NSLog(@&quot;touchesBegan&quot;);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;输出结果&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;2019-03-08 22:38:59.038452+0800 touchesBegan
2019-03-08 22:39:00.056746+0800 1-----&amp;lt;MNPerson: 0x604000207840&amp;gt;
2019-03-08 22:39:00.057891+0800 MNPerson - dealloc
2019-03-08 22:39:02.058011+0800 2-----(null)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;解释：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;gcd的block会自动对auto变量进行copy操作&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;block内部对 auto 变量的强弱引用，取决于指针类型&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;1 中的auto变量是 person，没声明默认对象是 strong 类型，所以 gcd1 会对 person进行 1s的强引用&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;gcd2 中的变量是 weakPerson，看到是__wesk指针，所以block内部不会对其产生强引用&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;随后，gcd1 对 person进行1s的强引用之后，gcd1 的block销毁，person对象销毁，打印MNPerson dealloc&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;最终，2s过后打印 2——weakPerson，因为person对象在gcd1 block结束之后，释放掉了，所以此时person是空，因为是weak指针，对象是null不会crash，最终打印null&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h4 id=&quot;对象类型的auto变量&quot;&gt;对象类型的auto变量&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;当 block 内部访问了对象类型的auto变量时
    &lt;ul&gt;
      &lt;li&gt;如果block在展示，不会对 auto 变量产生强引用&lt;/li&gt;
      &lt;li&gt;如果 block 被 拷贝到堆上
        &lt;ul&gt;
          &lt;li&gt;会调用 block 内部的 copy 函数&lt;/li&gt;
          &lt;li&gt;copy 函数内部会调用 _Block_object_assign 函数&lt;/li&gt;
          &lt;li&gt;_Block_object_assign 函数会根据auto变量的修饰符 &lt;em&gt;( strong、 weak、unsafe_unretained )&lt;/em&gt; 做出对应的操作，看对内部auto变量进行强引用还是弱引用(类似于 retain)&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;如果 block 从 堆上移除
        &lt;ul&gt;
          &lt;li&gt;会调用 block 内部的 dispose 函数&lt;/li&gt;
          &lt;li&gt;dispose函数内部会调用_Block_object_dispose 函数&lt;/li&gt;
          &lt;li&gt;_Block_object_dispose 类似于 release，会对auto变量进行自动释放(当引用计数器=0的时候 )&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/16fd041bfab9a542?raw=true&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;block中的copy&quot;&gt;block中的copy&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;在ARC环境下，编译器会根据情况，自动将栈上的block拷贝到堆上，比如以下几种情况
    &lt;ul&gt;
      &lt;li&gt;block 作为函数返回值的时候&lt;/li&gt;
      &lt;li&gt;将block复制给__strong指针的时候&lt;/li&gt;
      &lt;li&gt;block作为Cocoa API中方法名含有usingBlock的方法参数事
        &lt;ul&gt;
          &lt;li&gt;比如：&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[array enumerateObjectsUsingBlock:XXX]&lt;/code&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;__block-修饰符的使用&quot;&gt;__block 修饰符的使用&lt;/h3&gt;

&lt;p&gt;题目：以下代码的是否编译通过，可以的话输出结果是什么&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;int a = 10;
void (^block)() = ^{
    a = 20;
    NSLog(@&quot;a = %d&quot;,a);
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;结果如下：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/16fd041c01813d3b?raw=true&quot; alt=&quot;image-20190308225448279&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;思考：无法编译，为啥呢？编译的时候，block应该是会把auto变量捕获进去的，那block结构中应该有a才对啊&lt;/em&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;//main函数
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 

        int a = 10;
        void (*block)() = ((void (*)())&amp;amp;__main_block_impl_0((void *)__main_block_func_0, &amp;amp;__main_block_desc_0_DATA, a));

    }
    return 0;
}

//block执行地址
  static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  int a = __cself-&amp;gt;a; // bound by copy
  NSLog((NSString *)&amp;amp;__NSConstantStringImpl__var_folders_kh_0rp73c0s2mvfp5gjf25j5y6h0000gn_T_main_1a12fa_mi_0,a);}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;block执行的时候，内部是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__main_block_func_0&lt;/code&gt; 函数，而a的声明，是在&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main&lt;/code&gt;函数，两个函数相互独立，对于他们来说，a都是一个局部变量，而且两个函数中都对a初始化，两个函数的中a不是同一个，那怎么可以在 执行函数中，修改main函数中的局部变量呢，所以编译报错！&lt;/p&gt;

&lt;p&gt;如何改？&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;方案一：使用static&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;static int a = 10;
void (^block)() = ^{
    a = 20;
    NSLog(@&quot;a = %d&quot;,a);
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;因为static修饰的auto变量，最终在block中进行的不是值传递，而是地址传递，措意执行函数中的a 和 main 函数中的a，是同一个地址 ==&amp;gt; 等于同一个a，所以可以修改，输出20&lt;/p&gt;

&lt;p&gt;但是使用static，就会变成静态变量，永远在内存中&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;方案二： 使用__blcok&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;__block auto int a = 10;
void (^block)() = ^{
    a = 20;
    NSLog(@&quot;a = %d&quot;,a);
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_a_0 *a; // by ref ==&amp;gt; auto的话，是int a，__block，变成对象了
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;struct __Block_byref_a_0 {
  void *__isa;
__Block_byref_a_0 *__forwarding;==&amp;gt; 指向自己的结构体
 int __flags;
 int __size;
 int a; ==&amp;gt; 10在这里
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;a = 20;最终转成 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(a-&amp;gt;__forwarding-&amp;gt;a) = 20;&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;解释下：__forwarding  是指向结构体本身的指针，等价于a本身，其实就是通过a的结构体指针，拿到里面的成员a，再对他赋值&lt;/p&gt;

  &lt;p&gt;指针传递，所以可以修改 auto 变量，通过block，间接引用 auto 变量&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/16fd041c02adada0?raw=true&quot; alt=&quot;image-20190309205908169&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;__block的内存管理&quot;&gt;__block的内存管理&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;当 block 在栈上的时候，不会对内部的__block 变量产生强引用&lt;/li&gt;
  &lt;li&gt;当 block 从栈上被 copy 到堆上的时候
    &lt;ul&gt;
      &lt;li&gt;会调用block内部的copy函数&lt;/li&gt;
      &lt;li&gt;copy函数内部会调用_Block_object_assign 函数&lt;/li&gt;
      &lt;li&gt;_Block_object_assign 函数会对 __block 变量进行一次 retain操作，产生强引用&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;抄图分析 :&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/16fd041c0a00edf1?raw=true&quot; alt=&quot;image-20190309210956453&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/16fd041c256fd8b2?raw=true&quot; alt=&quot;image-20190309211009229&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;当block从堆中移除时
    &lt;ul&gt;
      &lt;li&gt;会调用 block 内部的 dispose 函数&lt;/li&gt;
      &lt;li&gt;dispose内部会调用_Block_object_dispose函数&lt;/li&gt;
      &lt;li&gt;_Block_object_dispose函数会对&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__block&lt;/code&gt;变量进行一次release操作，如果retainCount为0，自动释放该__block变量&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/16fd041c0ece8edf?raw=true&quot; alt=&quot;image-20190309211246277&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/16fd041c303af7ee?raw=true&quot; alt=&quot;image-20190309211257030&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;总结：&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;block在栈上的时候，不会对内部的变量产生强引用&lt;/li&gt;
  &lt;li&gt;当block从栈上 copy 到堆上的时候，内部都会调用 __Block_object_assign
    &lt;ul&gt;
      &lt;li&gt;如果是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__block&lt;/code&gt;修饰的变量，会__block修饰的对象产生强引用&lt;/li&gt;
      &lt;li&gt;如果是普通auto变量，看修饰的指针类型是strong 还是 weak(unsafe_unretained)
        &lt;ul&gt;
          &lt;li&gt;strong修饰的，block就会对内部的auto变量产生强引用&lt;/li&gt;
          &lt;li&gt;weak修饰的，block就不会对内部的auto变量产生强引用&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;特别注意！上述条件仅在ARC环境下生效，如果是MRC环境下，block不会对内部auto变量产生强引用！&lt;strong&gt;(MRC下不会进行retain操作)&lt;/strong&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;当block从堆上移除的时候，内部会调用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__Block_object_dispose &lt;/code&gt;函数，相当于对&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;block&lt;/code&gt;内部所持有的对象进行移除release操作，如果retainCount为0，自动释放该__block变量&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;__block中的-_-forwarding-指针&quot;&gt;__block中的 _ forwarding 指针&lt;/h3&gt;

&lt;p&gt;内存拷贝的时候，如果block从栈被copy到堆上，肯定也希望内部的变量一起存储到堆上(让变量的生命周期可控，才不会被回收)&lt;/p&gt;

&lt;p&gt;加入变量a在栈上，在栈上的指针，指向堆上的 block，堆上的block的 forwarding指向他自己，就可以保证，修改&amp;amp;获取的变量，都是堆上的变量&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/16fd041c38a0d8fc?raw=true&quot; alt=&quot;image-20190309213120820&quot; /&gt;&lt;/p&gt;

&lt;p&gt;最终，__block指向的变量，是指向堆上的&lt;/p&gt;

&lt;h3 id=&quot;__block-修饰的类型&quot;&gt;__block 修饰的类型&lt;/h3&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@implementation MNObject

- (void)dealloc{
    NSLog(@&quot;MNObject - dealloc&quot;);
}

@end


--------------------------------------------

typedef void (^MNBlock)();

MNBlock block;
{
    MNObject *obj = [[MNObject alloc]init];
    __block __weak MNObject *weakObj = obj;
    
    block = ^{
        NSLog(@&quot;----------%p&quot;,weakObj);
    };
}
block();

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;问，上述代码的输出顺序是？&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;2019-03-09 21:57:56.673296+0800 Block-Demo[72692:8183596] MNObject - dealloc
2019-03-09 21:57:56.673520+0800 Block-Demo[72692:8183596] ----------0x0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;解释：ARC下&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/16fd041c4f4998af?raw=true&quot; alt=&quot;image-20190309220353476&quot; /&gt;&lt;/p&gt;

&lt;p&gt;上述代码，block 持有的是 weakObj，weak指针，所以block内部的__block结构体，对他内部持有的person不强引用！所以出了 小括号后，person没有被强引用，生命gg，先dealloc，输出&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dealloc&lt;/code&gt;，之后进行block调用，打印 ———&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;特别注意，上述逻辑进在ARC下，如果在MRC下，中间结构体对象，不会对person 进行retain操作! 即便 person 是强指针修饰，也不会对内部的person对象进行强引用！&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;MRC环境下&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;MNBlock block;
{
    MNObject *obj = [[MNObject alloc]init];
    block = [^{
        NSLog(@&quot;----------%p&quot;,obj);
    }copy];
    
    [obj release];
}
block();

[block release];

--------------------
输出:
2019-03-09 21:59:56.673296+0800 Block-Demo[72692:8183596] MNObject - dealloc
2019-03-09 21:59:56.673520+0800 Block-Demo[72692:8183596] ----------0x0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;上述代码，obj 是 __strong 修饰，但是并没有被 block 强引用！可见MRC环境下，__修饰的对象，生成的中间block对象不会对 auto变量产生强引用。&lt;/p&gt;

&lt;h3 id=&quot;block的循环应用问题&quot;&gt;Block的循环应用问题&lt;/h3&gt;

&lt;p&gt;传送门：&lt;a href=&quot;https://www.jianshu.com/p/fc2f4d207d25&quot;&gt; 实际开发中-Block导致循环引用的问题(ARC环境下)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;考题：MRC 下，block的循环引用如何解决呢？&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;方案1：unsafe_unretained&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;MRC下，没有__weak，所以只能用_unsafe_unretained指针，原理和 weak 一样(ARC环境下不推荐使用，可能导致野指针，推荐使用weak)&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;__unsafe_unretained MNObject *weakSelf = self;
self.block = [^{
    NSLog(@&quot;----------%p&quot;,weakSelf);
}copy];
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;方案2： __block&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;__block self;
self.block = [^{
    NSLog(@&quot;----------%p&quot;,self);
}copy];
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;why? 上面关于 __block的总结&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;特别注意！上述条件仅在ARC环境下生效，如果是MRC环境下，block不会对内部auto变量产生强引用！(MRC下不会进行retain操作)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/16fd041c3c2e27cf?raw=true&quot; alt=&quot;image-20190309224535679&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;方案3: 手动在block函数内将对象制空，并且必须手动保证block调用&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;MNObject *obj = [[MNObject alloc]init];
__unsafe_unretained MNObject *weakObj = obj;
obj.block = [^{
    NSLog(@&quot;----------%p&quot;,obj);
    obj = nil;
}copy];

obj.block();
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/16fd041c5c94ae28?raw=true&quot; alt=&quot;image-20190309225056495&quot; /&gt;&lt;/p&gt;

&lt;p&gt;但是这个一定要注意，block必须调用，因为对象指针的清空操作，是写在block函数中的，如果没调用block，循环引用问题还是会存在，所以不推荐使用。&lt;/p&gt;

&lt;p&gt;实际开发中，循环引用的检测工具推荐，facebook开源的 &lt;a href=&quot;https://github.com/facebook/FBRetainCycleDetector&quot;&gt;FBRetainCycleDetector&lt;/a&gt;，用过的都说好~&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h4 id=&quot;话外篇补充---block-和-delegate使用场景&quot;&gt;话外篇补充 - Block 和 delegate使用场景&lt;/h4&gt;

&lt;p&gt;&lt;em&gt;个人愚见&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;直接异步返回的，可以用block，比如网络请求，无需其他人工动作触发的&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;如果是需要类似点击才能触发的，比如 Button的点击事件，可以用 delegate&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;老实说，block其实非常难，能考得特别深，本文也只是简单探究&amp;amp;总结下中级iOS常见的block考题，以及对Block底层的初步探究，如果是像我所在的三线城市，去面试那种非一线公司的话，如果能掌握本文，可能block相关的题目能答个八九不离十吧！&lt;em&gt;(可能题目会变换组合，但是万变不离其宗)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;block的文章其实很多，但是如果要真的深入理解，还是得动手，这里推荐初中级iOSer可以跟着本文的思路，一步一步跟着探究试试，本文只是起个抛砖引玉的作用&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;友情演出:&lt;a href=&quot;https://github.com/CoderMJLee&quot;&gt;小马哥MJ&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;参考资料&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;&quot;&gt;Objective-C 高级编程 iOS与OS X多线程和内存管理&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.jianshu.com/p/fc2f4d207d25&quot;&gt;实际开发中-Block导致循环引用的问题(ARC环境下)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://blog.sunnyxx.com/2015/07/04/ios-interview/&quot;&gt;招聘一个靠谱的 iOS&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/ChenYilong/iOSInterviewQuestions/blob/master/01%E3%80%8A%E6%8B%9B%E8%81%98%E4%B8%80%E4%B8%AA%E9%9D%A0%E8%B0%B1%E7%9A%84iOS%E3%80%8B%E9%9D%A2%E8%AF%95%E9%A2%98%E5%8F%82%E8%80%83%E7%AD%94%E6%A1%88/%E3%80%8A%E6%8B%9B%E8%81%98%E4%B8%80%E4%B8%AA%E9%9D%A0%E8%B0%B1%E7%9A%84iOS%E3%80%8B%E9%9D%A2%E8%AF%95%E9%A2%98%E5%8F%82%E8%80%83%E7%AD%94%E6%A1%88%EF%BC%88%E4%B8%8B%EF%BC%89.md#45-addobserverforkeypathoptionscontext%E5%90%84%E4%B8%AA%E5%8F%82%E6%95%B0%E7%9A%84%E4%BD%9C%E7%94%A8%E5%88%86%E5%88%AB%E6%98%AF%E4%BB%80%E4%B9%88observer%E4%B8%AD%E9%9C%80%E8%A6%81%E5%AE%9E%E7%8E%B0%E5%93%AA%E4%B8%AA%E6%96%B9%E6%B3%95%E6%89%8D%E8%83%BD%E8%8E%B7%E5%BE%97kvo%E5%9B%9E%E8%B0%83&quot;&gt;ChenYilong/iOSInterviewQuestions&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Wed, 27 Feb 2019 20:00:00 +0800</pubDate>
        <link>https://minilv.github.io/2019/02/27/BlockFile/</link>
        <guid isPermaLink="true">https://minilv.github.io/2019/02/27/BlockFile/</guid>
        
        <category>面试</category>
        
        
      </item>
    
      <item>
        <title>面试驱动技术 - Category 相关考点</title>
        <description>&lt;blockquote&gt;
  &lt;p&gt;面试驱动技术合集（初中级iOS开发），关注仓库，及时获取更新 &lt;a href=&quot;https://github.com/miniLV/Interview-series&quot;&gt;Interview-series&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/1693261e7f1da012?raw=true&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Category 相关的问题一般初中级问的比较多，一般最深的就问到关联对象，本文把比较常见的 Category 的问题都罗列解决了一下，如果还有其他常见的 Category 的试题欢迎补充~&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h2 id=&quot;i-category&quot;&gt;I. Category&lt;/h2&gt;

&lt;h3 id=&quot;category相关面试题&quot;&gt;Category相关面试题&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Category实现原理？&lt;/li&gt;
  &lt;li&gt;实际开发中，你用Category做了哪些事？&lt;/li&gt;
  &lt;li&gt;Category能否添加成员变量，如果可以，如何添加？&lt;/li&gt;
  &lt;li&gt;load 、initialize方法的区别是什么，他们在category中的调用顺序？以及出现继承时他们之间的调用过程？&lt;/li&gt;
  &lt;li&gt;Category 和 Class Extension的区别是什么？&lt;/li&gt;
  &lt;li&gt;为什么分类会“覆盖”宿主类的方法？&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;1category的特点&quot;&gt;1.Category的特点&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;运行时决议
    &lt;ul&gt;
      &lt;li&gt;通过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;runtime&lt;/code&gt; 动态将分类的方法合并到类对象、元类对象中&lt;/li&gt;
      &lt;li&gt;实例方法合并到类对象中，类方法合并到元类对象中&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;可以为系统类添加分类&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;2分类中可以添加哪些内容&quot;&gt;2.分类中可以添加哪些内容&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;实例方法&lt;/li&gt;
  &lt;li&gt;类方法&lt;/li&gt;
  &lt;li&gt;协议&lt;/li&gt;
  &lt;li&gt;属性&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;分类中原理解析&quot;&gt;分类中原理解析&lt;/h3&gt;

&lt;p&gt;使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc MNPerson+Test.m&lt;/code&gt; 函数，生产一个cpp文件,窥探其底层结构(编译状态)&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;struct _category_t {
    //宿主类名称 - 这里的MNPerson
    const char *name;
    
    //宿主类对象,里面有isa
    struct _class_t *cls;
    
    //实例方法列表
    const struct _method_list_t *instance_methods;
    
    //类方法列表
    const struct _method_list_t *class_methods;
    
    //协议列表
    const struct _protocol_list_t *protocols;
    
    //属性列表
    const struct _prop_list_t *properties;
};

//_class_t 结构
struct _class_t {
    struct _class_t *isa;
    struct _class_t *superclass;
    void *cache;
    void *vtable;
    struct _class_ro_t *ro;
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;每个分类都是独立的&lt;/li&gt;
  &lt;li&gt;每个分类的结构都一致，都是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;category_t&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;函数转换&quot;&gt;函数转换&lt;/h4&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@implementation MNPerson (Test)

- (void)test{
    NSLog(@&quot;test - rua~&quot;);
}

@end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/1692a2b47788e5c7?raw=true&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;static void 
attachCategories(Class cls, category_list *cats, bool flush_caches)
{
    if (!cats) return;
    if (PrintReplacedMethods) printReplacements(cls, cats);

    bool isMeta = cls-&amp;gt;isMetaClass();

    // fixme rearrange to remove these intermediate allocations
    
    /* 二维数组( **mlists =&amp;gt; 两颗星星，一个)
     [
        [method_t,],
        [method_t,method_t],
        [method_t,method_t,method_t],
     ]
     
     */
    method_list_t **mlists = (method_list_t **)
        malloc(cats-&amp;gt;count * sizeof(*mlists));
    property_list_t **proplists = (property_list_t **)
        malloc(cats-&amp;gt;count * sizeof(*proplists));
    protocol_list_t **protolists = (protocol_list_t **)
        malloc(cats-&amp;gt;count * sizeof(*protolists));

    // Count backwards through cats to get newest categories first
    int mcount = 0;
    int propcount = 0;
    int protocount = 0;
    int i = cats-&amp;gt;count;//宿主类，分类的总数
    bool fromBundle = NO;
    while (i--) {//倒序遍历，最先访问最后编译的分类
        
        // 获取某一个分类
        auto&amp;amp; entry = cats-&amp;gt;list[i];

        // 分类的方法列表
        method_list_t *mlist = entry.cat-&amp;gt;methodsForMeta(isMeta);
        if (mlist) {
            //最后编译的分类，最先添加到分类数组中
            mlists[mcount++] = mlist;
            fromBundle |= entry.hi-&amp;gt;isBundle();
        }

        property_list_t *proplist = 
            entry.cat-&amp;gt;propertiesForMeta(isMeta, entry.hi);
        if (proplist) {
            proplists[propcount++] = proplist;
        }

        protocol_list_t *protolist = entry.cat-&amp;gt;protocols;
        if (protolist) {
            protolists[protocount++] = protolist;
        }
    }

    auto rw = cls-&amp;gt;data();

    prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
    
    // 核心：将所有分类的对象方法，附加到类对象的方法列表中
    rw-&amp;gt;methods.attachLists(mlists, mcount);
    free(mlists);
    if (flush_caches  &amp;amp;&amp;amp;  mcount &amp;gt; 0) flushCaches(cls);

    rw-&amp;gt;properties.attachLists(proplists, propcount);
    free(proplists);

    rw-&amp;gt;protocols.attachLists(protolists, protocount);
    free(protolists);
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;void attachLists(List* const * addedLists, uint32_t addedCount) {
    if (addedCount == 0) return;
    
    if (hasArray()) {
        // many lists -&amp;gt; many lists
        uint32_t oldCount = array()-&amp;gt;count;
        uint32_t newCount = oldCount + addedCount;
        
        //realloc - 重新分配内存 - 扩容了
        setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
        array()-&amp;gt;count = newCount;
        
        //memmove,内存挪动
        //array()-&amp;gt;lists 原来的方法列表
        memmove(array()-&amp;gt;lists + addedCount,
                array()-&amp;gt;lists,
                oldCount * sizeof(array()-&amp;gt;lists[0]));
        
        //memcpy - 将分类的方法列表 copy 到原来的方法列表中
        memcpy(array()-&amp;gt;lists,
               addedLists,
               addedCount * sizeof(array()-&amp;gt;lists[0]));
    }
    ...
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;画图分析就是&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/1692a68348190846?raw=true&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/1692a687869587d1?raw=true&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/1692a688d74c9d22?raw=true&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/1692a68b11df0d39?raw=true&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/1692a68d560570e4?raw=true&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;3实际开发中你用category做了哪些事&quot;&gt;3.实际开发中，你用Category做了哪些事？&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;声明私有方法&lt;/li&gt;
  &lt;li&gt;分解体积庞大的类文件&lt;/li&gt;
  &lt;li&gt;
    &lt;ul&gt;
      &lt;li&gt;把&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Framework&lt;/code&gt;的私有方法公开&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;。。。&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;4category实现原理&quot;&gt;4.Category实现原理？&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;Category编译之后，底层结构是category_t，里面存储着分类的各种信息，包括 对象方法、类方法、属性、协议信息&lt;/li&gt;
  &lt;li&gt;分类的在编译后，方法并不会直接添加到类信息中，而是要在程序运行的时候，通过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;runtime&lt;/code&gt;, 讲Category的数据，&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;5为什么分类会覆盖宿主类的方法&quot;&gt;5.为什么分类会“覆盖”宿主类的方法？&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;其实不是真正的“覆盖”，宿主类的同名方法还是存在&lt;/li&gt;
  &lt;li&gt;分类将附加到类对象的方法列表中，整合的时候，分类的方法优先放到前面&lt;/li&gt;
  &lt;li&gt;OC的函数调用底层走的是msg_send() 函数，它做的是方法查找，因为分类的方法优先放在前面，所以通过选择器查找到分类的方法之后直接调用，宿主类的方法看上去就像被“覆盖”而没有生效&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;6category-和-class-extension的区别是什么&quot;&gt;6.Category 和 Class Extension的区别是什么？&lt;/h4&gt;

&lt;h4 id=&quot;class-extension扩展&quot;&gt;&lt;em&gt;Class Extension(扩展)&lt;/em&gt;&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;声明私有属性&lt;/li&gt;
  &lt;li&gt;声明私有方法&lt;/li&gt;
  &lt;li&gt;声明私有成员变量&lt;/li&gt;
  &lt;li&gt;编译时决议，Category 运行时决议&lt;/li&gt;
  &lt;li&gt;不能为系统类添加扩展&lt;/li&gt;
  &lt;li&gt;只能以声明的形式存在，多数情况下，寄生于宿主类的.m文件中&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;ii-load-initialize&quot;&gt;II. load 、initialize&lt;/h2&gt;

&lt;h3 id=&quot;load实现原理&quot;&gt;load实现原理&lt;/h3&gt;
&lt;blockquote&gt;
  &lt;ul&gt;
    &lt;li&gt;类第一次加载进内存的时候，会调用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;+ load&lt;/code&gt; 方法，无需导入，无需使用&lt;/li&gt;
    &lt;li&gt;每个类、分类的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;+ load&lt;/code&gt; 在程序运行过程中只会执行一次&lt;/li&gt;
    &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;+ load&lt;/code&gt; 走的不是消息发送的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;objc_msgSend&lt;/code&gt; 调用，而是找到 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;+ load&lt;/code&gt; 函数的地址，直接调用&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;void call_load_methods(void)
{
    static bool loading = NO;
    bool more_categories;

    loadMethodLock.assertLocked();

    // Re-entrant calls do nothing; the outermost call will finish the job.
    if (loading) return;
    loading = YES;

    void *pool = objc_autoreleasePoolPush();

    do {
        // 1. Repeatedly call class +loads until there aren’t any more
        while (loadable_classes_used &amp;gt; 0) {
            //先加载宿主类的load方法(按照编译顺序，调用load方法)
            call_class_loads();
        }

        // 2. Call category +loads ONCE
        more_categories = call_category_loads();

        // 3. Run more +loads if there are classes OR more untried categories
    } while (loadable_classes_used &amp;gt; 0  ||  more_categories);

    objc_autoreleasePoolPop(pool);

    loading = NO;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;static void schedule_class_load(Class cls)
{
    if (!cls) return;
    assert(cls-&amp;gt;isRealized());  // _read_images should realize

    if (cls-&amp;gt;data()-&amp;gt;flags &amp;amp; RW_LOADED) return;

    // Ensure superclass-first ordering
    // 递归调用，先将父类添加到load方法列表中，再将自己加进去
    schedule_class_load(cls-&amp;gt;superclass);

    add_class_to_loadable_list(cls);
    cls-&amp;gt;setInfo(RW_LOADED); 
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h4 id=&quot;调用顺序&quot;&gt;调用顺序&lt;/h4&gt;

&lt;ol&gt;
  &lt;li&gt;先调用宿主类的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;+ load&lt;/code&gt; 函数
    &lt;ul&gt;
      &lt;li&gt;按照编译先后顺序调用（先编译，先调用）&lt;/li&gt;
      &lt;li&gt;调用子类的+load之前会先调用父类的+load&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;再调用分类的的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;+ load&lt;/code&gt; 函数
    &lt;ul&gt;
      &lt;li&gt;按照编译先后顺序调用（先编译，先调用）&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;实验证明：宿主类先调用，分类再调用&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;2019-02-27 17:28:00.519862+0800 load-Initialize-Demo[91107:2281575] MNPerson + load
2019-02-27 17:28:00.520032+0800 load-Initialize-Demo[91107:2281575] MNPerson (Play) + load
2019-02-27 17:28:00.520047+0800 load-Initialize-Demo[91107:2281575] MNPerson (Eat) + load
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/1692e5511b580ce9?raw=true&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;2019-02-27 17:39:10.354050+0800 load-Initialize-Demo[91308:2303030] MNDog + load (宿主类1)
2019-02-27 17:39:10.354237+0800 load-Initialize-Demo[91308:2303030] MNPerson + load (宿主类2)
2019-02-27 17:39:10.354252+0800 load-Initialize-Demo[91308:2303030] MNDog (Rua) + load (分类1)
2019-02-27 17:39:10.354263+0800 load-Initialize-Demo[91308:2303030] MNPerson (Play) + load(分类2)
2019-02-27 17:39:10.354274+0800 load-Initialize-Demo[91308:2303030] MNPerson (Eat) + load(分类3)
2019-02-27 17:39:10.354285+0800 load-Initialize-Demo[91308:2303030] MNDog (Run) + load(分类4)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h4 id=&quot;initialize实现原理&quot;&gt;Initialize实现原理&lt;/h4&gt;
&lt;blockquote&gt;
  &lt;ul&gt;
    &lt;li&gt;类第一次接收到消息的时候，会调用该方法，需导入，并使用&lt;/li&gt;
    &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;+ Initialize&lt;/code&gt; 走的是消息发送的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;objc_msgSend&lt;/code&gt; 调用&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h4 id=&quot;initialize题目出现&quot;&gt;Initialize题目出现&lt;/h4&gt;

&lt;div class=&quot;language-objc highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cm&quot;&gt;/*父类*/&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;@interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MNPerson&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;NSObject&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;@end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;@implementation&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MNPerson&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;NSLog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;MNPerson + initialize&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;@end&lt;/span&gt;

&lt;span class=&quot;cm&quot;&gt;/*子类1*/&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;@interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MNTeacher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MNPerson&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;@end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;@implementation&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MNTeacher&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;@end&lt;/span&gt;

&lt;span class=&quot;cm&quot;&gt;/*子类2*/&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;@interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MNStudent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MNPerson&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;@end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;@implementation&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MNStudent&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;@end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;问题出现:以下会输出什么结果?&lt;/p&gt;

&lt;div class=&quot;language-objc highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;argc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[])&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;@autoreleasepool&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MNTeacher&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;alloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MNStudent&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;alloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;结果如下：&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;2019-02-27 17:57:33.305655+0800 load-Initialize-Demo[91661:2331296] MNPerson + initialize
2019-02-27 17:57:33.305950+0800 load-Initialize-Demo[91661:2331296] MNPerson + initialize
2019-02-27 17:57:33.306476+0800 load-Initialize-Demo[91661:2331296] MNPerson + initialize
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;为什么打印了三次？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/169321d806839d28?raw=true&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;原理分析：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;initialize&lt;/code&gt; 在类第一次接收消息的时候会调用，OC里面的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[ xxx ]&lt;/code&gt; 调用都可以看成 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;objc_msgSend&lt;/code&gt;,所以这时候，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[MNTeacher alloc]&lt;/code&gt; 其实内部会调用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[MNTeacher initialize]&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;initialize&lt;/code&gt; 调用的时候，要先实现自己父类的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;initialize&lt;/code&gt; 方法，第一次调用的时候，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MNPerson&lt;/code&gt; 没被使用过，所以未被初始化，要先调用一下父类的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[MNPerson initialize]&lt;/code&gt;,输出第一个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MNPerson + initialize&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MNPerson&lt;/code&gt; 调用了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;initialize&lt;/code&gt; 之后，轮到&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MNTeacher&lt;/code&gt; 类自己了，由于他内部没有实现 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;initialize&lt;/code&gt;方法，所以调用父类的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;initialize&lt;/code&gt;, 输出第二个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MNPerson + initialize&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;然后轮到&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[MNStudent alloc]&lt;/code&gt;，内部也是调用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[MNStudent initialize]&lt;/code&gt;, 然后判断得知 父类&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MNPerson&lt;/code&gt;类调用过&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;initialize&lt;/code&gt;了，因此调用自身的就够了，由于他和&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MNTeacher&lt;/code&gt; 一样，也没实现&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;initialize&lt;/code&gt; 方法，所以同理调用父类的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[MNPerson initialize]&lt;/code&gt;,输出第3个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MNPerson + initialize&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;initialize-与-load-的区别&quot;&gt;initialize 与 load 的区别&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;load 是类第一次加载的时候调用，initialize 是类第一次接收到消息的时候调用，每个类只会initialize一次（父类的initialize方法可能被调用多次）&lt;/li&gt;
  &lt;li&gt;load 和 initialize，加载or调用的时候，都会先调用父类对应的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;load&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;initialize&lt;/code&gt; 方法，再调用自己本身的;&lt;/li&gt;
  &lt;li&gt;load 和 initialize 都是系统自动调用的话，都只会调用一次&lt;/li&gt;
  &lt;li&gt;调用方式也不一样，load 是根据函数地址直接调用，initialize 是通过&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;objc_msgSend&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;调用时刻，load是runtime加载类、分类的时候调用（只会调用一次）&lt;/li&gt;
  &lt;li&gt;调用顺序:
    &lt;ul&gt;
      &lt;li&gt;load:
        &lt;ul&gt;
          &lt;li&gt;先调用类的load
            &lt;ul&gt;
              &lt;li&gt;先编译的类，优先调用load&lt;/li&gt;
              &lt;li&gt;调用子类的load之前，会先调用父类的load&lt;/li&gt;
            &lt;/ul&gt;
          &lt;/li&gt;
          &lt;li&gt;在调用分类的load&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;initialize：
        &lt;ul&gt;
          &lt;li&gt;先初始化父列&lt;/li&gt;
          &lt;li&gt;再初始化子类（可能最终调用的是父类的初始化方法）&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;!-- --&gt;

&lt;div class=&quot;language-objc highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cm&quot;&gt;/*父类*/&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;@interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MNPerson&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;NSObject&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;@end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;@implementation&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MNPerson&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;NSLog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;MNPerson + initialize&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;NSLog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;MNPerson + load&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;@end&lt;/span&gt;

&lt;span class=&quot;cm&quot;&gt;/*子类1*/&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;@interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MNTeacher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MNPerson&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;@end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;@implementation&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MNTeacher&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;NSLog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;MNTeacher + load&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;@end&lt;/span&gt;

&lt;span class=&quot;cm&quot;&gt;/*子类2*/&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;@interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MNStudent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MNPerson&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;@end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;@implementation&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MNStudent&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;NSLog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;MNStudent + load&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;@end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;问题出现:以下会输出什么结果?&lt;/p&gt;

&lt;div class=&quot;language-objc highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;argc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[])&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;@autoreleasepool&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MNTeacher&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;答案出现&quot;&gt;答案出现！！！&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;2019-02-27 18:17:12.034392+0800 load-Initialize-Demo[92064:2370496] MNPerson + load
2019-02-27 18:17:12.034555+0800 load-Initialize-Demo[92064:2370496] MNStudent + load
2019-02-27 18:17:12.034569+0800 load-Initialize-Demo[92064:2370496] MNTeacher + load
2019-02-27 18:17:12.034627+0800 load-Initialize-Demo[92064:2370496] MNPerson + initialize
2019-02-27 18:17:12.034645+0800 load-Initialize-Demo[92064:2370496] MNPerson + initialize
2019-02-27 18:17:12.034658+0800 load-Initialize-Demo[92064:2370496] MNTeacher + load
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;输出比预期多了不少，为什么连 load 也出现了？&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/169321e995812c15?raw=true&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;解释：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;前三个 load 很好理解，程序一运行，runtime 直接将全部的类加载到内存中，最先输出；&lt;/li&gt;
  &lt;li&gt;第一个 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MNPerson + initialize&lt;/code&gt;，因为是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MNTeacher&lt;/code&gt;的调用，所以会先让父类&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MNPerson&lt;/code&gt; 调用一次&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;initialize&lt;/code&gt;，输出第一个 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MNPerson + initialize&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;第二个 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MNPerson + initialize&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MNTeacher&lt;/code&gt; 自身调用，由于他自己没有实现 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;initialize&lt;/code&gt;, 调用父类的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;initialize&lt;/code&gt;， 输出第二个 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MNPerson + initialize&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;最后一个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MNTeacher + load&lt;/code&gt;可能其实有点奇怪，不是说 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;load&lt;/code&gt;只会加载一次吗，而且他还不走 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;objc_msgSend&lt;/code&gt; 吗，怎么还能调用这个方法？
    &lt;ul&gt;
      &lt;li&gt;因为！当类第一次加载进内存的时候，调用的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;load&lt;/code&gt; 方法是系统调的，这时候不走 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;objc_msgSend&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;但是，你现在是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[MNTeacher load]&lt;/code&gt;啊，这个就是objc_msgSend(MNTeacher,@selector(MNTeacher))，这就跑到&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MNTeacher + load&lt;/code&gt;里了！&lt;/li&gt;
      &lt;li&gt;只是一般没人手动调用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;load&lt;/code&gt; 函数，但是，还是可以调用的！&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h2 id=&quot;iii-关联对象associatedobject&quot;&gt;III. 关联对象AssociatedObject&lt;/h2&gt;

&lt;h3 id=&quot;category能否添加成员变量如果可以如何添加&quot;&gt;Category能否添加成员变量，如果可以，如何添加？&lt;/h3&gt;
&lt;blockquote&gt;
  &lt;p&gt;这道题实际上考的就是关联对象&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;如果是普通类声明生命属性的话&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@interface MNPerson : NSObject

@property (nonatomic, copy)NSString *property;

@end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;上述代码系统内部会自动三件事：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;帮我们生成一个生成变量_property&lt;/li&gt;
  &lt;li&gt;生成一个 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;get&lt;/code&gt; 方法 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;- (NSString *)property&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;生成一个 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;set&lt;/code&gt; 方法 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;- (void)setProperty:(NSString *)property&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-objc highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;@implementation&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MNPerson&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;NSString&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_property&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;setProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NSString&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_property&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NSString&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_property&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;@end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;分类也是可以添加属性的 - 类结构里面，有个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;properties&lt;/code&gt; 列表，里面就是
存放属性的;&lt;/p&gt;

&lt;p&gt;分类里面，生成属性，只会生成方法的声明，不会生成成员变量 &amp;amp;&amp;amp; 方法实现！&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/1692f73fd3b74f8d?raw=true&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;翻译：实例变量不能放在分类中&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;所以：&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;不能直接给category 添加成员变量，但是可以间接实现分类有成员变量的效果(效果上感觉像成员变量)&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@interface MNPerson (Test)

@property (nonatomic, assign) NSInteger age;

@end

@implementation MNPerson (Test)

@end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/1692f88838824ba5?raw=true&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;person.age = 10&lt;/code&gt;等价于 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[person setAge:10]&lt;/code&gt;，所以证明了，给分类声明属性之后，并没有添加其对应的实现！&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h3 id=&quot;关联对象&quot;&gt;关联对象&lt;/h3&gt;

&lt;p&gt;objc_setAssociatedObject Api&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;objc_setAssociatedObject(    &amp;lt;#id  _Nonnull object#&amp;gt;, (对象)
                             &amp;lt;#const void * _Nonnull key#&amp;gt;,(key)
                             &amp;lt;#id  _Nullable value#&amp;gt;,(关联的值)
                             &amp;lt;#objc_AssociationPolicy policy#&amp;gt;)(关联策略)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;关联策略，等价于属性声明&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
    OBJC_ASSOCIATION_ASSIGN = 0,          
    OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, 
    OBJC_ASSOCIATION_COPY_NONATOMIC = 3,  
    OBJC_ASSOCIATION_RETAIN = 01401,      
    OBJC_ASSOCIATION_COPY = 01403         
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/1692f9182dcd9a6f?raw=true&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;比如这里的age属性，默认声明是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@property (nonatomic, assign) NSInteger age;&lt;/code&gt;，就是 assign，所以这里选择&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OBJC_ASSOCIATION_ASSIGN&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;取值&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;objc_getAssociatedObject(&amp;lt;#id  _Nonnull object#&amp;gt;, &amp;lt;#const void * _Nonnull key#&amp;gt;)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;面试题---以下代码输出的结果是啥&quot;&gt;面试题 - 以下代码输出的结果是啥&lt;/h3&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;int main(int argc, const char * argv[]) {
    @autoreleasepool {

        MNPerson *person = [[MNPerson alloc]init];

        {
            MNPerson *test = [[MNPerson alloc]init];
            objc_setAssociatedObject(person,
                                     @&quot;test&quot;,
                                     test,
                                     OBJC_ASSOCIATION_ASSIGN);
        }
        
        NSLog(@&quot;%@&quot;,objc_getAssociatedObject(person, @&quot;test&quot;));
    }
    return 0;
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/1692fb5b60a63547?raw=true&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;原因，关联的对象是person，关联的value是 test，test变量 出了他们的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{}&lt;/code&gt; 作用域之后，就会销毁;
此时通过key 找到 对应的对象，访问对象内部的value，因为test变量已经销毁了，所以程序崩溃了，这也说明了 =&amp;gt; &lt;strong&gt;内部 test 对 value是强引用！&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;关联对象的本质&quot;&gt;关联对象的本质&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;在分类中，因为类的实例变量的布局已经固定，使用 @property 已经无法向固定的布局中添加新的实例变量（这样做可能会覆盖子类的实例变量），所以我们需要使用关联对象以及两个方法来模拟构成属性的三个要素。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;引用自 &lt;a href=&quot;https://draveness.me/ao&quot;&gt;关联对象 AssociatedObject 完全解析&lt;/a&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;关联对象的原理&quot;&gt;关联对象的原理&lt;/h3&gt;
&lt;p&gt;实现关联对象技术的核心对象有&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;AssociationsManager&lt;/li&gt;
  &lt;li&gt;AssociationsHashMap&lt;/li&gt;
  &lt;li&gt;ObjectAssociationMap&lt;/li&gt;
  &lt;li&gt;ObjcAssociation&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;class AssociationsManager {
    static spinlock_t _lock;//自旋锁，保证线程安全
    static AssociationsHashMap *_map;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;class AssociationsHashMap : public unordered_map&amp;lt;disguised_ptr_t, ObjectAssociationMap&amp;gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;class ObjectAssociationMap : public std::map&amp;lt;void *, ObjcAssociation&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;class ObjcAssociation {
    uintptr_t _policy;
    id _value;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以关联对象代码为例:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  objc_setAssociatedObject(obj, @selector(key), @&quot;hello world&quot;, OBJC_ASSOCIATION_COPY_NONATOMIC);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/16931f33935797e0?raw=true&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;关联对象并不是存储在被关联对象本身的内存中的&lt;/li&gt;
  &lt;li&gt;关联对象，存储在全局的一个统一的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AssociationsManager&lt;/code&gt;中&lt;/li&gt;
  &lt;li&gt;关联对象其实就是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ObjcAssociation&lt;/code&gt; 对象,关联的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;value&lt;/code&gt; 就放在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ObjcAssociation&lt;/code&gt; 内&lt;/li&gt;
  &lt;li&gt;关联对象由 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AssociationsManager&lt;/code&gt; 管理并在  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AssociationsHashMap&lt;/code&gt; 存储&lt;/li&gt;
  &lt;li&gt;对象的指针以及其对应 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ObjectAssociationMap&lt;/code&gt; 以键值对的形式存储在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AssociationsHashMap&lt;/code&gt; 中&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ObjectAssociationMap&lt;/code&gt; 则是用于存储关联对象的数据结构&lt;/li&gt;
  &lt;li&gt;每一个对象都有一个标记位 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;has_assoc&lt;/code&gt; 指示对象是否含有关联对象&lt;/li&gt;
  &lt;li&gt;存储在全局的一个统一的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AssociationsManager&lt;/code&gt; 内部有一持有一个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_lock&lt;/code&gt;，他其实是一个spinlock_t(自旋锁),用来保证&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AssociationsHashMap&lt;/code&gt;操作的时候，是线程安全的&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Category&lt;/code&gt; 相关的问题一般初中级问的比较多，一般最深的就问到&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;关联对象&lt;/code&gt;，上面的问题以及解答已经把比较常见的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Category&lt;/code&gt; 的问题都罗列解决了一下，如果还有其他常见的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Category&lt;/code&gt; 的试题欢迎补充~&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;本文基于 &lt;a href=&quot;https://github.com/CoderMJLee&quot;&gt;MJ老师&lt;/a&gt; 的基础知识之上，结合了包括 &lt;a href=&quot;https://github.com/draveness&quot;&gt;draveness&lt;/a&gt; 在内的一系列大神的文章总结的，如果不当之处，欢迎讨论~&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;友情演出:&lt;a href=&quot;https://github.com/CoderMJLee&quot;&gt;小马哥MJ&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;参考资料:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://draveness.me/ao&quot;&gt;关联对象 AssociatedObject 完全解析&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://nshipster.com/associated-objects/&quot;&gt;associated-objects&lt;/a&gt;&lt;/p&gt;

</description>
        <pubDate>Wed, 27 Feb 2019 20:00:00 +0800</pubDate>
        <link>https://minilv.github.io/2019/02/27/category/</link>
        <guid isPermaLink="true">https://minilv.github.io/2019/02/27/category/</guid>
        
        <category>面试</category>
        
        
      </item>
    
      <item>
        <title>iOS：添加一个全局悬浮按钮</title>
        <description>&lt;p&gt;在多设备测试和联调场景里，最常见的问题不是“有没有问题”，而是“当前这台设备跑的到底是哪一个包”。如果不能快速确认包体对应的环境、版本和 Build 信息，排查过程就很容易绕远路。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/168a2d6d5de66c8d?raw=true&quot; alt=&quot;made in 小蠢驴的配图&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这篇文章要解决的就是这个问题：做一个全局悬浮按钮，在应用任意页面都能直接看到当前环境，并按需补充版本号和 Build 信息。&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/168a3021b7941302?raw=true&quot; alt=&quot;需求设计图.png&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;思路&quot;&gt;思路：&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;既然需要全局展示，就应该把控件加在最上层，也就是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UIWindow&lt;/code&gt; 上。&lt;/li&gt;
  &lt;li&gt;需求里既有文字也有背景图，用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UIButton&lt;/code&gt; 来承载会更直接。&lt;/li&gt;
  &lt;li&gt;按钮会遮挡部分界面内容，因此必须支持拖动，交互上可以通过手势来处理。&lt;/li&gt;
  &lt;li&gt;整体实现并不复杂，本质上就是创建一个可拖拽的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UIButton&lt;/code&gt;，再把它挂到 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UIWindow&lt;/code&gt; 上。&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h4 id=&quot;知识1按钮显示-2-行文字&quot;&gt;知识1：按钮显示 2 行文字&lt;/h4&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;// UIButton 支持多行标题显示
button.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;

// title 中插入 &quot;\n&quot; 即可换行
title = @&quot;123\n666&quot;;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;知识2获取-version-和-build-号&quot;&gt;知识2：获取 Version 和 Build 号&lt;/h4&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;NSString *versionStr = [[[NSBundle
       mainBundle]infoDictionary]valueForKey:@&quot;CFBundleShortVersionString&quot;];
NSString *buildStr = [[[NSBundle
       mainBundle]infoDictionary]valueForKey:@&quot;CFBundleVersion&quot;];
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/168a2d6d5e1e9cdc?raw=true&quot; alt=&quot;image.png&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;知识3拖动的本质是更新-frameorigin&quot;&gt;知识3：拖动的本质是更新 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;frame.origin&lt;/code&gt;&lt;/h4&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;// 拖动时更新控件的 x 坐标
- (CGRect)changeXWithFrame:(CGRect)originalFrame point:(CGPoint)point{
    BOOL q1 = originalFrame.origin.x &amp;gt;= 0;
    BOOL q2 = originalFrame.origin.x + originalFrame.size.width &amp;lt;= screenW;
    
    if (q1 &amp;amp;&amp;amp; q2) {
        originalFrame.origin.x += point.x;
    }
    return originalFrame;
}

// 拖动时更新控件的 y 坐标
- (CGRect)changeYWithFrame:(CGRect)originalFrame point:(CGPoint)point{
    
    BOOL q1 = originalFrame.origin.y &amp;gt;= 0;
    BOOL q2 = originalFrame.origin.y + originalFrame.size.height &amp;lt;= screenH;
    if (q1 &amp;amp;&amp;amp; q2) {
        originalFrame.origin.y += point.y;
    }
    return originalFrame;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;知识4越界处理与回弹&quot;&gt;知识4：越界处理与回弹&lt;/h4&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;// 记录按钮是否越界
        BOOL isOver = NO;
        if (frame.origin.x &amp;lt; 0) {
            frame.origin.x = 0;
            isOver = YES;
            
        } else if (frame.origin.x + frame.size.width &amp;gt; screenW) {
            frame.origin.x = screenW - frame.size.width;
            isOver = YES;
        }

        if (frame.origin.y &amp;lt; 0) {
            frame.origin.y = 0;
            isOver = YES;
            
        } else if (frame.origin.y+frame.size.height &amp;gt; screenH) {
            frame.origin.y = screenH - frame.size.height;
            isOver = YES;
        }
        
        if (isOver) {
            // 如果越界，则回弹到可见区域
            [UIView animateWithDuration:0.3 animations:^{
                self.frame = frame;
            }];
        }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;知识5支持不同方向的拖动限制&quot;&gt;知识5：支持不同方向的拖动限制&lt;/h4&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;MNAssistiveTouchTypeNone = 0,         // 不限制方向，可自由拖动
MNAssistiveTouchTypeVerticalScroll,   // 只能垂直移动
MNAssistiveTouchTypeHorizontalScroll, // 只能水平移动
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  switch (type) {
        case MNAssistiveTouchTypeNone:
        {
            水平方向坐标 ++；
            竖直方向坐标 ++；
            break;
        }case MNAssistiveTouchTypeHorizontalScroll:{
            水平方向坐标 ++；
            break;
        }
        case MNAssistiveTouchTypeVerticalScroll:{
            竖直方向坐标 ++；
            break;
        }
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;最终效果&quot;&gt;最终效果&lt;/h4&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/miniLV/github_images_miniLV/blob/master/juejin/168a2d80147f8435?raw=true&quot; alt=&quot;demo.gif&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;集成与使用&quot;&gt;集成与使用&lt;/h2&gt;

&lt;p&gt;通过 CocoaPods 引入：&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;pod&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;MNFloatBtn&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;导入头文件后，就可以直接使用：&lt;/p&gt;

&lt;div class=&quot;language-objc highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;#import &amp;lt;MNFloatBtn/MNFloatBtn.h&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果希望悬浮按钮在任何环境下都显示，可以直接调用：&lt;/p&gt;

&lt;div class=&quot;language-objc highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MNFloatBtn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;更推荐在 Debug 环境中显示，避免影响线上包：&lt;/p&gt;

&lt;div class=&quot;language-objc highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MNFloatBtn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;showDebugModeWithType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MNAssistiveTypeNone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;不再需要时，可以移除悬浮按钮：&lt;/p&gt;

&lt;div class=&quot;language-objc highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MNFloatBtn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;hidden&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;也可以为按钮补充点击事件：&lt;/p&gt;

&lt;div class=&quot;language-objc highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MNFloatBtn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;sharedBtn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;btnClick&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UIButton&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;NSLog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;btn.btnClick ~&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果希望按钮上默认展示当前构建日期，可以这样设置：&lt;/p&gt;

&lt;div class=&quot;language-objc highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MNFloatBtn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;sharedBtn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;setBuildShowDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;YES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果项目区分测试、开发、生产等 API 环境，也可以直接配置环境映射：&lt;/p&gt;

&lt;div class=&quot;language-objc highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;#define kAddress            @&quot;testapi.miniLV.com&quot;
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;//#define kAddress            @&quot;devapi.miniLV.com&quot;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//#define kAddress            @&quot;api.miniLV.com&quot;&lt;/span&gt;
    
&lt;span class=&quot;c1&quot;&gt;// 自定义不同 Host 对应的展示文案&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;NSDictionary&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;envMap&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;@{&lt;/span&gt;
                         &lt;span class=&quot;s&quot;&gt;@&quot;测试&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;testapi.miniLV.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                         &lt;span class=&quot;s&quot;&gt;@&quot;开发&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;devapi.miniLV.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                         &lt;span class=&quot;s&quot;&gt;@&quot;生产&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;api.miniLV.com&quot;&lt;/span&gt;
                         &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
                             
&lt;span class=&quot;c1&quot;&gt;// 设置当前 Host，并展示对应的环境标识&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MNFloatBtn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;sharedBtn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;setEnvironmentMap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;envMap&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;currentEnv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kAddress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; 
    
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/miniLV/MNFloatBtn&quot;&gt;demo地址&lt;/a&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;喜欢的可以给个star，不胜感激~&lt;/em&gt;&lt;/p&gt;
</description>
        <pubDate>Thu, 31 Jan 2019 00:00:00 +0800</pubDate>
        <link>https://minilv.github.io/2019/01/31/MNFloatBtn/</link>
        <guid isPermaLink="true">https://minilv.github.io/2019/01/31/MNFloatBtn/</guid>
        
        <category>工具</category>
        
        
      </item>
    
  </channel>
</rss>
