0%

在iOS开发中,UINavigationController是很常用的Controller,对它的一般操作就像操作一个栈,push和pop。但也经常会遇到pop和push无法优雅的完成的操作,比如退回到中间的某个VC上,或者在第一个VC之前添加一个VC等,更甚者要重新构造整个VC的顺序,这时候setViewControllers方法就排上用场了,它使对VC栈的操作不再局限于push和pop,而是构造整个VC栈并应用到当前的UINavigationController中,这个方法支持iOS3.0+,放心使用。


#Sample

1
2
3
4
5
6
NSMutableArray * viewControllers = [self.navigationController.viewControllers mutableCopy];
[viewControllers removeLastObject];
[viewControllers addObject:newController];

[self.navigationController setViewControllers:viewControllers animated:YES];
// [viewControllers relase] // if non-arc

感谢 Allen(Weibo) 提供的代码和思路




#说明
下面这段摘自Api文档

You can use this method to update or replace the current view controller stack without pushing or popping each controller explicitly. In addition, this method lets you update the set of controllers without animating the changes, which might be appropriate at launch time when you want to return the navigation controller to a previous state.

If animations are enabled, this method decides which type of transition to perform based on whether the last item in the items array is already in the navigation stack. 

1.If the view controller is currently in the stack, but is not the topmost item, this method uses a pop transition; 
2.if it is the topmost item, no transition is performed. 
3.If the view controller is not on the stack, this method uses a push transition. 

Only one transition is performed, but when that transition finishes, the entire contents of the stack are replaced with the new view controllers. For example, if controllers A, B, and C are on the stack and you set controllers D, A, and B, this method uses a pop transition and the resulting stack contains the controllers D, A, and B.

Have fun!

Image1 icon

最近项目要重构,首当其冲的就是代码结构,因为很多原因之前很少考虑代码结构的事情。终于要抽出一部分时间来重构这个项目,首先是整个项目的结构和代码逻辑不太符合MVC,又顺便了解了一下iOS里的MVC模式的概念。首先MVC模式不光定义了每一部分在整个应用中扮演的角色,也定义了各个部分相互沟通交流的方式。每一部分都扮演着不同的角色,分工明确,降低耦合,减少依赖,使得每一部分都能够复用,这也是MVC模式的意义和目的所在。下面就简单描述一下MVC模式里对每一个角色的职能和责任。

Model

Model层对象应该是封装了一定的数据规范,并且定义了管理和处理这些数据的逻辑和计算。简单说就是Model对象不仅定义了数据结构,还要包括对数据结构的操作和处理逻辑。比如从网络、sqlite、UserDefault里获取需要在View里展现的数据以及存入用户感兴趣的数据等等。其实Model里是包含业务逻辑的,这一点和Web开发差异很大,之前在用Java开发Web程序时使用MVC,M就是POJO,只包括定义数据结构,不包含对这些数据的处理(处理的部分放在一个叫Service层里),也称之为贫血模型。相对应的充血模型就类似这里的M,是包含对数据的操作和处理,ROR里的ActiveRecord就是这样的。

View

View层的理解就很简单了,就是用户能看得见的东西,比如UIKit里的各种系统自带控件等。View对象应该知道如何把自己展示给用户并且对用户的操作做出回应。View层对象的主要用途在于展示出应用的Model层数据并且允许用户通过交互修改这些数据。

Controller

Image2 icon

Controller层对象相当于一个中间人,负责协调应用中的View层对象和Model层对象的关系,也是View和Model相互沟通的媒介。除此之外,Controller还负责组织和协调应用中的任务以及管理其他对象的声明周期。

相互的沟通

Image3 icon

Model层不直接和View沟通,当Model层对象改变(比如通过网络获取到了新的数据),它会通知Controller对象,Controller对象收到通知更新对应的View。当View层有改变(比如用户通过交互创建或修改了数据,称为User Action),View会通过Controller对象去创建或修改Model层的数据。 Model层和View层是相互不知道对方的,它们的沟通是通过Controller这个中间人来协调处理。

Jekyll默认的社会化评论组件是disqus,第三方SNS是facebook,twitter等,不方便大陆用户使用,发现国内也有类似的社会化评论组件,比如友言等,经比较发现友言更简单易用。

替换的整个过程很简单,分为两大步:
首先要注册一个友言的账户,点击获取代码,就能获得一段和你用户相关的js代码。类似下面这样:

1
2
3
4
<!-- UY BEGIN -->
<div id="uyan_frame"></div>
<script type="text/javascript" id="UYScript" src="http://v1.uyan.cc/js/iframe.js?UYUserId=YOUR_USER_ID" async=""></script>
<!-- UY END -->

