coderTomato.github.io/bluetooth at master · coderTomato/coderTomato.github.io · GitHub
Skip to content

Latest commit

 

History

History
<!DOCTYPE HTML>
<html lang="en-US" >
    
    <head>
        
        <meta charset="UTF-8">
        <title>bluetooth | Mou</title>
        <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
        <meta name="description" content="">
        <meta name="generator" content="GitBook 1.0.3">
        <meta name="HandheldFriendly" content="true"/>
        <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
        <meta name="apple-mobile-web-app-capable" content="yes">
        <meta name="apple-mobile-web-app-status-bar-style" content="black">
        <link rel="apple-touch-icon-precomposed" sizes="152x152" href="../gitbook/images/apple-touch-icon-precomposed-152.png">
        <link rel="shortcut icon" href="../gitbook/images/favicon.ico" type="image/x-icon">
        
    
    
    
    <link rel="next" href="../touchid/README.html" />
    
    
    <link rel="prev" href="../memory/README.html" />
    

        
    </head>
    <body>
        
        
<link rel="stylesheet" href="../gitbook/style.css">


        
    <div class="book"  data-level="17" data-basepath=".." data-revision="1462863912916">
    

<div class="book-summary">
    <div class="book-search">
        <input type="text" placeholder="Type to search" class="form-control" />
    </div>
    <ul class="summary">
        
    	
    	
    	

        

        
    
        
        <li class="chapter " data-level="0" data-path="index.html">
            
                
                    <a href="../index.html">
                        <i class="fa fa-check"></i>
                        
                         Markdown语法
                    </a>
                
            
            
        </li>
    
        
        <li class="chapter " data-level="1" data-path="base/README.html">
            
                
                    <a href="../base/README.html">
                        <i class="fa fa-check"></i>
                        
                            <b>1.</b>
                        
                         base
                    </a>
                
            
            
        </li>
    
        
        <li class="chapter " data-level="2" data-path="uiview/README.html">
            
                
                    <a href="../uiview/README.html">
                        <i class="fa fa-check"></i>
                        
                            <b>2.</b>
                        
                         UIView
                    </a>
                
            
            
        </li>
    
        
        <li class="chapter " data-level="3" data-path="jiugongge/README.html">
            
                
                    <a href="../jiugongge/README.html">
                        <i class="fa fa-check"></i>
                        
                            <b>3.</b>
                        
                         jiugongge
                    </a>
                
            
            
        </li>
    
        
        <li class="chapter " data-level="4" data-path="uiscrollview/README.html">
            
                
                    <a href="../uiscrollview/README.html">
                        <i class="fa fa-check"></i>
                        
                            <b>4.</b>
                        
                         UIScrollView
                    </a>
                
            
            
        </li>
    
        
        <li class="chapter " data-level="5" data-path="uitableview/README.html">
            
                
                    <a href="../uitableview/README.html">
                        <i class="fa fa-check"></i>
                        
                            <b>5.</b>
                        
                         UITableView
                    </a>
                
            
            
        </li>
    
        
        <li class="chapter " data-level="6" data-path="autolayout/README.html">
            
                
                    <a href="../autolayout/README.html">
                        <i class="fa fa-check"></i>
                        
                            <b>6.</b>
                        
                         autolayout
                    </a>
                
            
            
        </li>
    
        
        <li class="chapter " data-level="7" data-path="map/README.html">
            
                
                    <a href="../map/README.html">
                        <i class="fa fa-check"></i>
                        
                            <b>7.</b>
                        
                         map
                    </a>
                
            
            
        </li>
    
        
        <li class="chapter " data-level="8" data-path="notification/README.html">
            
                
                    <a href="../notification/README.html">
                        <i class="fa fa-check"></i>
                        
                            <b>8.</b>
                        
                         notification
                    </a>
                
            
            
        </li>
    
        
        <li class="chapter " data-level="9" data-path="nstimer/README.html">
            
                
                    <a href="../nstimer/README.html">
                        <i class="fa fa-check"></i>
                        
                            <b>9.</b>
                        
                         nstimer
                    </a>
                
            
            
        </li>
    
        
        <li class="chapter " data-level="10" data-path="oc/README.html">
            
                
                    <a href="../oc/README.html">
                        <i class="fa fa-check"></i>
                        
                            <b>10.</b>
                        
                         OC
                    </a>
                
            
            
        </li>
    
        
        <li class="chapter " data-level="11" data-path="rac/README.html">
            
                
                    <a href="../rac/README.html">
                        <i class="fa fa-check"></i>
                        
                            <b>11.</b>
                        
                         rac
                    </a>
                
            
            
        </li>
    
        
        <li class="chapter " data-level="12" data-path="review1/README.html">
            
                
                    <a href="../review1/README.html">
                        <i class="fa fa-check"></i>
                        
                            <b>12.</b>
                        
                         review1
                    </a>
                
            
            
        </li>
    
        
        <li class="chapter " data-level="13" data-path="review2/README.html">
            
                
                    <a href="../review2/README.html">
                        <i class="fa fa-check"></i>
                        
                            <b>13.</b>
                        
                         review2
                    </a>
                
            
            
        </li>
    
        
        <li class="chapter " data-level="14" data-path="audio/README.html">
            
                
                    <a href="../audio/README.html">
                        <i class="fa fa-check"></i>
                        
                            <b>14.</b>
                        
                         audio
                    </a>
                
            
            
        </li>
    
        
        <li class="chapter " data-level="15" data-path="twocode/README.html">
            
                
                    <a href="../twocode/README.html">
                        <i class="fa fa-check"></i>
                        
                            <b>15.</b>
                        
                         twocode
                    </a>
                
            
            
        </li>
    
        
        <li class="chapter " data-level="16" data-path="memory/README.html">
            
                
                    <a href="../memory/README.html">
                        <i class="fa fa-check"></i>
                        
                            <b>16.</b>
                        
                         memory
                    </a>
                
            
            
        </li>
    
        
        <li class="chapter active" data-level="17" data-path="bluetooth/README.html">
            
                
                    <a href="../bluetooth/README.html">
                        <i class="fa fa-check"></i>
                        
                            <b>17.</b>
                        
                         bluetooth
                    </a>
                
            
            
        </li>
    
        
        <li class="chapter " data-level="18" data-path="touchid/README.html">
            
                
                    <a href="../touchid/README.html">
                        <i class="fa fa-check"></i>
                        
                            <b>18.</b>
                        
                         touchid
                    </a>
                
            
            
        </li>
    
        
        <li class="chapter " data-level="19" data-path="uiviewcontroller/README.html">
            
                
                    <a href="../uiviewcontroller/README.html">
                        <i class="fa fa-check"></i>
                        
                            <b>19.</b>
                        
                         UIViewController
                    </a>
                
            
            
        </li>
    
        
        <li class="chapter " data-level="20" data-path="delegate_kvo/README.html">
            
                
                    <a href="../delegate_kvo/README.html">
                        <i class="fa fa-check"></i>
                        
                            <b>20.</b>
                        
                         delegate kvo
                    </a>
                
            
            
        </li>
    
        
        <li class="chapter " data-level="21" data-path="datasave/README.html">
            
                
                    <a href="../datasave/README.html">
                        <i class="fa fa-check"></i>
                        
                            <b>21.</b>
                        
                         dataSave
                    </a>
                
            
            
        </li>
    
        
        <li class="chapter " data-level="22" data-path="pickerview/README.html">
            
                
                    <a href="../pickerview/README.html">
                        <i class="fa fa-check"></i>
                        
                            <b>22.</b>
                        
                         pickerView
                    </a>
                
            
            
        </li>
    
        
        <li class="chapter " data-level="23" data-path="quartz2d/README.html">
            
                
                    <a href="../quartz2d/README.html">
                        <i class="fa fa-check"></i>
                        
                            <b>23.</b>
                        
                         quartz2D
                    </a>
                
            
            
        </li>
    
        
        <li class="chapter " data-level="24" data-path="uitextfield/README.html">
            
                
                    <a href="../uitextfield/README.html">
                        <i class="fa fa-check"></i>
                        
                            <b>24.</b>
                        
                         UITextField
                    </a>
                
            
            
        </li>
    
        
        <li class="chapter " data-level="25" data-path="uidynamic/README.html">
            
                
                    <a href="../uidynamic/README.html">
                        <i class="fa fa-check"></i>
                        
                            <b>25.</b>
                        
                         UIDynamic
                    </a>
                
            
            
        </li>
    
        
        <li class="chapter " data-level="26" data-path="coreanimation/README.html">
            
                
                    <a href="../coreanimation/README.html">
                        <i class="fa fa-check"></i>
                        
                            <b>26.</b>
                        
                         coreAnimation
                    </a>
                
            
            
        </li>
    
        
        <li class="chapter " data-level="27" data-path="cocoapods/README.html">
            
                
                    <a href="../cocoapods/README.html">
                        <i class="fa fa-check"></i>
                        
                            <b>27.</b>
                        
                         Cocoapods
                    </a>
                
            
            
        </li>
    
        
        <li class="chapter " data-level="28" data-path="thread/README.html">
            
                
                    <a href="../thread/README.html">
                        <i class="fa fa-check"></i>
                        
                            <b>28.</b>
                        
                         Thread
                    </a>
                
            
            
        </li>
    
        
        <li class="chapter " data-level="29" data-path="network/README.html">
            
                
                    <a href="../network/README.html">
                        <i class="fa fa-check"></i>
                        
                            <b>29.</b>
                        
                         network
                    </a>
                
            
            
        </li>
    


        
        <li class="divider"></li>
        <li>
            <a href="http://www.gitbook.io/" target="blank" class="gitbook-link">Published using GitBook</a>
        </li>
        
    </ul>
