随笔 - 35  文章 - 21  trackbacks - 0
<2009年8月>
2627282930311
2345678
9101112131415
16171819202122
23242526272829
303112345

常用链接

留言簿

随笔分类

随笔档案

文章分类

搜索

  •  

最新评论

阅读排行榜

评论排行榜

1 Launch Keychain Access from your local Mac and from the login keychain, filter by the Certificates category. You will see an expandable option called “Apple Development Push Services”
2 Right click on “Apple Development Push Services” > Export “Apple Development Push Services ID123″. Save this as apns-dev-cert.p12 file somewhere you can access it. There is no need to enter a password.
3 The next command generates the cert in Mac’s Terminal for PEM format (Privacy Enhanced Mail Security Certificate):
openssl pkcs12 -in apns-dev-cert.p12 -out apns-dev-cert.pem -nodes -clcerts
posted @ 2012-05-29 11:07 lincode 阅读(714) | 评论 (0)编辑 收藏

这个 bug 在 xcode 4.3 以下会出现,4.3 以后已经修正了。
解决方法为:找到 target 的图标,更改其 Other Linker Flags 为: -all_load 或 -force_load
-force_load,后跟随一个文件位置,可以更精确地加载所需文件。
 
苹果的解释为 : http://developer.apple.com/library/mac/#qa/qa1490/_index.html

简单点说就是,Objective-C 的动态特性使得需要,为链接器添加一个标签(设置 Other Linker Flags 为 -ObjC)来解决通过 Category 向类添加方法的问题。
但这个标签 -ObjC 在 64 位 和 iOS 中有问题,需要使用 -all_load 或 -force_load。

总结如下:
如果,第三库中没有 category,Other Linker Flags 无需设置
如果,第三方库中有 category,需要设置为 -ObjC
如果,某些 Xcode 版本中,出现问题,修改设置为 -all_load
posted @ 2012-04-23 14:56 lincode 阅读(1796) | 评论 (0)编辑 收藏
获得 Crash Report:
1 itunesConnect 的后台会提供一个 Crash report 表;
2 把一台打开了开发模式的机器接入 Mac,Xcode 的 Organizer 中能查看这台设备的 Crash Report;
3 若使用了 Umeng.com, Bugsense.com 之类的工具。

阅读 Crash Report:
这之前需要一个名为 AppName.app.dSYM 的文件。Xcode 中,Archive 一个项目之后,可以在 Organizer 的 Archives 分页中,找到所有项目的 Archvie 文件。
右键点击一个, Show Package Content,就能看到一个类似 AppName-3-19-12.app.PM.xcarchive  的文件,show in finder 这个文件,就能找到 .dSYM 文件。

在 Ternimal 中执行,若是 来自于 iphone 3G 的机器,就需要使用 armv6 代替 armv7.

 atos -o AppName.app.dSYM/Contents/Resources/DWARF/AppName  -arch armv7 0x0000b82

这样就能看到,地址对应的类,函数,代码行数。这个命令只能解析出客户代码的位置。若是错误堆栈中的系统调用,是无法翻译出来的。
posted @ 2012-03-18 13:56 lincode 阅读(981) | 评论 (0)编辑 收藏
Apple 提供了一个地址方向解析的服务 MKReverseGeocoder,上传一个经纬度,返回一个详细的地理位置信息。但这个服务在中国不太稳定,时常不可用。
Google map 也提供了一个类似的服务,是访问一个 google map 的 api,这里试图封装了 google map 的服务。使使用 apple 和 google 的服务的接口基本一致,替换起来很容易。Google map 的这个服务在中国的状态比 apple 稍微好一些,但也有不稳定的时候。我猜想,apple 也许是使用 google 的服务封装了自己的 MKReverseGeocoder。若是如此,这里的尝试也就没有什么意义了。

DOUHttpRequest 是对 ASIHTTPRequest 的一个简单封装。这些代码可以 继承自 MKReverseGeocoder。这样,使用方法就和 MKReverseGeocoder 一样了。

static NSString* kGeoServerUrl = @"http://maps.google.com/maps/api/geocode/json?latlng=%f,%f&sensor=true&language=en";
static NSString* kLatitudeUserInfoKey = @"latitudeUserInfoKey";
static NSString* kLongitudeUserInfoKey = @"longitudeUserInfoKey";

//
// It's tje solution for replacing MKReverseGeocoder that has problem in China.
//
- (void)startedReverseGeoderWithLatitude:(double)latitude longitude:(double)longitude {
  NSString *url = [NSString stringWithFormat:kGeoServerUrl, latitude, longitude];
  DOUHttpRequest *req = [DOUHttpRequest requestWithURL:[NSURL URLWithString:url] target:self];
  
  NSNumber *lat = [NSNumber numberWithDouble:latitude];
  NSNumber *lon = [NSNumber numberWithDouble:longitude];
  req.userInfo = [NSDictionary dictionaryWithObjectsAndKeys:lat, kLatitudeUserInfoKey, lon, kLongitudeUserInfoKey, nil];
  
  DOUService *service = [DOUService sharedInstance];  
  [service addRequest:req];
}


