posted in iOS 

The class NSDateFormatter is an easy-to-use tool. Just like SimpleDateFormat in Java.

Convert String to NSDate

let str = "2016-06-01T00:00:00Z"
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
let date = dateFormatter.dateFromString(str)

Convert NSDate to String

let description = "Time is \(dateFormatter.stringFromDate(date))"

TAG: Convert, String, NSDate, NSDateFormatter

posted in Uncategorized 

最近网盘行业风波不断,网盘供应商不是停止服务就是限制服务。当年网盘风头莫名其妙刮了起来,各大互联网公司用了疯狂赠送空间,不要钱地去推广,换来了今天人走茶凉,继续运营没有盈利,停止运营对不起用户的尴尬状况。国内的市场真的很浮躁,当年的网盘、微博、手游,现在的可穿戴设备、直播,土豪们一掷千金抢占市场,然后把整个行业给玩坏了。

国内的巨头们鲜有老老实实做生意的,百度尤甚。当初因巨额的空间入了百度网盘的坑。把图片、视频、镜像文件等大尺寸资源备份到百度网盘;word、pdf、各种使用频率高而Size不大的文件同步到坚果云。之前一段时间用得是相当舒心。但是现在,由于最近的网盘整顿风波,加之发现百度网盘可以未经允许读取用户文件(老司机跟我说现在百度下不了小电影了,个人空间上的小电影会被百度检查出来),决定还是要逐步脱坑。

想来百度要死也不会在这几天死,先解决隐私问题。

使用 VeraCrypt 创建虚拟加密磁盘

逛异次元的时候看到了这篇文章(VeraCrypt - 最佳免费开源跨平台的文件加密工具 (支持U盘加密/硬盘分区加密)),发现这是一个很不错的解决方案。

VeraCrypt 会在电脑上创建一个虚拟的加密磁盘,该虚拟磁盘以单个文件的形式保存在本地硬盘上。使用各网盘的同步工具可以很轻松地同步整个虚拟磁盘。而需要访问该虚拟磁盘的时候,挂在该虚拟磁盘文件,输入密码,即刻执行文件的增删查改操作。VeraCrypt 为加密而生,AES 的加密强度还是让人放心。

使用 VeraCrypt 创建加密磁盘的方法只略微增加了操作步骤,但是极大地提高了文件安全性,再也不用担心网盘供应商随意浏览你的文件了。同时,我们可以只把需要保密的文件丢入磁盘中,而电影、音乐、软件安装包等大文件,而且又不担心别人访问的资源,可以直接同步而不放入加密磁盘。这样因为加密而带来的不便就大大减少了。
(某度没节操也不是一两天了,想着自己同步到网盘的文件被内部员工随意浏览,哪天出BUG了外部人员可以随意访问用户文件,或者某度又为了利益出卖了用户,种种的“意外”我就不得安心)。

另外,VeraCrypt 提供了密钥、加密文件等多种验证方法,同时可以添加对整个硬盘加密或者对U盘加密,根据需要可以加密电脑上的其他文件。毕竟电脑只要联网,就有可能被入侵。还是要提高一下安全意识。

使用 OS X 系统内置的加密DMG磁盘文件功能

如果只有简单的加密需求,其实系统内置的 DMG 磁盘文件功能更加方便。".dmg" 文件相信大家都不陌生,dmg 文件常见于应用安装包。开发者将应用的可运行文件打包到虚拟磁盘中,供用户下载。用户下载到电脑上,直接双击挂载磁盘,即可访问虚拟磁盘中的文件。

DMG 文件是可以加密的。如果我们使用加密的 DMG文件来保存我们的隐私文件,由于系统本身对该文件的支持,我们会获得比 VeraCrypt 更好的体验。

Step 1: 创建一个空白映像文件

打开 Disk Utility (磁盘工具) 应用,按下 “⌘N” (或者点击菜单栏的文件 -> 新建映像 -> 空白映像)。

加密方法可以选择 AES128 或 AES256,看个人喜好。

映像格式需要解释一下。可选的格式有稀疏磁盘映像、读/写磁盘映像 和 DVD/CD 主映像。DVD那个不用管,读/写磁盘映像即是我们下载应用安装包的那个格式(.dmg),其实有可读写和只读两种形式。磁盘大小在创建时确定,此后不可变。