然后要切换到本地来,由于Jekyll的评论组件是插件式的,很方便修改,分为下面2个步骤

  1. 修改_config.yml文件中comments:下的provider:的值为custom(默认是disqus)
  2. 在_includes目录下新建一个目录custom,在custom目录下新建一个文件comments,文件的内容就是上面从友言获得的那段代码。

push到GitHub,刷新页面查看效果吧

这么做的原理很简单,看一下youname.github.com/_includes/JB/comments文件的
看最后一个when语句,当site.JB.comments.provider的值为custom时,就加载custom/comments文件,那么其实site.JB.comments.provider的值就是刚才在_config.yml中设置的那个provider,这样就能说的通了。

Have fun!

FMDB是Objective-C上操作Sqlite的开源库,与原生的操作sqlite数据库相比,有以下几个优点:

  1. 操作方便、简单、代码优雅,易于维护;
  2. 线程安全,用着更放心,很少出现过锁死数据库文件以及Crash的现象。

FMDatabase不是线程安全的,一个FMDatabase对象一定不能在多线程中使用,为了保证线程安全,可以在FMDB中采取下面两种方式:

  1. 每个线程都创建一个FMDatabase对象,使用之前打开连接,用完关闭销毁;
  2. 使用FMDatabaseQueue来保证线程安全,一个FMDatabaseQueue的对象可以在多线程中共享使用。

使用FMDatabase时,一般这样来做:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//创建一个 FMDatabase的对象
FMDatabase *db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];
//判断db是否打开,在使用之前一定要确保是打开的
if ([db open]) {
//使用FMDatabase操作数据库
FMResultSet *s = [db executeQuery:@"SELECT * FROM myTable"];
while ([s next]) {
//retrieve values for each record
}

//关闭
[db close];
}
db = nil;

上面的这段代码是使用FMDatabase操作数据库的一个典型的使用方式,可以看到,其实我们关注的只是使用它来对数据库进行增删改查的操作,却每次都要写这些打开和关闭的操作,代码也显得臃肿,bad smell。用过Java中著名的Spring框架的同学都记得里面对数据库操作提供了一个Template的机制,比如JdbcTemplate、HibernateTemplate等,使用回调函数非常优雅的分离了创建连接、关闭连接和使用数据库连接操作数据库,下面就来模拟这个实现。
首先做个抽象,在上面代码的真正的逻辑中,我们只要拿到db变量就能满足我们的需要了,那么我们就把这一块抽象出来,在这里我们使用oc里的block来实现回调功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//创建一个工具类TWFmdbUtil
@implementation TWFmdbUtil
+ (void) execSqlInFmdb:(void (^)(FMDatabase *db))block {
NSString *dbPath = @"dbpath"; //sqlite数据库文件的路径
//创建一个FMDatabase的对象
FMDatabase *db = [FMDatabase databaseWithPath:dbPath];
//使用之前保证数据库是打开的
if ([db open]) {
@try {
block(db); //调用block来回调实现具体的逻辑
}
@catch (NSException *exception) {
//处理异常,也可以直接抛出,这样调用者就能捕获到异常信息
NSLog(@"TWFmdbUtil exec sql exception: %@", exception);
}
@finally {
[db close]; //如果[db open]就要保证能关闭
}
} else {
//如果打开失败,则打印出错误信息
NSLog(@"db open failed, path:%@, errorMsg:%@", dbPath, [db lastError]);
}
db = nil;
}
@end

现在使用的时候就能够像下面这样来实现了:

1
2
3
4
5
6
7
[TWFmdbUtil execSqlInFmdb:^(FMDatabase *db) {
//处理业务逻辑
FMResultSet *s = [db executeQuery:@"SELECT * FROM myTable"];
while ([s next]) {
//retrieve values for each record
}
}];

这样的代码看起来是不是优雅多了呢?我们无需关心数据库的创建和关闭操作,只需要关心我们的业务逻辑就可以了。

历史总是惊人的相似,FMDatabaseQueue的使用就是采用这样的方式来处理的,来看一段fmdb主页上提供的一个例子:

1
2
3
4
5
6
7
8
9
10
11
FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:aPath];
[queue inDatabase:^(FMDatabase *db) {
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:1]];
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:2]];
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:3]];

FMResultSet *rs = [db executeQuery:@"select * from foo"];
while ([rs next]) {
//...
}
}];

更多实例请移步FMDB在GitHub上的主页
或者访问@唐巧_boy 关于FMDB的这篇文章

Have Fun!

使用diff查看文件更改信息