- (NSDictionary *)addressDictionary:(NSObject *)obj {
  
  NSArray* ary = nil;
  if (IS_INSTANCE_OF(obj, NSDictionary)) {
    NSObject* data = [(NSDictionary*)obj objectForKey:@"results"];
    if (IS_INSTANCE_OF(data, NSArray)) {
      ary = (NSArray*)data;
      NSDictionary *dic = [ary objectAtIndex:0];
      
      NSArray *addressComps = [dic objectForKey:@"address_components"];
      
      //NSString *streetNumber = @"";
      NSString *route = @"";
      NSString *locality = @"";
      NSString *country = @"";
      for (NSDictionary *comp in addressComps) {
        NSArray *types = [comp objectForKey:@"types"];
        NSString *type = [types objectAtIndex:0];
        
//        if ([type isEqualToString:@"street_number"]) {
//          streetNumber = [comp objectForKey:@"long_name"];
//        }
        
        if ([type isEqualToString:@"route"]) {
          route = [comp objectForKey:@"long_name"];
        }
        
        if ([type isEqualToString:@"locality"]) {
          locality = [comp objectForKey:@"long_name"];
        }
        
        if ([type isEqualToString:@"country"]) {
          country = [comp objectForKey:@"long_name"];
        }        
      }
      
      NSDictionary *addressDic = [NSDictionary dictionaryWithObjectsAndKeys:route, kABPersonAddressStreetKey,
                                  locality, kABPersonAddressCityKey,                                        
                                  country, kABPersonAddressCountryKey, nil];
      return addressDic;
    }
  }
  return nil;
}


- (void)requestFinished:(DOUHttpRequest *)req {
  NSError *error = [req error];
  if (!error) {
    DebugLog(@"str:%@", [req responseString]);
    
    NSObject *obj = [[req responseString] JSONValue];
    NSDictionary *addressDic = [self addressDictionary:obj];
    
    CLLocationCoordinate2D coordinate;
    coordinate.latitude = [[req.userInfo objectForKey:kLatitudeUserInfoKey] doubleValue];
    coordinate.longitude = [[req.userInfo objectForKey:kLongitudeUserInfoKey] doubleValue]; 
    MKPlacemark *placemark = [[[MKPlacemark alloc] initWithCoordinate:coordinate 
                                                   addressDictionary:addressDic] autorelease];
    [self reverseGeocoder:nil didFindPlacemark:placemark];
  }
}

- (void)requestFailed:(DOUHttpRequest *)req { 
  [self reverseGeocoder:nil didFailWithError:[req error]];
}


#pragma mark - MKReverseGeocoderDelegate

static NSString * const AppleLanguagesKey = @"AppleLanguages";

