呃...

其實不會有什麼精深的東西,也就只是把我學習過程記錄一下...

2015年1月25日 星期日

Objectvie-C review (features, Delegation, protocol, category, KVC)

也是很久前的draft 囧 都是一些老生常談講到爛到不能再爛的東西,但是每次都會忘掉,就單純做個紀錄,因此此篇非常雜亂!
===================================

Obj-C introduction
dynamic language - dynamic type (id) and binding, a little more effort than C++, but has more flexible
http://rintarou.dyndns.org/2010/12/09/objective-c-淺談/
GC - suspend on iOS & OS X 10.8 (variety of algorithm)
property - access method(auto synthesize), encpulation, dot syntax, attribute configuration, KVO
hint:internal access - directly get and using setter to set instance variables

cocoa vs. cocoa touch
http://www.cnblogs.com/kkun/archive/2012/04/25/2470166.html

protocol
即類似java interface,主要是由於OC本身沒有多重繼承機制,所以protocol
可讓Class之間互相溝通與使用對方的method,protocol最頻繁被使用於
delegate 與 data source DP
Delegation
如我的blog所說,是一種DP讓Class之間可以互動並可委託其他Class執行
自己不希望執行的功能。其class互動與溝通有其他很多方法可以替代,但
是此方法可以充分發揮MVC精神,例如View不應該處理的MC,可透過此 
DP完成這種概念。
由於protocol具@optional與@require(default)兩種method的設定,因
此在委託方中implement方法的使用時,要記得當使用@optional的方法時
,要多利用introspection機制確定方法有沒有被conform。
在使用introspection機制時也要再另外注意一件事,就是當method被頻繁
的call or callback時,應該想辦法將introspection機制與頻繁被執行的部分
分隔開來,避免浪費多餘的效能。(performance optimal)
優化機制作法:
override delegate setter,在當中先進行introspection機制並將結果
cache,未來直接使用cache的結果來判斷是否可以執行method即可。

category
在其他language中會發生繼承下來的方法不夠用或是沒找到自己想用的,這時
多數作法是在subclass增加方法,或是被經常用到的方法就新增到superclass裏
,這種情況下若是不複雜的且是自己implement的superclass可能不會造成太大
困擾,但若是接手他人的code或是superclass已經發展到很複雜並且太多subclass
在使用,若不想動到原本的code並且不會讓每個subclass都要繼承新增的method
,category所實現的額外加入method就非常的好用。
若沒有取名的category稱為類別延伸,通常用於隱藏不想public的instance variable
。需注意累目的方法名稱要加上prefix,避免override原始method
其存在主要是實現clean code 想法,透過category可對method分門別類,而不會
導致某個class既肥大又雜散


Observer Desgin Pattern:
KVC - KVO
http://rintarou.dyndns.org/2011/07/22/objective-c-programming-筆記-2/

KVC - https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/KeyValueCoding/Articles/Overview.html#//apple_ref/doc/uid/20001838-SW1

透過String格式的identifier間接access object properties/instance variable,KVC與@property一樣,會自動使用accessor。
若是instance variable,則會自動建立accessor去存取variable,但因為KVC的判斷方法是 找外內部的accessor ->找變數 ->找不到
,因此一般都會為instance variable套上property,降低效率成本。
符合KVC格式 - 
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/KeyValueCoding/Articles/Compliant.html#//apple_ref/doc/uid/20002172-BAJEAIEE

Attribut, to-one and Many format

無論Attribute或to-one,找不到key會引用valueForUndefinedKey:回傳一個exception,可自行override這個函數。

attribute :

Simple value,譬如String, bool, int, float, NSNumber等,key path可直接指定variable name,當attribute非object時(scalar type or struct),需implement setNilValueForKey:。
若是scalar type時,setValue需使用NSNumber wrap,可使用literal syntax,另外要注意的是當傳入數字字串時是可以使用的,會自動call NSString的method幫忙轉成NSNumber,但須注意應避免此種狀況,減少效率成本。
若是struct則一定需要implement setter和getter,且setValue時需使用NSValue wrap C struct再傳入,傳入的值會自動unwrap,且在obsevser被call時的change parameter一樣要使用NSValue的getValue去unwrap。
(scalar type or struct) - https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/KeyValueCoding/Articles/DataTypes.html#//apple_ref/doc/uid/20002171-BAJEAIEE