</div>

    <div class="book-body">
        <div class="body-inner">
            <div class="book-header">
    <!-- Actions Left -->
    <a href="#" class="btn pull-left toggle-summary" aria-label="Toggle summary"><i class="fa fa-align-justify"></i></a>
    <a href="#" class="btn pull-left toggle-search" aria-label="Toggle search"><i class="fa fa-search"></i></a>
    
    <div id="font-settings-wrapper" class="dropdown pull-left">
        <a href="#" class="btn toggle-dropdown" aria-label="Toggle font settings"><i class="fa fa-font"></i>
        </a>
        <div class="dropdown-menu font-settings">
    <div class="dropdown-caret">
        <span class="caret-outer"></span>
        <span class="caret-inner"></span>
    </div>

    <div class="buttons">
        <button type="button" id="reduce-font-size" class="button size-2">A</button>
        <button type="button" id="enlarge-font-size" class="button size-2">A</button>
    </div>

    <div class="buttons font-family-list">
        <button type="button" data-font="0" class="button">Serif</button>
        <button type="button" data-font="1" class="button">Sans</button>
    </div>

    <div class="buttons color-theme-list">
        <button type="button" id="color-theme-preview-0" class="button size-3" data-theme="0">White</button>
        <button type="button" id="color-theme-preview-1" class="button size-3" data-theme="1">Sepia</button>
        <button type="button" id="color-theme-preview-2" class="button size-3" data-theme="2">Night</button>
    </div>
</div>

    </div>

    <!-- Actions Right -->
    
    <div class="dropdown pull-right">
        <a href="#" class="btn toggle-dropdown" aria-label="Toggle share dropdown"><i class="fa fa-share-alt"></i>
        </a>
        <div class="dropdown-menu font-settings dropdown-left">
            <div class="dropdown-caret">
                <span class="caret-outer"></span>
                <span class="caret-inner"></span>
            </div>
            <div class="buttons">
                <button type="button" data-sharing="twitter" class="button">Twitter</button>
                <button type="button" data-sharing="google-plus" class="button">Google</button>
                <button type="button" data-sharing="facebook" class="button">Facebook</button>
                <button type="button" data-sharing="weibo" class="button">Weibo</button>
                <button type="button" data-sharing="instapaper" class="button">Instapaper</button>
            </div>
        </div>
    </div>
    

    
    <a href="#" target="_blank" class="btn pull-right google-plus-sharing-link sharing-link" data-sharing="google-plus" aria-label="Share on Google Plus"><i class="fa fa-google-plus"></i></a>
    
    
    <a href="#" target="_blank" class="btn pull-right facebook-sharing-link sharing-link" data-sharing="facebook" aria-label="Share on Facebook"><i class="fa fa-facebook"></i></a>
    
    
    <a href="#" target="_blank" class="btn pull-right twitter-sharing-link sharing-link" data-sharing="twitter" aria-label="Share on Twitter"><i class="fa fa-twitter"></i></a>
    
    

    <!-- Title -->
    <h1>
        <i class="fa fa-circle-o-notch fa-spin"></i>
        <a href="../" >Mou</a>
    </h1>
</div>

            <div class="page-wrapper" tabindex="-1">
                <div class="page-inner">
                
                
                    <section class="normal" id="section-gitbook_13">
                    
                        <h1 id="ios">iOS中的蓝牙</h1>
<h3 id="">概述</h3>
<h4 id="ios4">iOS中提供了4个框架用于实现蓝牙连接</h4>
<ul>
<li><p>1.GameKit.framework(用法简单)</p>
<ul>
<li><code>只能用于iOS设备之间的同个应用内连接</code>,多用于游戏(eg.拳皇,棋牌类),从<code>iOS7开始过期</code></li>
</ul>
</li>
<li><p>2.MultipeerConnectivity.framework(代替1)</p>
<ul>
<li><code>只能用于iOS设备之间的连接,从iOS7开始引入</code>,主要用于<code>非联网状态</code>下,通过wifi或者蓝牙进行文件共享(仅限于沙盒的文件),多用于附近无网聊天</li>
</ul>
</li>
<li><p>3.ExternalAccessory.framework(MFi)</p>
<ul>
<li><code>可用于第三方蓝牙设备交互</code>,但是蓝牙设备必须经过<code>苹果MFi认证</code>(国内很少)</li>
</ul>
</li>
<li><p>4.CoreBluetooth.framework(时下热门)</p>
<ul>
<li><code>可用于第三方蓝牙设备交互</code>,必须要支持蓝牙4.0</li>
<li>硬件至少是4s,系统至少是iOS6</li>
<li>蓝牙4.0以低功耗著称,一般也叫BLE(Bluetooth Low Energy)</li>
<li>目前应用比较多的案例:运动手环,嵌入式设备,智能家居</li>
</ul>
</li>
</ul>
<h4 id="">设计到的系统/框架</h4>
<ul>
<li>HealthKit/物联网HomeKit/wathOS1,2/iBeacon</li>
</ul>
<h1 id="gamekit">GameKit用法</h1>
<h2 id="">一.准备工作</h2>
<ul>
<li><p>1.搭建UI
<img src="Snip20150928_1.png" alt="图1"></p>
</li>
<li><p>2.拖线</p>
</li>
</ul>
<pre><code class="lang-objc"><span class="hljs-comment">// 图片</span>
<span class="hljs-keyword">@property</span> (<span class="hljs-keyword">weak</span>, <span class="hljs-keyword">nonatomic</span>) <span class="hljs-keyword">IBOutlet</span> <span class="hljs-built_in">UIImageView</span> *imageView;

<span class="hljs-comment">// 建立连接</span>
- (<span class="hljs-keyword">IBAction</span>)buildConnect:(<span class="hljs-keyword">id</span>)sender{}

<span class="hljs-comment">// 发送数据</span>
- (<span class="hljs-keyword">IBAction</span>)sendData:(<span class="hljs-keyword">id</span>)sender{}
</code></pre>
<h2 id="">二.连接蓝牙</h2>
<ul>
<li>显示可以连接的蓝牙设备列表</li>
</ul>
<pre><code class="lang-objc">- (<span class="hljs-keyword">IBAction</span>)buildConnect:(<span class="hljs-keyword">id</span>)sender {
    <span class="hljs-comment">// 创建弹窗</span>
    GKPeerPickerController *ppc = [[GKPeerPickerController alloc] init];
    <span class="hljs-comment">// 设置代理  @interface ViewController () &lt;GKPeerPickerControllerDelegate&gt;</span>
    ppc<span class="hljs-variable">.delegate</span> = <span class="hljs-keyword">self</span>;
    <span class="hljs-comment">// 展示</span>
    [ppc show];
}
</code></pre>
<ul>
<li>监听蓝牙的连接</li>
</ul>
<pre><code class="lang-objc"><span class="hljs-preprocessor">#pragma mark -GKPeerPickerControllerDelegate</span>
<span class="hljs-comment">// 连接成功就会调用</span>
- (<span class="hljs-keyword">void</span>)peerPickerController:(GKPeerPickerController *)picker <span class="hljs-comment">// 弹窗</span>
              didConnectPeer:(<span class="hljs-built_in">NSString</span> *)peerID <span class="hljs-comment">// 连接到的蓝牙设备号</span>
                   toSession:(GKSession *)session <span class="hljs-comment">// 连接会话(通过它进行数据交互)</span>
{
    <span class="hljs-built_in">NSLog</span>(<span class="hljs-string">@"%s, line = %d"</span>, __FUNCTION__, __LINE__);
    <span class="hljs-comment">// 弹窗消失</span>
    [picker dismiss];
}
</code></pre>
<h2 id="">三.利用蓝牙传输数据</h2>
<ul>
<li>点击图片从相册中选择一张显示本机<ul>
<li>可以修改imaV为Btn,也可以为imaV添加手势<ul>
<li>1.修改imageView的用户交互
<img src="bleIma/Snip20150928_2.png" alt="图2"></li>
<li>2.添加手势到图片上
<img src="bleIma/Snip20150928_3.png" alt="图3"></li>
<li>3.拖出手势的响应事件
<img src="bleIma/Snip20150928_4.png" alt="图4"></li>
<li>4.完善相册选择图片代码</li>
</ul>
</li>
</ul>
</li>
</ul>
<pre><code class="lang-objc">    <span class="hljs-comment">// 手势-点击从相册中选一张照片</span>
- (<span class="hljs-keyword">IBAction</span>)tapImage:(UITapGestureRecognizer *)sender {
    <span class="hljs-built_in">NSLog</span>(<span class="hljs-string">@"%s, line = %d"</span>, __FUNCTION__, __LINE__);
    <span class="hljs-comment">// 先判断是否有相册</span>
    <span class="hljs-keyword">if</span> (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]) {
        <span class="hljs-keyword">return</span>;
    }
    <span class="hljs-comment">// 创建弹出的控制器</span>
    UIImagePickerController *ipc = [[UIImagePickerController alloc] init];
    <span class="hljs-comment">// 设置图片来源为相册</span>
    ipc<span class="hljs-variable">.sourceType</span> = UIImagePickerControllerSourceTypePhotoLibrary;
    <span class="hljs-comment">// 设置代理 @interface ViewController () &lt;UINavigationControllerDelegate, UIImagePickerControllerDelegate&gt;</span>
    ipc<span class="hljs-variable">.delegate</span> = <span class="hljs-keyword">self</span>;
    <span class="hljs-comment">// modal出来</span>
    [<span class="hljs-keyword">self</span> presentViewController:ipc animated:<span class="hljs-literal">YES</span> completion:<span class="hljs-literal">nil</span>];
}
<span class="hljs-preprocessor">#pragma mark - UINavigationControllerDelegate, UIImagePickerControllerDelegate</span>
<span class="hljs-comment">// 选中某图片后调用</span>
- (<span class="hljs-keyword">void</span>)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(<span class="hljs-built_in">NSDictionary</span>&lt;<span class="hljs-built_in">NSString</span> *,<span class="hljs-keyword">id</span>&gt; *)info
{
    <span class="hljs-comment">// 控制器返回</span>
    [picker dismissViewControllerAnimated:<span class="hljs-literal">YES</span> completion:<span class="hljs-literal">nil</span>];
    <span class="hljs-comment">// 设置图片</span>
    <span class="hljs-keyword">self</span><span class="hljs-variable">.imageView</span><span class="hljs-variable">.image</span> = info[UIImagePickerControllerOriginalImage];
}
</code></pre>
<ul>
<li>点击发送数据完成图片显示到另一个蓝牙机器上<ul>
<li>1.分析需要通过GKSession对象来传递数据,所以在<code>peerPickerController:didConnectPeer:didConnectPeer:</code>的方法中保存session会话</li>
</ul>
</li>
</ul>
<pre><code class="lang-objc"><span class="hljs-keyword">@property</span> (<span class="hljs-keyword">nonatomic</span>, <span class="hljs-keyword">strong</span>) GKSession *session; <span class="hljs-comment">/**&lt; 蓝牙连接会话 */</span>

