顯示具有 Objective-C 標籤的文章。 顯示所有文章
顯示具有 Objective-C 標籤的文章。 顯示所有文章

2011年2月16日 星期三

Objective-C的類別初始化

在Java中,要對class variable作初始化,可以透過static method或是static block;而在Objective-C則是必須透過initialize

在Objective-C中,物件的預設建構式為init,是用來對類別的單一物件作初始化。而除了這個method,其實還有另一個預設的初始化method,叫做"initialize"。這是一個static method,所以是針對class variable來做初始化的。

但是有幾點跟Java比較不同的:
  • Java的類別初始化,若是透過static method,則必須做明確的呼叫;但是Objective-C的initialize,則是會在該類別第一次載入時被自動呼叫(跟Java的static block較類似)。
  • Java的static block僅會被執行一次;Objective-C的initialize則是在父類別與子類別被載入時,會分別個執行一次。(若是實際上只有子類別被使用,則實際的載入動作會先載入父類別並執行initialize,然後再載入子類別與執行initialize
因此,在使用initialize應該特別注意是否會被子類別進行重複呼叫。解決方法是在initialize執行時,檢查當時的類別是否為父類別或是特定類別,範例程式如下:
+ (void)initialize {
if(self == [XXXX class]) {
// initialize class variables here
}
}
其中,XXXX指的是父類別名稱。

Create an UUID by using Objective-C

許多computer language都有方法可以產生UUID,所以Objective-C當然也不例外。目前查詢到而且驗證過可用的方法有兩種:
  • 使用C function:使用以下程式碼即可
- (NSString *)createUUID{
// Create universally unique identifier (object)
CFUUIDRef uuidObject = CFUUIDCreate(kCFAllocatorDefault);
// Get the string representation of CFUUID object.
NSString *uuidStr = [(NSString *)CFUUIDCreateString(kCFAllocatorDefault, uuidObject) autorelease];
CFRelease(uuidObject);
return uuidStr;
}

  • 使用NSProcessInfo 類別:使用以下程式碼即可
    NSString *uuid = [[NSProcessInfo processInfo] globallyUniqueString];
兩種方法都可以產生UUID,但是長度不一樣。NSProcessInfo因為參考比較多資訊來產生,所以產生的長度會比較長,為58個字元長度

Improve data upload speed on iPhone (for using ASIHTTPRequest)

在先前的文章就曾經介紹過使用在先前的文章就曾經介紹過使用ASIHTTPRequest來開發iOS上的HTTP通訊應用。它也的確讓coding變得比較簡單容易。不過,最近在實務上,卻碰到一個問題,就是當上傳大檔案或大量資料時,傳輸速率似乎不是很理想。經過一番股溝之後,發現在ASIHTTPRequest本身的API中,有相關設定可以改善。那就是透過static method
[ASIHTTPRequest setShouldThrottleBandwidthForWWAN:YES]
,來對流量作限制。事實上,該method的原意,似乎是在3G/GPRS網路下作流量管控的,不過API的說明也提到,對於大資料的上下傳也會有速度改善的效果。

但是,在不是很嚴謹的測試之後,發現該method的效果在大資料上傳才會比較明顯。對於下載,似乎不但沒有改善的效果,反而會減慢。而API文件也提到,使用該API應該盡量在需要的情況在開啟,否則平時應該是要設定在NO的狀態。但是,由於該method的設定效果是針對整個ASIHTTPRequest的,而非針對單一request物件,因此在不需要的情況下,必須明確的使用
[ASIHTTPRequest setShouldThrottleBandwidthForWWAN:NO]
來關閉功能。

2010年10月28日 星期四

Objective-C 之 日期字串格式化與轉換

在撰寫程式的時候,時常需要把日期物件跟它的字串表示式來做互轉的動作。在Java中,可以透過java.text.SimpleDateFormat來做。而在Objective-C中,也有類似的類別,叫做NSDateFormatter。

NSDateFormatter的用法其實很簡單,就是建立一個NSDateFormatter物件,然後設定需要的dateFormat。之後如果是要把日期物件轉換為字串表示法,則用stringFromDate:;反之,則是使用dateFromString:。詳細內容可以參考:Simple methods for date formatting and transcoding

2010年10月7日 星期四

Objective-C的@synchronized

在Objective-C中,也有synchronized這個keyword,也是用來作thread synchronization,正是語法是
@synchronized(id) { ... }
,其中id是只要鎖定的物件pointer。而比較常用的情況是
@synchronized(self)
有趣的是,如果@synchronized(self)語法出現在一般的instance method,則self是指該instance;但是如果是出現在static method,則self是指該class本身。

2010年9月26日 星期日

Singleton pattern for Objective-C

在Java中,要撰寫一個應用Singleton pattern的class十分容易。但是在Objective-C中,就沒這麼簡單了。原因在於物件與記憶體的配置與管理機制是完全不同的(個人見解,不見得對)。

其實原理是都很類似,就是設法讓app只產生一個object instance,而且是透過特定的static method來獲得這個instance,而不是透過constructor來建立各個獨立的instance。對於一些某些需要集中管理的資訊或是資源,singleton pattern十分有用。

在Objective-C中,因為無法宣告constructor為private,所以很難避免使用者去呼叫。另外,因為Objective-C的alloc/retain/release/dealloc的機制,所以我們也必須去處理有關instance reference count的問題,不然很容易會出現記憶體管理的問題...而造成災難。

如何撰寫呢?首先,先在header file中定義要取得singleton instance的static method,例如:
+ (id) sharedInstance;
然後,在對應的.m檔案中,添加下列內容:
static MyManager *instance = nil;

@implementation MyManager

#pragma mark Singleton Methods
+ (id)sharedInstance {
@synchronized(self) {
if(sharedMyManager == nil)
sharedMyManager = [[super allocWithZone:NULL] init];
}
return sharedMyManager;
}

+ (id)allocWithZone:(NSZone *)zone {
return [[self sharedManager] retain];
}

- (id)copyWithZone:(NSZone *)zone {
return self;
}

- (id)retain {
return self;
}

- (unsigned)retainCount {
return UINT_MAX; //denotes an object that cannot be released
}

- (void)release {
// never release
}

- (id)autorelease {
return self;
}

- (id)init {
if (self = [super init]) {
// initialize your instance variables
}
return self;
}

- (void)dealloc {
// Should never be called, but just here for clarity really.
// release your instance variables
[super dealloc];
}
@end
首先,是定義全域變數instance,用來表示此class的singleton instance。然後實作之前定義的singleton method,讓它傳回該instance variable,如此一來,只要呼叫此method,就可以取得同一個instance。其他的method都是override原本NSObject的定義。主要是用來處理reference count的問題,用來避免該instance被不小心release掉。其中,在init跟dealloc,可以對自己定義的instance variables做初始化跟回收。原則上,Objective-C的singleton pattern大概就是長這樣。

心得:比起Java,真的複雜多了...而且老實說,我也不知道這樣想是不是真的沒問題...只是目前google到的寫法,大多是長得類似這樣。

2010年8月5日 星期四

DOM-based XML utility For iPhone

在Mac OSX上,分別有NSXMLParserNSXMLDocument來處理有關SAX與DOM方式的XML parsing。然而在iOS上的Cocoa Touch,卻只剩下SAX的NSXMLParser的處理方式。相較於DOM來說,SAX的處理方式是較為快速與節省資源的,也或許是這樣才會在iOS上只保留這種方式的parsing。

然而,如果只是簡單格式的XML讀取,用NSXMLParser來parse可能是很快速簡便。但是如果要處理的XML格式眾多或是格式複雜,那就可能不是那麼好過了...而且NSXMLParser也只能處理讀取時的parsing。如果是要將資料輸出成XML格式,那不使用DOM的處理將會變得比較難以處理。

後來在internet上,發現這篇APXML: NSXMLDocument 』substitute' for iPhone/iPod Touch,才知道有這個APXML的DOM-based簡易套件可以使用。說它是簡易套件還真不為過,含Header file在內,全部也才不過6~7個source code files,每個也都不算太大、太複雜。
既然是DOM-based parser,當然除了可以讀取,也可以輸出成為XML。使用上很簡單,只要
#import "APXML.h"
就可以使用到全部的相關類別,不必一一去import個別的class。
Parse XML:
APDocument *doc = [APDocument documentWithXMLString:xmlstring];
取得Root Element:
APElement *rootElem = [doc rootElement];
讀取所有子element:
NSArray *childElements = [rootElem childElements];
讀取element name:
NSString *elementName = elem.name;
讀取element value:
NSString *elementValue = elem.value;
讀取特定名稱之attribute:
NSString *attrValue = [elem valueForAttributeNamed:attributeName];

輸出部分則為
Create Element:
APElement *elem = [APElement elementWithName:elementName];
Set attribute & element value:
[elem addAttributeNamed:attrName withValue:attiValue];
[elem appendValue:elementValue];
Add child element:
[elem addChild: childElem];
Create XML Document:
APDocument *doc = [[APDocument alloc] initWithRootElement: elem];
Output XML string:
NSString *xmlString = [doc xml];

使用語法算是十分簡單、直覺。
其實,要自己實作DOM-based parser也不算太難,但是總是比較費工夫、品質也需要時間來鍛鍊。既然有現成又不錯的lib,當然能善加利用會更好囉~

好用的HTTP lib(for iPhone & Mac OSX)

HTTP API其實在Cocoa裡面是有的,透過NSURL、NSURLRequest、NSURLConnection等類別來達成,也分別支援同步與非同步傳輸。使用上雖然不算是太難,但是仍然有不少繁瑣的手續要做,要處理的delegate method也不算少,讓人感覺還是有點不是那麼方便。

因此,後來在internet上,找到一個叫做ASIHTTPRequest的lib,這個lib是以BSD license的方式open source的。顧名思義,這個lib的主要工作就是提供HTTP protocol的通訊機制,而且用很簡單的方式讓developer可以操作request、response,以及設定POST parameter,甚至是上下傳檔案等等。本身也內建啟用Cookie機制,所以對於一些有session tracking的web services也很方便。當然也提供有同步與非同步的傳輸。
官網位置:http://allseeing-i.com/ASIHTTPRequest/

心得:該Lib除了可以在Mac OSX上使用外,也可以在iOS上使用,而且目前已經與iOS4相容。使用上算是很簡單,也沒碰過啥大問題。如果要說缺點,就是放到project中,會導致project code在compile之後多出不少容量吧XD

2010年8月4日 星期三

動態調整TableVewCell的高度

因為工作需求,原本要使用UITextView來顯示多行文字,但是發現使用了UITextView之後,Cell可以觸發select動作的區域將變得很小(受到UITextView的區域影響)。所以改用別的方式處理。

首先,發現原來UILabel是可以顯示多行文字的,只要把property numberOfLines設為0,就可以支援多行文字,但是必須呼叫[label sizeToFit],來調整UILabel的大小。

而預設的UITableViewCell的textLabel也可以這樣調整,所以就可以顯示多行文字。然而,UITableView還有個問題,就是有cell height的問題。因為cell height似乎是在cell顯示前就會設定好,所以即使在tableView:cellForRowAtIndexPath:這個method裡面去調整cell的高度也是無效。必須透過tableView:heightForRowAtIndexPath:來回傳特定cell的高度。

但是,對於多行文字來說,因為內容行數是不固定的,所以必須在顯示時計算實際行數文字的總高度。幸好在參考iPhone SDK: Resizing a UITableViewCell to Hold Variable Amounts of Text, Part 2 of 2這篇之後,得到解答。就是利用NSString裡面的sizeWithFont:constrainedToSize:lineBreakMode:來根據指定的字型高度跟斷行方式來計算。可以使用Objective-C的Category機制,將此method放置到NSString中。以下是參考上述文章所寫的code:
@implementation NSString (TextHeight)
-(CGFloat) textHeightForSystemFontOfSize:(CGFloat)size {
//Calculate the expected size based on the font and linebreak mode of the label
CGFloat maxWidth = [UIScreen mainScreen].bounds.size.width - 50;
CGFloat maxHeight = 9999;
CGSize maximumLabelSize = CGSizeMake(maxWidth,maxHeight);
CGSize expectedLabelSize = [self sizeWithFont:[UIFont systemFontOfSize:size] constrainedToSize:maximumLabelSize lineBreakMode:UILineBreakModeWordWrap];
return expectedLabelSize.height;
}
@end

透過此method,就可以在tableView:heightForRowAtIndexPath:中回傳正確的文字內容高度了。