-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathatom.xml
More file actions
142 lines (79 loc) · 47.7 KB
/
Copy pathatom.xml
File metadata and controls
142 lines (79 loc) · 47.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>欢迎光临</title>
<subtitle>多多的技术博客</subtitle>
<link href="/atom.xml" rel="self"/>
<link href="http://yoursite.com/"/>
<updated>2017-09-19T10:19:23.000Z</updated>
<id>http://yoursite.com/</id>
<author>
<name>Rick Wong</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>关于iOS动画那些事儿:什么是图层树</title>
<link href="http://yoursite.com/2017/09/20/%E5%85%B3%E4%BA%8EiOS%E5%8A%A8%E7%94%BB%E9%82%A3%E4%BA%9B%E4%BA%8B%E5%84%BF-%E4%BB%80%E4%B9%88%E6%98%AF%E5%9B%BE%E5%B1%82%E6%A0%91/"/>
<id>http://yoursite.com/2017/09/20/关于iOS动画那些事儿-什么是图层树/</id>
<published>2017-09-19T17:45:02.000Z</published>
<updated>2017-09-19T10:19:23.000Z</updated>
<content type="html"><![CDATA[<h2 id="概览"><a href="#概览" class="headerlink" title="概览"></a>概览</h2><p>Core Animation不止是用来做动画的。它实际上是从一个叫做Layer Kit演变而来,做动画只是其冰山一角。</p><p>Core Animation是一个复合引擎,它是尽可能快地组合屏幕上不同的可视内容,这些内容是被分解成独立的图层,存储在图层树体系之中。图层树形成了UIKit及其在iOS应用程序中所能在屏幕上看见的一切基础。</p><p>本系列文章将从图层树开始,讲述Core Animation的静态组合及布局特性。</p><h2 id="图层与视图"><a href="#图层与视图" class="headerlink" title="图层与视图"></a>图层与视图</h2>]]></content>
<summary type="html">
<h2 id="概览"><a href="#概览" class="headerlink" title="概览"></a>概览</h2><p>Core Animation不止是用来做动画的。它实际上是从一个叫做Layer Kit演变而来,做动画只是其冰山一角。</p>
<p>Cor
</summary>
<category term="CoreAnimation" scheme="http://yoursite.com/categories/CoreAnimation/"/>
<category term="图层树" scheme="http://yoursite.com/tags/%E5%9B%BE%E5%B1%82%E6%A0%91/"/>
</entry>
<entry>
<title>关于Debug的那些事儿:浅谈一些LLDB调试命令</title>
<link href="http://yoursite.com/2017/09/11/%E5%85%B3%E4%BA%8EXcode%E7%9A%84%E9%82%A3%E4%BA%9B%E4%BA%8B%E5%84%BF-%E6%B5%85%E8%B0%88%E4%B8%80%E4%BA%9BLLDB%E8%B0%83%E8%AF%95%E5%91%BD%E4%BB%A4/"/>
<id>http://yoursite.com/2017/09/11/关于Xcode的那些事儿-浅谈一些LLDB调试命令/</id>
<published>2017-09-10T16:17:40.000Z</published>
<updated>2017-09-20T06:54:13.000Z</updated>
<content type="html"><![CDATA[<h2 id="概览"><a href="#概览" class="headerlink" title="概览"></a>概览</h2><p>我们平时开发调试输出信息除了<code>printf</code>或者<code>NSLog</code>以外,还可使用<code>LLDB</code>来更便利的调试。</p><p><code>LLDB</code>是<code>LLVM</code>下的调试器。<code>Xcode4.0</code>开始,编译器开始改用<code>LLVM</code>,相应的调试器也从<code>gdb</code>改为<code>LLDB</code>。<code>Xcode5.0</code>以后所有工程均自动设置为使用<code>LLDB</code>。</p><p>那么日常开发中常用的<code>LLDB</code>命令和使用技巧有哪些呢?</p><h2 id="常用命令"><a href="#常用命令" class="headerlink" title="常用命令"></a>常用命令</h2><p>使用场景:程序断点的输出<code>log</code>处</p><h3 id="p-po"><a href="#p-po" class="headerlink" title="p / po"></a>p / po</h3><p><code>p</code>用来输出基本数据类型,<code>po</code>用于输出<code>Objective-C</code>对象。<br>如下可输出<code>view</code>的基本信息:</p><pre><code>po [self view] </code></pre><p>而不是 <code>po self.view</code>,<code>Xcode</code>默认不支持后者输出,需要额外设置,详见:下文4.常见问题之4.2. 找不到方法:unsupported expression with unknown type 以及 <a href="http://www.jianshu.com/p/aaf8523f6ebf" target="_blank" rel="external">让Xcode的控制台支持<code>po frame</code>等的打印</a></p><ul><li><p>输出<code>view</code>下<code>subview</code>的数量。由于<code>subview</code>的数量是一个<code>int</code>类型的值,所以可使用命令<code>p</code>:</p><pre><code>p (int)[[[self view] subviews] count]</code></pre><p>最后看到输出如下:</p><pre><code>(int) $2 = 2;</code></pre><p> 输出信息中为何带有<code>$1、$2</code>的字样?<br> 因为每次查询的结果都会保存在一些持续变量中<code>($[0-9]+)</code>,这样既可在后面查询中直接使用这些值。如下,可以重新取回<code>$1</code>的值:</p><pre><code>po $1(UIView *) $1 = 0x0824c800 <UITableView:0x824c800; frame = (0 20; 768 1004); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x74c3010>; layer = <CALayer: 0x74c2710>; contentOffset: {0, 0}></code></pre><p> 如此依然可以取到之前[<code>self view]</code>的值。</p></li></ul><h3 id="expr-运行时修改变量值"><a href="#expr-运行时修改变量值" class="headerlink" title="expr : 运行时修改变量值"></a>expr : 运行时修改变量值</h3><p>可在调试时动态执行指定表达式,并将结果打印出来。用于在调试过程中修改变量的值。</p><ul><li><p>用法</p><ul><li><p>程序运行至断点处中断时,改变变量的值。</p><p> (断点前设置的变量如下:)</p><pre><code>int a = 1;NSLog(@"实际值:%d", a);</code></pre><p> 输入如下命令:</p><pre><code>expr a = 2;</code></pre><p> 会看到如下输出:</p><pre><code>(int) $0 = 2;</code></pre><p> 继续运行程序,程序输出信息如下:</p><pre><code>实际值:2</code></pre><p> 如上可以看出,变量a的值被改变了。</p></li><li><p>新声明一个变量对象</p><p> 如下所示:</p><pre><code>expr int $b=2</code></pre><p> 输出新声明对象的值。(<code>PS</code>: 对象名前要加$)</p><pre><code>p $b</code></pre></li><li><p>可以仅编辑断点,让它自动填充需要修改的变量值,并且可选择在此断点处不进入断点模式,仅修改指定变量的值,然后自动执行后续代码(不停留断点)。</p><ul><li><p>方法:<br>右击断点选择“<code>Edit Breadpoint...</code>”(或按住<code>cmd+option</code>,单击断点)如图所示:</p><p><img src="/images/makeBreakPoint.png" alt="makeBreakPoint"></p><p> 注意选中最后一行“<code>Automatically continue after evaluating</code>“的选择框,保证运行到此断点时填充变量的值,然后继续运行,不会停留在此断点处保持调试模式。<br> 运行程序,会得到如图所示两次设置变量的值的输出。<br> 禁用断点以后,重新运行程序则上述设置的变量值在运行时则不会被改变了。</p></li></ul></li><li><p>设置断点触发条件</p><p>可设置断点的触摸生效条件,即可在运行时针对特定的数据进行分析,观察程序是否正常运行。如下图所示:</p><p><img src="/images/triggerBreadPoint.png" alt="triggerBreadPoint"></p><p>上述截图中可看到如下语句:</p><pre><code>(BOOL)[(NSString*)[item valueForKey:@"ID"] isEqualToString:@"93306"]</code></pre><p>通过此语句告诉编译器,当符合条件时,此断点才会生效,进入断点调试模式。</p></li><li><p>格式化输出数据</p><p>替代<code>NSLog</code>输出方式,可在编辑断点使其输出格式化字符串像平时编码一样。<br>平时编码可能会使用<code>[NSString stringWithFormat:]</code>输出格式化字符串,不过在断点调试中无效果,需要使用<code>alloc/init</code>形式,如下所示:</p><pre><code>po [[NSString alloc] initWithFormat:@"Item index is: %d", index]</code></pre><p>或者如图所示使用:</p><p><img src="/images/format1.png" alt="format1"></p><p>运行程序就可看到相应输出<code>log</code>。</p></li></ul></li><li><p>call</p><ul><li><p>含义</p><p> 调用。前面所讲<code>po</code>和<code>p</code>也有调用功能,因此一般在不需显示输出,或方法无返回值时使用<code>call</code>。</p></li><li><p>用法</p><p> 在程序停在断点处时输入如下命令:</p><pre><code>call [self.view setBackgroundColor:[UIColor redColor]]</code></pre><p> 然后过掉断点继续运行程序,会发现<code>view</code>的背景颜色会变成红色了。在调试时灵活运行<code>call</code>命令可起到事半功倍作用。</p></li></ul></li></ul><h3 id="bt"><a href="#bt" class="headerlink" title="bt"></a>bt</h3><p>打印调用堆栈,加<code>all</code>可打印所有<code>thread</code>的堆栈。</p><h3 id="image"><a href="#image" class="headerlink" title="image"></a>image</h3><ul><li><p>功能:<code>image</code>命令可用于寻址,有多个组合命令。</p></li><li><p>用法:寻找栈地址对应的代码位置</p><p> 比如:</p><pre><code>NSArray *arr = @[@"1", @"2"];NSLog(@"%@", arr[2]);</code></pre><p> 运行这段代码会抛出如下异常:</p><pre><code>*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayI objectAtIndex:]: index 2 beyond bounds [0 .. 1]'*** First throw call stack:(0 CoreFoundation 0x000000010501fb0b __exceptionPreprocess + 1711 libobjc.A.dylib 0x0000000104a84141 objc_exception_throw + 482 CoreFoundation 0x0000000104f5d38b -[__NSArrayI objectAtIndex:] + 1553 testDemo 0x00000001044b66ae -[ViewController viewDidLoad] + 1744 UIKit 0x00000001055e601a -[UIViewController loadViewIfRequired] + 12355 UIKit 0x00000001055e645a -[UIViewController view] + 276 UIKit 0x00000001054ae98a -[UIWindow addRootViewControllerViewIfPossible] + 657 UIKit 0x00000001054af070 -[UIWindow _setHidden:forced:] + 2948 UIKit 0x00000001054c1ebe -[UIWindow makeKeyAndVisible] + 429 UIKit 0x000000010543b37f -[UIApplication _callInitializationDelegatesForMainScene:transitionContext:] + 434610 UIKit 0x00000001054415e4 -[UIApplication _runWithMainScene:transitionContext:completion:] + 170911 UIKit 0x000000010543e7f3 -[UIApplication workspaceDidEndTransaction:] + 18212 FrontBoardServices 0x00000001085f95f6 __FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__ + 2413 FrontBoardServices 0x00000001085f946d -[FBSSerialQueue _performNext] + 18614 FrontBoardServices 0x00000001085f97f6 -[FBSSerialQueue _performNextFromRunLoopSource] + 4515 CoreFoundation 0x0000000104fc5c01 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 1716 CoreFoundation 0x0000000104fab0cf __CFRunLoopDoSources0 + 52717 CoreFoundation 0x0000000104faa5ff __CFRunLoopRun + 91118 CoreFoundation 0x0000000104faa016 CFRunLoopRunSpecific + 40619 UIKit 0x000000010543d08f -[UIApplication _run] + 46820 UIKit 0x0000000105443134 UIApplicationMain + 15921 testDemo 0x00000001044b6a1f main + 11122 libdyld.dylib 0x0000000107e8965d start + 123 ??? 0x0000000000000001 0x0 + 1)libc++abi.dylib: terminating with uncaught exception of type NSException</code></pre><p> 通过分析如上崩溃栈,初步定为出错地址是<code>0x00000001044b66ae</code>(可根据执行文件名判断,或者最小的栈地址)。<br> 通过输入以下命令可进一步精确定位:</p><pre><code>image lookup --address 0x00000001044b66ae</code></pre><p> 命令执行后返回如下:</p><pre><code>Address: testDemo[0x00000001000016ae] (testDemo.__TEXT.__text + 174)Summary: testDemo`-[ViewController viewDidLoad] + 174 at ViewController.m:22</code></pre><p> 由此,我们可以看到出错的位置是<code>ViewController.m</code>的第<code>22</code>行。</p><p> 更多命令参见:<a href="http://lldb.llvm.org/lldb-gdb.html" target="_blank" rel="external">The LLDB Debugger</a><br> 另外,<code>facebook</code>开源了他们扩展的<a href="https://github.com/facebook/chisel" target="_blank" rel="external">LLDB命令库</a></p></li></ul><h2 id="简称-amp-别名"><a href="#简称-amp-别名" class="headerlink" title="简称 & 别名"></a>简称 & 别名</h2><h3 id="简称"><a href="#简称" class="headerlink" title="简称"></a>简称</h3><p>很多时候,<code>LLDB</code>完整的命令很长。如上面的 <code>image lookup --address</code> 这个组合命令。当然,<code>LLDB</code>命令也支持通过简称的方式调用命令。</p><pre><code>如上可简写为 im loo -a</code></pre><h3 id="别名"><a href="#别名" class="headerlink" title="别名"></a>别名</h3><p>和<code>gdb</code>时代一样,有些命令如<code>p、call</code>等和<code>gdb</code>下是一致的。其实是<code>LLDB</code>一些命令的别名:<br>如:</p><pre><code>p是fram variable的别名p view实际上是frame variable view。</code></pre><ul><li>自定义别名</li></ul><p>除了系统默认的<code>LLDB</code>别名外,也可自定义别名。如下:</p><pre><code>command alias ioa image lookup --address %1</code></pre><p>意思为命令<code>image lookup --address</code>添加了一个<code>ioa</code>的别名。<br>然后执行如下命令,就可得到和前者完整输入一样的效果。</p><h2 id="常见问题"><a href="#常见问题" class="headerlink" title="常见问题"></a>常见问题</h2><h3 id="不明类型-amp-类型不匹配"><a href="#不明类型-amp-类型不匹配" class="headerlink" title="不明类型 & 类型不匹配"></a>不明类型 & 类型不匹配</h3><p>如下面命令:</p><pre><code>(lldb) p NSLog(@"%@",[self.view viewWithTag:1001])error: 'NSLog' has unknown return type; cast the call to its declared return typeerror: 1 errors parsing expression</code></pre><p>如果在<code>LLDB</code>命令中有<code>unknown type</code>的类似错误(多见于<code>id</code>类型,比如<code>NSArray</code>中的某个值),则需要显式的声明类型。如上,应这样修改:</p><pre><code>p (void)NSLog(@"%@",[self.view viewWithTag:1001])</code></pre><p>可以得到正确的结果了。<br>另外,<code>LLDB</code>不支持宏,需要自己替换。</p><h3 id="找不到方法:unsupported-expression-with-unknown-type"><a href="#找不到方法:unsupported-expression-with-unknown-type" class="headerlink" title="找不到方法:unsupported expression with unknown type"></a>找不到方法:unsupported expression with unknown type</h3><p>常见在输出<code>frame</code>时,会得到如下错误信息:</p><pre><code>(lldb) po self.view.frameerror: unsupported expression with unknown typeerror: unsupported expression with unknown typeerror: 2 errors parsing expression</code></pre><p><code>LLDB</code>无法通过点属性访问的方法打印<code>framework</code>里面的对象,但是自己在程序中定义的则可以,上述改动如下:</p><pre><code>(lldb) p (CGRect)[self.view frame](CGRect) $0 = origin=(x=0, y=0) size=(width=320, height=480)</code></pre>]]></content>
<summary type="html">
<h2 id="概览"><a href="#概览" class="headerlink" title="概览"></a>概览</h2><p>我们平时开发调试输出信息除了<code>printf</code>或者<code>NSLog</code>以外,还可使用<code>LLDB
</summary>
<category term="Debug" scheme="http://yoursite.com/categories/Debug/"/>
<category term="Xcode" scheme="http://yoursite.com/tags/Xcode/"/>
</entry>
<entry>
<title>关于Objective-C的那些事儿:给分类(category)添加属性/关联对象 AssociatedObject</title>
<link href="http://yoursite.com/2017/09/08/%E5%85%B3%E4%BA%8EObjective-C%E7%9A%84%E9%82%A3%E4%BA%9B%E4%BA%8B%E5%84%BF-%E7%BB%99%E5%88%86%E7%B1%BB-category-%E6%B7%BB%E5%8A%A0%E5%B1%9E%E6%80%A7-%E5%85%B3%E8%81%94%E5%AF%B9%E8%B1%A1-AssociatedObject/"/>
<id>http://yoursite.com/2017/09/08/关于Objective-C的那些事儿-给分类-category-添加属性-关联对象-AssociatedObject/</id>
<published>2017-09-07T16:00:51.000Z</published>
<updated>2017-09-10T15:20:00.000Z</updated>
<content type="html"><![CDATA[<h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><p>在Category中并不会生成实例变量和存取方法,需要手动实现。所以一般常使用Runtime<code>关联对象</code>为已经存在的类添加<code>属性</code>。</p><h2 id="使用方法"><a href="#使用方法" class="headerlink" title="使用方法"></a>使用方法</h2><h3 id="引入运行时头文件"><a href="#引入运行时头文件" class="headerlink" title="引入运行时头文件"></a>引入运行时头文件</h3><pre><code>#import <objc/runtime.h></code></pre><h3 id="在匿名分类或者-h中添加属性"><a href="#在匿名分类或者-h中添加属性" class="headerlink" title="在匿名分类或者.h中添加属性"></a>在匿名分类或者.h中添加属性</h3><h4 id="二者区别"><a href="#二者区别" class="headerlink" title="二者区别"></a>二者区别</h4><ul><li>匿名分类中添加的是私有属性,只在本类中可以使用,类的实例中不可使用。</li><li><p>.h中添加的的在类的实例中也可使用。</p><pre><code>// 分类的头文件@interface ClassName (CategoryName)//我要添加一个实例也可以访问的变量所以就写在这里了@property (nonatomic, strong) NSString *str;@end// 匿名分类@interface ClassName ()@end</code></pre></li><li><p>在实现里面写要添加属性的getter、setter方法</p><pre><code>@implementation ClassName (CategoryName) -(void)setStr:(NSString *)str { objc_setAssociatedObject(self, &strKey, str, OBJC_ASSOCIATION_COPY); } -(NSString *)str { return objc_getAssociatedObject(self, &strKey); }@end</code></pre><ul><li><p>如上所示:<code>setStr:</code>方法中使用的<code>objc_setAssociatedObject</code>方法有四个参数:</p><ul><li>源对象</li><li><p>标记属性的key(关联时用来标记是哪一个属性的key)<br>常见有三种写法:</p><pre><code>// 利用静态变量地址唯一不变的特性1、static void *strKey = &strKey; 使用strKey作为key值;2、static NSString *strKey = @"strKey"; 或者 static char strKey; 使用&strKey作为key值;3、用selector,使用getter方法的名称作为key值。【推荐使用】</code></pre></li><li><p>关联对象<br><code>runtime</code>提供的方法如下:</p><pre><code> // 关联对象void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)// 获取关联的对象id objc_getAssociatedObject(id object, const void *key)// 移除关联的对象void objc_removeAssociatedObjects(id object) </code></pre><p> 变量说明:</p><pre><code>id object:被关联的对象const void *key:关联的key,要求唯一id value:关联的对象objc_AssociationPolicy policy:内存管理的策略</code></pre></li><li><p>关联策略<br>关联策略(<code>objc_AssociationPolicy</code>)指的是枚举值,policy的enum值包含以下几种</p><pre><code>enum {OBJC_ASSOCIATION_ASSIGN = 0, //关联对象的属性是弱引用 OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, //关联对象的属性是强引用并且关联对象不使用原子性OBJC_ASSOCIATION_COPY_NONATOMIC = 3, //关联对象的属性是copy并且关联对象不使用原子性OBJC_ASSOCIATION_RETAIN = 01401, //关联对象的属性是copy并且关联对象使用原子性 OBJC_ASSOCIATION_COPY = 01403 //关联对象的属性是copy并且关联对象使用原子性};当对象被释放时,会根据此策略决定是否释放关联的对象。</code></pre></li></ul></li></ul></li></ul><h1 id="关联对象的内存管理"><a href="#关联对象的内存管理" class="headerlink" title="关联对象的内存管理"></a>关联对象的内存管理</h1><ul><li>关联对象的释放实际与移除时机并不总是一致,如果使用关联策略<code>OBJC_ASSOCIATION_ASSIGN</code>进行关联对像即便释放,其在内存中也不会移除,如果在使用此关联对象时会crash。</li><li>关联对象与被关联对象本身的存储并无直接关系,它是存储在单独的哈希表中。</li><li>管理对象的五种关联策略与属性的限定符非常类似,多数情况下会使用<code>OBJC_ASSOCIATION_RETAIN_NONATOMIC</code>的管理策略,可保证持有关联对象。</li><li>关联对象释放时机:<br> 一个对象的所有关联对象是在此对象被释放时调用<code>_object_remove_assocations</code>函数中被移除的。</li></ul><h1 id="使用场景"><a href="#使用场景" class="headerlink" title="使用场景"></a>使用场景</h1><p>Associated Objects的三个主要使用场景:</p><ul><li>为现有的类添加私有变量以帮助实现细节;</li><li>为现有的类添加公有属性;</li><li><p>为KVO创建一个关联的观察者。<br>有时在分类中使用KVO,推荐使用关联的对象作为观察者,尽量避免对象观察自身。</p><h1 id="相关函数"><a href="#相关函数" class="headerlink" title="相关函数"></a>相关函数</h1><p>在<code>runtime</code>源码中可以找到如下三个与<code>Associated Objects</code>相关的三个函数:</p><pre><code>void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);void objc_getAssociatedObject(id object, const void *key);void objc_removeAssociatedObjects(id object);</code></pre></li><li><p>objc_setAssociatedObject 用于给对象添加关联对象,传入nil则可以移除已有的关联对象;</p></li><li>objc_getAssociatedObject 用于获取关联对象;</li><li>objc_removeAssociatedObjects 用于移除一个对象的所有关联对象。</li></ul><p><code>objc_removeAssociatedObjects</code> 函数我们一般用不上,因为它会移除一个对象的所有关联对象,将该对象恢复”原始”状态。这样会把别人添加的关联对象也一并移除。<br>因此会选择通过给 <code>objc_setAssociatedObject</code> 函数传入ni来移除某个已有的关联对象。</p>]]></content>
<summary type="html">
<h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><p>在Category中并不会生成实例变量和存取方法,需要手动实现。所以一般常使用Runtime<code>关联对象</code>为已经存在的类
</summary>
<category term="Objective-C" scheme="http://yoursite.com/categories/Objective-C/"/>
<category term="iOS" scheme="http://yoursite.com/tags/iOS/"/>
</entry>
<entry>
<title>关于Objective-C的那些事儿:Category(分类,类别)</title>
<link href="http://yoursite.com/2017/09/06/%E5%85%B3%E4%BA%8EObjective-C%E7%9A%84%E9%82%A3%E4%BA%9B%E4%BA%8B%E5%84%BF-Category-%E5%88%86%E7%B1%BB-%E7%B1%BB%E5%88%AB/"/>
<id>http://yoursite.com/2017/09/06/关于Objective-C的那些事儿-Category-分类-类别/</id>
<published>2017-09-06T12:02:00.000Z</published>
<updated>2017-09-09T11:21:55.000Z</updated>
<content type="html"><![CDATA[<h2 id="概览"><a href="#概览" class="headerlink" title="概览"></a>概览</h2><p><code>Category</code>是<code>OC</code>中特有的方法,(在<code>java</code>中不存在)分类只是用来扩展类时不需要继承关系那么复杂,特殊的语法处理特殊的问题,语法就是如此发展而来。</p><pre><code>、、、objectivec // 声明@interface 类名(分类名称)@end// 实现@implementation 类名(分类名称)@end、、、</code></pre><ul><li><p>分类只是用来扩展类时不需要继承关系那么麻烦。如果使用继承关系扩充一个类,有一个弊病(面向对象弊病):</p><ul><li>高耦合性:只要父类稍作改变,子类就会受到严重影响,甚至导致子类不可用,给开发带来困难。</li></ul></li></ul><h2 id="使用场景:"><a href="#使用场景:" class="headerlink" title="使用场景:"></a>使用场景:</h2><p>把不同的功能写在不同的类中去,并且命名;分类模块一般以功能命名,而不是以作者命名。</p><ul><li>凡是要扩充一些工具方法,优先考虑分类,如果没有发现合适的类扩充,则在新建类。</li><li>开发中经常要写一些分类,把有价值方法抽出来,方便拓展使用<ul><li>水印功能比较好用,可抽出来,以后使用</li><li>可经常打造自己的分类,积累越多,以后开发越轻松,打造自己的<code>类库</code> <code>框架</code>供调用,即造轮子。</li></ul></li></ul><h2 id="特点:"><a href="#特点:" class="headerlink" title="特点:"></a>特点:</h2><ul><li>在分类中可调用原类的方法,也可访问原类的成员变量</li><li>原类声明的成员变量,可在分类中直接访问</li><li>不能在分类中声明成员变量</li><li>如果在分类中定义且实现了原类中相同的方法,则原类中的方法会被覆盖,应当避免</li></ul><h2 id="作用:"><a href="#作用:" class="headerlink" title="作用:"></a>作用:</h2><p><code>Category</code>可以给某一个类扩充一些方法(不修改原来类的代码),但是不可以扩充成员变量(通过关联可以,详见:<a href="http://wangmasterpro.com/2017/09/08/关于Objective-C的那些事儿-给分类-category-添加属性-关联对象-AssociatedObject/" target="_blank" rel="external">关于Objective-C的那些事儿:给分类(category)添加属性/关联对象 AssociatedObject</a>)。</p><h2 id="使用注意"><a href="#使用注意" class="headerlink" title="使用注意"></a>使用注意</h2><ul><li><p>分类只能增加方法,不能增加成员变量;如果想增加变量,可以考虑通过集成创建子类,或者穿件关联也可。</p></li><li><p>分类方法实现中可访问原来类中声明的成员变量;</p></li><li><p>分类方法优先级最高:分类可重新实现原来类中方法,但会覆盖掉原来的方法,导致原来方法无法使用。</p><ul><li>调用顺序:优先去分类中查找,然后再去原来类中找,最后去父类中找。分类(最后参与编译的分类优先) -> 原来类 -》 父类</li></ul></li><li><p>多个分类中如果实现了相同的方法,只有最后一个参与编译的才有效。</p></li></ul><h2 id="优点"><a href="#优点" class="headerlink" title="优点"></a>优点</h2><p>适合团队开发。</p><ul><li><p>一个庞大的类可以分模块开发;</p></li><li><p>一个庞大的类可以由多个人来编写,利于团队合作。</p></li></ul><h2 id="分类协议"><a href="#分类协议" class="headerlink" title="分类协议"></a>分类协议</h2><p>分类协议可以定义在单独的.h和.m文件中,也可定义在原来类中。</p><ul><li>一般都是单独定义在单独文件中</li><li>也可定义在原来类中。(分类大部分情况都是单独文件,很少写在原来类里面)</li></ul><p>OC语法:冒号继承,尖号遵守协议,括号分类;</p><h2 id="Category和Extension"><a href="#Category和Extension" class="headerlink" title="Category和Extension"></a>Category和Extension</h2><h3 id="概念"><a href="#概念" class="headerlink" title="概念"></a>概念</h3><p><code>Extension</code>是<code>Category</code>的一个特例,其名字为匿名<br><code>extension</code>在编译器处理,是类的一部分,随类的生命周期变化。<br>一般用来隐藏类的私有信息,不被外界访问,须有类的源码才能为一个类添加<code>extension</code>,因此无法为系统的类如<code>NSSArray</code>添加<code>extension</code>。</p><h3 id="二者比较"><a href="#二者比较" class="headerlink" title="二者比较"></a>二者比较</h3><ul><li>相同点:都可为一个类添加方法</li><li><p>区别:</p><ul><li><code>Category</code>在定义方法不实现,编译器不会报错,运行调用时出错;Extensions在实现中不实现编译器会警告。</li><li><code>Category</code>只能用于添加方法,不能用于添加成员变量(运行时添加关联对象也可添加)。<code>extension</code>中声明的方法和添加的成员变量是私有的,只有主实现能调用,外部类无法调用。</li><li><code>Category</code>增加方法如果与类方法同名会被覆盖,因为<code>Category</code>优先级高,而<code>Extension</code>则会报错。</li></ul></li></ul><h2 id="Category如何加载"><a href="#Category如何加载" class="headerlink" title="Category如何加载"></a>Category如何加载</h2><p>如果在分类中实现了原类的方法,如<code>methodA</code>,需要注意:</p><ul><li><code>category</code>的方法没有‘完全替换掉’原类已有的方法,即<code>Category</code>和原类都会有<code>methodA</code>,<code>Category</code>加载完成后,类的方法列表里会有两个<code>methodA</code>。</li><li><code>category</code>的方法被放到了新方法列表的前面,而原类的方法被放到了新方法列表的后面。因为运行时查找方法时是顺着方法列表的顺序查找的,只要找到对应名字的方法就会停止查找。</li></ul><h2 id="Category和-load方法"><a href="#Category和-load方法" class="headerlink" title="Category和+load方法"></a>Category和+load方法</h2><p>如果在原类和其<code>Category</code>中都有<code>load</code>方法时:</p><ul><li>在原类的<code>+load</code>方法调用时,可以调用<code>Category</code>中声明的方法吗?<br>可以调用,<code>Category</code>到原来类的附加工作会先与<code>+load</code>方法的执行。</li><li><p>其调用顺序是怎么样的?<br><code>+load</code>方法的执行顺序:先本类,后<code>Category</code>,而<code>Category</code>的<code>+load</code>执行顺序是根据编译顺序决定的。</p><p> <code>PS</code>:编译顺序是根据<code>Xcode</code>的<code>Build Setting</code>中的<code>Compile Sources</code>中的类顺序决定。</p></li></ul>]]></content>
<summary type="html">
<h2 id="概览"><a href="#概览" class="headerlink" title="概览"></a>概览</h2><p><code>Category</code>是<code>OC</code>中特有的方法,(在<code>java</code>中不存在)分类
</summary>
<category term="Objective-C" scheme="http://yoursite.com/categories/Objective-C/"/>
<category term="iOS" scheme="http://yoursite.com/tags/iOS/"/>
</entry>
<entry>
<title>iOS悬浮按钮的实现</title>
<link href="http://yoursite.com/2016/10/29/iOS%E6%82%AC%E6%B5%AE%E6%8C%89%E9%92%AE%E7%9A%84%E5%AE%9E%E7%8E%B0/"/>
<id>http://yoursite.com/2016/10/29/iOS悬浮按钮的实现/</id>
<published>2016-10-29T15:18:52.000Z</published>
<updated>2017-09-09T11:22:57.000Z</updated>
<content type="html"><![CDATA[<h2 id="需求描述"><a href="#需求描述" class="headerlink" title="需求描述"></a>需求描述</h2><p>项目中为了实现“新手礼包”悬浮按钮需求:悬浮按钮在项目当中若干页面出现,且按钮不随着页面的上下和左右滑动而改变位置。<a id="more"></a></p><h3 id="悬浮按钮实现效果图:"><a href="#悬浮按钮实现效果图:" class="headerlink" title="悬浮按钮实现效果图:"></a>悬浮按钮实现效果图:</h3><p><img src="/images/DriftButton_Global1.gif" alt="DriftButton_Global1"> <img src="/images/DriftButton_Global2.gif" alt="DriftButton_Global2"></p><p>-》页面垂直滑动时按钮位置不动:</p><p><img src="/images/DriftButton_Global3.gif" alt="DriftButton_Global3"></p><h3 id="实现方法:"><a href="#实现方法:" class="headerlink" title="实现方法:"></a>实现方法:</h3><font size="3"><strong>一、自定义悬浮按钮,并将按钮置于程序窗口之上【单例】</strong></font><p>RWDriftButton.h</p><pre><code><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="meta">#import <span class="meta-string"><UIKit/UIKit.h></span></span></div><div class="line"></div><div class="line"><span class="class"><span class="keyword">@interface</span> <span class="title">RWDriftButton</span> : <span class="title">UIButton</span></span></div><div class="line"><span class="keyword">@property</span> (<span class="keyword">nonatomic</span>, <span class="keyword">copy</span>) <span class="keyword">void</span> (^clickHandler)(); <span class="comment">// 悬浮按钮的点击回调</span></div><div class="line">+ (<span class="keyword">instancetype</span>)shareInstance;</div><div class="line"><span class="keyword">@end</span>;</div></pre></td></tr></table></figure></code></pre><p>RWDriftButton.m</p><pre><code><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div></pre></td><td class="code"><pre><div class="line"><span class="meta">#import <span class="meta-string">"RWDriftButton.h"</span></span></div><div class="line"></div><div class="line"><span class="comment">// 大于iOS8以上</span></div><div class="line"><span class="meta">#define QRIOS8Later ([[[UIDevice currentDevice] systemVersion] floatValue] >=8.0 ? YES : NO)</span></div><div class="line"><span class="comment">// 屏幕宽</span></div><div class="line"><span class="meta">#define QRMainW (!QRIOS8Later?[UIScreen mainScreen].bounds.size.width:[[[UIScreen mainScreen] fixedCoordinateSpace] bounds].size.width)</span></div><div class="line"><span class="comment">// 屏幕高</span></div><div class="line"><span class="meta">#define QRMainH (!QRIOS8Later?[UIScreen mainScreen].bounds.size.height:[[[UIScreen mainScreen] fixedCoordinateSpace] bounds].size.height)</span></div><div class="line"></div><div class="line"><span class="class"><span class="keyword">@implementation</span> <span class="title">RWDriftButton</span></span></div><div class="line">+ (<span class="keyword">instancetype</span>)shareInstance {</div><div class="line"> <span class="keyword">static</span> <span class="built_in">dispatch_once_t</span> onceToken;</div><div class="line"> <span class="keyword">static</span> RWDriftButton *DriftButton;</div><div class="line"> <span class="built_in">dispatch_once</span>(&onceToken, ^{</div><div class="line"> DriftButton = [RWDriftButton buttonWithType:<span class="built_in">UIButtonTypeCustom</span>];</div><div class="line"> <span class="built_in">CGFloat</span> DriftBottom = <span class="number">8</span>;</div><div class="line"> <span class="built_in">CGFloat</span> DriftRight = <span class="number">8</span>; <span class="comment">// 礼包右边距</span></div><div class="line"> <span class="built_in">CGFloat</span> DriftBtnW = <span class="number">42</span>; <span class="comment">// 礼包宽度</span></div><div class="line"> <span class="built_in">CGFloat</span> DriftBtnH = <span class="number">42</span>; <span class="comment">// 礼包高度</span></div><div class="line"> <span class="built_in">CGFloat</span> TarbarHight = <span class="number">49</span>; <span class="comment">// 导航控制器底部tarbar高度</span></div><div class="line"> <span class="built_in">CGFloat</span> DriftButtonX = QRMainW - DriftRight - DriftBtnW;</div><div class="line"> <span class="built_in">CGFloat</span> DriftButtonY = QRMainH - DriftBottom - DriftBtnW;</div><div class="line"> DriftButton.frame = <span class="built_in">CGRectMake</span>(DriftButtonX, DriftButtonY - TarbarHight, DriftBtnW, DriftBtnH);</div><div class="line"> DriftButton.backgroundColor = [<span class="built_in">UIColor</span> orangeColor];</div><div class="line"> DriftButton.layer.cornerRadius = DriftBtnW * <span class="number">0.5</span>;</div><div class="line"> [DriftButton addTarget:DriftButton action:<span class="keyword">@selector</span>(click:) forControlEvents:<span class="built_in">UIControlEventTouchUpInside</span>];</div><div class="line"> });</div><div class="line"> <span class="keyword">return</span> DriftButton;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">/**</span></div><div class="line"> * 点击礼包Block回调</div><div class="line"> *</div><div class="line">* @param sender </div><div class="line">*/</div><div class="line">- (<span class="keyword">void</span>)click:(<span class="keyword">id</span>)sender</div><div class="line">{</div><div class="line"> <span class="keyword">if</span> (<span class="keyword">self</span>.clickHandler) {</div><div class="line"> <span class="keyword">self</span>.clickHandler();</div><div class="line"> }</div><div class="line">}</div><div class="line"><span class="keyword">@end</span></div></pre></td></tr></table></figure></code></pre><font size="3"><strong>二、RWBaseViewController为项目中已有的基类,在基类中设置单例悬浮按钮隐藏</strong></font><p>RWBaseViewController.m:</p><pre><code><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// /*项目中所有页面的基类*/</span></div><div class="line"></div><div class="line"><span class="meta">#import <span class="meta-string">"RWBaseViewController.h"</span></span></div><div class="line"></div><div class="line"><span class="class"><span class="keyword">@interface</span> <span class="title">RWBaseViewController</span> ()</span></div><div class="line"></div><div class="line"><span class="keyword">@end</span></div><div class="line"></div><div class="line"><span class="class"><span class="keyword">@implementation</span> <span class="title">RWBaseViewController</span></span></div><div class="line"></div><div class="line">- (<span class="keyword">void</span>)viewDidLoad {</div><div class="line"> [<span class="keyword">super</span> viewDidLoad];</div><div class="line"> <span class="comment">// Do any additional setup after loading the view.、</span></div><div class="line"> </div><div class="line"> [RWDriftButton shareInstance].hidden = <span class="literal">YES</span>; <span class="comment">// 基类不需要显示,子类如果需要显示悬浮按钮,设置hidden为NO即可;</span></div><div class="line"> </div><div class="line"> <span class="keyword">self</span>.navigationItem.title = <span class="string">@"基类页面[不显示悬浮按钮]"</span>;</div><div class="line"> <span class="keyword">self</span>.view.backgroundColor = [<span class="built_in">UIColor</span> grayColor];</div><div class="line">}</div></pre></td></tr></table></figure></code></pre><font size="3"><strong>三、RWDriftViewController为项目中的浮层类,根据需求在浮层类中的声明周期方法中设置悬浮按钮的显示以及显示效果</strong></font><p>RWDriftViewController.m:</p><pre><code><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div></pre></td><td class="code"><pre><div class="line"><span class="meta">#import <span class="meta-string">"RWDriftViewController.h"</span></span></div><div class="line"><span class="meta">#import <span class="meta-string">"RWBaseViewController.h"</span></span></div><div class="line"><span class="meta">#import <span class="meta-string">"RWDriftButton.h"</span></span></div><div class="line"></div><div class="line"><span class="class"><span class="keyword">@interface</span> <span class="title">RWDriftViewController</span> ()</span></div><div class="line"><span class="keyword">@property</span> (<span class="keyword">nonatomic</span>, <span class="keyword">strong</span>) RWDriftButton *DriftButton;</div><div class="line"><span class="keyword">@end</span></div><div class="line"></div><div class="line"><span class="class"><span class="keyword">@implementation</span> <span class="title">RWDriftViewController</span></span></div><div class="line"></div><div class="line">- (<span class="keyword">void</span>)viewDidLoad {</div><div class="line"> [<span class="keyword">super</span> viewDidLoad];</div><div class="line"> <span class="comment">// Do any additional setup after loading the view.</span></div><div class="line"> <span class="keyword">self</span>.navigationItem.title = <span class="string">@"悬浮类[继承自悬浮类的页面可显示悬浮按钮]"</span>;</div><div class="line"> <span class="keyword">self</span>.view.backgroundColor = [<span class="built_in">UIColor</span> brownColor];</div><div class="line"> <span class="keyword">if</span> (![RWDriftButton shareInstance].superview) {</div><div class="line"> [[<span class="built_in">UIApplication</span> sharedApplication].keyWindow addSubview:[RWDriftButton shareInstance]];</div><div class="line"> }</div><div class="line"> [[<span class="built_in">UIApplication</span> sharedApplication].keyWindow bringSubviewToFront:[RWDriftButton shareInstance]];</div><div class="line">}</div><div class="line"></div><div class="line">- (<span class="keyword">void</span>)viewWillAppear:(<span class="built_in">BOOL</span>)animated {</div><div class="line"> [<span class="keyword">super</span> viewWillAppear:<span class="literal">YES</span>];</div><div class="line"> [RWDriftButton shareInstance].hidden = <span class="literal">NO</span>;</div><div class="line">}</div><div class="line"></div><div class="line">- (<span class="keyword">void</span>)viewDidAppear:(<span class="built_in">BOOL</span>)animated {</div><div class="line"> [<span class="keyword">super</span> viewDidAppear:<span class="literal">YES</span>];</div><div class="line">[RWDriftButton shareInstance].hidden = <span class="literal">NO</span>;</div><div class="line"> [[RWDriftButton shareInstance] setClickHandler:^{</div><div class="line"> RWBaseViewController *RWBaseVc = [RWBaseViewController new];</div><div class="line"> [<span class="keyword">self</span>.navigationController pushViewController:RWBaseVc animated:<span class="literal">YES</span>];</div><div class="line"> }];</div><div class="line">}</div><div class="line"></div><div class="line">- (<span class="keyword">void</span>)viewDidDisappear:(<span class="built_in">BOOL</span>)animated {</div><div class="line"> [<span class="keyword">super</span> viewWillDisappear:<span class="literal">YES</span>];</div><div class="line"> [RWDriftButton shareInstance].hidden = <span class="literal">YES</span>;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">@end</span></div></pre></td></tr></table></figure></code></pre><h3 id="如上,满足业务各种需求的悬浮按钮—全局-就完美实现了。在使用过程中如果需求有差异可稍加改动即可。"><a href="#如上,满足业务各种需求的悬浮按钮—全局-就完美实现了。在使用过程中如果需求有差异可稍加改动即可。" class="headerlink" title="如上,满足业务各种需求的悬浮按钮—全局 就完美实现了。在使用过程中如果需求有差异可稍加改动即可。"></a>如上,满足业务各种需求的悬浮按钮—全局 就完美实现了。在使用过程中如果需求有差异可稍加改动即可。</h3><h3 id="Demo"><a href="#Demo" class="headerlink" title="Demo"></a><a href="https://github.com/WangMasterpro/DriftButton_Global" title="Demo" target="_blank" rel="external"><font color="Red">Demo</font></a></h3><p><a href="https://github.com/WangMasterpro/DriftButton_Global" target="_blank" rel="external">iOS全局悬浮按钮</a></p><h5 id="如各位喜欢博文的烦请各位点个赞,如有帮到大家请在github上给demo个star,如有问题可在博客下方留言,我会及时回复哒"><a href="#如各位喜欢博文的烦请各位点个赞,如有帮到大家请在github上给demo个star,如有问题可在博客下方留言,我会及时回复哒" class="headerlink" title=" 如各位喜欢博文的烦请各位点个赞,如有帮到大家请在github上给demo个star,如有问题可在博客下方留言,我会及时回复哒"></a><font color="Green"> 如各位喜欢博文的烦请各位点个赞,如有帮到大家请在github上给demo个star,如有问题可在博客下方留言,我会及时回复哒</font></h5>]]></content>
<summary type="html">
<h2 id="需求描述"><a href="#需求描述" class="headerlink" title="需求描述"></a>需求描述</h2><p>项目中为了实现“新手礼包”悬浮按钮需求:悬浮按钮在项目当中若干页面出现,且按钮不随着页面的上下和左右滑动而改变位置。
</summary>
<category term="iOS" scheme="http://yoursite.com/categories/iOS/"/>
<category term="iOS" scheme="http://yoursite.com/tags/iOS/"/>
</entry>
</feed>