而稀疏磁盘映像则是创建后磁盘大小可以根据磁盘储存文件的大小来动态调整(初次生成时,磁盘文件为创建时指定的文件大小。在挂载卸载后,磁盘随即调整自身尺寸)。该格式被用于系统自带的磁盘加密 FileVault 功能。

而稀疏捆绑磁盘映像,该格式被用于 Time Machine 。更适用于文件备份。
(其实我也不是很懂,具体请参考wikipedia-稀疏磁盘映像)。

显然,我们应该选稀疏磁盘映像,至于捆不捆绑,我觉得无所谓了。

挂载该加密的磁盘文件

双击刚才创建的虚拟磁盘文件,按照系统提示输入密码,即可访问该磁盘。如果你启用了 KeyChain,那么系统会提示你是否保存密码。如果你保存了密码,下次挂载该磁盘,系统会自动读取密码,避免手动输入。

为了方便,我选择了保存密码,酱紫我在本机就可以像未加密映像一样使用了。毕竟我只想我的文件在同步到网盘后不会被其他人读取而已。

移动磁盘文件到自动同步目录

聪明的人在创建时即保存到同步目录了。不聪明的我们,事后把文件转移到百度云同步盘、坚果云同步盘、OneDrive、iCloud Drive 或 Dropbox。


TAG: 网盘, 加密, 同步, 稀疏磁盘映像

posted in Uncategorized 

写着写着代码,终于忍受不了在Xcode、Emulator、Chrome、Dash等应用中切来切去的麻烦。Dock栏可不是那么好点的。于是花了一段时间查找Mac上有哪些App可以实现快捷键切换应用程序。
最后,终于挑定了一款应用的时候,突然想起来 Alfred 来。于是尝试了一下,效果完美。

Alfred 无愧神器之名:)


Step 1: Create Blank Workflow

Add a blank workflow, fill the name, description and other field Alfred required.

Step 2: Add Hotkey Trigger

Click the “+” button on the upper right, select the Trigger -> Hotkey. Now a new window shows, type the Hotkey you want to use.

Step 3: Add Action

Click the “+” button, this time select the Action -> Launch Apps/ Files. Drop the app you want to open from Finder to Alfred.

Step 4: Link the Trigger to the Action

Link the hotkey trigger to the launch apps action. Done!

A workflow to trigger the chrome and xcode

最终效果是,在 Chrome 还没有打开的情况下,将启动 Chrome。如果 Chrome 已经启动,则切换到 Chrome。

posted in iOS 

It’s necessary to convert custom object to NSData when using NSUserDefaults or NSKeyedArchiver. Furthermore, a dictionary or an array with custom class.

Here shows steps to do it.

1. Define your class
class Book: NSObject, NSCoding {
    var id:Int
    var name:String?
    var author:String?
    
    private static let KeyId = "key_id"
    private static let KeyName = "key_name"
    private static let KeyAuthor = "key_author"
    
    init(id: Int){
        self.id = id
    }
    
    required init? (coder aDecoder: NSCoder){
        self.id = aDecoder.decodeIntegerForKey(Book.KeyId)
        self.name = aDecoder.decodeObjectForKey(Book.KeyName) as? String
        self.author = aDecoder.decodeObjectForKey(Book.KeyAuthor) as? String
    }
    
    func encodeWithCoder(aCoder: NSCoder) {
        aCoder.encodeInteger(id, forKey: Book.KeyId)
        aCoder.encodeObject(name, forKey: Book.KeyName)
        aCoder.encodeObject(author, forKey: Book.KeyAuthor)
    }
}

You must extend your class from NSObject and implement the NSCoding protocal. Thus, you get init(coder:) and encodeWithCoder(aCode:)

2. Save data
let book = Book(id: 1)
book.name = "The first book"
book.author = "IMLC.ME"

let book2 = Book(id:2)
book.name = "The second book"
book.author = "IMLC.ME"

let books = [book, book2]

let ud = NSUserDefaults.standardUserDefaults()
ud.setObject(NSKeyedArchiver.archivedDataWithRootObject(book), forKey: "book")
ud.setObject(NSKeyedArchiver.archivedDataWithRootObject(book), forKey: "book-array")

The code above shows how to save book and books (an array) into NSUserDefaults.

3. Restore data
if let bookData = ud.objectForKey("book") as? NSData,
    let bookArrayData = ud.objectForKey("book-array") as? NSData{
    let restoredBook = NSKeyedUnarchiver.unarchiveObjectWithData(bookData) as? Book
    let restoredBookArray = NSKeyedUnarchiver.unarchiveObjectWithData(bookArrayData) as? [Book]
    
    print("Restored book \(restoredBook.name!) and book array [\(restoredBookArray[0].name!),\(restoredBookArray[1].name!)]")
}