to-one : 

Object。使用方法 ex: [Object setValue:@“” forKeyPath:@“firstName”];

to-many : (NSArray, NSSet, NSDictionary)

1. collection不能使用nil當值,所以value不可直接傳nil,但是可以使用NSNull,會建立一個nil的instance放入。
2. 回傳的非單一值,是同identifier的allvalue
3. 分為indexed (Array )and unorder(set)

KVC implement detail: 
https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/KeyValueCoding/Articles/SearchImplementation.html#//apple_ref/doc/uid/20000955-CJBBBFFA
原則:
1 .先找是否有@property auto-synthesize的 method(可以被override)或是否有自己根據rule而implement method
2. 若設定可以直接取值,則直接找是否有定義值並取值
3. 都沒有,auto invokes 找不到key的method

Encrypt & Decrypt context in iOS by CommonCryptor and communicate to PHP mcrypt (blowfish, AES, DES, 3DES...)

這是一篇好幾個月前的draft,久到我只記得怎麼做,卻不知道為什麼會這樣做的文章 XD

==============
繼上次使用3rd-party的blowfish加密library,這次換使用iOS public source 中的CommonCryptor library。用好好的幹嘛換?原因是chilkat的試用期限到了 囧,這裡想必會有一個疑問:如果有試用期,明知有試用期,為什麼還使用?因為被誤入歧途,說什麼那個限制是假的......Orz。總之,也是多了解一種3rd-party的library,多學到一些東西囉。

在學習CommonCryptor的過程中,其實還有不少內容並不是非常了解,不過至少可以work了 XD
==============
(以下是好幾個月後補的,所以Blowfish的algorithm細節已經記不得了,看到寫死的部份若有疑惑,請查Blowfish的algorithm XD)

這個method是加密的前置作業,在這當中可以看到我對傳入的String轉成Data並加大Data的長度,這是有關於encrypt的input長度,由於PHP以及iOS兩邊實作的Blowfish algorithm本身雖然一樣,但在input以及output可能多少有些不同,詳情要自行到wiki去查查blowfish的algorithm.. XD 但總之長度必須要符合兩邊Blowfish的要求,才不會導致encrypt或是未來decrypt出問題。
另外我對實際做encrypt或decrypt的部分另外寫成一個method,稍後會提到,總之若沒意外,計算完data length後丟入encrypt會return data出來,最後將data convert成String丟回去即可。噢,最後一樣可以看到我對加密後的data長度減8,這也是因為Blowfish的加密algorithm的制式output,詳情一樣請wiki blowfish XD

-(id) encode:(NSString *)str{
    NSMutableData *dataStr=[[NSMutableData alloc] initWithData:[str dataUsingEncoding:NSUTF8StringEncoding]];
    if (([dataStr length]%8)!=0) {
        [dataStr increaseLengthBy:(8-[dataStr length]%8)];
    }
    NSData *encryptData=[self crypt:kCCEncrypt cinpherText:dataStr];
    NSString *returnStr=[BitHelper bytesToHex:(unsigned char*)[encryptData bytes] length:(int)[encryptData length]-8];
    return returnStr;
}

這個method則是decrypt的前置作業,當這個method被呼叫的時候,會傳入一串16進位的字串,但此字串資料實際上是byte,所以需convert回去byte型態。同樣的由於PHP以及iOS兩邊Blowfish的input與output不同,所以output的長度我也必須確認,但是相比iOS這邊encrypt後丟給PHP需顧慮PHP看不看得懂,PHP進行encrypt後的output反而只需要確認是否有額外增加長度而填入dirty data即可。因此除了0或1的情況,其餘只要確認收到資料格式最後是否有dirty data並捨棄即可。

-(id) decode:(NSString *)str{
    NSMutableString *decodeStr=[[NSMutableString alloc]initWithData:[self crypt:kCCDecrypt cinpherText:[BitHelper hexToBytes:str]] encoding:NSUTF8StringEncoding];
    //dirty padding in data, for example:[{"m_id":"44"}]@@@, after "]" character exist some bytes and print can't see that, so need to clear they.
    if ([decodeStr length]!=8) {
        NSRange range=[decodeStr rangeOfString:@"]" options:NSBackwardsSearch];
        NSUInteger datalength=range.location+range.length;
        NSMutableData *returnData=[[NSMutableData alloc]initWithData:[self crypt:kCCDecrypt cinpherText:[BitHelper hexToBytes:str]]];
        [returnData setLength:datalength];
            return returnData;
    }
    return [self crypt:kCCDecrypt cinpherText:[BitHelper hexToBytes:str]];

}