1
2
3
4
5
6
7
8
9
10
#查看未暂存文件的变化(与最近一次的暂存/提交比较)
$ git diff
#查看已暂存文件的变化(与最近一次提交比较)
$ git diff --cached
#查看与版本库中任一版本的变化
$ git diff 3e4e
#查看任意两个版本间的变化
$ git diff 3e4e 5d5a
#具体到某个文件
$ git diff 3e4e 5d5a index.md

查看任意版本下的某个文件

1
2
#查看某个版本下某个文件内容
$ git show i5d5a index.md

最近在OSChina上翻译版块有一个系列(共4篇)关于Guava/Google Collections库的文章,我也有幸翻译了一部分。Guava的中文意思是番石榴,这个库的功能和名字一样诱人,很好很强大,使用起来也很方便,强烈推荐。
这几篇文章都是2009年写的,现在的Guava库应该已经更新了很多,不过对于了解Guava库还是OK的。

Guava托管在Google Code上的地址在这里,目前最新版本是14.0

Java8带有Lambda表达式的预览版的JDK已经放出来了(地址在最下面),新特性有以下四个:

  1. Lambda表达式(或称之为“闭包”或者“匿名函数”)

  2. 扩展的目标类型

  3. 方法和构造器引用

  4. 接口默认方法

本文先介绍一下Java8中很值得期待的Lambda表达式,Lambda表达式,等同于大多说动态语言中常见的闭包、匿名函数的概念。其实这个概念并不是多么新鲜的技术,在C语言中的概念类似于一个函数指针,这个指针可以作为一个参数传递到另外一个函数中。由于Java是相对较为面向对象的语言,一个Java对象中可以包含属性和方法(函数),方法(函数)不能孤立于对象单独存在。这样就产生了一个问题,有时候需要把一个方法(函数)作为参数传到另外一个方法中的时候(比如回调功能),就需要创建一个包含这个方法的接口,传递的时候传递这个接口的实现类,一般是用匿名内部类的方式来。如下面代码,首先创建一个Runnable的接口,在构造Thread时,创建一个Runnable的匿名内部类作为参数:

1
2
3
4
5
new Thread(new Runnable() {  
public void run() {
System.out.println("hello");
}
}).start();

类似这种情况的还有swing中button等控件的监听器,如下面代码所示,创建该接口的一个匿名内部类实例作为参数传递到button的addActionListener方法中。

1
2
3
4
5
6
7
8
9
public interface ActionListener {   
void actionPerformed(ActionEvent e);
}

button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
ui.dazzle(e.getModifiers());
}
});

这样的代码的缺点是有代码笨重,可读性差,不能引用外面的非final的变量等。lambda表达式就是为了解决这类问题而诞生的。

在介绍Java8中的Lambda表达式之前,首先介绍一个概念叫“函数式接口”(functional interfaces)。对于任意一个Java接口,如果接口中只定义了唯一一个方法,那么这个接口就称之为“函数式接口”。比如JDK中的ActionListener、Runnable、Comparator等接口。

先来看一下Java8中的lambda表达式的使用示例:

创建一个线程:

1
new Thread(() -> {System.out.println("hello");}).start();

可以看到这段代码比上面创建线程的代码精简了很多,也有很好的可读性。
() -> {System.out.println(“hello”);}就是传说中的lambda表达式,等同于上面的new Runnable(),lambda大体分为3部分:

1.最前面的部分是一对括号,里面是参数,这里无参数,就是一对空括号

2.中间的是 -> ,用来分割参数和body部分

3.是body部分,可以是一个表达式或者一个语句块。如果是一个表达式,表达式的值会被作为返回值返回;如果是语句块,需要用return语句指定返回值。如下面这个lambda表达式接受一个整形的参数a,返回a的平方

1
2
3
(int a) -> a^2   
//等同于
(int a) -> {return a^2;}

继续看更多的例子:

1
2
3
4
5
(int x, int y) -> x + y  

() -> 42

(String s) -> { System.out.println(s); }

创建一个FileFilter,文件过滤器:

1
FileFilter java = (File f) -> f.getName().endsWith(".java")

创建一个线程:

1
2
3
new Thread(() -> {  
//do sth here...
}).start()

创建一个Callable:

1
Callable<String> c = () -> "done";

而且lambda表达式可以赋值给一个变量:

1
2
Comparator<String> c;  
c = (String s1, String s2) -> s1.compareToIgnoreCase(s2);

当然还可以作为方法的返回值:

1
2
3
4
5
public Runnable toDoLater() {  
return () -> {
System.out.println("later");
};
}