<span class="hljs-comment">// 连接成功就会调用</span>
- (<span class="hljs-keyword">void</span>)peerPickerController:(GKPeerPickerController *)picker <span class="hljs-comment">// 弹窗</span>
              didConnectPeer:(<span class="hljs-built_in">NSString</span> *)peerID <span class="hljs-comment">// 连接到的蓝牙设备号</span>
                   toSession:(GKSession *)session <span class="hljs-comment">// 连接会话(通过它进行数据交互)</span>
{
    <span class="hljs-built_in">NSLog</span>(<span class="hljs-string">@"%s, line = %d"</span>, __FUNCTION__, __LINE__);
    <span class="hljs-comment">// 弹窗消失</span>
    [picker dismiss];
    <span class="hljs-comment">// 保存会话</span>
    <span class="hljs-keyword">self</span><span class="hljs-variable">.session</span> = session;
}
</code></pre>
<ul>
<li>发送</li>
</ul>
<pre><code class="lang-objc"><span class="hljs-comment">// 发送数据</span>
- (<span class="hljs-keyword">IBAction</span>)sendData:(<span class="hljs-keyword">id</span>)sender {
    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">self</span><span class="hljs-variable">.imageView</span><span class="hljs-variable">.image</span> == <span class="hljs-literal">nil</span>) <span class="hljs-keyword">return</span>; <span class="hljs-comment">// 有图片才继续执行</span>
    <span class="hljs-comment">// 通过蓝牙链接会话发送数据到所有设备</span>
    [<span class="hljs-keyword">self</span><span class="hljs-variable">.session</span> sendDataToAllPeers:UIImagePNGRepresentation(<span class="hljs-keyword">self</span><span class="hljs-variable">.imageView</span><span class="hljs-variable">.image</span>) <span class="hljs-comment">// 数据</span>
                        withDataMode:GKSendDataReliable <span class="hljs-comment">// 枚举:发完为止</span>
                               error:<span class="hljs-literal">nil</span>];

}
</code></pre>
<ul>
<li>接收</li>
</ul>
<pre><code class="lang-objc"><span class="hljs-comment">// 连接成功就会调用</span>
- (<span class="hljs-keyword">void</span>)peerPickerController:(GKPeerPickerController *)picker <span class="hljs-comment">// 弹窗</span>
              didConnectPeer:(<span class="hljs-built_in">NSString</span> *)peerID <span class="hljs-comment">// 连接到的蓝牙设备号</span>
                   toSession:(GKSession *)session <span class="hljs-comment">// 连接会话(通过它进行数据交互)</span>
{
    <span class="hljs-built_in">NSLog</span>(<span class="hljs-string">@"%s, line = %d"</span>, __FUNCTION__, __LINE__);
    <span class="hljs-comment">// 弹窗消失</span>
    [picker dismiss];
    <span class="hljs-comment">// 保存会话</span>
    <span class="hljs-keyword">self</span><span class="hljs-variable">.session</span> = session;
    <span class="hljs-comment">// 处理接收到的数据[蓝牙设备接收到数据时,就会调用 [self receiveData:fromPeer:inSession:context:]]</span>
    <span class="hljs-comment">// 设置数据接受者为:self</span>
    [<span class="hljs-keyword">self</span><span class="hljs-variable">.session</span> setDataReceiveHandler:<span class="hljs-keyword">self</span>
                       withContext:<span class="hljs-literal">nil</span>];
}
<span class="hljs-preprocessor">#pragma mark - 蓝牙设备接收到数据时,就会调用</span>
- (<span class="hljs-keyword">void</span>)receiveData:(<span class="hljs-built_in">NSData</span> *)data <span class="hljs-comment">// 数据</span>
           fromPeer:(<span class="hljs-built_in">NSString</span> *)peer <span class="hljs-comment">// 来自哪个设备</span>
          inSession:(GKSession *)session <span class="hljs-comment">// 连接会话</span>
            context:(<span class="hljs-keyword">void</span> *)context
{
    <span class="hljs-built_in">NSLog</span>(<span class="hljs-string">@"%s, line = %d"</span>, __FUNCTION__, __LINE__);
    <span class="hljs-comment">// 显示</span>
    <span class="hljs-keyword">self</span><span class="hljs-variable">.imageView</span><span class="hljs-variable">.image</span> = [<span class="hljs-built_in">UIImage</span> imageWithData:data];
    <span class="hljs-comment">// 写入相册</span>
    UIImageWriteToSavedPhotosAlbum(<span class="hljs-keyword">self</span><span class="hljs-variable">.imageView</span><span class="hljs-variable">.image</span>, <span class="hljs-literal">nil</span>, <span class="hljs-literal">nil</span>, <span class="hljs-literal">nil</span>);
}
</code></pre>
<h2 id="">四.注意</h2>
<ul>
<li>只能用于iOS设备之间的链接</li>
<li>只能用于同一个应用程序之间的连接</li>
<li>最好别利用蓝牙发送比较大的数据</li>
</ul>
<h1 id="multipeerconnectivity">MultipeerConnectivity</h1>
<ul>
<li><p>在iOS7中,引入了一个全新的框架——Multipeer Connectivity(多点连接)。</p>
</li>
<li><p>利用Multipeer Connectivity框架,即使在<code>没有连接到WiFi(WLAN)或移动网络(xG)</code>的情况下,距离较近的Apple设备(iMac/iPad/iPhone)之间可基于<code>蓝牙和WiFi(P2P WiFi)</code>技术进行发现和连接实现近场通信。</p>
</li>
<li><p>Multipeer Connectivity扩充的功能与利用AirDrop传输文件非常类似,可以将其看作AirDrop不能直接使用的补偿,代价是需要自己实现。</p>
</li>
<li><p>手机不联网也能跟附近的人聊得火热的<code>FireChat</code>和<code>See You Around</code>等近场聊天App、近距离无网遥控交互拍照神器<code>拍咯App</code>就是基于Multipeer Connectivity框架实现。</p>
</li>
<li><p>相比AirDrop,Multipeer Connectivity在进行发现和会话时并不要求同时打开WiFi和蓝牙,也不像AirDrop那样强制打开这两个开关,而是根据条件适时选择使用蓝牙或(和)WiFi。</p>
</li>
<li><p>粗略测试情况如下:</p>
<ul>
<li>双方WiFi和蓝牙<code>都未打开</code>:无法发现。</li>
<li>双方都开启<code>蓝牙</code>:通过蓝牙发现和传输。</li>
<li><p>双方都开启<code>WiFi</code>:通过WiFi Direct发现和传输,速度接近AirDrop(Reliable速率稍低),不知道同一WLAN下是否优先走局域网?</p>
</li>
<li><p>双方都<code>同时开启了WiFi和蓝牙</code>:应该是模拟AirDrop,通过低功耗蓝牙技术扫描发现握手,然后通过WiFi Direct传输。</p>
</li>
</ul>
</li>
</ul>
<h4 id="">常用类</h4>
<ul>
<li>MCPeerID</li>
</ul>
<p>类似sockaddr,用于标识连接的两端endpoint,通常是<code>昵称或设备名称</code>。</p>
<p>该对象只开放了displayName属性,私有MCPeerIDInternal对象持有的设备相关的_idString/_pid64字段并<code>未公开</code>。
在许多情况下,客户端同时广播并发现同一个服务,这将导致一些混乱,尤其是在<code>client/server</code>模式中。所以,每一个服务都应有一个类型标示符——<code>serviceType,它是由ASCII字母、数字和“-”组成的短文本串,最多15个字符</code>。</p>
<ul>
<li>MCNearbyServiceAdvertiser</li>
</ul>
<p>类似broadcaster,<code>可以接收,并处理用户请求连接的响应。但是,这个类会有回调,告知有用户要与您的设备连接,然后可以自定义提示框,以及自定义连接处理</code>。</p>
<p>主线程(com.apple.main-thread(serial))创建MCNearbyServiceAdvertiser并启动startAdvertisingPeer。
MCNearbyServiceAdvertiserDelegate异步回调(didReceiveInvitationFromPeer)切换回主线程。
在主线程didReceiveInvitationFromPeer中创建MCSession并invitationHandler(YES, session)接受会话连接请求(accept参数为YES)。</p>
<ul>
<li>MCNearbyServiceBrowser</li>
</ul>
<p>类似servo listen+client connect,<code>用于搜索附近的用户,并可以对搜索到的用户发出邀请加入某个会话中</code>。
主线程(com.apple.main-thread(serial))创建MCNearbyServiceBrowser并启动startBrowsingForPeers。
MCNearbyServiceBrowserDelegate异步回调(foundPeer/lostPeer)切换回主线程。
主线程创建MCSession并启动invitePeer。</p>
<ul>
<li>MCSession</li>
</ul>
<p><code>启用和管理Multipeer连接会话中的所有人之间的沟通。 通过Sesion,给别人发送数据</code>
注意,peerID并不具备设备识别属性。
类似TCP链接中的socket。创建MCSession时,需指定自身MCPeerID,类似bind。
为避免频繁的会话数据通知阻塞主线程,MCSessionDelegate异步回调(didChangeState/didReceiveCertificate/didReceiveData/didReceiveStream)有一个专门的回调线程——com.apple.MCSession.callbackQueue(serial)。为避免阻塞MCSeesion回调线程,最好新建数据读(写)线程!</p>
<ul>
<li>MCAdvertiserAssistant/MCBrowserViewController</li>
</ul>
<p><code>MCAdvertiserAssistant   //可以接收,并处理用户请求连接的响应。没有回调,会弹出默认的提示框,并处理连接。</code>
<code>MCBrowserViewController 弹出搜索框,需要手动modal</code></p>
<p>MCAdvertiserAssistant为针对Advertiser封装的管理助手;MCBrowserViewController继承自UIViewController,提供了基本的UI应用框架。
MCBrowser/MCAdvertiser的回调线程一般是delegate所在线程Queue:com.apple.main-thread(serial)。</p>
<h1 id="exteralaccessory">ExteralAccessory</h1>
<ul>
<li>MFI:(make for iPhone/iPad/iTouch)专门为苹果设备制作的设备</li>
<li>支持MFI的设备开发使用此框架</li>
</ul>
<h1 id="basek">baseK(相关基础知识)</h1>
<h3 id="">蓝牙常见名称和缩写</h3>
<ul>
<li>BLE:(Bluetooth low energy)蓝牙4.0设备因为低耗电,也叫BLE</li>
<li>peripheral,central:外设和中心设备,发起链接的是central(一般是指手机),被链接的设备是peripheral(运动手环)</li>
<li>service and characteristic:(服务和特征)每个设备会提供服务和特征,类似于服务端的API,但是结构不同.<code>每个设备会有很多服务</code>,每个服务中包含<code>很多字段</code>,这些字段的权限一般分为读(read),写(write),通知(notify)几种,就是我们连接设备后具体需要操作的内容</li>
<li>Description:每个characteristic可以对应一个或者多个Description用于描述characteristic的信息或属性(eg.范围,计量单位)</li>
</ul>
<h3 id="">蓝牙基础知识</h3>
<ul>
<li><p>CoreBluetooth框架的核心其实是俩东西:peripheral和central,对应他们分别有一组相关的API和类
<img src="ble_01.png" alt="图4.1.1"></p>
</li>
<li><p>这两组api粉笔对应不同的业务常见:左侧叫中心模式,就是以你的app作为中心,连接其他的外设的场景;而右侧称为外设模式,使用<code>手机作为外设</code>连接其他中心设备操作的场景</p>
</li>
<li><p>服务和特征(service and characteristic)</p>
<ul>
<li>每个设备都会有1个or多个服务</li>
<li>每个服务里都会有1个or多个特征</li>
<li>特征就是具体键值对,提供数据的地方</li>
<li>每个特征属性分为:读,写,通知等等</li>
</ul>
</li>
</ul>
<pre><code class="lang-objc"><span class="hljs-keyword">typedef</span> NS_OPTIONS(NSUInteger, CBCharacteristicProperties) {
    CBCharacteristicPropertyBroadcast                                                = <span class="hljs-number">0x01</span>,
    CBCharacteristicPropertyRead                                                    = <span class="hljs-number">0x02</span>,
    CBCharacteristicPropertyWriteWithoutResponse                                    = <span class="hljs-number">0x04</span>,
    CBCharacteristicPropertyWrite                                                    = <span class="hljs-number">0x08</span>,
    CBCharacteristicPropertyNotify                                                    = <span class="hljs-number">0x10</span>,
    CBCharacteristicPropertyIndicate                                                = <span class="hljs-number">0x20</span>,
    CBCharacteristicPropertyAuthenticatedSignedWrites                                = <span class="hljs-number">0x40</span>,
    CBCharacteristicPropertyExtendedProperties                                        = <span class="hljs-number">0x80</span>,
    CBCharacteristicPropertyNotifyEncryptionRequired NS_ENUM_AVAILABLE(NA, <span class="hljs-number">6</span>_0)        = <span class="hljs-number">0x100</span>,
    CBCharacteristicPropertyIndicateEncryptionRequired NS_ENUM_AVAILABLE(NA, <span class="hljs-number">6</span>_0)    = <span class="hljs-number">0x200</span>
};
</code></pre>
<ul>
<li>外设,服务,特征的关系
<img src="ble_02.png" alt="图4.1.2"></li>
</ul>
<h2 id="ble">BLE中心模式流程</h2>
<ul>
<li>1.建立中心角色</li>
<li>2.扫描外设(Discover Peripheral)</li>
<li>3.连接外设(Connect Peripheral)</li>
<li>4.扫描外设中的服务和特征(Discover Services And Characteristics)<ul>
<li>4.1 获取外设的services</li>
<li>4.2 获取外设的Characteristics,获取characteristics的值,,获取Characteristics的Descriptor和Descriptor的值</li>
</ul>
</li>
<li>5.利用特征与外设做数据交互(Explore And Interact)</li>
<li>6.订阅Characteristic的通知</li>
<li>7.断开连接(Disconnect)</li>
</ul>
<h2 id="ble">BLE外设模式流程</h2>
<ul>
<li>1.启动一个Peripheral管理对象</li>
<li>2.本地peripheral设置服务,特征,描述,权限等等</li>
<li>3.peripheral发送广告</li>
<li>4.设置处理订阅,取消订阅,读characteristic,写characteristic的代理方法</li>
</ul>
<h2 id="">蓝牙设备的状态</h2>
<ul>
<li>1.待机状态(standby):设备没有传输和发送数据,并且没有连接到任何外设</li>
<li>2.广播状态(Advertiser):周期性广播状态</li>
<li>3.扫描状态(Scanner):主动搜索正在广播的设备</li>
<li>4.发起链接状态(Initiator):主动向扫描设备发起连接</li>
<li>5.主设备(Master):作为主设备连接到其它设备.</li>
<li>6.从设备(Slave):作为从设备链接到其它设备</li>
</ul>
<h2 id="">蓝牙设备的五种工作状态</h2>
<ul>
<li>准备(Standby)</li>
<li>广播(Advertising)</li>
<li>监听扫描(Scanning)</li>
<li>发起连接(Initiating)</li>
<li>已连接(Connected)</li>
</ul>
<h2 id="">蓝牙和版本使用限制</h2>
<ul>
<li>蓝牙2.0:越狱设备</li>
<li>BLE:iOS6以上</li>
<li>MFI认证设备:无限制</li>
</ul>
<h2 id="ble">BLE测试</h2>
<ul>
<li>两台BLE设备</li>
<li>如何让iOS模拟器也能测试BLE?<ul>
<li>买一个CSR蓝牙4.0 USB适配器,插在Mac上</li>
<li>在终端输入sudo nvram bluetoothHostControllerSwitchBehavior=&quot;never&quot;</li>
<li>重启Mac</li>
<li>用Xcode4.6调试代码,将程序跑在iOS6.1模拟器上</li>
<li>苹果把iOS7.0模拟器对BLE的支持移除了</li>
</ul>
</li>
</ul>
<h1 id="ble-coding">BLE中心模式流程-coding</h1>
<h2 id="ble">BLE中心模式流程</h2>
<ul>
<li>1.建立中心角色</li>
<li>2.扫描外设(Discover Peripheral)</li>
<li>3.连接外设(Connect Peripheral)</li>
<li>4.扫描外设中的服务和特征(Discover Services And Characteristics)<ul>
<li>4.1 获取外设的services</li>
<li>4.2 获取外设的Characteristics,获取characteristics的值,,获取Characteristics的Descriptor和Descriptor的值</li>
</ul>
</li>
<li>5.利用特征与外设做数据交互(Explore And Interact)</li>
<li>6.订阅Characteristic的通知</li>
<li>7.断开连接(Disconnect)</li>
</ul>
<h2 id="">准备环境</h2>
<ul>
<li>1.Xcode7.0</li>
<li>2.手机</li>
<li>3.外设(手机+LightBlue)</li>
</ul>
<h2 id="">实现步骤</h2>
<h4 id="1cb">1.导入CB头文件,建立主设备管理类,设置主设备代理</h4>
<pre><code class="lang-objc"><span class="hljs-preprocessor">#import <span class="hljs-title">&lt;CoreBluetooth/CoreBluetooth.h&gt;</span></span>
<span class="hljs-class"><span class="hljs-keyword">@interface</span> <span class="hljs-title">XMGBLEController</span> () &lt;<span class="hljs-title">CBCentralManagerDelegate</span>&gt;</span>
<span class="hljs-keyword">@property</span> (<span class="hljs-keyword">nonatomic</span>, <span class="hljs-keyword">strong</span>) CBCentralManager *cMgr; <span class="hljs-comment">/**&lt; 中心管理设备 */</span>
<span class="hljs-keyword">@end</span>
<span class="hljs-class"><span class="hljs-keyword">@implementation</span> <span class="hljs-title">XMGBLEController</span></span>
<span class="hljs-preprocessor">#pragma mark - 懒加载</span>
<span class="hljs-comment">// 1.建立中心管理者</span>
- (CBCentralManager *)cMgr
{
    <span class="hljs-keyword">if</span> (!_cMgr) {
        <span class="hljs-built_in">NSLog</span>(<span class="hljs-string">@"%s, line = %d"</span>, __FUNCTION__, __LINE__);
        <span class="hljs-comment">/*
         设置主设备的代理,CBCentralManagerDelegate
         必须实现的:
         - (void)centralManagerDidUpdateState:(CBCentralManager *)central;//主设备状态改变调用,在初始化CBCentralManager的适合会打开设备,只有当设备正确打开后才能使用
         其他选择实现的委托中比较重要的:
         - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI; //找到外设
         - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral;//连接外设成功
         - (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;//外设连接失败
         - (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;//断开外设
         */</span>
        _cMgr = [[CBCentralManager alloc] initWithDelegate:<span class="hljs-keyword">self</span> queue:dispatch_get_main_queue()]; <span class="hljs-comment">// 线程不传默认是主线程</span>
    }
    <span class="hljs-keyword">return</span> _cMgr;
}
- (<span class="hljs-keyword">void</span>)viewDidLoad {
    [<span class="hljs-keyword">super</span> viewDidLoad];
    <span class="hljs-keyword">self</span><span class="hljs-variable">.title</span> = <span class="hljs-string">@"BLE"</span>;
    <span class="hljs-keyword">self</span><span class="hljs-variable">.view</span><span class="hljs-variable">.backgroundColor</span> = [<span class="hljs-built_in">UIColor</span> orangeColor];
    <span class="hljs-comment">// 初始化</span>
    [<span class="hljs-keyword">self</span> cMgr];
    <span class="hljs-comment">// 不能在此处扫描,因为状态还没变为打开</span>
    <span class="hljs-comment">//[self.cMgr scanForPeripheralsWithServices:nil options:nil];</span>
}
</code></pre>
<h4 id="2">2.扫描外设</h4>
<ul>
<li>扫描的方法防治cMgr成功打开的代理方法中</li>
<li>只有设备成功打开,才能开始扫描,否则会报错</li>
</ul>
<pre><code class="lang-objc"><span class="hljs-preprocessor">#pragma mark - CBCentralManagerDelegate</span>
<span class="hljs-comment">// 中心管理者状态改变, 在初始化CBCentralManager的时候会打开设备,只有当设备正确打开后才能使用</span>
- (<span class="hljs-keyword">void</span>)centralManagerDidUpdateState:(CBCentralManager *)central
{
    <span class="hljs-built_in">NSLog</span>(<span class="hljs-string">@"%s, line = %d"</span>, __FUNCTION__, __LINE__);
    <span class="hljs-keyword">switch</span> (central<span class="hljs-variable">.state</span>) {
        <span class="hljs-keyword">case</span> CBCentralManagerStateUnknown:
            <span class="hljs-built_in">NSLog</span>(<span class="hljs-string">@"&gt;&gt;&gt;CBCentralManagerStateUnknown"</span>);
            <span class="hljs-keyword">break</span>;
        <span class="hljs-keyword">case</span> CBCentralManagerStateResetting:
            <span class="hljs-built_in">NSLog</span>(<span class="hljs-string">@"&gt;&gt;&gt;CBCentralManagerStateResetting"</span>);
            <span class="hljs-keyword">break</span>;
        <span class="hljs-keyword">case</span> CBCentralManagerStateUnsupported:
            <span class="hljs-built_in">NSLog</span>(<span class="hljs-string">@"&gt;&gt;&gt;CBCentralManagerStateUnsupported"</span>);
            <span class="hljs-keyword">break</span>;
        <span class="hljs-keyword">case</span> CBCentralManagerStateUnauthorized:
            <span class="hljs-built_in">NSLog</span>(<span class="hljs-string">@"&gt;&gt;&gt;CBCentralManagerStateUnauthorized"</span>);
            <span class="hljs-keyword">break</span>;
        <span class="hljs-keyword">case</span> CBCentralManagerStatePoweredOff:
            <span class="hljs-built_in">NSLog</span>(<span class="hljs-string">@"&gt;&gt;&gt;CBCentralManagerStatePoweredOff"</span>);
            <span class="hljs-keyword">break</span>;
        <span class="hljs-keyword">case</span> CBCentralManagerStatePoweredOn:
            <span class="hljs-built_in">NSLog</span>(<span class="hljs-string">@"&gt;&gt;&gt;CBCentralManagerStatePoweredOn"</span>);
            <span class="hljs-comment">// 2.开始扫描周围的外设</span>
            <span class="hljs-comment">/*
             第一个参数nil就是扫描周围所有的外设,扫描到外设后会进入
             - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI;
             */</span>
            [<span class="hljs-keyword">self</span><span class="hljs-variable">.cMgr</span> scanForPeripheralsWithServices:<span class="hljs-literal">nil</span> options:<span class="hljs-literal">nil</span>];

            <span class="hljs-keyword">break</span>;
        <span class="hljs-keyword">default</span>:
        <span class="hljs-keyword">break</span>;
    }
}
<span class="hljs-comment">// 扫描到设备会进入到此代理方法</span>
- (<span class="hljs-keyword">void</span>)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(<span class="hljs-built_in">NSDictionary</span>&lt;<span class="hljs-built_in">NSString</span> *,<span class="hljs-keyword">id</span>&gt; *)advertisementData RSSI:(<span class="hljs-built_in">NSNumber</span> *)RSSI
{
    <span class="hljs-built_in">NSLog</span>(<span class="hljs-string">@"%s, line = %d, per = %@, data = %@, rssi = %@"</span>, __FUNCTION__, __LINE__, peripheral, advertisementData, RSSI);
    <span class="hljs-comment">// 接下来连接设备</span>
}
</code></pre>
<h4 id="3">3.连接外设</h4>
<ul>
<li>扫描手环,打印结果
<img src="bleIma/Snip20150928_5.png" alt="图4.2.1"></li>
<li>根据打印结果</li>
</ul>
<pre><code class="lang-objc"><span class="hljs-comment">// 扫描到设备会进入到此代理方法</span>
- (<span class="hljs-keyword">void</span>)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(<span class="hljs-built_in">NSDictionary</span>&lt;<span class="hljs-built_in">NSString</span> *,<span class="hljs-keyword">id</span>&gt; *)advertisementData RSSI:(<span class="hljs-built_in">NSNumber</span> *)RSSI
{
    <span class="hljs-built_in">NSLog</span>(<span class="hljs-string">@"%s, line = %d, per = %@, data = %@, rssi = %@"</span>, __FUNCTION__, __LINE__, peripheral, advertisementData, RSSI);

    <span class="hljs-comment">// 3.接下来可以连接设备</span>
    <span class="hljs-comment">//如果你没有设备,可以下载一个app叫lightbule的app去模拟一个设备</span>
    <span class="hljs-comment">//这里自己去设置下连接规则,我设置的是二维码扫描到的运动手环的设备号</span>
    <span class="hljs-comment">// 判断设备号是否扫描到</span>
    <span class="hljs-keyword">if</span> ([peripheral<span class="hljs-variable">.name</span> isEqualToString:<span class="hljs-string">@"OBand-75"</span>]) {
        <span class="hljs-comment">/*
         一个主设备最多能连7个外设,每个外设最多只能给一个主设备连接,连接成功,失败,断开会进入各自的委托
         - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral;//连接外设成功的委托
         - (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;//外设连接失败的委托
         - (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;//断开外设的委托
         */</span>
        <span class="hljs-comment">// 保存外设,否则方法结束就销毁</span>
        <span class="hljs-keyword">self</span><span class="hljs-variable">.per</span> = peripheral;
        [<span class="hljs-keyword">self</span><span class="hljs-variable">.cMgr</span> connectPeripheral:<span class="hljs-keyword">self</span><span class="hljs-variable">.per</span> options:<span class="hljs-literal">nil</span>];
    }<span class="hljs-keyword">else</span>
    {
        <span class="hljs-comment">// 此处Alert提示未扫描到设备,重新扫描</span>
<span class="hljs-preprocessor">#warning noCode</span>
        <span class="hljs-built_in">NSLog</span>(<span class="hljs-string">@"没扫描到 &gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;  %s, line = %d"</span>, __FUNCTION__, __LINE__);
    }
}
<span class="hljs-comment">// 外设连接成功</span>
- (<span class="hljs-keyword">void</span>)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
    <span class="hljs-built_in">NSLog</span>(<span class="hljs-string">@"%s, line = %d"</span>, __FUNCTION__, __LINE__);
    <span class="hljs-built_in">NSLog</span>(<span class="hljs-string">@"&gt;&gt;&gt;连接到名称为(%@)的设备-成功"</span>,peripheral<span class="hljs-variable">.name</span>);
}
<span class="hljs-comment">// 外设连接失败</span>
- (<span class="hljs-keyword">void</span>)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(<span class="hljs-built_in">NSError</span> *)error
{
    <span class="hljs-built_in">NSLog</span>(<span class="hljs-string">@"%s, line = %d"</span>, __FUNCTION__, __LINE__);
}
<span class="hljs-comment">// 断开连接(丢失连接)</span>
- (<span class="hljs-keyword">void</span>)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(<span class="hljs-built_in">NSError</span> *)error
{
    <span class="hljs-built_in">NSLog</span>(<span class="hljs-string">@"%s, line = %d"</span>, __FUNCTION__, __LINE__);
}
</code></pre>
<h4 id="4">4.扫描外设中的服务和特征</h4>
<pre><code class="lang-objc">  设备链接成功后,就可以扫描设备的服务(services)了,同样是通过代理,扫描到结果后会触发某代理方法.
  注意:此时CBCentralManagerDelegate已经不能满足需求,需要新的CBPeripheralDelegate来搞定.
  该协议中包含了central与peripheral的许多回调方法
  (eg.:获取services,获取characteristics,获取characteristics的值,获取characteristics的Descriptor以及Descriptor的值,写数据,读RSSI,用通知的方式订阅数据等等).