重頭戲來了,在這method中可以看到使用了CCCryptorStatus的variable以及CCCrypt的C function,這是由iOS的CommonCryptor library所提供的,CCCryptorStatus是用來判斷加解密的status是成功或失敗,最重要的是在CCCrypt,當把所有參數丟入CCCrypt並成功加解密後就會取得Bytes型態的資料,回傳後再交給NSData處理即可,這樣就完成了iOS的加解密功能了!

P.S.:
1.
由於是因為和PHP串接,所以會需要自己確認data length以及有沒有dirty data,若是encrypt以及decrypt的input與output都是來自iOS的CommonCryptor的話,就不需要這些步驟了,因為iOS的CommonCryptor會看得懂自己做的input與output。

2.
CCCrypt必須放入很多個參數,我有的已經有點忘了,我就其中記得的說明一下:
operation - 在encode或decode裏可看到,也就是告知CCCrypt我現在要做decrypt或encrypt
kCCAlgorithmBlowfish - trace這個常數就可以知道,這是告知CCCrypt要用什麼algorithm去做,即使是要使用別的加解密演算法,像是AES, DES或3DES這些加密演算法,其實這個function都已經做好,是一個非常方便的library!
kCCOptionPKCS7Padding - 就是data的padding
[KeyAscii bytes] - Blowfish用來加解密的key,由於是C function,所以必須丟入bytes型態。
[ivAscii bytes] -  Blowfish另一把key,印象中是在上一把key與data XOR後,再XOR這把key做二度加密 (不一定正確,細節忘了 XD)
[data bytes] - 愈加解密的資料
[datalength]- 資料的長度
buffer - 資料加解密後會被放在這個buffer中,回傳後記得free掉自行malloc的buffer。
bufferSize - 告知buffer有多大
&numBytesCrypted - 實際收到的buffer的長度

-(NSData *)crypt:(uint32_t) operation cinpherText:(NSData *) data{
    NSData *keyAscii = [@"5399602753996027" dataUsingEncoding:NSUTF8StringEncoding];
    NSData *ivAscii = [@"53996027" dataUsingEncoding:NSUTF8StringEncoding];
    size_t bufferSize = 2048;
    size_t datalength=[data length]+8;
    if (operation==kCCEncrypt)
        datalength=[data length];
    void *buffer = malloc(bufferSize);
    size_t numBytesCrypted = 0;
    CCCryptorStatus destatus = CCCrypt(operation, kCCAlgorithmBlowfish,
                                       kCCOptionPKCS7Padding,
                                       [keyAscii bytes], [keyAscii length],
                                       [ivAscii bytes],
                                       [data bytes], datalength, /* input */
                                       buffer, bufferSize, /* output */
                                       &numBytesCrypted);
    
//    NSLog(@"get %lu %lu %p %d", bufferSize, numBytesDecrypted, data, destatus);
    if (destatus == kCCSuccess && operation==kCCDecrypt) {
        //the returned NSData takes ownership of buffer and will free it on dealloc
        return [NSMutableData dataWithBytesNoCopy:buffer length:numBytesCrypted];
    }else if(destatus == kCCSuccess && operation==kCCEncrypt)
        return [NSMutableData dataWithBytesNoCopy:buffer length:numBytesCrypted];
    
    free(buffer); //free the buffer;
    return nil;
}

================
老實說這篇其實有點慚愧,現在寫出來只是為了消化一些draft,我原本想說可以完整的解釋每個細節以及blowfish algorithm再寫出來,但最後還是沒有完成,可能是後來的工作在加解密這塊沒什麼需求,所以我就懶了吧.. XD 希望自己未來有一天可以補上這邊的knowledge Orz

2015年1月23日 星期五

Design Pattern in iOS (Singleton, notification, delegation, KVC-KVO, class cluster)

嗚哇~好幾個月沒發表啦 囧 其實是一直寫在draft而且整理不完,哭哭 Orz
先分享最近工作上的presentation slide好了,不過是簡單屬於Developer都知道
的東西就是 XD

http://www.slideshare.net/YiShouChen/design-patterns-in-ios

就加減看一下啦 XD