在 2.0+ 版本支持通过局域网进行跨平台、跨设备、跨系统分享传输数据。
支持平台:iOS、iPadOS、macOS、Browser
注意:数据的发送与接收,此功能需要两台设备才能进行操作。
举个例子:
接收设备:一台 MacBook Air
发送设备:一台 MacBook Pro
在接收设备上,打开“接收”界面,然后放置即可。
具体见下面:
发送数据时,大部分操作都集中在发送设备,具体步骤如下图:
当发送完成后,即可在接收设备中浏览接收到的文件,例如:图片、视频。
具体操作为切换到“设置”界面即可,如下图:
发送和接收设备,可以是一台 Mac 电脑和一台移动设备(iPhone),不过对应的 App 有不同,都可以通过在 AppStore 中搜索“NearSend”下载。
NearSend 已经实现了移动设备和 Mac 之间的数据传输互通。
https://itunes.apple.com/app/id6446226378
注意 需要将应用版本升级到 2.0 及以上。
例如:使用 iPhone 设备上的 Safari 打开 http://192.168.1.102/
根据界面按钮进行操作,即可分享数据。
因为现在数据都是通过蓝牙模块进行传输,因此,如果蓝牙模块没有开启或者损坏,则无法进行传输。
问题解决方法:
如上图所示,默认接收文件存储在 ~/Downloads/NearSend
文件夹下。
因为接收文件是实时写入的,也就是说,当发送方设备进行文件传输时,接收方设备会在相应位置创建文件进行接收&写入,所以在传输过程中是会看到文件为 0 字节的情况,一旦文件接收完毕,则文件写入完成,大小也会随之更新。
]]>In the process of use, it is inevitable to encounter some problems, the purpose of this article is to solve the common problems encountered by users in the process of use.
In version 2.0+, data sharing and transmission over LAN is supported across platforms, devices, and systems.
Supported platforms: iOS, iPadOS, macOS, Browser
Notice:Data transmission and reception, this function requires two devices to operate.
For example:
Receiving device: An MacBook Air
Sending device: An MacBook Pro
On the receiving device, open the “Receive” page and place it.
See below:
When sending data, most of the operations are concentrated on the sending device, and the specific steps are as follows:
When the sending is complete, you can browse the received files, such as pictures and videos, in the receiving device.
The specific operation is to switch to the “Setting” page, as shown below:
Finder
to locate the specified folder, which contains the received file data.The sending and receiving devices can be a Mac computer and a mobile device (iPhone), but the corresponding apps are different and can be downloaded by searching for “NearSend” in the AppStore.
NearSend already enables data transfer between mobile devices and Macs.
https://itunes.apple.com/app/id6446226378
Note You need to upgrade your app version to 2.0 and above.
Example: Open http://192.168.1.102/
using Safari on your iPhone device.
Follow the interface buttons to share data.
Because data is now transmitted through the Bluetooth module, if the Bluetooth module is not turned on or damaged, it cannot be transmitted.
Problem Resolution:
As shown in the image above, the default receive file is stored under the ~/Downloads/NearSend
folder.
Because the receiving file is written in real time, that is, when the sender device transfers the file, the receiving device will create a file in the corresponding location for receive & write, so it will see that the file is 0 bytes during the transfer, and once the file is received, the file is written and the size is updated.
]]>在 2.0+ 版本支持通过局域网进行跨平台、跨设备、跨系统分享传输数据。
支持平台:iOS、iPadOS、macOS、Browser
注意:数据的发送与接收,此功能需要两台设备才能进行操作。
举个例子:
接收设备:一台 iPad
发送设备:一台 iPhone
在接收设备上,打开“接收”界面,然后放置即可。
具体见下面:
发送数据时,大部分操作都集中在发送设备,具体步骤如下图:
当发送完成后,即可在接收设备中浏览接收到的文件,例如:图片、视频。
具体操作为切换到“浏览”界面即可,如下图:
发送和接收设备,可以是一台 Mac 电脑和一台移动设备(iPhone),不过对应的 App 有不同,都可以通过在 AppStore 中搜索“NearSend”下载。
NearSend 已经实现了移动设备和 Mac 之间的数据传输互通。
https://itunes.apple.com/app/id6446226378
注意 需要将应用版本升级到 2.0 及以上。
例如:使用 iPhone 设备上的 Safari 打开 http://192.168.1.102/
根据界面按钮进行操作,即可分享数据。
因为现在数据都是通过蓝牙模块进行传输,因此,如果蓝牙模块没有开启或者损坏,则无法进行传输。
问题解决方法:
如上图所示,默认接收文件存储在 NearSend 沙盒中。
因为接收文件是实时写入的,也就是说,当发送方设备进行文件传输时,接收方设备会在相应位置创建文件进行接收&写入,所以在传输过程中是会看到文件为 0 字节的情况,一旦文件接收完毕,则文件写入完成,大小也会随之更新。
]]>In the process of use, it is inevitable to encounter some problems, the purpose of this article is to solve the common problems encountered by users in the process of use.
In version 2.0+, data sharing and transmission over LAN is supported across platforms, devices, and systems.
Supported platforms: iOS, iPadOS, macOS, Browser
Notice:Data transmission and reception, this function requires two devices to operate.
For example:
Receiving device: An iPad
Sending device: An iPhone
On the receiving device, open the “Receive” page and place it.
See below:
When sending data, most of the operations are concentrated on the sending device, and the specific steps are as follows:
When the sending is complete, you can browse the received files, such as pictures and videos, in the receiving device.
The specific operation is to switch to the “Folder” page, as shown below:
The sending and receiving devices can be a Mac computer and a mobile device (iPhone), but the corresponding apps are different and can be downloaded by searching for “NearSend” in the AppStore.
NearSend already enables data transfer between mobile devices and Macs.
https://itunes.apple.com/app/id6446226378
Note You need to upgrade your app version to 2.0 and above.
Example: Open http://192.168.1.102/
using Safari on your iPhone device.
Follow the interface buttons to share data.
Because data is now transmitted through the Bluetooth module, if the Bluetooth module is not turned on or damaged, it cannot be transmitted.
Problem Resolution:
As shown in the preceding figure, the default receive file is stored in the NearSend sandbox.
Because the receiving file is written in real time, that is, when the sender device transfers the file, the receiving device will create a file in the corresponding location for receive & write, so it will see that the file is 0 bytes during the transfer, and once the file is received, the file is written and the size is updated.
]]>下载应用请前往 @AppStore ,搜索 ‘NearSend’ 即可。
To download the app, go to @AppStore and search for ‘NearSend’.
E-mail:feedback.nearsend@gmail.com
]]>Effective date: March 24, 2023
NearSend (“us”, “we”, or “our”) operates the NearSend mobile application (the “Service”).
This page informs you of our policies regarding the collection, use, and disclosure of personal data when you use our Service and the choices you have associated with that data. Our Privacy Policy for NearSend is created with the help of the Free Privacy Policy Generator.
We use your data to provide and improve the Service. By using the Service, you agree to the collection and use of information in accordance with this policy. Unless otherwise defined in this Privacy Policy, terms used in this Privacy Policy have the same meanings as in our Terms and Conditions.
We collect several different types of information for various purposes to provide and improve our Service to you.
While using our Service, we may ask you to provide us with certain personally identifiable information that can be used to contact or identify you (“Personal Data”). Personally identifiable information may include, but is not limited to:
When you access the Service by or through a mobile device, we may collect certain information automatically, including, but not limited to, the type of mobile device you use, your mobile device unique ID, the IP address of your mobile device, your mobile operating system, the type of mobile Internet browser you use, unique device identifiers and other diagnostic data (“Usage Data”).
We use cookies and similar tracking technologies to track the activity on our Service and hold certain information.
Cookies are files with small amount of data which may include an anonymous unique identifier. Cookies are sent to your browser from a website and stored on your device. Tracking technologies also used are beacons, tags, and scripts to collect and track information and to improve and analyze our Service.
You can instruct your browser to refuse all cookies or to indicate when a cookie is being sent. However, if you do not accept cookies, you may not be able to use some portions of our Service.
NearSend uses the collected data for various purposes:
Your information, including Personal Data, may be transferred to — and maintained on — computers located outside of your state, province, country or other governmental jurisdiction where the data protection laws may differ than those from your jurisdiction.
If you are located outside China and choose to provide information to us, please note that we transfer the data, including Personal Data, to China and process it there.
Your consent to this Privacy Policy followed by your submission of such information represents your agreement to that transfer.
NearSend will take all steps reasonably necessary to ensure that your data is treated securely and in accordance with this Privacy Policy and no transfer of your Personal Data will take place to an organization or a country unless there are adequate controls in place including the security of your data and other personal information.
NearSend may disclose your Personal Data in the good faith belief that such action is necessary to:
The security of your data is important to us, but remember that no method of transmission over the Internet, or method of electronic storage is 100% secure. While we strive to use commercially acceptable means to protect your Personal Data, we cannot guarantee its absolute security.
We may employ third party companies and individuals to facilitate our Service (“Service Providers”), to provide the Service on our behalf, to perform Service-related services or to assist us in analyzing how our Service is used.
These third parties have access to your Personal Data only to perform these tasks on our behalf and are obligated not to disclose or use it for any other purpose.
We may use third-party Service Providers to monitor and analyze the use of our Service.
Firebase is analytics service provided by Google Inc.
You may opt-out of certain Firebase features through your mobile device settings, such as your device advertising settings or by following the instructions provided by Google in their Privacy Policy.
We also encourage you to review the Google’s policy for safeguarding your data: https://support.google.com/analytics/answer/6004245. For more information on what type of information Firebase collects, please visit please visit the Google Privacy & Terms web page.
Our Service may contain links to other sites that are not operated by us. If you click on a third party link, you will be directed to that third party’s site. We strongly advise you to review the Privacy Policy of every site you visit.
We have no control over and assume no responsibility for the content, privacy policies or practices of any third party sites or services.
Our Service does not address anyone under the age of 18 (“Children”).
We do not knowingly collect personally identifiable information from anyone under the age of 18. If you are a parent or guardian and you are aware that your Children has provided us with Personal Data, please contact us. If we become aware that we have collected Personal Data from children without verification of parental consent, we take steps to remove that information from our servers.
We may update our Privacy Policy from time to time. We will notify you of any changes by posting the new Privacy Policy on this page.
We will let you know via email and/or a prominent notice on our Service, prior to the change becoming effective and update the “effective date” at the top of this Privacy Policy.
You are advised to review this Privacy Policy periodically for any changes. Changes to this Privacy Policy are effective when they are posted on this page.
If you have any questions about this Privacy Policy, please contact us:
By email: feedback.nearsend@gmail.com
]]>ipa
包做一些解析。但是,因为 Apple 的安全机制,我们很难再获取 ipa
文件。为了解决这个问题,作者想到了逆向工程,文章记录了如何操作,并成功实现 ipa
文件提取。
作者使用的是 iPhone 5s,iOS 12.5.5
越狱操作可以使用爱思助手提供的一键越狱
功能完成
https://github.com/KJCracks/Clutch
有两种方法可以实现将 Clutch 拷贝至已越狱设备
1、可以通过爱思助手将 Clutch 可执行文件拷贝到 /usr/bin
路径下
2、通过终端拷贝
$ scp Clutch root@设备ip:/usr/bin/
密码默认:alpine
,设备ip可用通过查看网络信息获取,需要注意的是,设备与电脑需要连接在同一网络下。
1、连接设备
$ ssh root@设备ip
通过 ssh 连接设备,密码默认:alpine
。
连接成功后可以在设备 /usr/bin/
目录下查看 Clutch
是否存在。
2、给 Clutch 添加可执行权限
1 | $ cd /usr/bin |
3、确定 Clutch 是否可执行
可以在命令行输入 Clutch,显示下图则说明操作成功
1 | $ Clutch -b // 砸壳,砸壳后文件是二进制文件 |
1、获取设备上的安装应用
$ Clutch -i
2、砸壳
$ Clutch -d 6
表示要砸 云哈利波特:魔法觉醒
这款应用的壳。
3、DONE
上图中 DONE
即表示砸壳成功,后面则是ipa
文件路径,可以通过爱思助手或终端将 ipa
文件导出。
scp -r root@设备ip:/private/var/mobile/Documents/Dumped/xxx.ipa ~/Desktop/
密码默认:alpine
。
到此,整个砸壳和提取 ipa
文件操作结束。
随着音视频、云游戏越来越火,一个开源解决方案 WebRTC 成为了众多技术者绕不过的框架。在了解 WebRTC 基础流程后,笔者也萌生了一个想法:使用 WebRTC 实现一个点对点的视频 & 文本单聊程序。根据 WebRTC 的框架能力,视频聊天属于其基础功能。想到就做,笔者开始将想法落地。
笔者在实现点对点视频通信过程中,遇到了 iOS 模拟器红屏问题。本文主要记录如何解决这一问题。主要对 SDP 进行解读,分享在编辑 SDP 过程中遇到的问题。
WebRTC 虽然可以实现点对点通信,但是在其通道建立过程中,有一个信令交互环节,则必要无法完全脱离服务器,为此我们需要搭建一个信令服务器。
我们可以使用 node.js 来实现一个简单的 websocket 服务器,其只需要做一件事:广播收到的数据。我们还可以选择使用 swift 来实现这样一个简单的信令服务器。
总结一下,我们需要的环境:
在环境搭建完毕,工程实现完成后,急切点开应用程序的视频通话按钮后,看到的是如下图效果
是的,笔者遇到了模拟器上运行视频通话后红屏的问题。
这似乎是 WebRTC 在 H264 上的一个 bug,官方也发现了,所以出了一个 错误报告。该 bug 的具体原因不在本文讨论。而想要解决该问题,则有两种方案。
援引 Stack Overflow 上的方案
1 | Fix is to not set attribute 'kCVPixelBufferIOSurfacePropertiesKey' in RTCVideoDecoderH264.mm for TARGET_IPHONE_SIMULATOR |
笔者亲测有效。此方案的麻烦点在于,我们改动完源代码后,需要自行编译,重新生成 WebRTC.framework
文件。
WebRTC 在 iOS 模拟器上会出现红屏问题,主要还是因为框架对 H264 的解码出现的 bug,我们可以通过在模拟器上不使用 H264 来规避此问题。因此,我们可以在应用层编辑 SDP 内容来达到禁用 H264 编码的目的。
想要编辑 SDP,我们首先需要了解它。SDP(Session Description Protocol),会话描述的协议,它不包含传输协议。
1 | +---------------------+ |
从上文可以知道 SDP 有五个部分:
下面根据 RTCPeerConnection.createOffer 产生的 SDP 进行解读(RFC4566)。
1 | // SDP 类型,有 offer、answer |
1 | // m=<media> <port> <proto> <fmt> ... |
上文中提到修改源代码来解决红屏问题。另外一种方式就是修改 SDP 内容,而 SDP 的内容修改,也有两个方法。其一是修改底层源代码生成 offer 的实现,将 H264 在模拟器环境下排除。另一种则是在应用层修改 SDP 内容。
在实际修改过程中,很可能会遇到 Session Description is NULL.
报错。相信笔者,我们的实现思路是没有错的,只要再注意一些小细节即可。
RTCPeerConnection.setRemoteDescription
前修改 SDP 内容。\r\n
符号。我们需要确保将上文示例 SDP 内容修改如下即可
1 | m=video 9 UDP/TLS/RTP/SAVPF 100 101 127 124 35 36 123 122 125 |
文中图片若有侵权,请联系笔者及时删除
]]>今日账单 的 x-callback-url
格式为:
1 | niffler://x-callback-url/[动作]?[动作参数]&[x-callback 参数] |
/account
使用指定的名字创建账本
/bill
使用指定的名称和数字创建账单
name
(必选)账本或账单项目名称
value
,当动作为bill时,为必选参数。账单开销数值。
type
,当动作为bill时,为必选参数。账单开销类型,0 支出,1 收入,2 已报销。
remark
(可选),备注信息
payway
(可选),账单支付方式
1.创建一个名为“装修”,备注信息为“店铺”的账本。
1 | niffler://x-callback-url/account?name=装修&remark=店铺 |
2.创建一条类别为“饮食”的“午餐”通过“现金”支出 12 元的账单。
1 | niffler://x-callback-url/bill?name=饮食&type=0&remark=午餐&value=12&payway=现金 |
本文档对应 App 版本为 3.7.6 及以上
]]>Niffler’s x-callback-url
format is:
1 | niffler://x-callback-url/[action]?[action parameter]&[x-callback parameter] |
/account
Create account with specified name
/bill
Create a bill with the specified name and number
name
(required), Account or bill item name
value
, This parameter is required when the action is bill. Billing overhead figures.
type
, This parameter is required when the action is bill. Billing expense type, 0 expenses, 1 income, 2 reimbursed.
remark
(optional), Remark Information
payway
(optional), Bill payment method
1.Create a account named “Decoration” with a remark of “Shop”.
1 | niffler://x-callback-url/account?name=Decoration&remark=Shop |
2.Create a bill of “Lunch” with the category “Diet” to spend $ 12 in “Cash”.
1 | niffler://x-callback-url/bill?name=Diet&type=0&remark=Lunch&value=12&payway=Cash |
This document corresponds to the App version 3.7.6 and above
]]>单元测试是在软件开发过程中要进行的最低级别的测试活动,软件的独立单元将在与程序的其他部分相隔离的情况下进行测试。
1)在一个复杂的项目中添加某功能模块时,可以快捷的进行针对性测试,而不用将整个项目 Run 起来。
2)可以便捷的对某个具体方法进行测试。
iOS 开发(或者 MacOS、tvOS、watchOS 等)中,单元测试有多种方式,主要分为 Xcode 提供的以及第三方测试框架这两类:
Xcode自带
XCTest:XCTest 是 Xcode自带的单元测试工具,其前身是 OCUnit,随着 Xcode 的发展,XCTest 已经越来越完善,功能也越强大。
第三方框架
GHUnit:GHUnit 是 GitHub 上著名的开源测试框架,可视化、开源、扩展等功能,让其相比 XCTest 更加强大(现在的 XCTest 也很完善了,不过 GHUnit 比较老,现在已经停止维护,不建议使用)
OCMock:OCMock 也是 Github 上的著名开源测试框架,用于 Mock、Stub,为测试提供数据作假功能。
以上三种就是比较主流的测试 Xcode 单元测试途径,还有一些 BDD 行为测试框架:
什么是 BDD
BDD(Behavior Driven Development),即行为驱动开发,是敏捷开发技术之一,通过自然语言定义系统行为,以功能使用者的角度,编写需求场景,且这些行为描述可以直接形成需求文档,同时也是测试标准。
其他的,还有一些其他测试工具、测试方式,如:
1、创建测试类
注意,所有测试类都继承自 XCTestCase。
2、测试类的结构
默认创建的单元测试类为一个 .m
文件,里面包含了以下四个方法:
- (void)setUp
:在每个测试用例开始前调用,可以做一些测试准备工作,为可选方法。- (void)tearDown
:在每个测试用例结束后调用,可以做一些测试收尾工作,为可选方法。- (void)testExample
:默认创建的测试用例。- (void)testPerformanceExample
:性能测试方法。3、测试流程
当我们默认执行测试时,系统找到所有的测试类,并执行每个测试方法;我们也可以选择性地执行某些测试而已,比如,在 scheme 中 disable 某个用例,或者直接在测试导航栏中每个测试用例后面的运行按钮,单独执行某个测试。
默认流程如下:
上一个测试类 -> 当前类+ (void)setUp
-> [ - (void)setUp
-> 测试方法 -> - (void)tearDown
] (循环直至当前类测试方法全部执行完) -> 当前类 + (void)tearDown
-> 下一个测试类
4、测试方法
测试方法以 test 为前缀,没有参数,返回值为 void,方法中用断言来判断测试的正确性:
1 | - (void)testColorIsRed { |
5、测试断言
断言一般由判断条件、字符串 format、字符串参数组成,参数可选,在 XCTest 中,断言有以下分类:
Specta 和 Expecta 都是出自 Github 作者 Orta 之手,他最出名的开源框架莫过于 Cocoapods。
Specta 是一个轻量级 BBD 测试框架,其为 DSL (Domain-Specific Language) 模式,让测试更加接近于自然语言描述,更加易懂。
1、主要有以下特点:
2、语法介绍
SpecBegin 声明了一个测试类,SpecEnd 结束类声明
describe (context) 块声明了一组实例
it (example/specify) 是一个单一的样例
beforeAll 是一个执行于全部同级块之前的块,仅仅执行一次。afterAll 与beforeAll相反,是在全部同级块之后执行的块。仅仅执行一次。
beforeEach/afterEach,在每一个同级块执行的时候,都会执行一次,而beforeAll/afterAll仅仅会执行一次
it/waitUntil/done()。异步调用,注意完毕异步操作之后。必须调用done()函数。例如以下:
1 | it(@"should do some stuff asynchronously", ^{ |
注意:在describe局部使用sharedExamplesFor定义shared examples。能够在它作用域内覆盖全局的shared examples。
3、一般使用:
1 | SpecBegin(Car) |
上面例子对 Car 这个类做测试,通过多个上下文嵌套(describe/context),结合不同的条件(beforeEach),来作出不同的断言(it);当我们某个测试失败时,我们会收到一段很明确的错误信息,比如:汽车启动后应该移动到指定位置这个用例测试失败,那么我们会收到 Car move to when the engine is running should move to given position 这么一段话。这样非常接近自然语言的描述会让我们很快知道错误出在哪里。
4、注意:
如果想用 SPEC_BEGIN 和 SPEC_END 替代 SpecBegin and SpecEnd,应该在引入头文件之前写上 #define SPT_CEDAR_SYNTAX
如果要使用 XCTest Resporter,那么在 Test Scheme 中,把 SPTXCTestReporter 字段值改为 SPECTA_REPORTER_CLASS
把环境变量 SPECTA_SHUFFLE 设置为 1 启用测试拖拽(test shuffling)
Expecta 是基于 Objective-C/Cocoa 的断言框架,XCTest 自带的断言 XCAssert 有好几个基础操作,不过基础的断言不太丰富,和 Specta 也没有很适配。 Expecta 不一样,将匹配过程从断言中剥离开,可以很好地适配 Specta 的 DSL 断言块。
1、Expecta 有以下几个特点:
没有类型限制,比如数值 1,并不用关心它是整形还是浮点数
链式编程,可读性高,如:expect(foo).notTo.equal(1)
反向匹配,断言不匹配只需加上 .notTo 或者 .toNot,如:expect(x).notTo.equal(y)
延时匹配,可以在链式表达式中加入 .will、.willNot、.after(interval) 等操作来延时匹配
可扩展,支持增加自定义匹配
2、基础匹配 API:
1 | expect(x).to.equal(y); // x 与 y 相等 |
3、异步匹配
1 | describe(@"WebImage", ^{ |
4、自定义使用
1 | #import <Specta/Specta.h> |
Expecta 和 Specta 需要配合使用,与 XCTest 一样都是基于 XCTestCase 实现。在断言的使用上,XCTest 太过死板,Expecta 和 Specta 则很灵活,可以满足大部分场景需求。
]]>可是现状,大家对于持久化的选择方案仍多数是 FMDB。笔者猜测,最大的原因可能就是性能。
Core Data 是一个模型层的技术,帮助开发者建立代表程序状态的模型层。同时也是一种持久化技术,它能将模型对象的状态持久化到磁盘。它是完全独立于 UI 层级的框架,是作为模型层框架被设计出来的。
Core Data 不是一个 O/RM,但它比 O/RM 能做的更多。它也不是一个 SQL wrapper。它默认使用 SQL,但它是一种更高级的抽象概念。
Core Data 有相当多可用的组件。当所有的组件都捆绑到一起的时候,我们把它称作 Core Data 堆栈,这个堆栈有两个主要部分。
一部分是关于对象图管理,这正是你需要很好掌握的那一部分,并且知道怎么使用。
另一部分是关于持久化,比如,保存你模型对象的状态,然后再恢复模型对象的状态。
堆栈结构如下
NSPersistentStoreCoordinator 是一个位于本地存储文件与缓存层(NSManagedObjectContext)之间的一个持久化层,它是真实操作数据库本地文件。
NSManagedObjectContext 是一个被管理数据的上下文,它实际上是对所有数据库操作的一个缓存层,把所有的操作都先缓存起来避免大量磁盘 IO 造成不流畅,在操作完数据库后调用其 save 方法,就可以把数据库操作提交给持久化层(NSPersistentStoreCoordinator),由持久化层一次性写入数据库文件。
NSManagedObject 是被管理的数据记录,对应数据库的一个表。
另外,Core Data 可以将多个 stores 附属于同一个持久化存储协调器,并且除了存储 SQL 格式外,还有很多存储类型可供选择。
最常见的方案如下
下面是笔者定义的一个 Event 表的元素组成
定义数据模型
1 | class MXWEventModel: NSObject { |
1 | let context = persistentContainer.viewContext |
1 | public func delete(id: Int64) { |
1 | public func update(id: Int64, model: AnyObject) { |
1 | public func fetch(id: Int64) -> AnyObject? { |
在功能迭代过程中,难免会遇到要修改 .xcdatamodeld
文件。例如,新增或删除一个实体、增加或删除一个原有实体的属性等。如果开发者没有设置数据迁移,那更新后原有的数据将会被清空,所以此时需要进行数据的迁移操作。
Core Data 可以设置轻量级的数据迁移,系统会自动分析差异,进行映射,这种方式只适用于简单的增删实体或是增删属性等操作。除此之外还有一种相当复杂的自定义数据迁移。
1 | // MARK: - Core Data stack |
对于 Core Data,Apple 官方很久之前就已经推出,但是并不受开发者青睐。笔者在这段时间的学习过程中也在思考这个问题。
下面是笔者学习中遇到的注意点:
最后,文章开头给出了性能比较。但是,笔者认为,在客户端并没有很大量的数据写入,只要开发者在使用过程中稍作注意,性能应该不是否决 Core Data 技术方案的理由。反倒,Core Data 对 iCloud 很好的支持,以及数据迁移备份,这些都可以很容易实现。笔者认为,完全可以考虑使用它做客户端的数据持久化方案。
来到日本已经有一段时间了,为什么直到现在才开始想起来写一点感想呢。一是,时间不多。二是,之前体验不多。
这次来日本,抱着学习、求证的心态。身边的人问起,笔者的回答一向都是,过来“浪”了。因为不是有很具体的目的过来的。在大学毕业时,就因为一些原因,对这边有兴趣。而现在,有机会过来了。那么,就能好好验证之前自己的困惑了。
目前为止,最大的体会就是,日本绝对是个生活的好地方。
环境很不错,在大马路上走,都基本闻不到尾气。晨跑笔者是直接在马路边、公园里、居住区进行。
周末出去逛逛街,看看各种地方。
除了这些安静,空寂的街道。在人气比较旺的旅游区,也是大同小异。
这个红色的邮筒,超级有个性,很难得见到在发达国家还保持着一些比较原始的习惯,还包括看报纸。
久负盛名的团地。
至于吃的嘛,那就很随意了。
当然,具有标志性的建筑,东京塔,也要来看看的。
最后,附上一些生活杂类拍照。
看了这么多,应该很多人都会想过来体验一把。客观来说,这边除了工作,其他都很好。为什么这么说呢?工作就涉及到了日本文化问题了,或许有很多人喜欢,但是对于我们这些受欧美影响较多的人来说,或许,当真的亲身体验时,可能又是另一番感受了吧。
]]>Effective date: October 28, 2018
Niffler (“us”, “we”, or “our”) operates the Niffler mobile application (the “Service”).
This page informs you of our policies regarding the collection, use, and disclosure of personal data when you use our Service and the choices you have associated with that data. Our Privacy Policy for Niffler is created with the help of the Free Privacy Policy Generator.
We use your data to provide and improve the Service. By using the Service, you agree to the collection and use of information in accordance with this policy. Unless otherwise defined in this Privacy Policy, terms used in this Privacy Policy have the same meanings as in our Terms and Conditions.
We collect several different types of information for various purposes to provide and improve our Service to you.
While using our Service, we may ask you to provide us with certain personally identifiable information that can be used to contact or identify you (“Personal Data”). Personally identifiable information may include, but is not limited to:
When you access the Service by or through a mobile device, we may collect certain information automatically, including, but not limited to, the type of mobile device you use, your mobile device unique ID, the IP address of your mobile device, your mobile operating system, the type of mobile Internet browser you use, unique device identifiers and other diagnostic data (“Usage Data”).
We use cookies and similar tracking technologies to track the activity on our Service and hold certain information.
Cookies are files with small amount of data which may include an anonymous unique identifier. Cookies are sent to your browser from a website and stored on your device. Tracking technologies also used are beacons, tags, and scripts to collect and track information and to improve and analyze our Service.
You can instruct your browser to refuse all cookies or to indicate when a cookie is being sent. However, if you do not accept cookies, you may not be able to use some portions of our Service.
Niffler uses the collected data for various purposes:
Your information, including Personal Data, may be transferred to — and maintained on — computers located outside of your state, province, country or other governmental jurisdiction where the data protection laws may differ than those from your jurisdiction.
If you are located outside China and choose to provide information to us, please note that we transfer the data, including Personal Data, to China and process it there.
Your consent to this Privacy Policy followed by your submission of such information represents your agreement to that transfer.
Niffler will take all steps reasonably necessary to ensure that your data is treated securely and in accordance with this Privacy Policy and no transfer of your Personal Data will take place to an organization or a country unless there are adequate controls in place including the security of your data and other personal information.
Niffler may disclose your Personal Data in the good faith belief that such action is necessary to:
The security of your data is important to us, but remember that no method of transmission over the Internet, or method of electronic storage is 100% secure. While we strive to use commercially acceptable means to protect your Personal Data, we cannot guarantee its absolute security.
We may employ third party companies and individuals to facilitate our Service (“Service Providers”), to provide the Service on our behalf, to perform Service-related services or to assist us in analyzing how our Service is used.
These third parties have access to your Personal Data only to perform these tasks on our behalf and are obligated not to disclose or use it for any other purpose.
We may use third-party Service Providers to monitor and analyze the use of our Service.
Firebase is analytics service provided by Google Inc.
You may opt-out of certain Firebase features through your mobile device settings, such as your device advertising settings or by following the instructions provided by Google in their Privacy Policy.
We also encourage you to review the Google’s policy for safeguarding your data: https://support.google.com/analytics/answer/6004245. For more information on what type of information Firebase collects, please visit please visit the Google Privacy & Terms web page.
Our Service may contain links to other sites that are not operated by us. If you click on a third party link, you will be directed to that third party’s site. We strongly advise you to review the Privacy Policy of every site you visit.
We have no control over and assume no responsibility for the content, privacy policies or practices of any third party sites or services.
Our Service does not address anyone under the age of 18 (“Children”).
We do not knowingly collect personally identifiable information from anyone under the age of 18. If you are a parent or guardian and you are aware that your Children has provided us with Personal Data, please contact us. If we become aware that we have collected Personal Data from children without verification of parental consent, we take steps to remove that information from our servers.
We may update our Privacy Policy from time to time. We will notify you of any changes by posting the new Privacy Policy on this page.
We will let you know via email and/or a prominent notice on our Service, prior to the change becoming effective and update the “effective date” at the top of this Privacy Policy.
You are advised to review this Privacy Policy periodically for any changes. Changes to this Privacy Policy are effective when they are posted on this page.
If you have any questions about this Privacy Policy, please contact us:
By email: app_sev@163.com
]]>正如离开时对领导所说,自己是带着情怀来的,但是很可惜,要带着遗憾离开。
自己在很早就使用 ZAKER 来看新闻,当时来到广州,经过各种面试考验,如愿来到老东家。慢慢的想着要将它变得更好,或者是说自己能够为它的成长贡献力量。两年多下来,看着它慢慢迭代,和两年前比较,已经不是同一个产品啦,变化还是很大的。
最开始进来时,就被小组的团队氛围所触动。没错,我们是一个很年轻的团队,大家可以很自然的沟通,可以很放松,可以很时髦,但是我们也很努力。
两年多来,自己不断被影响,努力前行。从一个开发新手,到一个完全可以自己 hold 住需求的开发人员。自己的成长,也得到了肯定。期间经历团队同事的离开,经历引导人的离开,这些无不是一种心理磨练。特别是当面试自己的领导同事离开时,那种说出不的滋味。虽然明白天下无不散之宴席,但当自己真的面对时,还是需要心理建设的。苟富贵,勿相忘。江湖再见。这几句话我们会在散场的时候诉说的。或许若干年后,我们会真的体会它的深意。
最后,自己也不知道想要写什么,或许就是想留下一个印记吧。ZAKER 我曾经来过,ZAKER 我曾经为你激情付出。ZAKER 希望你越来越好!
]]>Web 页面当然不能凭空显示出来。根据 Web 浏览器地址栏中指定的 URL,Web 浏览器从 Web 服务器端获取文件资源(resource)等信息,从而显示出 Web 页面。像这种通过发送请求获取服务器资源的 Web 浏览器等,都可称为客户端(client)。
Web 使用一种名为 HTTP(HyperText Transfer Protocol,超文本传输协议)的协议作为规范,完成从客户端到服务器端等一系列运作流程。而协议是指规则的约定,可以说,Web 是建立在 HTTP 协议上通信的。
超文本传输协议(HTTP,HyperText Transfer Protocol) 是互联网上应用最为广泛的一种网络协议。所有的 WWW 文件都必须遵守这个标准。设计 HTTP 最初的目的是为了提供一种发布和接收 HTML 页面的方法。1960 年美国人 Ted Nelson 构思了一种通过计算机处理文本信息的方法,并称之为超文本(hypertext),这成为了 HTTP 超文本传输协议标准架构的发展根基。Ted Nelson 组织协调万维网协会(World Wide Web Consortium)和互联网工程工作小组(Internet Engineering Task Force )共同合作研究,最终发布了一系列的 RFC,其中著名的 RFC 2616 定义了 HTTP 1.1。
HTTP 的出生时间是 1989 年 3 月,那时候互联网还属于少数人。
CERN(欧洲核子研究组织)的蒂姆 • 伯纳斯 - 李(Tim BernersLee)博士提出了一种能让远隔两地的研究者们共享知识的设想。
最初设想的基本理念是:借助多文档之间相互关联形成的超文本(HyperText),连成可相互参阅的 WWW(World Wide Web,万维网)。
现在已提出了 3 项 WWW 构建技术,分别是:把 SGML(Standard Generalized Markup Language,标准通用标记语言)作为页面的文本标记语言的 HTML(HyperText Markup Language,超文本标记语言);作为文档传递协议的 HTTP ;指定文档所在地址的 URL(Uniform Resource Locator,统一资源定位符)。
WWW 这一名称,是 Web 浏览器当年用来浏览超文本的客户端应用程序时的名称。现在则用来表示这一系列的集合,也可简称为 Web。
1990 年 11 月,CERN 成功研发了世界上第一台 Web 服务器和 Web 浏览器。
HTTP 于 1990 年问世。那时的 HTTP 并没有作为正式的标准被建立。现在的 HTTP 其实含有 HTTP1.0 之前版本的意思,因此被称为 HTTP/0.9。
HTTP 正式作为标准被公布是在 1996 年的 5 月,版本被命名为 HTTP/1.0,并记载于 RFC1945。虽说是初期标准,但该协议标准至今仍被广泛使用在服务器端。
1997 年 1 月公布的 HTTP/1.1 是目前主流的 HTTP 协议版本。当初的标准是 RFC2068,之后发布的修订版 RFC2616 就是当前的最新版本。
为了理解 HTTP,我们有必要事先了解一下 TCP/IP 协议族。通常使用的网络(包括互联网)是在 TCP/IP 协议族的基础上运作的。而 HTTP 属于它内部的一个子集。
计算机与网络设备要相互通信,双方就必须基于相同的方法。比如,如何探测到通信目标、由哪一边先发起通信、使用哪种语言进行通信、怎样结束通信等规则都需要事先确定。不同的硬件、操作系统之间的通信,所有的这一切都需要一种规则。而我们就把这种规则称为协议(protocol)。
TCP/IP 协议族里重要的一点就是分层。TCP/IP 协议族按层次分别分为以下 4 层:应用层、传输层、网络层和数据链路层。
分层的好处:若某个地方需要改变设计时,不需要把所有部分整体替换掉,只需把变动的层替换掉即可。把各层之间的接口部分规划好之后,每个层次内部的设计就能够自由改动了。
应用层决定了向用户提供应用服务时通信的活动。
TCP/IP 协议族内预存了各类通用的应用服务。比如,FTP(File Transfer Protocol,文件传输协议)和 DNS(Domain Name System,域名系统)。HTTP 协议也处于该层。
传输层为应用层,提供处于网络连接中的两台计算机之间的数据传输。
在传输层有两个性质不同的协议:TCP(Transmission Control Protocol,传输控制协议)和 UDP(User Data Protocol,用户数据报协议)。
TCP 提供可靠的字节流服务。所谓的字节流服务(Byte Stream Service)是指,为了方便传输,将大块数据分割成以报文段(segment)为单位的数据包进行管理。而可靠的传输服务是指,能够把数据准确可靠地传给对方。简单来说,TCP 协议为了更容易传送大数据才把数据分割,而且 TCP 协议能够确认数据最终是否送达到对方。而为了准确无误地将数据传输到目标,TCP 采用了三次握手策略。
网络层用来处理在网络上流动的数据包。数据包是网络传输的最小数据单位。该层规定了通过怎样的路径(所谓的传输路线)到达对方计算机,并把数据包传送给对方。
与对方计算机之间通过多台计算机或网络设备进行传输时,网络层所起的作用就是在众多的选项内选择一条传输路线。
IP(Internet Protocol)网际协议位于网络层。需要注意的是可“IP”和“IP 地址”的区别,“IP”其实是一种协议的名称。
IP 协议的作用是把各种数据包传送给对方。其中两个重要的条件是 IP 地址和 MAC 地址(Media Access Control Address)。
IP 地址指明了节点被分配到的地址,MAC 地址是指网卡所属的固定地址。IP 地址可以和 MAC 地址进行配对。IP 地址可变换,但 MAC 地址基本上不会更改。基本上各大网卡制作厂商都被预制分配了 MAC 地址区间段。
IP 间的通信依赖 MAC 地址。在网络上,通信的双方在同一局域网(LAN)内的情况是很少的,通常是经过多台计算机和网络设备中转才能连接到对方。而在进行中转时,会利用下一站中转设备的 MAC 地址来搜索下一个中转目标。这时,会采用 ARP 协议(Address Resolution Protocol)。ARP 是一种用以解析地址的协议,根据通信方的 IP 地址就可以反查出对应的 MAC 地址。
在到达通信目标前的中转过程中,那些计算机和路由器等网络设备只能获悉很粗略的传输路线。这种机制称为路由选择(routing)。
用来处理连接网络的硬件部分。包括控制操作系统、硬件的设备驱动、NIC(Network Interface Card,网络适配器,即网卡),及光纤等物理可见部分(还包括连接器等一切传输媒介)。
硬件上的范畴均在链路层的作用范围之内。
在数据链路层还有一个常见的网络协议 LLDP。了解更多可以查看《数据链路层之 LLDP》。
为了更好的理解上图,我们使用 HTTP 🌰 说明。
作为发送端的客户端在应用层(HTTP 协议)发出一个想看某个 Web 页面的 HTTP 请求。
在传输层(TCP 协议)把从应用层处收到的数据(HTTP 请求报文)进行分割,并在各个报文上打上标记序号及端口号后转发给网络层。
在网络层(IP 协议),增加作为通信目的地的 MAC 地址后转发给链路层。这样一来,发往网络的通信请求就准备齐全了。
接收端的服务器在链路层接收到数据,按序往上层发送,一直到应用层。当传输到应用层,才能算真正接收到由客户端发送过来的 HTTP 请求。
发送端在层与层之间传输数据时,每经过一层时必定会被打上一个该层所属的首部信息。反之,接收端在层与层传输数据时,每经过一层时会把对应的首部消去。这种把数据信息包装起来的做法称为封装(encapsulate)。
DNS(Domain Name System)服务是和 HTTP 协议一样位于应用层的协议。它提供域名到 IP 地址之间的解析服务。
计算机既可以被赋予 IP 地址,也可以被赋予主机名和域名。🌰 www.chars.tech。
用户通常使用主机名或域名来访问对方的计算机,而不是直接通过 IP 地址访问。因为与 IP 地址的一组纯数字相比,用字母配合数字的表示形式来指定计算机名更符合人类的记忆习惯。
但要让计算机去理解名称,相对而言就变得困难了。因为计算机更擅长处理一长串数字。为了解决上述的问题,DNS 服务应运而生。DNS 协议提供通过域名查找 IP 地址,或逆向从 IP 地址反查域名的服务。
至此,大致可以回答开篇的问题了,我们在浏览器输入框中输入想浏览的网页地址之后,发生了哪些事情呢?
距离从云南回来已经好些天了。但,脑海中还会时常回忆起那里的景色。或许对于一些人来说,那些也是很通常的环境。旅行,不就是不同环境生活的人,暂时交换体验吗?这次的旅行,发现自己变化很大,从心性上来说,应该成熟些了吧。对于这次的旅行,还是想能够记下来,毕竟对于我来说,是一份很珍贵的记忆。
云南(不对,应该还包括四川。因为这次出行,已经各种跨省跨境的游玩了)不仅是个景色优美的地方,而且美食也是很重要的角色。相信我辈吃货们很关注那里有什么好吃的吧。这里首先来推荐几款一定要尝尝的美食吧。
这是当时在扎窝洛码头住民宿时老板做的。看着很辣,一向清淡饮食的我被吓着了,试着吃了一口之后,完全停不下来,不仅不辣,还香气扑鼻。好吃!爽!
这种黄李子只能在丽江买到,可能是特色水果吧。最棒的吃法是,使用当地泉水或井水,洗干净吃。不要问为什么,你自己去了尝试一下就明白了。至于是不是叫黄李子,这个就很难说了,一路上问了不少人。感觉没有一个是认真回答这个问题的,都随便叫一个名字。哈哈哈,这个“随便”的用法也是和当地人学的。
以上这两种菜是在大理洱海畔的双廊一家餐馆吃的,味道嘛,和吃一般蔬菜一样,只不过名字比较有意思。
以上三样都是在大理古城逛吃发现的。
到了云南,肯定不能不吃云南米线。不过,有点分不清这个和米粉有什么区别啦。
当然,吃的东西不仅仅如此,只不过这些都是比较有意思的。
除了吃,当然就是玩啦。
第一天早上来到丽江,因为太早。从火车站,直接坐18路公交进入市区,18路,记得郴州火车站的公交也是18路。好巧呀。
车子可以直接到达丽江古城的忠义市场,以前需要的古城维修费,现在也不需要啦。下车之后找个地方吃了碗当地的云南米线,物价也不是很贵。接下来,绕着古城,找到南门,然后进去逛一逛。清晨空气很棒,古城里的公园很多运动的爷爷奶奶。还看到一对夫妻,前一天晚上就在他们的SUV中度过的,居然还带着宠物狗出门。真的很惬意呀!把行李寄存在客栈之后,就直接去丽江古城逛吃咯。
丽江古城由束河古镇、大研古城、白沙古镇组成。最方便、最热闹的是大研古城,也就是人们常说的丽江古城。这里商业气息比较重,有很多小吃和店铺,人流量也是比较大的。虽然束河古城比较清净,但相应的是交通不怎么方便。因为客栈距离丽江古城东门,所以吃住还是很方便的。古城不是封闭的,你可以从任何你喜欢的地方进入。
这是在狮子山附近看到的古城全貌。真的很幸运,前一天这里还在下雨,到的这一天却赶上放晴。
“大水车”不仅是古城的一个景点,它旁边也是古城北门。这附近有去泸沽湖的汽车,也有去其他景点的车。
之前也去过乌镇,这种旧时建筑,可能风格都差不多,美不美,在于你!
走累了,坐在这石桥上吃黄李子,桥木有拍,当然看不见啦。
逛吃饱了之后,买了两件纳西族的衣服,准备期间穿起来。走出古城,办理好入住。准备出发去“拉市海”。拉市海边的花田和草地,让人眼前一亮,很久不见了。
租条皮划艇,自己划着。心之所往,片刻即至。
划了半天皮划艇,晚上都不想出门啦,在客栈收拾好,准备次日的行程–玉龙雪山。没错,奔着4680去的。
不能不说是遗憾,这个也不由想起大四时候,和室友去爬华山,想看日出。然后那天大雾!这个玉龙雪山是准备了一天的时间,所以除了登4680,还有蓝月谷可以玩。
下午5:30准时下山返程,回到客栈。次日,就要去泸沽湖啦。
泸沽湖三分之一在云南 ,三分之二在四川,由湖泊和万亩草海组成。云南、 四川两省都可以进入。
先来一个全景,不错,这个就是一个未被污染的天然水域。迎面出来凉凉的风,好久没有这样的感觉啦。
在泸沽湖,是住在四川境内的扎窝洛码头【老知青之家】民宿,这里是摩梭族人聚居区。在这里,体验当地人生活。
来看看漂亮的民宿。
老板有两个和我一般年纪的儿子,下午刚好跟着一起去了【摩梭博物馆】
这个是“莫言”题字的哟!不清楚是在获奖前还是后啦。
晚上还去看了当地的篝火晚会!
次日,和两个小老板一起环湖游啦,因为景区现在已经禁止骑行啦,少了一大乐趣!
看见水面上的水性杨花了吗?那个就是可以吃的!
这两天在泸沽湖呆得都不想离开啦。
次日,来到大理古城。
晚上逛吃,休息好。因为~
次日清晨起来,在古城里逛悠个早餐,租了个小电驴,开启洱海环行!
先来到崇圣寺三塔,不错,就是那个明信片经常出现的地方!
中午到双廊吃午餐,继续一路向前,出发。
晚上回到客栈,都不敢想象,今天环洱海有150公里。到了这个时候,旅行进度条也要撑不住啦。不久就要返程啦。
最后一天还是在大理,不过没有具体目标,早上多睡了一会,然后出门觅食。最后在洱海公园好好逛了逛。
这次旅行,真的很放松。这期间,可以不管手机上的信息,不用理会各种事物。在那里,都是使用现金,离开网络,离开忙碌的世界。
社会在快速发展,人到底是变得更幸福快乐了吗?
回来之后,又要开启自己的学习节奏,既然走了这条路,就要坚持下去。现在已经开设了知乎专栏《通俗易懂的算法》、《如何独立开发一个完整应用》,有兴趣的童鞋欢迎关注和推荐给他人。
]]>“算法的运行时间以不同的速度增加”这句话应该如何理解呢?下面我们通过🌰来看看这句话到底想表达什么。
小明现在需要编写一个查找算法,这个算法服务于学校图书馆,目的是帮助童鞋们能够快速的找到自己需要的书籍所在位置。
假设小明现在只会“二分查找”和“简单查找”。一方面,二分查找的速度很快,小明必须在 10 秒钟内找到书籍所在位置,否则童鞋们没有更多耐心等待。另一方面,简单查找算法编写起来更容易,因此出现 bug 的可能性更小。
为了检验这两种算法的耗时,小明决定计算两种算法在列表包含 100 个元素的情况下需要的时间。
假设检查一个元素需要 1 毫秒。使用简单查找时,小明必须检查 100 个元素,因此需要 100 毫秒才能查找完毕。而使用二分查找时,只需检查 7 个元素(log2 100大约为7),因此需要 7 毫秒就能查找完毕。然而,实际要查找的列表可能包含 10 亿个元素,在这种情况下,简单查找需要多长时间呢?二分查找又需要多长时间呢?
小明使用包含 10 亿个元素的列表运行二分查找,运行时间为 30 毫秒(log2 1 000 000 000大约为30)。他心里想,二分查找的速度大约为简单查找的 15 倍,因为列表包含 100 个元素时,简单查找需要 100 毫秒,而二分查找需要 7 毫秒。因此,列表包含 10 亿个元素时,简单查找需要 30 × 15 = 450 毫秒,完全符合在 10 秒内查找完毕的要求。小明决定使用简单查找。这是正确的选择吗?
不是。实际上,小明错了,而且错得离谱。列表包含 10 亿个元素时,简单查找需要 10 亿毫秒,相当于 11 天!为什么会这样呢?因为二分查找和简单查找的运行时间的增速不同。
简单查找 | 二分查找 | 元素个数 |
---|---|---|
100 毫秒 | 7 毫秒 | 100 |
10 秒 | 14 毫秒 | 10 000 |
11 天 | 30 毫秒 | 1 000 000 000 |
随着元素数量的增加,二分查找需要的额外时间并不多,而简单查找需要的额外时间却很多。因此,随着列表的增长,二分查找的速度比简单查找快得多。小明以为二分查找速度为简单查找的 15 倍,这不对:列表包含 10 亿个元素时,为 3300 万倍。有鉴于此,仅知道算法需要多长时间才能运行完毕还不够,还需知道运行时间如何随列表增长而增加。这正是大O表示法的用武之地。
大O表示法指出了算法有多快。例如,假设列表包含 n 个元素。简单查找需要检查每个元素,因此需要执行 n 次操作。使用大O表示法,这个运行时间为 O(n)。单位秒呢?没有!大O表示法指的并非以秒为单位的速度。大O表示法让你能够比较操作数,它指出了算法运行时间的增速。
假设你使用简单查找在电话簿中找人。你知道,简单查找的运行时间为 O(n),这意味着在最糟情况下,必须查看电话簿中的每个条目。如果要查找的是 Chars ——电话簿中的第一个人,一次就能找到,无需查看每个条目。考虑到一次就找到了 Chars,请问这种算法的运行时间是 O(n)还是 O(1) 呢?
简单查找的运行时间总是为 O(n)。查找 Chars 时,一次就找到了,这是最佳的情形,但大O表示法说的是最糟的情形。因此,你可以说,在最糟情况下,必须查看电话簿中的每个条目,对应的运行时间为 O(n)。这是一个保证——你知道简单查找的运行时间不可能超过 O(n)。
说明
除最糟情况下的运行时间外,还应考虑平均情况的运行时间,这很重要。最糟情况和平均情况将在第4章讨论。
下面按从快到慢的顺序列出了你经常会遇到的5种大O运行时间。
1、算法的速度指的并非时间,而是操作数的增速。
2、谈论算法的速度时,说的是随着输入的增加,其运行时间将以什么样的速度增加。
3、算法的运行时间用大O表示法表示。
4、O(log n) 比 O(n)快,当需要搜索的元素越多时,前者比后者快得越多。
对算法有兴趣的童鞋可以关注专栏《通俗易懂的算法》,也欢迎大家多多投稿分享。
同时欢迎大家加入移动开发交流Q群交流讨论,Q群号:811237468
]]>LLDP(Link Layer Discovery Protocol,链路层发现协议)就是用于这个目的的协议。LLDP 定义在 802.1ab 中,它是一个二层协议,它提供了一种标准的链路层发现方式。LLDP 协议使得接入网络的一台设备可以将其主要的能力,管理地址,设备标识,接口标识等信息发送给接入同一个局域网络的其它设备。当一个设备从网络中接收到其它设备的这些信息时,它就将这些信息以MIB的形式存储起来。
这些 MIB 信息可用于发现设备的物理拓扑结构以及管理配置信息。需要注意的是 LLDP 仅仅被设计用于进行信息通告,它被用于通告一个设备的信息并可以获得其它设备的信息,进而得到相关的 MIB 信息。它不是一个配置、控制协议,无法通过该协议对远端设备进行配置,它只是提供了关于网络拓扑以及管理配置的信息,这些信息可以被用于管理、配置的目的,如何用取决于信息的使用者。
LLDP 的框架结构如图所示
此图也表明 LLDP 就是一个信息发现与通告协议,LLDP 的实体主要维护了两个 MIB 库,一个 local system MIB,一个 remote system MIB。从其名字也可以看出,一个用于维护本地相关的设备 MIB 信息,一个用于维护远端设备 MIB 信息。
LLDP 通过与上图中右侧的几个 MIB 库交互来初始化并维护 local system MIB,并将本地的相关信息通告出去;同时当接收到来自其它设备的信息时就将其更新到remote system MIB 中。通过这种工作方式,一个设备就可以将自己的信息通告出去并获得网络中其它设备的相关信息,最终获得反应网络拓扑以及其它配置信息的两个 MIB 库。这两个库可以被其用户用来完成各种功能。需要说明的是LLDP 信息的通告以及接收处理不受端口的STP状态的影响。
封装有 LLDPDU 的报文称为 LLDP 帧,其封装格式有两种:Ethernet II 和 SNAP(Subnetwork Access Protocol,子网访问协议)。
上图是以 Ethernet II 格式封装的 LLDP 帧,其中各字段的含义如下:
上图是以 SNAP 格式封装的 LLDP 帧,其中各字段的含义如下:
目的地址实际上包括三个,分别为 01-80-C2-00-00-0E,01-80-C2-00-00-03,01-80-C2-00-00-00。这三个地址分别用于不同的目的,它们可以跨越不同的网络。
TPMR 以及 S-VLAN,C-VLAN 都是 802.1Q 中的概念,包括这三者的网络以及各个地址的作用范围如下图所示
QinQ 的理论基础,是 IEEE 定义的 802.1ad。在这个规范里面,IEEE 提出了一个概念,它认为汇聚和接入层那里有这么两种设备:S-VLAN Bridge 和 Provider Edge Bridge,再往下就是 Customer System 了(注意,这里说 System 而不是 Bridge,是因为 Customer 接进来的未必是二层设备,可能也是三层设备)。VLAN 空间也被分成两个 VLAN 空间,即 S-VLAN 和 C-VLAN,S 即 Service Provider,C 即 Customer。在 S-VLAN Bridge 上,只有 S-VLAN 空间,而在 Provider Edge Bridge 上,则既有 S-VLAN 空间,又有 C-VLAN 空间。相应的,这个 Bridge 就被从逻辑上划分为两部分,支持 S-VLAN 功能的部分称之为 S-VLAN Component,支持 C-VLAN 功能的部分称之为 C-VLAN Component。S-VLAN Bridge 只包含 S-VLAN Component。
除了两种 Bridge 的概念,802.1ad 还提出了三种 Service 类型和四种 Port 类型,其中一种 Port 是内部 Port,对用户不可见,其它三种 Port 分别对应了三种不同的 Service,即运营商可以通过在交换机上配置三种不同的 Port 类型,来相应的为用户提供三种不同类型的 Service。三种 Service 分别是 Port Based Service,C-Tag Based Service 和 S-Tag Based Service。四种Port 分别是 Customer Network Port (CN), Customer Edge Port(CE),Provider Network Port(PN),Provider Edge Port(内部 Port)。
所谓 Port Based Service,就是说某个 Service 是基于 Port 的,从该 Port 进来的所有报文,都被认为是属于某一个特定的 Customer 的,不管它是否带 C-Tag,带了什么样的 C-Tag,这些信息统统被忽视。所有从这个 Port 进来的报文被赋予一个 S-VLAN,该 S-VLAN 被用来标识该 Customer,或说该 Service。提供这种 Service 的 Port 就是CN Port。CN Port 的实质就是运营商为一个 Customer 提供一个专门的 Port,不跟别的 Customer 共享。注意,从这个 Port 上进来的报文不能带 S-Tag,否则会被丢弃。也就是说,对于 S-Tag 而言,这个 Port 是 Access Port,而不是 Trunk Port。这是跟后面的 S-Tag Based service 本质的不同。
所谓的 S-Tag Based Service,就是说从一个 Port 上进来的报文,根据 S-VLAN 来把它们划分到不同的 Customer,换句话说,是用 S-VLAN 来标记 Customer。提供这种 Service 的 Port 也是 CN Port,只不过这个时候的 CN Port,必须配置成 Trunk Port,只识别 S-VLAN,根据 S-VLAN 来标识 Customer,转发报文。
所谓的 C-Tag Based Service,就是指报文携带 C-Tag 进入 Port,在该 Port 上基于 C-VLAN 来标识 Customer,一个 Port 上可以支持多个 Customer。用来支持 C-Tag Based Service 的 Port 就是 CE Port,CE Port 是 C-VLAN Component 的一部分,对于 C-VLAN 而言,CE Port 是 Trunk Port。它不识别 S-Tag。
PN Port 是 S-VLAN Component 的一部分,它跟 CN Port 唯一的不同是 CN Port 面向 Customer Network,而它面向 Provider Network,在实际的交换机中通常被配置成 Uplink Port,而且通常都是 VLAN Trunk Mode(相对于 Access Mode)。
对于一个拥有 S-VLAN Component 和 C-VLAN Component 的 Provider Edge Bridge 而言,在做 Mac Forwarding/Learning 的时候,有两种模式,一种是用 S-VLAN+MAC
,另外一种则是 S-VLAN+C-VLAN+MAC
,前者即所谓的 C-VLAN Unaware Mode,而后者则是 C-VLAN Aware Mode。
C-VLAN Aware Mode 带来的好处是显而易见的,因为它将 VLAN 空间从 4K 扩展到了 16M,但是它的问题在于,当前绝大多数芯片都不支持,就算支持了,也不太可能支持到理论上的 16M。
现实世界中用户的需求是千奇百怪,有一种需求,是市场上现存的交换机所解决不了的。在讨论这种需求之前,先看一下当前交换机的做法。无论各个厂家的实现差别有多大,但是有一点大家都是一样的,就是在接入交换机上,通过 Port 或者 C-VLAN 来识别用户,然后为每个用户分配一个 S-VLAN,然后用 S-VLAN 来做后续处理,如 ACL/QoS/Mac Learning/Mac Forwarding 等。
但是运营商,特别是欧美的一些运营商可能有这样的需求,为了描述的方便,我们假设有个运营商 A,它在为它的客户提供服务的时候,有的时候需要租用别的运营商,假设是运营商 B 的网络,在租用网络的时候,A 这些 B 的客户,运营商 B 需要给 A 分配 S-VLAN,而且往往是一个 S-VLAN 多少钱,因为 VLAN 是稀缺资源,特别是网络比较大的时候。这个时候,如果运营商 A 为它自己的客户每个都分配一个 S-VLAN,那么相应的它就需要向 B 也申请很多个 S-VLAN,不划算,这个时候它就想在自己的接入设备上,不用 S-VLAN 来标识 Customer,给所有的 Customer 分配同一个 S-VLAN,用该 S-VLAN 来穿越 B 的网络,这个时候,S-VLAN 的意义不是代表 Customer,而是代表一个 Tunnel。
问题关键在于,如果不用 S-VLAN 来代表 Customer,在 A 的接入设备上,如何来对不同的 Customer 来做区分处理呢?用 C-VLAN 肯定是不行的,因为不同 Port 上的 C-VLAN 代表的 Customer 可能是不同的。Centec 的交换机,在芯片内部用一个不同于 C-VLAN 和 S-VLAN 的 CustomerID 来标识 Customer,用这个值来做后续的一系列 Customer 的处理,非常强大。
尽管 QinQ 貌似很好很强大,并且受到热烈追捧,但是这不能掩盖它的先天不足。
QinQ 的最大不足就是它无法对运营商网络完全隐藏 Customer 信息,因为它可以让运营商 Core Network 的设备看不到 Customer VLAN,但是无法让它们看不到 Customer MAC。而这一点有两个不利的影响,一个是 Scalability 非常差,如果中间的设备都是二层设备,会导致 MAC 表非常大;第二个不利的影响则是,一旦 Customer 网络出现了环路,会导致 Provider Network 里面的设备不断进行 MAC Learning,万一有 ARP 之类报文,还可能冲击 CPU。
如果 Customer VLAN 对运营商网络不可见,那么就起不到扩展 VLAN 空间的作用,运营商的 VLAN 空间就仍然只有 4K。
而 PBB,即所谓的 MAC-in-MAC 则能很好的解决 QinQ 的这个不足,因为 PBB 不仅在原来的报文上新增一个 VLAN,还新增 MACSa/MACDa 以及 24 个 bit 的 Isid(用来标识 Service),它可以完全对运营商的 Core Network 设备隐藏 Customer 信息,且能利用 Isid 来支持 16M Customer/Service。
当然PBB也有PBB的问题,目前看不到它有成为主流技术的趋势。
LLDPDU 是 LLDP 的有效负载,用于承载要发送的消息。LLDPDU 的格式如下图所示
LLDPDU 采用了 TLV 的格式,即 type+length+value
的格式,type 表示 TLV 的类型,length 是以字节为单位的 TLV 的长度,value 是该 TLV 的值。其中 Chassis ID TLV,Port ID TLV Time To Live TLV 以及 End Of LLDPDU TLV 是强制的,必须包含的部分,除此之外在 TLV Time To Live TLV 和 End Of LLDPDU TLV 之间可以包含 0 个到多个可选的其它 TLV。
TLV 是组成 LLDPDU 的单元,每个 TLV 都代表一个信息。LLDPDU 的 TLV 可以分为两大类
802.1
组织定义 TLV、802.3
组织定义 TLV 以及其他组织定义的 TLV。这些 TLV 用于增强对网络设备的管理,可根据实际需要选择是否在 LLDPDU 中发送。
TLV 的基本格式如图所示
TLV的类型域的定义及分配如下图所示
其中 type0-8
属于基本的 TLV 集合。对于其中的 Mandatory 的 TLV,它是必须包含在 LLDP 中的。
组织定义 TLV 集合的格式如下图所示
其中
几个强制的必须包含的 TLV 的定义如下。非强制的可以参考 IEEE802.1AB。
该 TLV 用于标识 LLDPDU 的结束。其格式如下
由于 length=0,因此它不包含 value 域。
该 TLV 用于通告该 LLDPDU 发送者的 Chassis ID。由于有很多方式可用来标识一个 Chassis,因此在该类 TLV 中包含一个子类型域用于告诉接收者,发送者的 Chassis ID 采用的是哪一种标识方式。其格式如图所示
每个 LLDPDU 必须包含且仅包含一个该类型的 TLV。由于 Chassis ID 实际上是用于标识设备的,因此在连接可用时它应该保持不变。 Chassis 子类型所可能的取值如图所示
它用于标识发送该 LLDPDU 的设备的端口。类似于 Chassis ID,有很多方式可以标识一个 Port,因此该 TLV 也包含一个子类型域。其格式如下图所示
每个 LLDPDU 必须包含一个且只能包含一个该类型的 TLV。同时,当端口可用时,从该端口发送出去的 LLDPDU 的该 TLV 应该保持不变。 其子类型的可能取值如下图所示
该 TLV 用于告诉接收端,它接收到的这些信息的有效期有多长。其格式如图所示
TTL 的时间单位是秒,由于只有 2 个字节长,因而最大有效时间是 65536 秒。如果在这个时间到期了还没有新的 LLDPDU 被收到,则该 TLV 所属的那个 LLDPDU 携带的信息会被从 MIB 中删除。如果收到了新的 LLDPDU,则
Chassis ID+Port ID
来判断是否来自于同一个源,这也是要求这两者保持不变的原因)。每一个 LLDPDU 必须包含且只能包含一个该类型的 TLV。
LLDP 是一个用于信息通告和获取的协议,但是需要注意的一点是,LLDP 发送的信息通告不需要确认,不能发送一个请求来请求获取某些信息,也就是说 LLDP 是一个单向的协议,只有主动通告一种工作方式,无需确认,不能查询、请求(比如像 ARP 协议那样请求某个 IP 的 MAC 地址)。
LLDP 主要完成如下工作:
从本地 MIB 库中提取信息,并将信息封装到 LLDP 帧中。LLDP 帧的发送有两种触发方式,一是定时器到期触发,一是设备状态发生了变化触发。
识别并处理接收到的 LLDPDU 帧。
维护远端设备 LLDP MIB 信息库。
当本地或远端设备 MIB 信息库中有信息发生变化时,发出通告事件。
LLDPDU 的发送可以被如下事件触发:
LLDP 的常规发送时间是建立在系统的 Tick 之上的,间隔为 1 秒一个,为了防止在共享介质的 LAN(shared media LAN)中同时出现大量的 LLDPDU(因为接入同一个LAN的多个系统的时间是同步的,因而多个系统上的基于 Tick 的1秒定时器可能同时到期),发送定时器引入了一个随机的抖动,这就使得常规的 LLDP 帧的发送间隔时间的平均值仍是 1 秒,但是具体到某一次到期时间可能并不是准确的 1 秒。
同时为了防止在有多个端口需要发送 LLDPDU 的系统中,所有的端口的定时器都在同一时间到期,因而标准建议将采用某种机制将多个发送实例的定时器到期时间给错开,以避免一个系统在同一时刻发送大量的 LLDPDU。
LLDPDU 的发送状态机如图所示
对于该状态机:
LLDP 发送定时器状态机如图所示
localChange 表示本地信息是否发生改变;txTTR 表示下一次定时器到期的时间;newNeighbor 表示是否发现了新的邻居,并由接收状态设置,由该状态机清除;txTick 表示基于系统时间的1秒定时器是否到期。
对于该状态机:
这里有取值范围的几个变量都是可配置的变量。
从上述两个状态机的工作状态可以看出,发送定时器状态机用于维护信用量以及是否允许发送 LLDPDU 帧,而发送状态机根据这两个信息来决定是否发送。另外需要注意的是 LLDP 所使用的所有定时器操作都是“基于系统时间的 1 秒定时器的”,每当这个定时器到期时它除了会将 txTick 设置为 TRUE 外,还会处理其它的定时功能。
LLDP 帧的接收由 3 个阶段组成:帧的识别、帧的校验、LLDP 远端 MIB 信息库更新。
由在 LLDP/LSAP(链路服务访问点)进行,检查的内容是帧的目的地是否是 LLDP 的组播 MAC 地址,帧的类型是否是 LLDP。
该过程会首先根据 TLV 的格式定义依次校验 Chassis ID TLV,Port ID TLV, Time To Live TLV,如果这三个 TLV 都存在且有效,才会进一步的解码可选的 TLV 直到遇到 End Of LLDPDU TLV,然后根据获得的信息更新远端 MIB 信息库。
在前两步都通过之后,LLDPDU 的接收者就需要根据解析出来的信息更新远端 MIB 信息库。在 MIB 信息库中,LLDP 使用 Chassis ID+Port ID
来标识、存储来自不同源的信息。
Chassis ID+Port ID
的信息,则使用收到的帧中的新的 TTL 来更新 TTL。并用对于收到的新的 LLDPPDU 中的每一种 type,如果有变化就进行更新,如果某种 type 原来不存在,则需要将其添加到 MIB 库中。更新时,如果需要添加新的 Chassis ID+Port ID
的表项,或者为某个 Chassis ID+Port ID
添加新的 TLV,则可能遇到没有内存的问题,标准没有规定必须如何处理,只是给出了一些建议:
LLDPDU 携带的 TTL(Time To Live)值会影响接收端的处理方式,如果它不为 0,则更新相应信息的老化时间,如果接收到的 LLDPDU 中的 TTL 等于 0,则将立刻老化掉相应的信息(即与该 LLDPDU 的发送者相关的MIB信息)。
如果一个 Chassis ID+Port ID
标识的信息的 TTL 超时,则相应的 MIB 信息会被删除。
LLDPDU 的接收状态机如图所示
LLDP 可以工作在多种模式下:
由于 LLDP 可以单独工作在发送或接收模式下,因此 LLDP 协议的实现需要支持单独初始化发送或者接收功能。当工作模式发生变化时,需要根据老的/新的工作模式来关闭/打开发送或者接收的功能。
至此,LLDP 相关知识点已经介绍完,希望对大家有所帮助。
]]>