Saving and loading data with swift dictionary is very similar to array, I wouldn’t give an another sample here.

TAG: Swift, NSData, Convert, Custom class

posted in Android 

死坑死坑,新买的手机竟然不能用来调试。今天终于被我查到解决办法了(应该说终于有大神写的解决办法被收录然后被我查到了)。
ps:本解决办法同样适用于 MX4 & 魅蓝

在 Mac 下,修改 adb_usb.ini 文件(Mac 是在/Users/[username]/.android/adb_usb.ini 目录下,username 即是你的用户名;Windows 是在/Users/user/.android 目录下),往里面添加新的一行
0x2a45
adb_usb.ini

保存退出。
在命令行下依次输入:

adb kill-server
adb devices

这时会显示
terminal_adb_devices

此时,adb 识别到手机,但是提示未认证。
在点亮手机屏幕的情况下,用 adb 连接手机,手机会提示你认证这台电脑,确认即可。
 
如果输入 adb 显示 command not found,这表示你的Mac 上未配置 adb 环境变量。
可以参考我的另一篇博文进行配置。
 
参考:
wispy316的专栏

posted in Android 

1. 记录 adb 所在目录位置


记住自己adb 程序的目录位置,如在我的电脑中是:/Users/android-sdk-macosx/platform-tools/

2. 创建 .bash_profile 文件


terminal 中输入

touch .bash_profile

3. 打开 .bash_profile 文件

open -e .bash_profile

该命令会自动打开文本编辑器。

(1) 如果你发现里面已经有内容了,请在 PATH 后面添加

:/Users/android-sdk-macosx/platform-tools/

包括最前面的英文状态下的冒号。如果 PATH 后面的路径被双引号括了起来,那么请把上面这句添加到双引号的里面。

(2) 如果你打开的文件是空白的,请输入

export PATH=${PATH}:/Users/android-sdk-macosx/platform-tools/

保存后关闭。

4. 添加环境变量

source .bash_profile

5. 在终端输入 adb,在设置正常的情况下会弹出一大堆的adb 使用提示。

posted in JavaScript/JQuery 

As for the reason only string can be stored in localStorage, an object need to be converted to string format (JSON text).

Save object

var books = [{id:3, name:"From Zero To One"}, {id:4, name:"Head First Java"}];
localStorage.setItem("books", JSON.stringify(books));

Load object

var booksJSON = localStorage.getItem("books");
if( booksJSON == null ){
    // No data stored in the localStorage
    return;
}
var books = JSON.parse(booksJSON);
for(var i=0; i < books.length; ++i) {
    var book = books[i];
    var id = book.id;
    var name = book.name;
}

posted in JavaScript/JQuery 

这是由荷兰程序员 Gabor de Mooij 提出的一种比较简单的方法。Gabor de Mooij 称之为 minimalist approach。现摘录如下:

定义一个 Foo 类
var Foo = {
    createNew: function(){
        var instance = {};
        instance.description = "A new class instance";
        instance.bar = function() { console.log(instance.description)};
        return instance;
    }
}
创建一个 Foo 实例
var foo = Foo.createNew();
foo.bar();
类的继承
var NewFoo = {
    createNew = function(){
        var instance = Foo.createNew();
        instance.title = "A new title";
        instance.newBar = function(){
            alert(instance.description);
        };
    }
}
私有方法和属性
var Foo = {
    createNew: function(){
        var instance = {};
        instance.description = "A new class instance"; //description 是私有的,只有通过 bar() 来访问
        instance.bar = function() { console.log(instance.description)};
        return instance;
    }
}
静态成员变量
var Foo = {
    id: 101010; // id 在多个实例间共享
    createNew: function(){
        var instance = {};
        instance.description = "A new class instance"; //description 是私有的,只有通过 bar() 来访问
        instance.bar = function() { console.log(instance.description)};
        return instance;
    }
}

var foo1 = Foo.createNew();
var foo2 = Foo.createNew();
foo2.id = 233333;
console.log("Foo id:"+foo1.id);

posted in Android 