</code></pre>
<ul>
<li>4.1 获取外设的services<ul>
<li>首先设置外设的代理,并搜寻services</li>
<li>然后在代理方法<code>peripheral:didDiscoverServices:</code>中遍历services</li>
</ul>
</li>
</ul>
<pre><code class="lang-objc"><span class="hljs-comment">// 外设连接成功</span>
- (<span class="hljs-keyword">void</span>)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
    <span class="hljs-built_in">NSLog</span>(<span class="hljs-string">@"%s, line = %d"</span>, __FUNCTION__, __LINE__);
    <span class="hljs-built_in">NSLog</span>(<span class="hljs-string">@"&gt;&gt;&gt;连接到名称为(%@)的设备-成功"</span>,peripheral<span class="hljs-variable">.name</span>);
    <span class="hljs-comment">//设置的peripheral代理CBPeripheralDelegate</span>
    <span class="hljs-comment">//@interface ViewController : UIViewController&lt;CBCentralManagerDelegate,CBPeripheralDelegate&gt;</span>
    [peripheral setDelegate:<span class="hljs-keyword">self</span>];

    <span class="hljs-comment">//扫描外设Services,成功后会进入方法:-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{</span>
    [peripheral discoverServices:<span class="hljs-literal">nil</span>];
    <span class="hljs-comment">/*
     Discovers the specified services of the peripheral.
     An array of CBUUID objects that you are interested in. Here, each CBUUID object represents a UUID that identifies the type of service you want to discover.
     */</span>
}