- (void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFindPlacemark:(MKPlacemark *)placemark {

  NSArray *array = [[NSUserDefaults standardUserDefaults] objectForKey:AppleLanguagesKey];
  NSString *currentLanguage = [array objectAtIndex:0];
  
  // set current language as english
  [[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:@"zh-Hans", nil] 
                                            forKey:AppleLanguagesKey];
  NSString *local = [placemark.locality lowercaseString];
 
  [AppContext sharedInstance].currentCityUid = local;
  
  // reset current language
  [[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:currentLanguage, nil] 
                                            forKey:AppleLanguagesKey];
}

- (void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFailWithError:(NSError *)error {
  TraceLog(@"reverseGeocoder :%@", [error localizedDescription]);  
}
posted @ 2012-01-12 21:27 lincode 阅读(1311) | 评论 (0)编辑 收藏

定义

OAuth(开放授权)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用。

OAuth允许用户提供一个令牌,而不是用户名和密码来访问他们存放在特定服务提供者的数据。每一个令牌授权一个特定的网站(例如,视频编辑网站)在特定的时段(例如,接下来的2小时内)内访问特定的资源(例如仅仅是某一相册中的视频)。这样,OAuth允许用户授权第三方网站访问他们存储在另外的服务提供者上的信息,而不需要分享他们的访问许可或他们数据的所有内容。



认证和授权过程


在认证和授权的过程中涉及的三方包括:
  • 服务提供方,用户使用服务提供方来存储受保护的资源,如照片,视频,联系人列表。
  • 用户,存放在服务提供方的受保护的资源的拥有者。
  • 客户端,要访问服务提供方资源的第三方应用,通常是网站,如提供照片打印服务的网站。在认证过程之前,客户端要向服务提供者申请客户端标识。

使用OAuth进行认证和授权的过程如下所示:

  1. 用户访问客户端的网站,想操作用户存放在服务提供方的资源。
  2. 客户端服务提供方请求一个临时令牌。
  3. 服务提供方验证客户端的身份后,授予一个临时令牌。
  4. 客户端获得临时令牌后,将用户引导至服务提供方的授权页面请求用户授权。在这个过程中将临时令牌和客户端的回调连接发送给服务提供方
  5. 用户服务提供方的网页上输入用户名和密码,然后授权该客户端访问所请求的资源。
  6. 授权成功后,服务提供方引导用户返回客户端的网页。
  7. 客户端根据临时令牌从服务提供方那里获取访问令牌。
  8. 服务提供方根据临时令牌和用户的授权情况授予客户端访问令牌。
  9. 客户端使用获取的访问令牌访问存放在服务提供方上的受保护的资源。


OAuth 2.0

OAuth 2.0是OAuth协议的下一版本,但不向后兼容OAuth 1.0。 OAuth 2.0关注客户端开发者的简易性,同时为Web应用,桌面应用和手机,和起居室设备提供专门的认证流程。规范还在IETF OAuth工作组的开发中 ,按照Eran Hammer-Lahav的说法,OAuth将于2010年末完成。

Facebook的新的Graph API只支持OAuth 2.0,Google在2011年3月亦宣佈Google API對OAuth 2.0的支援。

posted @ 2011-10-27 18:15 lincode 阅读(280) | 评论 (0)编辑 收藏

名称

REST,即Representational State Transfer的缩写。我对这个词组的翻译是"表现层状态转化"。

如果一个架构符合REST原则,就称它为RESTful架构。

要理解RESTful架构,最好的方法就是去理解Representational State Transfer这个词组到底是什么意思,它的每一个词代表了什么涵义。如果你把这个名称搞懂了,也就不难体会REST是一种什么样的设计。

资源(Resources)

REST的名称"表现层状态转化"中,省略了主语。"表现层"其实指的是"资源"(Resources)的"表现层"。

所谓"资源",就是网络上的一个实体,或者说是网络上的一个具体信息。它可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的实在。你可以用一个URI(统一资源定位符)指向它,每种资源对应一个特定的URI。要获取这个资源,访问它的URI就可以,因此URI就成了每一个资源的地址或独一无二的识别符。

所谓"上网",就是与互联网上一系列的"资源"互动,调用它的URI。

表现层(Representation)

"资源"是一种信息实体,它可以有多种外在表现形式。我们把"资源"具体呈现出来的形式,叫做它的"表现层"(Representation)。

比如,文本可以用txt格式表现,也可以用HTML格式、XML格式、JSON格式表现,甚至可以采用二进制格式;图片可以用JPG格式表现,也可以用PNG格式表现。

URI只代表资源的实体,不代表它的形式。严格地说,有些网址最后的".html"后缀名是不必要的,因为这个后缀名表示格式,属于"表现层"范畴,而URI应该只代表"资源"的位置。它的具体表现形式,应该在HTTP请求的头信息中用Accept和Content-Type字段指定,这两个字段才是对"表现层"的描述。

状态转化(State Transfer)

访问一个网站,就代表了客户端和服务器的一个互动过程。在这个过程中,势必涉及到数据和状态的变化。

互联网通信协议HTTP协议,是一个无状态协议。这意味着,所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生"状态转化"(State Transfer)。而这种转化是建立在表现层之上的,所以就是"表现层状态转化"。

客户端用到的手段,只能是HTTP协议。具体来说,就是HTTP协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源。

综述

综合上面的解释,我们总结一下什么是RESTful架构:

  (1)每一个URI代表一种资源;

  (2)客户端和服务器之间,传递这种资源的某种表现层;

  (3)客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化"。

误区

RESTful架构有一些典型的设计误区。

最常见的一种设计错误,就是URI包含动词。因为"资源"表示一种实体,所以应该是名词,URI不应该有动词,动词应该放在HTTP协议中。

举例来说,某个URI是/posts/show/1,其中show是动词,这个URI就设计错了,正确的写法应该是/posts/1,然后用GET方法表示show。

如果某些动作是HTTP动词表示不了的,你就应该把动作做成一种资源。比如网上汇款,从账户1向账户2汇款500元,错误的URI是:

  POST /accounts/1/transfer/500/to/2

正确的写法是把动词transfer改成名词transaction,资源不能是动词,但是可以是一种服务:

  POST /transaction HTTP/1.1
  Host: 127.0.0.1
  
  from=1&to=2&amount=500.00

另一个设计误区,就是在URI中加入版本号

  http://www.example.com/app/1.0/foo

  http://www.example.com/app/1.1/foo

  http://www.example.com/app/2.0/foo

因为不同的版本,可以理解成同一种资源的不同表现形式,所以应该采用同一个URI。版本号可以在HTTP请求头信息的Accept字段中进行区分(参见Versioning REST Services):

  Accept: vnd.example-com.foo+json; version=1.0

  Accept: vnd.example-com.foo+json; version=1.1

  Accept: vnd.example-com.foo+json; version=2.0


原帖:http://www.ruanyifeng.com/blog/2011/09/restful.html
posted @ 2011-10-27 18:02 lincode 阅读(283) | 评论 (0)编辑 收藏

PhoneGap 是一个移动开发框架。通过 PhoneGap,开发者可以使用 JavaScript 调用手机的原生功能,例如,获取经纬度,让手机振动等。
主页 http://www.phonegap.com/ 。
源码 https://github.com/phonegap/phonegap-android 。

PhoneGap 在早期,应该是使用 WebView 的 addJavaScriptInterface 方法,来为 JS 提供调用原生功能可能。addJavaScriptInterface ,可以将一个 Java 对象绑定到一个 JS 对象。是的,JS对象可以调用 Java方法。但在 PhoneGap 1.0.0 这个版本中,PhoneGap 改变了方法。

以振动功能为例,我们可以看一下程序调用的流程:

1 在 JS 中,启动命令

main.js / navigator.notification.vibrate(0);

notification.js / Notification.vibrate.vibrate 中执行了 PhoneGap.exec(null, null, "Notification", "vibrate", [mills]);

phonegap.js / PhoneGap.exc 中执行了 var r = prompt(PhoneGap.stringify(args), "gap:"+PhoneGap.stringify([service, action, callbackId, true]));

这时,WebView 就会企图弹出一个窗口。这时使用 android 提供的 WebChromeClient 的 API 就可以截获 WebView 的这个动作 。

2 JAVA 中,处理命令
WebView 的 WebChromClient 实现了下面这个函数:

public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result)

在 onJsPrompt 中执行了 String r = pluginManager.exec(service, action, callbackId, message, async);

PlugManager 会根据收到参数,将命令分发给特定的 Plugin。这个例子中,接收的 plugin 是:Notification。
落实到 Notification 的 exec 函数:会执行这一行: this.vibrate(args.getLong(0));

振动的实现为:

 

 public void vibrate(long time){
        
// Start the vibration, 0 defaults to half a second.
        if (time == 0) {
time 
= 500;
}
        Vibrator vibrator 
= (Vibrator) this.ctx.getSystemService(Context.VIBRATOR_SERVICE);
        vibrator.vibrate(time);
}

 


3 Java 处理完后的数据,需要给 JS 一个反馈:
这里 PhoneGap 使用了一个在客户端本地实现的 XHRServer,具体到代码中就是一个JAVA 类 CallbackServer。

分两个部分介绍其行为:
本地 XHRServer,
思想是,后台每执行完一个命令,都会将结果存在 CallbackServer 中的一个链表中,具体为CallbackServr的 private LinkedList<String> javascript;
这个结果其实是一段字符串表示的 JS 函数调用。例如检测网络调用的结果为:PhoneGap.callbackSuccess('Network Status1',{status:1,message:"wifi",keepCallback:true});
 XHRServer 的行为很简单,只要有请求来,就把链表中的最先进来的提出来,返回给客户端。没有请求来,则 10秒钟返回一个空的回复,以维持XHRServer。
Webview 作为客户端:
在 WebView 中,会有一个轮询机制,这可以参考 PhoneGap.JSCallack 和 PhoneGap.JSCallbackPolling 两个函数来访问 XHRServer。XHRServer,返回的结果就是 WebView 需要调用的 JS 函数。 在 JS 中,eval() 函数,将返回的结果变为一个可以执行的对象,在 Webview 中执行,可以认为这即是回调函数 Callback。这也是为什么 PhoneGap 为何命名 XHRServer 为 CallbackServer 的原因。

posted @ 2011-09-20 10:20 lincode 阅读(3720) | 评论 (1)编辑 收藏
dip: device independent pixels(设备独立像素)。不同设备有不同的显示效果,这个和设备硬件有关,一般我们为了支持WVGA、HVGA和QVGA 推荐使用这个,不依赖像素。 

dp:(与密度无关的像素)一种基于屏幕密度的抽象单位。在每英寸160点的显示器上,1dp = 1px。 

px: pixels(像素). 不同设备显示效果相同,一般我们HVGA代表320x480像素,这个用的比较多。 

pt: point(磅),是一个标准的长度单位,1pt=1/72英寸,用于印刷业,非常简单易用; 

sp: scaled pixels(放大像素). 主要用于字体显示best for textsize。 
posted @ 2011-09-16 17:58 lincode 阅读(710) | 评论 (1)编辑 收藏

android 中自定义的对象序列化的问题有两个选择一个是Parcelable,另外一个是Serializable。

一 序列化原因:

1.永久性保存对象,保存对象的字节序列到本地文件中;
2.通过序列化对象在网络中传递对象;
3.通过序列化在进程间传递对象。 

二 至于选取哪种可参考下面的原则:

1.在使用内存的时候,Parcelable 类比Serializable性能高,所以推荐使用Parcelable类。
2.Serializable在序列化的时候会产生大量的临时变量,从而引起频繁的GC。
3.Parcelable不能使用在要将数据存储在磁盘上的情况,因为Parcelable不能很好的保证数据的持续性在外界有变化的情况下。尽管Serializable效率低点, 也不提倡用,但在这种情况下,还是建议你用Serializable 。


实现:
1 Serializable 的实现,只需要继承  implements Serializable 即可。这只是给对象打了一个标记,系统会自动将其序列化。

2 Parcelabel 的实现,需要在类中添加一个静态成员变量 CREATOR,这个变量需要继承 Parcelable.Creator 接口。
public class MyParcelable implements Parcelable {
     
private int mData;

     
public int describeContents() {
         
return 0;
     }

     
public void writeToParcel(Parcel out, int flags) {
         out.writeInt(mData);
     }

     
public static final Parcelable.Creator<MyParcelable> CREATOR
             
= new Parcelable.Creator<MyParcelable>() {
         
public MyParcelable createFromParcel(Parcel in) {
             
return new MyParcelable(in);
         }

         
public MyParcelable[] newArray(int size) {
             
return new MyParcelable[size];
         }
     };
     
     
private MyParcelable(Parcel in) {
         mData 
= in.readInt();
     }
 }

 
posted @ 2011-09-16 16:16 lincode 阅读(22107) | 评论 (0)编辑 收藏
生命周期
Android 系统在Activity 生命周期中加入一些钩子,我们可以在这些系统预留的钩子中做一些事情。
例举了 7 个常用的钩子:
protected void onCreate(Bundle savedInstanceState)
protected void onStart()
protected void onResume()
protected void onPause()
protected void onStop()
protected void onRestart()
protected void onDestroy()

简要说明:
onCreate(Bundle savedInstanceState):创建activity时调用。设置在该方法中,还以Bundle中可以提出用于创建该 Activity 所需的信息。
onStart():activity变为在屏幕上对用户可见时,即获得焦点时,会调用。
onResume():activity开始与用户交互时调用(无论是启动还是重新启动一个活动,该方法总是被调用的)。
onPause():activity被暂停或收回cpu和其他资源时调用,该方法用于保存活动状态的。。
onStop():activity被停止并转为不可见阶段及后续的生命周期事件时,即失去焦点时调用。
onRestart():重新启动activity时调用。该活动仍在栈中,而不是启动新的活动。
onDestroy():activity被完全从系统内存中移除时调用,该方法被调用可能是因为有人直接调用 finish()方法 或者系统决定停止该活动以释放资源。

横竖屏切换

1 切换到横屏
onSaveInstanceState
onPause
onStop
onDestroy
onCreate
onStart
onRestoreInstanceState
onResume

2 切换到竖屏,销毁了两次
onSaveInstanceState
onPause
onStop
onDestroyonCreate
onStart
onRestoreInstanceState
onResume
onSaveInstanceState
onPause
onStop
onDestroy
onCreate
onStart
onRestoreInstanceState
onResume

3 修改AndroidManifest.xml,把该Activity添加 android:configChanges="orientation",切横屏,只销毁一次。

onSaveInstanceState
onPause
onStop
onDestroy
onCreate
onStart
onRestoreInstanceState
onResume

再切回竖屏,发现不会再打印相同信息,但多打印了一行onConfigChanged

onSaveInstanceState
onPause
onStop
onDestroy
onCreate
onStart
onRestoreInstanceState
onResume
onConfigurationChanged

5 更改 android:configChanges="orientation" 改成 android:configChanges="orientation|keyboardHidden",切横屏,就只打印onConfigChanged

onConfigurationChanged

6 切回竖屏

onConfigurationChanged
onConfigurationChanged

总结:

1、不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次

2、设置Activity的android:configChanges="orientation"时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次

3、设置Activity的android:configChanges="orientation|keyboardHidden"时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法





posted @ 2011-09-16 10:32 lincode 阅读(3358) | 评论 (1)编辑 收藏
Python 的代码风格由 PEP 8 描述。这个文档描述了 Python 编程风格的方方面面。在遵守这个文档的条件下,不同程序员编写的 Python 代码可以保持最大程度的相似风格。这样就易于阅读,易于在程序员之间交流。


1 变量

常量 : 大写加下划线
USER_CONSTANT
对于不会发生改变的全局变量,使用大写加下划线。

私有变量
: 小写和一个前导下划线
_private_value
Python 中不存在私有变量一说,若是遇到需要保护的变量,使用小写和一个前导下划线。但这只是程序员之间的一个约定,用于警告说明这是一个私有变量,外部类不要去访问它。但实际上,外部类还是可以访问到这个变量。

内置变量 : 小写,两个前导下划线和两个后置下划线
__class__
两个前导下划线会导致变量在解释期间被更名。这是为了避免内置变量和其他变量产生冲突。用户定义的变量要严格避免这种风格。以免导致混乱。


2 函数和方法

总体而言应该使用,小写和下划线。但有些比较老的库使用的是混合大小写,即首单词小写,之后每个单词第一个字母大写,其余小写。但现在,小写和下划线已成为规范。

私有方法 : 小写和一个前导下划线
def _secrete(self):
    
print "don't test me."

这里和私有变量一样,并不是真正的私有访问权限。同时也应该注意一般函数不要使用两个前导下划线(当遇到两个前导下划线时,Python 的名称改编特性将发挥作用)。特殊函数后面会提及。

特殊方法 : 小写和两个前导下划线,两个后置下划线

def __add__(self, other):
    
return int.__add__(other)

这种风格只应用于特殊函数,比如操作符重载等。

函数参数 : 小写和下划线,缺省值等号两边无空格

def connect(self, user=None):
    self._user 
= user



3 类

类总是使用驼峰格式命名,即所有单词首字母大写其余字母小写。类名应该简明,精确,并足以从中理解类所完成的工作。常见的一个方法是使用表示其类型或者特性的后缀,例如:
SQLEngine
MimeTypes

对于基类而言,可以使用一个 Base 或者 Abstract 前缀
BaseCookie
AbstractGroup

class UserProfile(object):
    
def __init__(self, profile):
        
return self._profile = profile

    
def profile(self):
        
return self._profile



4 模块和包

除特殊模块 __init__ 之外,模块名称都使用不带下划线的小写字母。
若是它们实现一个协议,那么通常使用lib为后缀,例如:
import smtplib

import os
import sys



5 关于参数

5.1 不要用断言来实现静态类型检测
断言可以用于检查参数,但不应仅仅是进行静态类型检测。 Python 是动态类型语言,静态类型检测违背了其设计思想。断言应该用于避免函数不被毫无意义的调用。

5.2 不要滥用 *args 和 **kwargs
*args 和 **kwargs 参数可能会破坏函数的健壮性。它们使签名变得模糊,而且代码常常开始在不应该的地方构建小的参数解析器。


6 其他

6.1 使用 has 或 is 前缀命名布尔元素

is_connect = True
has_member 
= False


6.2 用复数形式命名序列

members = ['user_1''user_2']


6.3 用显式名称命名字典

person_address = {'user_1':'10 road WD''user_2' : '20 street huafu'}


6.4 避免通用名称
诸如 list, dict, sequence 或者 element 这样的名称应该避免。

6.5 避免现有名称
诸如 os, sys 这种系统已经存在的名称应该避免。


7 一些数字
一行列数 : PEP 8 规定为 79 列,这有些苛刻了。根据自己的情况,比如不要超过满屏时编辑器的显示列数。这样就可以在不动水平游标的情况下,方便的查看代码。

一个函数 : 不要超过 30 行代码, 即可显示在一个屏幕类,可以不使用垂直游标即可看到整个函数。
一个类 : 不要超过 200 行代码,不要有超过 10 个方法。
一个模块 不要超过 500 行


8 验证脚本

可以安装一个 pep8 脚本用于验证你的代码风格是否符合 PEP8。

>>easy_install pep8

>>pep8 -r --ignoire E501 Test.py

这个命令行的意思是,重复打出错误,并且忽略 501 错误(代码超过 79 行)。

posted @ 2011-02-02 00:45 lincode 阅读(9304) | 评论 (0)编辑 收藏
Emacs 不仅仅是一个文本编辑器,它还可用于文件管理。使用 Emacs 作为文件管理工具的话,也解决了跨平台问题,这样在不同平台下,你都可以使用一套工具来管理文件。

1 基本命令
Ctrl + x  d  : 打开文件管理视图,在文件管理视图中支持的操作如下表:
键值 效果    
Enter 打开文件
a 打开文件并关闭文件管理视图
打开文件,但是在一个新建视图中打开
关闭文件管理视图
C(大写) 复制文件
R(大写) 重命名文件
D(大写) 删除文件


2 基于文件集合的命令
这主要是使一条命令作用于几个文件。方法是标记你要操作的文件。
基本命令为 m

键值  效果    
m 标记文件
u 去掉标记
U 标记所有文件
t 反向所以文件
% m 基于正则表达式,标记文件


3 其他相关命令
在文件管理视图中还可以做到:
键值 效果    
g 刷新文件夹
^ 返回上级目录
+ 创建一个文件夹
压缩或解压文件


posted @ 2011-02-01 18:14 lincode 阅读(1053) | 评论 (0)编辑 收藏

我使用的是 goddady 的 VPS。家里使用 mac 。所以直接在 terminal 中使用 ssh 连接服务器即可。

> ssh 189.129.1.12 -l username
敲入密码,进入服务器。

> su
敲入密码,进入根用户。

> cd /etc/ngnix/
进入 ngnix 配置文件目录,进入 conf.d 目录

> vim ghs.conf
建立一个 配置文件

一个范例:

upstream ghs {
    ip_hash;
    server ghs.google.com;
    server 72.14.203.121;
    server 72.14.207.121;
    server 74.125.43.121;
    server 74.125.47.121;
    server 74.125.53.121;
    server 74.125.77.121;
    server 74.125.93.121;
    server 74.125.95.121;
    server 74.125.113.121;
    server 216.239.32.21;
    server 216.239.34.21;
    server 216.239.36.21;
    server 216.239.38.21;
}

server {
    listen       80;
    server_name  ghs.myhosts.com www.test.com;
    access_log   /data/logs/ghs_proxyaccess.log;

    location / {
        proxy_redirect off;
        proxy_set_header Host $host;
        proxy_pass http://ghs;
        proxy_set_header  X-Real-IP  $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_redirect false;
    }

}


>/etc/init.d/ngnix start

启动 nginx

在本机的浏览器中敲入反向代理 server 的 ip,能看到 ngnix 的蓝色的界面,这验证了 ngnix 启动正常

> ping ghs.myhosts.com
应该指向反向代理 server 的 ip

首先将你的域名例如,www.tests.com  绑定到 google app engine 的你的目标 application 上
然后,在你的域名服务提供商那里,添加一个 www.tests.com 指向 ghs.myhosts.com 的 CNAME 即可。

posted @ 2010-12-15 07:12 lincode 阅读(490) | 评论 (0)编辑 收藏

  transient:

  java有个特点就是序列化,简单地来说就是可以将这个类存储在物理空间(当然还是以文件的形式存在),那么当你从本地还原这个文件时,你可以将它转换为它本身。这可以极大地方便网络上的一些操作,但同时,因为涉及到安全问题,所以并不希望把类里面所有的东西都能存储(因为那样,别人可以通过序列化知道类里面的内容),那么我们就可以用上transient这个关键字,它的意思是临时的,即不会随类一起序列化到本地,所以当还原后,这个关键字定义的变量也就不再存在。

  volatile:

  Volatile 修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。

  Java 语言规范中指出:为了获得最佳速度,允许线程保存共享成员变量的私有拷贝,而且只当线程进入或者离开同步代码块时才与共享成员变量的原始值对比。

  这样当多个线程同时与某个对象交互时,就必须要注意到要让线程及时的得到共享成员变量的变化。

  而volatile关键字就是提示VM:对于这个成员变量不能保存它的私有拷贝,而应直接与共享成员变量交互。

  使用建议:在两个或者更多的线程访问的成员变量上使用volatile。当要访问的变量已在synchronized代码块中,或者为常量时,不必使用。

  由于使用volatile屏蔽掉了VM中必要的代码优化,所以在效率上比较低,因此一定在必要时才使用此关键字。

posted @ 2010-06-14 23:03 lincode 阅读(193) | 评论 (0)编辑 收藏

一 基本概念
单例模式是一种为类 提供唯一实例的设计模式。单例模式的目的是为了控制对象的创建,它可以限制创建的数目为一,但在情况改变的时候,也允许灵活地创建更多的对象。因为只有一个实例,所以也只有一套实例的类变量的拷贝,这很像 static 变量。

在 JAVA 中,单例模式不应该被当作一种实现全局变量的方法。更多的,如同工厂模式,单例模式允许你通过 确认某些先决条件是否满足 或者 以 lazily 方式按需创建的方式 来封装和控制创建过程。

二 编程实现
1 饥饿模式 Eager Singleton

public class MySingleton {
    
private static MySingleton fInstance = new MySingleton();
    
    
private MySingleton(){
        
// Construct object 
    }


    
public static MySingleton getInstance(){
        reutnr fInstance;
    }


}

2 懒汉模式 Lazy Singleton

public class MySingleton {
    
private static MySingleton fInstance;
    
    
private MySingleton(){
        
// construct object
    }

    
    
public static synchronized MySingleton getInstance(){
        
if (fInstance == null){
            fInstance 
= new MySingleton();
        }

        
return fInstance;
    }


}

由于只有一个私有构造器,所以单例类是无法被集成的。基于这一点,单例模式并不是一个面向对象模式,仅仅是一个基于对象的模式。

3 饥饿模式基本没有问题,懒汉模式则容易出现一些错误的编程方法
1)
// error, no synchronization on method 
public static MySingleton getInstance() 
 