从上面可以看到,一个lambda表达式被作为一个接口类型对待,具体对应哪个接口,编译器会根据上下文环境推断出来,如下面的lambda表达式就表示一个ActionListener.

1
ActionListener l = (ActionEvent e) -> ui.dazzle(e.getModifiers());

这有可能会造成一个表达式在不同的上下文中被作为不同的类型,如下面的这种情况,尽管两个表达式是相同的,上面的表达式被推断为Callable的类型,下面的会被推断为PrivilegedAction类型。

1
2
Callable<String> c = () -> "done";  
PrivilegedAction<String> a = () -> "done";

那么编译器是根据哪些因为决定一个表达式的类型呢?

如果一个表达式被推断为是T类型的,需要满足以下4个条件:

  1. T是函数式接口类型(只声明唯一一个方法)
  2. 表达式和T中声明的方法的参数个数一致,参数类型也一致
  3. 表达式和T中声明的方法的返回值类型一致
  4. 表达式和T中声明的方法抛出的异常一致
    有了这个准则,上面的疑问就迎刃而解了

参考:

1.State of the Lambda

2.Java8带有Lambda表达式版本的JDK下载地址

最初的Blog放在了GAE上,使用一个叫B3log的Blog程序,但是间歇性的被墙访问不了,或者就是打开页面很慢,用着纠结;然后打算自己搞个VPS,于是乎在42qu上买了个VPS,搭建了一个WordPress,使用很方便,但是需要经常担心服务器哪天挂掉,或者被攻击,或者哪个模块出问题要跑到服务器上检查。而且WordPress的绝大多数功能我也用不着,我一般就是写点文字放点代码而已,再加上Jekyll也很热,就尝试了一下Jekyll,发现这才是我想要的Blog形式,非常简单,页面也很简洁,写作方式也很Geek。打算暂时用Jekyll来写东西。

作者:Dustin Marx 发表日期:Fri, 03/02/2012 - 23:10.

Reddit Java网站最近有一个题目为“分享Java标准类库中一些有用的类”的讨论话题,注解栏为“有很多平常我们没有认识到的类,分享一些你经常使用而我们可能没有意识到的类吧!”。在这篇文章中,我看到下面回复超过40的一些类(大部分是JDK中的)。

有一些回复者分享的是与并发相关的Java类,如 Executors, java.util.concurrent.CountDownLatch, java.util.concurrent.atomic.AtomicInteger, ThreadLocal, java.util.concurrent 以及包下的所有类以及java.util.concurrent.atomic.

还有一些与 String 处理相关的类也被提到了,包括 StringBuffer, 和StringBuilder. 我在博文 String, StringBuffer, and StringBuilder: There Is A Performance Difference 中也提到了这些与String相关的类。其他与String相关并被提到的包括java.util.StringTokenizerApache Commons‘ StringUtils (在我的文章 Checking for Null or Empty or White Space Only String in Java中也有提到). java.util.Scanner 类也可以让简化文本解析。

在用户界面上, java.awt.geom 包, java.awt.Desktop 以及javax.swing.SwingUtilities被提到。 java.awt.Point 被高亮显示,原因总结为:”任何两个int值对可以很简单的被用来代替数组传递给函数,或者从函数中返回,都可以使用Point类”。java.awt.Robot 类也在文中被提到,我之前也在我的一篇文章Screen Snapshots with Java’s Robot 中提到。

不出所料,一些Java集合类也在列在其中。包括 java.util.EnumSetEnumMap (参考我的文章The Sleek EnumMap and EnumSet), java.util.ArrayDeque (参考我的文章The Java SE 6 Deque), java.util.PriorityQueue,java.util.Arrays, 以及 java.util.Collections (参考我的文章 The Java Collections Class).

以我之见,java.lang.ClassLoader, java.util.ServiceLoaderjava.nio.file.FileVisitor 是Reddit Java话题中提到的更精心构思和特别的类。我们大部分的情况下都是用强引用(strong reference),但java.lang.ref.WeakReference (弱引用)和 java.lang.ref.SoftReference (软引用)也在讨论中被提到。

最后,我经常使用的一个类和一个方法分别是 BigDecimal 类(我的一边文章中顺便提到了该类: Caution: Double to BigDecimal in Java )和 System.nanoTime()

结论:

我喜欢列表中的很多类,也能想到一些其他的例子。特别的,我想JDK7中的 Objects 类也能称得上很有用但鲜为人知的一个类把。我同样同意其中一个评论的说法:“把Google guava库添加进来吧!”,我也写过一些博文关于 Guava

原文:http://marxsoftware.blogspot.com/

翻译自:http://www.javaworld.com/community/node/8335