<span class="hljs-preprocessor">#pragma mark - CBPeripheralDelegate</span>
<span class="hljs-comment">// 发现外设的service</span>
- (<span class="hljs-keyword">void</span>)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(<span class="hljs-built_in">NSError</span> *)error
{
    <span class="hljs-keyword">if</span> (error)
    {
        <span class="hljs-built_in">NSLog</span>(<span class="hljs-string">@"&gt;&gt;&gt;Discovered services for %@ with error: %@"</span>, peripheral<span class="hljs-variable">.name</span>, [error localizedDescription]);
        <span class="hljs-keyword">return</span>;
    }

    <span class="hljs-keyword">for</span> (CBService *service <span class="hljs-keyword">in</span> peripheral<span class="hljs-variable">.services</span>) {
        <span class="hljs-built_in">NSLog</span>(<span class="hljs-string">@"service.UUID = %@"</span>, service<span class="hljs-variable">.UUID</span>);
        <span class="hljs-comment">//扫描每个service的Characteristics,扫描到后会进入方法: -(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error</span>
        [peripheral discoverCharacteristics:<span class="hljs-literal">nil</span> forService:service];
    }
}
</code></pre>
<ul>
<li>4.2 获取外设的characteris,获取Characteristics的值,获取Characteristics的Descriptor以及Descriptor的值</li>
</ul>
<pre><code class="lang-objc"><span class="hljs-comment">// 外设发现service的特征</span>
- (<span class="hljs-keyword">void</span>)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(<span class="hljs-built_in">NSError</span> *)error
{
    <span class="hljs-keyword">if</span> (error)
    {
        <span class="hljs-built_in">NSLog</span>(<span class="hljs-string">@"error Discovered characteristics for %@ with error: %@"</span>, service<span class="hljs-variable">.UUID</span>, [error localizedDescription]);
        <span class="hljs-keyword">return</span>;
    }
    <span class="hljs-keyword">for</span> (CBCharacteristic *characteristic <span class="hljs-keyword">in</span> service<span class="hljs-variable">.characteristics</span>)
    {
        <span class="hljs-built_in">NSLog</span>(<span class="hljs-string">@"service:%@ 的 Characteristic: %@"</span>,service<span class="hljs-variable">.UUID</span>,characteristic<span class="hljs-variable">.UUID</span>);
    }

<span class="hljs-preprocessor">#warning noCodeFor 优化,分开写是为了让大家看注释清晰,并不符合编码规范</span>
    <span class="hljs-comment">//获取Characteristic的值,读到数据会进入方法:-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error</span>
    <span class="hljs-keyword">for</span> (CBCharacteristic *characteristic <span class="hljs-keyword">in</span> service<span class="hljs-variable">.characteristics</span>){
        [peripheral readValueForCharacteristic:characteristic]; <span class="hljs-comment">// 外设读取特征的值</span>
    }

    <span class="hljs-comment">//搜索Characteristic的Descriptors,读到数据会进入方法:-(void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error</span>
    <span class="hljs-keyword">for</span> (CBCharacteristic *characteristic <span class="hljs-keyword">in</span> service<span class="hljs-variable">.characteristics</span>){
        [peripheral discoverDescriptorsForCharacteristic:characteristic]; <span class="hljs-comment">// 外设发现特征的描述</span>
    }
}