这么重要的问题竟然没有事先说明,太过分了。
今天更新了 Android 6.0 的 SDK 后发现项目一片红,吓尿了。
直接引用把:http://developer.android.com/about/versions/marshmallow/android-6.0-changes.html#behavior-apache-http-client
Android 6.0 release removes support for the Apache HTTP client. If your app is using this client and targets Android 2.3 (API level 9) or higher, use the HttpURLConnection class instead. This API is more efficient because it reduces network use through transparent compression and response caching, and minimizes power consumption. To continue using the Apache HTTP APIs, you must first declare the following compile-time dependency in your build.gradle file:

android {
    useLibrary 'org.apache.http.legacy'
}

大概意思就是,Android 6.0不再支持 Apache HTTP client。 请使用 HttpURLConnection 代替。想要继续使用 Apache HTTP client 的,请添加如上代码。

posted in Android 

  1. 开启手机的Root权限。(开放Root权限会失去保修,慎重考虑)

  2. 在 Terminal 下定位到 sdk/platform-tools 文件夹下,运行

su
chmod 777 /data

因为 adb shell 的无法添加-R参数递归修改权限,所以用adb必须要一级一级文件夹修改权限,而且…我运行chmod后提示“Unable to chmod /data: Operation not permitted ”。所以..

  1. 下载 Root Explorer,直接递归修改整个/data文件夹, done!
posted in iOS 

Following the document of TSMessages, I meet a trouble:
Could not read TSMessages config file from main bundle with name
As mentioned on the page GitHub issue, I fixed the first trouble I met.
Change pod file to:

pod 'TSMessages', :git => 'https://github.com/KrauseFx/TSMessages.git'

The problem has been fixed in the master branch but the author do not commit it to Pod yet.
 
The second problem is that the easiest way to use TSMessages do not works.

[TSMessage showNotificationWithTitle:@"Your Title"
                                subtitle:@"A description"
                                    type:TSMessageNotificationTypeError];

in swift:

TSMessage.showNotificationWithTitle("Title", subtitle:"subtitle", type:TSMessageNotificationType.Error)

Do nothing on my project.
I has to use an another method and it works fine.

TSMessage.showNotificationInViewController(self, title: "Title", subtitle: "Subtitle", type: TSMessageNotificationType.Error)

I do not figure it out why.

TAG: iOS, TSMessages, Swift

Integer 是 Java 5 引入的新特性,该特性能节省内存和改善性能。同样被引入缓存机制的还有 Byte,Short,Long,Character,缓存范围都是在 [-128,127] (Character是在[0,127])之间。

但是有几点需要注意的是:
1. 对 Integer 对象引入了 IntegerCache 类,其他封装类型也有对应的 XxxCache。
2. 该缓存特性只有在 autoboxing 过程中使用,换言之,使用 constructor 创建的 Integer 并不能被缓存。
3. 因为缓存机制的存在,在缓存范围内的对象都来自同一个缓存。带来的副作用是使用“==”地址比较运算符比较两个看似不一样的对象,得到的是 true。网上其他文章的说法是因而可以使用 == 来比较直接比较两个对象的值。但我觉得这样理解容易造成偏差。Java 规范并没有约定 [-128,127] 的范围内 == 用作值比较,这只是一个副作用。
4. 只有 Integer 对象可以通过指定 JVM 启动参数来修改缓存上限。

-XX:AutoBoxCacheMax=size

所以说写代码的时候还是不要把==用作值比较。
 
老实说这个缓存特性带来的不一致性要是导致了什么BUG实在很难排查。当用了这个特性写了点什么之后,难保后来者不会产生迷惑。所以日常编码中还是尽量避开这个点。
 
简单到爆的实验代码:
简单地验证Java Integer缓存特性

import com.sun.istack.internal.NotNull;
 
/**
 * Created by Lawrence on 15/11/9.
 */
public class Main {
    public static void main(String[] argv){
        Integer int1 = 100;
        Integer int2 = 100;
 
        if(int1==int2){
            print("int1 is equal to int2");
        } else {
            print("int1 is not equal to int2");
        }
        // prints "int1 is equal to int2"
 
        Integer int3 = 2333;
        Integer int4 = 2333;
 
        if(int3==int4){
            print("int3 is equal to int4");
        } else {
            print("int3 is not equal to int4");
        }
        // prints "int3 is not equal to int4"
 
        Integer int5 = 100;
        Integer int6 = new Integer(100);
 
        if(int5==int6){
            print("int5 is equal to int6");
        } else {
            print("int5 is not equal to int6");
        }
        // prints "int5 is not equal to int6"
    }
 
    public static void print(String str){
        System.out.println(str);
    }
 
}