if (fInstance==null
     fInstance 
= new MySingleton(); 
 }
 

 
return fInstance; 
}

2)
// Also an error, synchronization does not prevent 
// two calls of constructor. 
public static MySingleton getInstance() 
 
if (fInstance==null
   
synchronized (MySingleton.class
      fInstance 
= new MySingleton(); 
   }
 
 }
 
 
return fInstance; 
}
 

3) Double-checked locking 不要使用

public static MySingleton getInstance(){
    
if (fInstance == null ){
        
synchronized (MySingleton.class{
            
if(fInstance == null){
                fInstance 
= new MySingleton();
            }

        }

    }

}

为了避免每次调用 getInstance方法是抓取同步锁的消耗,有人发明了 Double-checked locking 。但不要使用,因为这样的代码将无法在编译器优化和多处理器共享内存的情况下工作。若想详细了解,附录中有对此做详细描述的链接。

三 总结

1 单例模式不应被滥用,比如不能为了得到一个全局变量而创建单例,单例是用于控制对象的创建过程的。只有真正的目的是控制对象创建的过程或数量时,才能考虑使用单例。在大部分情况下,单例模式是有代替方案的。比如经典的数据库连接类被以单例实现,其实可以以对象池模式实现。

2 使用单例模式,尽量使用饥饿模式 ,只有你能预测这个类一定会被创建,那么就可以使用饥饿模式。如果,一定需要推迟对象的创建时间。那么不要使用 Double-checked locking 之类的方法的来提高效率,这将得不偿失。

[1] DoubleCheckedLocking
http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
posted @ 2010-04-30 21:04 lincode 阅读(199) | 评论 (0)编辑 收藏

一 基本概念
这篇文章比较清楚地讲述了字符集和编码的基本概念
http://www.regexlab.com/zh/encoding.htm

摘抄:

各个国家和地区所制定的不同 ANSI 编码标准中,都只规定了各自语言所需的“字符”。比如:汉字标准(GB2312)中没有规定韩国语字符怎样存储。这些 ANSI 编码标准所规定的内容包含两层含义:

  1. 使用哪些字符。也就是说哪些汉字,字母和符号会被收入标准中。所包含“字符”的集合就叫做“字符集”。
  2. 规定每个“字符”分别用一个字节还是多个字节存储,用哪些字节来存储,这个规定就叫做“编码”。

各个国家和地区在制定编码标准的时候,“字符的集合”和“编码”一般都是同时制定的。因此,平常我们所说的“字符集”,比如:GB2312, GBK, JIS 等,除了有“字符的集合”这层含义外,同时也包含了“编码”的含义。

UNICODE 字符集”包含了各种语言中使用到的所有“字符”。用来给 UNICODE 字符集编码的标准有很多种,比如:UTF-8, UTF-7, UTF-16, UnicodeLittle, UnicodeBig 等。


二 eclipse 中对于编码方式的设置

1 源文件的编码设置

preference -> general -> Content Types 
右边选择文件类型,右下更新 缺省编码方式

2 控制台的编码设置
Run -> Run configuration( 或 Debug configuration)
右边选项卡中 common, 一般为最后一项
在 console encoding 的 other 中选取需要的 编码方式

三 java 中的 编码转换

byte[] bytes = oldStr.getBytes(); //默认编码方式下的字节数组
String newStr = new String( bytes, "UTF-8" ); //转换成 UTF-8 编码下的字符串


 
posted @ 2010-04-29 23:31 lincode 阅读(246) | 评论 (0)编辑 收藏

Java 中函数参数传递和函数返回值都是以值方式传递的。当然,对于对象,我们也可以说是引用的方式传递,其实传递也是值,只不过是引用值。引用是一个对象的别名,对于引用的修改就是对于对象本身的修改。

为了便于理解还是可以说成是两种类型,原始类型以值方式传递,对象以引用方式传递。

向函数里传递参数,已经有很多java教程讲解了。这里主要记录一个 函数返回值的问题。在返回一个对象时,是返回值本身的应用,还是拷贝这个值,再传拷贝的引用呢。这是需要考虑清楚的。

这个问题,是我在 不同手机上调试 J2ME 程序时遇到的。

具体如下,这是一个关于时间的工具类。我发觉 Calendar 的 getTime() 在有的机器下 如 Nokia,返回的 Calendar 当前时间的一个拷贝的引用,而 SAMSUNG 则直接返回 Calendar 的当前时间的引用。这导致,我在想得到一个时间所在那一的起始时间和结束时间时,总是得到相同的值,即后一次调用的值。按照,比较正常的理解,这里应该返回拷贝的引用比较正确,就是说 SAMSUNG 的 JVM 实现有些问题。面对这种情况,我只能先用 Date 类 返回 一个 long 值,再用 long 值构造一个新日期,即日历当前日期的拷贝,返回这个拷贝。

修改函数中的最后一行为
return new Date(fCalendar.getTime().getTime());

    private static Date fCalendar = Calendar.getInstance();


    
/**
             * Get the beginning of a day
             * 
@param date <description>
             * 
@return <description>
             
*/
    
public static Date getBeginOfDay( final Date pDate ) {
        
       fCalendar.setTime( pDate );
       
try{
           fCalendar.set( Calendar.HOUR_OF_DAY, 
0 );
           fCalendar.set( Calendar.MINUTE, 
0 );
           fCalendar.set( Calendar.SECOND, 
0 );
        }
catch(ArrayIndexOutOfBoundsException ex){
            ex.printStackTrace();
        } 
        
return fCalendar.getTime();
    }
   
    
/**
             * Get the end of a day
             * 
@param date <description>
             * 
@return <description>
             
*/
    
public static Date getEndOfDay( final Date pDate ){
        
       fCalendar.setTime( pDate );
       
try{
           fCalendar.set( Calendar.HOUR_OF_DAY, 
23 );
           fCalendar.set( Calendar.MINUTE, 
59 );
           fCalendar.set( Calendar.SECOND, 
59 );
        }
catch(ArrayIndexOutOfBoundsException ex){
            ex.printStackTrace();
        }
        
return fCalendar.getTime();
    }





posted @ 2010-02-24 21:36 lincode 阅读(2184) | 评论 (0)编辑 收藏
     摘要: 原则很简单: 不要使用 字符串链接操作符来合并多个字符串,除非性能无关紧要。相反,应该使用StringBuffer的append方法,或者采用其它的方案,比如使用字符数组,或者每次只处理一个字符串,而不是将它们组合起来。  阅读全文
posted @ 2010-02-10 18:46 lincode 阅读(395) | 评论 (0)编辑 收藏
J2me 中使用 Log, 可以使用搜索引擎搜索下载 log4j2me.

使用方法和 j2se 中有一些区别,下面是在 log4j2me 包中的一个例子

package log4j2me.test; 

import org.apache.log4j.*;
import org.apache.log4j.helpers.LogLog;

/**
   Very simple log4j usage example.

   
@author  Ceki G&uuml;lc&uuml;, Witmate  
 
*/

// Modifiers:    Witmate [Nov,2004: Modified for log4j2me]

public class Hello {

  
static Category log = Category.getInstance(Hello.class);
  
static Category log1 = Category.getInstance("Hello");

  
public static void main(String argv[]) {
    Category root 
= Category.getRoot();
    root.setPriority( Priority.DEBUG );
    Layout layout 
= new PatternLayout("%p [%t] %c - %m%n");
    
try {
      root.addAppender(
new FileAppender(layout, System.out));
    } 
catch(Exception e) {
      LogLog.warn(
"Could not open file appender.");
    }
    
    log.debug(
"Hello world.");
    log.info(
"What a beatiful day.");
    
    log1.debug(
"Hello world.");
    log1.info(
"What a beatiful day.");
  }
}




posted @ 2010-01-28 01:13 lincode 阅读(343) | 评论 (0)编辑 收藏
     摘要: Django 的 template 有继承功能。这两天遇到一个问题,
仅仅包含 {% extends "base.html" %} 一条语句的子 html 文件在浏览器中和 base.html 的渲染结果不一样。
搞了两天才找出问题的原因。是子文件选择了 UTF-8 格式。
所有的文件应该都选择 UTF-8 without BOM 格式。这样就得到一致的结果了。  阅读全文
posted @ 2009-12-31 06:37 lincode 阅读(217) | 评论 (0)编辑 收藏
     摘要: Douban API 提供 python 版本,同时 GAE(google app engine)对于 python 的支持是相当好。所以可以容易地将 Douban API 集成到 GAE。  阅读全文
posted @ 2009-11-28 08:13 lincode 阅读(574) | 评论 (2)编辑 收藏
     摘要: TortoiseSVN 注释中的变量替换的设置  阅读全文
posted @ 2009-08-05 06:01 lincode 阅读(289) | 评论 (0)编辑 收藏
     摘要: Blackberry MDS 是 Blackberry 模拟器 访问网络资源的服务器。

Blackberry java development environnement JDE 的安装包中已经包含了MDS。

但是有时候MDS无法启动。
  阅读全文
posted @ 2009-08-05 04:56 lincode 阅读(1008) | 评论 (6)编辑 收藏
     摘要: 目的是要在模拟器中显示中文和添加中文输入法。  阅读全文
posted @ 2009-08-05 04:50 lincode 阅读(690) | 评论 (8)编辑 收藏