<span class="hljs-comment">// 获取characteristic的值</span>
- (<span class="hljs-keyword">void</span>)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(nonnull CBCharacteristic *)characteristic error:(nullable <span class="hljs-built_in">NSError</span> *)error
{
    <span class="hljs-comment">//打印出characteristic的UUID和值</span>
    <span class="hljs-comment">//!注意,value的类型是NSData,具体开发时,会根据外设协议制定的方式去解析数据</span>
    <span class="hljs-built_in">NSLog</span>(<span class="hljs-string">@"%s, line = %d, characteristic.UUID:%@  value:%@"</span>, __FUNCTION__, __LINE__, characteristic<span class="hljs-variable">.UUID</span>, characteristic<span class="hljs-variable">.value</span>);
}
<span class="hljs-comment">// 获取Characteristics的 descriptor的值</span>
- (<span class="hljs-keyword">void</span>)peripheral:(CBPeripheral *)peripheral didUpdateValueForDescriptor:(nonnull CBDescriptor *)descriptor error:(nullable <span class="hljs-built_in">NSError</span> *)error
{
    <span class="hljs-comment">//打印出DescriptorsUUID 和value</span>
    <span class="hljs-comment">//这个descriptor都是对于characteristic的描述,一般都是字符串,所以这里我们转换成字符串去解析</span>
    <span class="hljs-built_in">NSLog</span>(<span class="hljs-string">@"%s, line = %d, descriptor.UUID:%@ value:%@"</span>, __FUNCTION__, __LINE__, descriptor<span class="hljs-variable">.UUID</span>, descriptor<span class="hljs-variable">.value</span>);
}
<span class="hljs-comment">// 发现特征Characteristics的描述Descriptor</span>
- (<span class="hljs-keyword">void</span>)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(nonnull CBCharacteristic *)characteristic error:(nullable <span class="hljs-built_in">NSError</span> *)error
{
    <span class="hljs-built_in">NSLog</span>(<span class="hljs-string">@"%s, line = %d"</span>, __FUNCTION__, __LINE__);
    <span class="hljs-keyword">for</span> (CBDescriptor *descriptor <span class="hljs-keyword">in</span> characteristic<span class="hljs-variable">.descriptors</span>) {
        <span class="hljs-built_in">NSLog</span>(<span class="hljs-string">@"descriptor.UUID:%@"</span>,descriptor<span class="hljs-variable">.UUID</span>);
    }
}
</code></pre>
<h4 id="5">5.写数据到特征中</h4>
<pre><code class="lang-objc"><span class="hljs-comment">// 5.将数据写入特征(自定义方法,为了看的更清楚,没别的意思)</span>
- (<span class="hljs-keyword">void</span>)yf_peripheral:(CBPeripheral *)peripheral writeData:(<span class="hljs-built_in">NSData</span> *)data forCharacteristic:(CBCharacteristic *)characteristic
{
    <span class="hljs-comment">/*
    typedef NS_OPTIONS(NSUInteger, CBCharacteristicProperties) {
        CBCharacteristicPropertyBroadcast                                                = 0x01,
        CBCharacteristicPropertyRead                                                    = 0x02,
        CBCharacteristicPropertyWriteWithoutResponse                                    = 0x04,
        CBCharacteristicPropertyWrite                                                    = 0x08,
        CBCharacteristicPropertyNotify                                                    = 0x10,
        CBCharacteristicPropertyIndicate                                                = 0x20,
        CBCharacteristicPropertyAuthenticatedSignedWrites                                = 0x40,
        CBCharacteristicPropertyExtendedProperties                                        = 0x80,
        CBCharacteristicPropertyNotifyEncryptionRequired NS_ENUM_AVAILABLE(NA, 6_0)        = 0x100,
        CBCharacteristicPropertyIndicateEncryptionRequired NS_ENUM_AVAILABLE(NA, 6_0)    = 0x200
    };
     打印出特征的权限(characteristic.properties),可以看到有很多种,这是一个NS_OPTIONS的枚举,可以是多个值
     常见的又read,write,noitfy,indicate.知道这几个基本够用了,前俩是读写权限,后俩都是通知,俩不同的通知方式
     */</span>
    <span class="hljs-built_in">NSLog</span>(<span class="hljs-string">@"%s, line = %d, characteristic.properties:%d"</span>, __FUNCTION__, __LINE__, characteristic<span class="hljs-variable">.properties</span>);

    <span class="hljs-comment">// 只有特征的properties中有写的属性时候,才写</span>
    <span class="hljs-keyword">if</span> (characteristic<span class="hljs-variable">.properties</span> &amp; CBCharacteristicPropertyWrite) {
        <span class="hljs-comment">// 这句才是正宗的核心代码</span>
        [peripheral writeValue:data forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
    }
}
</code></pre>
<h4 id="6">6.订阅特征的通知</h4>
<pre><code class="lang-objc"><span class="hljs-comment">// 设置通知</span>
- (<span class="hljs-keyword">void</span>)yf_peripheral:(CBPeripheral *)peripheral setNotifyForCharacteristic:(CBCharacteristic *)characteristic
{
    <span class="hljs-comment">// 设置通知, 数据会进入 peripheral:didUpdateValueForCharacteristic:error:方法</span>
    [peripheral setNotifyValue:<span class="hljs-literal">YES</span> forCharacteristic:characteristic];
}
<span class="hljs-comment">// 取消通知</span>
- (<span class="hljs-keyword">void</span>)yf_peripheral:(CBPeripheral *)peripheral cancelNotifyForCharacteristic:(CBCharacteristic *)characteristic
{
    [peripheral setNotifyValue:<span class="hljs-literal">NO</span> forCharacteristic:characteristic];
}
</code></pre>
<h4 id="7">7.断开连接</h4>
<pre><code class="lang-objc"><span class="hljs-comment">// 7.断开连接</span>
- (<span class="hljs-keyword">void</span>)yf_cMgr:(CBCentralManager *)cMgr stopScanAndDisConnectWithPeripheral:(CBPeripheral *)peripheral
{
    <span class="hljs-comment">// 停止扫描</span>
    [cMgr stopScan];
    <span class="hljs-comment">// 断开连接</span>
    [cMgr cancelPeripheralConnection:peripheral];
}
</code></pre>
<h1 id="ble-periphral">BLE-periphral外设模式流程</h1>
<h4 id="ble">之前在基础知识介绍过BLE应用的两种流程,如图:</h4>
<p><img src="ble_01.png" alt="图4.1.1"></p>
<ul>
<li>central模式用的都是左边的类,而peripheral模式用的是右边的类</li>
</ul>
<h2 id="peripheral">peripheral模式的流程</h2>
<ul>
<li>1.引入CoreBluetooth框架,初始化peripheralManager</li>
<li>2.设置peripheralManager中的内容</li>
<li>3.开启广播advertising</li>
<li>4.对central的操作进行响应<ul>
<li>4.1 读characteristics请求</li>
<li>4.2 写characteristics请求</li>
<li>4.4 订阅和取消订阅characteristics</li>
</ul>
</li>
</ul>
<h2 id="">准备环境</h2>
<ul>
<li>Xcode</li>
<li>真机(4s以上)</li>
</ul>
<h2 id="">具体操作步骤</h2>
<h4 id="1corebluetoothperipheralmanager">1.引入CoreBluetooth框架,初始化peripheralManager</h4>
<pre><code class="lang-objc"><span class="hljs-preprocessor">#import <span class="hljs-title">&lt;CoreBluetooth/CoreBluetooth.h&gt;</span></span>
<span class="hljs-class"><span class="hljs-keyword">@interface</span> <span class="hljs-title">XMGBLEPeripheralViewController</span> () &lt;<span class="hljs-title">CBPeripheralManagerDelegate</span>&gt;</span>
<span class="hljs-keyword">@property</span> (<span class="hljs-keyword">nonatomic</span>, <span class="hljs-keyword">strong</span>) CBPeripheralManager *pMgr; <span class="hljs-comment">/**&lt; 外设管理者 */</span>
<span class="hljs-keyword">@end</span>

<span class="hljs-class"><span class="hljs-keyword">@implementation</span> <span class="hljs-title">XMGBLEPeripheralViewController</span></span>
<span class="hljs-comment">// 懒加载</span>
- (CBPeripheralManager *)pMgr
{
    <span class="hljs-keyword">if</span> (!_pMgr) {
        _pMgr = [[CBPeripheralManager alloc] initWithDelegate:<span class="hljs-keyword">self</span> queue:dispatch_get_main_queue()];
    }
    <span class="hljs-keyword">return</span> _pMgr;
}

- (<span class="hljs-keyword">void</span>)viewDidLoad {
    [<span class="hljs-keyword">super</span> viewDidLoad];
    <span class="hljs-comment">// 调用get方法初始化,初始化后CBPeripheralManager状态改变会调用代理方法peripheralManagerDidUpdateState:</span>
    <span class="hljs-comment">// 模拟器永远也不会是CBPeripheralManagerStatePoweredOn状态</span>
    [<span class="hljs-keyword">self</span> pMgr];
}
</code></pre>
<h4 id="2peripheralmanager">2.设置peripheralManager中的内容</h4>
<ul>
<li>创建characteristics及其description,</li>
<li>创建service,把characteristics添加到service中,</li>
<li>再把service添加到peripheralManager中</li>
</ul>
<pre><code class="lang-objc"><span class="hljs-preprocessor">#pragma mark - CBPeripheralManagerDelegate</span>
<span class="hljs-comment">// CBPeripheralManager初始化后会触发的方法</span>
- (<span class="hljs-keyword">void</span>)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral
{
    <span class="hljs-keyword">if</span> (peripheral<span class="hljs-variable">.state</span> == CBPeripheralManagerStatePoweredOn) {
        <span class="hljs-comment">// 提示设备成功打开</span>
        [SVProgressHUD showSuccessWithStatus:<span class="hljs-string">@"xmg设备打开成功~"</span>];
        <span class="hljs-comment">// 配置各种服务入CBPeripheralManager</span>
        [<span class="hljs-keyword">self</span> yf_setupPMgr];
    }<span class="hljs-keyword">else</span>
    {
        <span class="hljs-comment">// 提示设备打开失败</span>
        [SVProgressHUD showErrorWithStatus:<span class="hljs-string">@"失败!"</span>];
    }
}

<span class="hljs-preprocessor">#pragma mark - 私有方法</span>
- (<span class="hljs-keyword">void</span>)yf_setupPMgr
{
    <span class="hljs-comment">// 特征描述的UUID</span>
    CBUUID *characteristicUserDescriptionUUID = [CBUUID UUIDWithString:CBUUIDCharacteristicUserDescriptionString];
    <span class="hljs-comment">// 特征的通知UUID</span>
    CBUUID *notifyCharacteristicUUID = [CBUUID UUIDWithString:notiyCharacteristicStrUUID];
    <span class="hljs-comment">// 特征的读写UUID</span>
    CBUUID *readwriteCharacteristicUUID = [CBUUID UUIDWithString:readwriteCharacteristicStrUUID];
    <span class="hljs-comment">// 特征的只读UUID</span>
    CBUUID *readCharacteristicUUID = [CBUUID UUIDWithString:readwriteCharacteristicStrUUID];
    CBUUID *ser1UUID = [CBUUID UUIDWithString:Service1StrUUID];
    CBUUID *ser2UUID = [CBUUID UUIDWithString:Service2StrUUID];


    <span class="hljs-comment">// 初始化一个特征的描述</span>
    CBMutableDescriptor *des1 = [[CBMutableDescriptor alloc] initWithType:characteristicUserDescriptionUUID value:<span class="hljs-string">@"xmgDes1"</span>];

    <span class="hljs-comment">// 可通知的特征</span>
    CBMutableCharacteristic *notifyCharacteristic = [[CBMutableCharacteristic alloc] initWithType:notifyCharacteristicUUID <span class="hljs-comment">// UUID</span>
                                                                                       properties:CBCharacteristicPropertyNotify <span class="hljs-comment">// 枚举:通知</span>
                                                                                            value:<span class="hljs-literal">nil</span> <span class="hljs-comment">// 数据先不传</span>
                                                                                      permissions:CBAttributePermissionsReadable]; <span class="hljs-comment">// 枚举:可读</span>
    <span class="hljs-comment">// 可读写的特征</span>
    CBMutableCharacteristic *readwriteChar = [[CBMutableCharacteristic alloc] initWithType:readwriteCharacteristicUUID
                                                                                properties:CBCharacteristicPropertyRead | CBCharacteristicPropertyWrite
                                                                                     value:<span class="hljs-literal">nil</span>
                                                                               permissions:CBAttributePermissionsReadable | CBAttributePermissionsWriteable];
    [readwriteChar setDescriptors:@[des1]]; <span class="hljs-comment">// 设置特征的描述</span>

    <span class="hljs-comment">// 只读特征</span>
    CBMutableCharacteristic *readChar = [[CBMutableCharacteristic alloc] initWithType:readCharacteristicUUID
                                                                           properties:CBCharacteristicPropertyRead
                                                                                value:<span class="hljs-literal">nil</span>
                                                                          permissions:CBAttributePermissionsReadable];

    <span class="hljs-comment">// 初始化服务1</span>
    CBMutableService *ser1 = [[CBMutableService alloc] initWithType:ser1UUID primary:<span class="hljs-literal">YES</span>];
    <span class="hljs-comment">// 为服务设置俩特征(通知, 带描述的读写)</span>
    [ser1 setCharacteristics:@[notifyCharacteristic, readwriteChar]];

    <span class="hljs-comment">// 初始化服务2,并且添加一个只读特征</span>
    CBMutableService *ser2 = [[CBMutableService alloc] initWithType:ser2UUID primary:<span class="hljs-literal">YES</span>];
    ser2<span class="hljs-variable">.characteristics</span> = @[readChar];

    <span class="hljs-comment">// 添加服务进外设管理者</span>
    <span class="hljs-comment">// 添加操作会触发代理方法peripheralManager:didAddService:error:</span>
    [<span class="hljs-keyword">self</span><span class="hljs-variable">.pMgr</span> addService:ser1];
    [<span class="hljs-keyword">self</span><span class="hljs-variable">.pMgr</span> addService:ser2];
}
</code></pre>
<h4 id="3">3.开启广播</h4>
<pre><code class="lang-objc"><span class="hljs-comment">// 添加服务进CBPeripheralManager时会触发的方法</span>
- (<span class="hljs-keyword">void</span>)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(<span class="hljs-built_in">NSError</span> *)error
{
    <span class="hljs-comment">// 由于添加了两次ser,所以方法会调用两次</span>
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">if</span> (!error) {
        i++;
    }

    <span class="hljs-comment">// 当第二次进入方法时候,代表两个服务添加完毕,此时要用到2,由于没有扩展性,所以新增了可变数组,记录添加的服务数量</span>
    <span class="hljs-keyword">if</span> (i == <span class="hljs-keyword">self</span><span class="hljs-variable">.servieces</span><span class="hljs-variable">.count</span>) {
        <span class="hljs-comment">// 广播内容</span>
        <span class="hljs-built_in">NSDictionary</span> *advertDict = @{CBAdvertisementDataServiceUUIDsKey: [<span class="hljs-keyword">self</span><span class="hljs-variable">.servieces</span> valueForKeyPath:<span class="hljs-string">@"UUID"</span>],
                                     CBAdvertisementDataLocalNameKey:LocalNameKey};
        <span class="hljs-comment">// 发出广播,会触发peripheralManagerDidStartAdvertising:error:</span>
        [peripheral startAdvertising:advertDict];
    }
}
<span class="hljs-comment">// 开始广播触发的代理</span>
- (<span class="hljs-keyword">void</span>)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(<span class="hljs-built_in">NSError</span> *)error
{

}

&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;分割线&gt;&gt;&gt;&gt;下面是修改的地方

<span class="hljs-keyword">@property</span> (<span class="hljs-keyword">nonatomic</span>, <span class="hljs-keyword">strong</span>) <span class="hljs-built_in">NSMutableArray</span> *servieces; <span class="hljs-comment">/**&lt; 服务可变数组 */</span>
<span class="hljs-comment">// 自定义服务</span>
- (<span class="hljs-built_in">NSMutableArray</span> *)servieces
{
    <span class="hljs-keyword">if</span> (!_servieces) {
        _servieces = [<span class="hljs-built_in">NSMutableArray</span> array];
    }
    <span class="hljs-keyword">return</span> _servieces;
}
<span class="hljs-preprocessor">#pragma mark - 私有方法</span>
- (<span class="hljs-keyword">void</span>)yf_setupPMgr
{
    ...

    <span class="hljs-comment">// 初始化服务1</span>
    CBMutableService *ser1 = [[CBMutableService alloc] initWithType:ser1UUID primary:<span class="hljs-literal">YES</span>];
    <span class="hljs-comment">// 为服务设置俩特征(通知, 带描述的读写)</span>
    [ser1 setCharacteristics:@[notifyCharacteristic, readwriteChar]];
    [<span class="hljs-keyword">self</span><span class="hljs-variable">.servieces</span> addObject:ser1];

    <span class="hljs-comment">// 初始化服务2,并且添加一个只读特征</span>
    CBMutableService *ser2 = [[CBMutableService alloc] initWithType:ser2UUID primary:<span class="hljs-literal">YES</span>];
    ser2<span class="hljs-variable">.characteristics</span> = @[readChar];
    [<span class="hljs-keyword">self</span><span class="hljs-variable">.servieces</span> addObject:ser2];

    <span class="hljs-comment">// 添加服务进外设管理者</span>
    <span class="hljs-comment">// 添加操作会触发代理方法peripheralManager:didAddService:error:</span>
    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">self</span><span class="hljs-variable">.servieces</span><span class="hljs-variable">.count</span>) {
        <span class="hljs-keyword">for</span> (CBMutableService *ser <span class="hljs-keyword">in</span> <span class="hljs-keyword">self</span><span class="hljs-variable">.servieces</span>) {
            [<span class="hljs-keyword">self</span><span class="hljs-variable">.pMgr</span> addService:ser];
        }
    }
}
</code></pre>
<h4 id="4central">4.对central的操作做出响应</h4>
<ul>
<li>4.1 读characteristics请求</li>
<li>4.2 写characteristics请求</li>
<li>4.3 订阅和取消订阅characteristics</li>
</ul>
<pre><code class="lang-objc"><span class="hljs-comment">// 外设收到读的请求,然后读特征的值赋值给request</span>
- (<span class="hljs-keyword">void</span>)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request
{
    <span class="hljs-built_in">NSLog</span>(<span class="hljs-string">@"%s, line = %d"</span>, __FUNCTION__, __LINE__);
    <span class="hljs-comment">// 判断是否可读</span>
    <span class="hljs-keyword">if</span> (request<span class="hljs-variable">.characteristic</span><span class="hljs-variable">.properties</span> &amp; CBCharacteristicPropertyRead) {
        <span class="hljs-built_in">NSData</span> *data = request<span class="hljs-variable">.characteristic</span><span class="hljs-variable">.value</span>;

        request<span class="hljs-variable">.value</span> = data;
        <span class="hljs-comment">// 对请求成功做出响应</span>
        [<span class="hljs-keyword">self</span><span class="hljs-variable">.pMgr</span> respondToRequest:request withResult:CBATTErrorSuccess];
    }<span class="hljs-keyword">else</span>
    {
        [<span class="hljs-keyword">self</span><span class="hljs-variable">.pMgr</span> respondToRequest:request withResult:CBATTErrorWriteNotPermitted];
    }
}
<span class="hljs-comment">// 外设收到写的请求,然后读request的值,写给特征</span>
- (<span class="hljs-keyword">void</span>)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(<span class="hljs-built_in">NSArray</span>&lt;CBATTRequest *&gt; *)requests
{
    <span class="hljs-built_in">NSLog</span>(<span class="hljs-string">@"%s, line = %d, requests = %@"</span>, __FUNCTION__, __LINE__, requests);
    CBATTRequest *request = requests<span class="hljs-variable">.firstObject</span>;
    <span class="hljs-keyword">if</span> (request<span class="hljs-variable">.characteristic</span><span class="hljs-variable">.properties</span> &amp; CBCharacteristicPropertyWrite) {
        <span class="hljs-built_in">NSData</span> *data = request<span class="hljs-variable">.value</span>;
        <span class="hljs-comment">// 此处赋值要转类型,否则报错</span>
        CBMutableCharacteristic *mChar = (CBMutableCharacteristic *)request<span class="hljs-variable">.characteristic</span>;
        mChar<span class="hljs-variable">.value</span> = data;
        <span class="hljs-comment">// 对请求成功做出响应</span>
        [<span class="hljs-keyword">self</span><span class="hljs-variable">.pMgr</span> respondToRequest:request withResult:CBATTErrorSuccess];
    }<span class="hljs-keyword">else</span>
    {
        [<span class="hljs-keyword">self</span><span class="hljs-variable">.pMgr</span> respondToRequest:request withResult:CBATTErrorWriteNotPermitted];
    }
}


<span class="hljs-comment">// 与CBCentral的交互</span>
<span class="hljs-comment">// 订阅特征</span>
- (<span class="hljs-keyword">void</span>)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic
{
    <span class="hljs-built_in">NSLog</span>(<span class="hljs-string">@"%s, line = %d, 订阅了%@的数据"</span>, __FUNCTION__, __LINE__, characteristic<span class="hljs-variable">.UUID</span>);
    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:<span class="hljs-number">2.0</span>
                                                      target:<span class="hljs-keyword">self</span>
                                                    selector:<span class="hljs-keyword">@selector</span>(yf_sendData:)
                                                    userInfo:characteristic
                                                     repeats:<span class="hljs-literal">YES</span>];

    <span class="hljs-keyword">self</span><span class="hljs-variable">.timer</span> = timer;

    <span class="hljs-comment">/* 另一种方法 */</span>
<span class="hljs-comment">//    NSTimer *testTimer = [NSTimer timerWithTimeInterval:2.0</span>
<span class="hljs-comment">//                                                 target:self</span>
<span class="hljs-comment">//                                               selector:@selector(yf_sendData:)</span>
<span class="hljs-comment">//                                               userInfo:characteristic</span>
<span class="hljs-comment">//                                                repeats:YES];</span>
<span class="hljs-comment">//    [[NSRunLoop currentRunLoop] addTimer:testTimer forMode:NSDefaultRunLoopMode];</span>

}
<span class="hljs-comment">// 取消订阅特征</span>
- (<span class="hljs-keyword">void</span>)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic
{
    <span class="hljs-built_in">NSLog</span>(<span class="hljs-string">@"%s, line = %d, 取消订阅了%@的数据"</span>, __FUNCTION__, __LINE__, characteristic<span class="hljs-variable">.UUID</span>);
    [<span class="hljs-keyword">self</span><span class="hljs-variable">.timer</span> invalidate];
    <span class="hljs-keyword">self</span><span class="hljs-variable">.timer</span> = <span class="hljs-literal">nil</span>;
}

- (<span class="hljs-keyword">void</span>)peripheralManagerIsReadyToUpdateSubscribers:(CBPeripheralManager *)peripheral
{
    <span class="hljs-built_in">NSLog</span>(<span class="hljs-string">@"%s, line = %d"</span>, __FUNCTION__, __LINE__);
}

<span class="hljs-comment">// 计时器每隔两秒调用的方法</span>
- (<span class="hljs-built_in">BOOL</span>)yf_sendData:(NSTimer *)timer
{
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    dateFormatter<span class="hljs-variable">.dateFormat</span> = <span class="hljs-string">@"yy:MM:dd:HH:mm:ss"</span>;

    <span class="hljs-built_in">NSString</span> *now = [dateFormatter stringFromDate:[<span class="hljs-built_in">NSDate</span> date]];
    <span class="hljs-built_in">NSLog</span>(<span class="hljs-string">@"now = %@"</span>, now);

    <span class="hljs-comment">// 执行回应central通知数据</span>
    <span class="hljs-keyword">return</span>  [<span class="hljs-keyword">self</span><span class="hljs-variable">.pMgr</span> updateValue:[now dataUsingEncoding:NSUTF8StringEncoding]
         forCharacteristic:timer<span class="hljs-variable">.userInfo</span>
      onSubscribedCentrals:<span class="hljs-literal">nil</span>];
}
</code></pre>
<h1 id="ibeacon">iBeacon简介</h1>
<ul>
<li>iBeacon起源:苹果在WWDC2013上正式推出了iBeacon,并且在iOS7设备商配置了该功能</li>
<li>iBeacon应用:苹果期望将其作为一种技术标准,这个标准允许移动App(包括iOS和Android设备)监听来自于iBeacon设备上的信号并作出响应.</li>
<li>iBeacon设备:配备有BLE通信功能,并使用BLE向周围发送自己特有的ID,移动设备上的App在接收到该ID后可以作出相应的反应.比如,我们在店铺里设置iBeacon发射器,便可以让应用接收到信息并将这一信息通知给服务器,服务器向我们的App返回与该店铺相关的产品或折扣信息.</li>
<li>本质上讲,iBeacon技术允许App了解他们在某个局部范围内的位置,并向用户分发基于位置的超文本上下文内容.</li>
</ul>
<h1 id="airdrop">AirDrop</h1>
<ul>
<li><p>苹果在<code>2010</code>推出的<code>OS X 10.7 Lion系统</code>中加入了全新的AirDrop功能,该功能<code>允许两台Mac机之间无线传输文件</code>。
区别于传统的局域网文件共享方式,AirDrop不要求两台机器在同一个网络内。
用户无需设置,只需要打开AirDrop文件夹即可查看到其他用户,分享文件变得非常便捷。</p>
</li>
<li><p>AirDrop不需要基于(无线)路由器或者手动建立热点组网,它是利用Mac与Mac之间的点对点网络来进行会话传输。
这一切由系统在后台完成,无需断开当前WiFi网络,也不影响当前连接WiFi网络的通信,就可以与其他Mac通过内置特定信道通信。</p>
</li>
<li><p>WWDC13上推出的iOS7也开始支持iOS设备之间使用AirDrop实现共享传输。
关于AirDrop的条件要求及内部机制,可参考<a href="http://www.zhihu.com/question/21681429" target="_blank">《为什么iOS 7 和 OS X 之间的AirDrop 不能互传?》</a>。
WWDC14推出的OS X 10.10 Yosemite操作系统,终于打通了与iOS移动设备之间的<a href="https://support.apple.com/zh-cn/HT204144" target="_blank">跨平台AirDrop传输</a>。
运行Mac OS X Yosemite 10.10版本的Mac设备(型号≥2012)和运行iOS 7及以上的iOS设备(≥iPhone5,≥iPad 4,iPad mini,≥iPod touch)之间才能实现跨平台文件传输。</p>
</li>
<li><p>根据官方资料显示,AirDrop基于蓝牙和WiFi实现(AirDrop does the rest using Wi-Fi and Bluetooth)。
具体来说,通过低功耗蓝牙技术(<a href="http://www.warski.org/blog/2014/01/how-ibeacons-work/" target="_blank">BLE</a>)进行发现(Advertising/Browsing),使用<a href="http://www.xuebuyuan.com/539020.html" target="_blank">WiFi Direct(P2P WiFi)</a>技术进行数据传输。
可参考《<a href="http://www.zhihu.com/question/21189545" target="_blank">iOS 7的AirDrop是利用什么信号来传输的?</a>》《<a href="http://ipad.about.com/od/iPad_Guide/ss/What-Is-Airdrop-How-Does-It-Work.htm" target="_blank">What Is AirDrop? How Does It Work?</a>》。</p>
</li>
<li><p>因此,开启AirDrop不要求双方必须联网或连接到同一局域网,但必须<code>同时打开WiFi和蓝牙</code>,且进行传输的两台设备必须保持在<code>9米</code>的范围之内。</p>
</li>
</ul>

                    
                    </section>
                
                
                </div>
            </div>
        </div>

        
        <a href="../memory/README.html" class="navigation navigation-prev " aria-label="Previous page: memory"><i class="fa fa-angle-left"></i></a>
        
        
        <a href="../touchid/README.html" class="navigation navigation-next " aria-label="Next page: touchid"><i class="fa fa-angle-right"></i></a>
        
    </div>
</div>

        
<script src="../gitbook/app.js"></script>

    
    <script src="https://cdn.mathjax.org/mathjax/2.4-latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
    

    
    <script src="../gitbook/plugins/gitbook-plugin-mathjax/plugin.js"></script>
    

<script>
require(["gitbook"], function(gitbook) {
    var config = {"fontSettings":{"theme":null,"family":"sans","size":2}};
    gitbook.start(config);
});
</script>

        
    </body>
    
</html>