Hash(MDx, SHA-xxx)
MD5を使う機会があったので、色々な言語での求め方を調べてみた。
Uinux系
md5
, md5sum
が使えれば↓でおk
md5sum <file> md5 <file> md5 -s <text>
brewでインストールするなら、
brew install md5sha1sum
shasum
もついてくる。
shasum [OPTION]... [FILE]... With no FILE, or when FILE is -, read standard input. -a, --algorithm 1 (default), 224, 256, 384, 512, 512224, 512256
Objective-C
長さCC_MD5_DIGEST_LENGTH
のバッファmd
を用意して、
unsigned char *CC_MD5(const void *data, CC_LONG len, unsigned char *md)
に渡すだけ。
巨大なバイト列を分割して処理する場合は、
int CC_MD5_Init(CC_MD5_CTX *c); int CC_MD5_Update(CC_MD5_CTX *c, const void *data, CC_LONG len); int CC_MD5_Final(unsigned char *md, CC_MD5_CTX *c);
を使う。
他のハッシュアルゴリズムを使う場合はMD5
の部分を変える。
用意されているアルゴリズムは、
MD2
, MD4
, MD5
, SHA1
, SHA224
, SHA256
, SHA384
, SHA512
#import <CommonCrypto/CommonDigest.h> NSString* convertDigest(unsigned char *digest, int digestLength) { NSMutableString *ret = [NSMutableString stringWithCapacity:digestLength * 2]; for(int i = 0; i < digestLength; ++i) [ret appendFormat:@"%02x", digest[i]]; return ret; } NSString* MD5Hash(const void *data, NSUInteger len) { unsigned char md[CC_MD5_DIGEST_LENGTH]; CC_MD5(data, (CC_LONG)len, md); return convertDigest(md, CC_MD5_DIGEST_LENGTH); } NSString* MD5HashArray(const void *datas[], NSArray *lenArray) { unsigned char md[CC_MD5_DIGEST_LENGTH]; CC_MD5_CTX ctx; CC_MD5_Init(&ctx); for (int i = 0; i < lenArray.count; ++i) CC_MD5_Update(&ctx, datas[i], (CC_LONG)[((NSNumber*)(lenArray[i])) unsignedIntegerValue]); CC_MD5_Final(md, &ctx); return convertDigest(md, CC_MD5_DIGEST_LENGTH); } #pragma mark - MD5 test static const NSString * const str = @"D364D54F-597E-4375-B329-2DE3B2E3539D"; static const char * const strs[] = { "D364D54F-", "597E-4375-", "B329-2DE3B2E3539D" }; static const int N = sizeof(strs)/sizeof(strs[0]); void testMD5(void) { NSData *data = [str dataUsingEncoding:NSUTF8StringEncoding]; NSLog(@"%@\n%@\n", MD5Hash([str UTF8String], str.length), MD5Hash(data.bytes, data.length)); NSMutableArray *lenArray = [NSMutableArray arrayWithCapacity:N*2]; for (int i = 0; i < N; ++i) [lenArray addObject:@(strlen(strs[i]))]; NSLog(@"%@", MD5HashArray((const void**)strs, lenArray)); }
Perl
# 関数形式 use Digest::MD5 qw(md5 md5_hex md5_base64); $digest = md5($data); $digest = md5_hex($data); $digest = md5_base64($data); # OO 形式 use Digest::MD5; $ctx = Digest::MD5->new; $ctx->add($data); $ctx->addfile(*FILE); $digest = $ctx->digest; $digest = $ctx->hexdigest; $digest = $ctx->b64digest;
OO形式を使えば、ファイル指定や分割して渡すこともできる。
#ファイル指定 open my $fh, '<', $filepath or die "Cannot open '$filepath': $!"; my $md5 = Digest::MD5->new->addfile($fh)->hexdigest;
#ブロック分割 my $md5 = Digest::MD5->new; while (<FILE>) { $md5->add($_); } my $md5str = $md5->b64digest;
他のアルゴリズムだいたい同じ感じ、 SHAxxxの場合、new でxxxを指定できる。 CPAN
use Digest::SHA my $alg = 256; my $sha = Digest::SHA->new($alg)->addfile($fh)->hexdigest;
Java
java.security.MessageDigest
を使う。
getInstance(alg)
でアルゴリズムを指定して、インスタンスを作る。
digest(byte[])
でハッシュ結果がbyte配列で得られる。
ブロック分割する場合は、update()
を使う。
MD2
, MD5
, SHA
, SHA-256
, SHA-384
, SHA-512
が使えるみたい。
import java.io.*; import java.security.*; public class HashTest { static public String hashWithAlgorihtm(String algorithm, byte[] data) throws NoSuchAlgorithmException { return convertDigest(MessageDigest.getInstance(algorithm).digest(data)); } static public String hashWithAlgorithm(String algorithm, InputStream in) throws NoSuchAlgorithmException, IOException { MessageDigest md = MessageDigest.getInstance(algorithm); byte[] buff = new byte[1024]; for (int len = 0;(len = in.read(buff, 0, buff.length)) >= 0; md.update(buff, 0, len)); return convertDigest(md.digest()); } static private String convertDigest(byte[] digest) { StringBuilder sb = new StringBuilder(digest.length * 2); for (byte b : digest) { String s = Integer.toHexString(b & 0xff); if (s.length() == 1) sb.append("0"); sb.append(s); } return sb.toString(); } static final String test = "D364D54F-597E-4375-B329-2DE3B2E3539D", fname = "test.txt"; static void test(String algorithm) { try (FileInputStream in = new FileInputStream(fname)) { System.out.println(hashWithAlgorihtm(algorithm, test.getBytes())); System.out.println(hashWithAlgorithm(algorithm, in)); } catch (IOException | NoSuchAlgorithmException e) { e.printStackTrace(); } } public static void main(String[] args) { try (PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(fname)))) { pw.print(test); } catch (IOException e) { e.printStackTrace(); } test("md5"); } }
C/C++
詳しく調べてないですが、Open SSLとかCrypto++を使えば良さそうです。
現バージョンのboostにはなかった... ※Boost.Uuidにsha1クラスがあるので、sha1だけなら作れるみたい。 http://nukesaq88.hatenablog.com/entry/2013/04/19/183424
Effective Objective-C 2.0 第3章(インターフェイスとAPI設計)メモ
項目15 名前空間の衝突を避けるプレフィックス名を使う
Appleが2文字のプレフィックスを使う権利を保有してるので、
- クラス名とかデリゲートメソッドに3文字以上のプレフィックスをつけようず
クラスだけでなく、クラス実装ファイル内で使っている純粋C関数やグローバル変数にも気をつける。
項目16 イニシャライザを用意せよ
指定イニシャライザ(designated initializer): スーパークラスの指定イニシャライザをsuperを通して呼び出し、自分のクラスで必要な初期化を最も詳細に行う初期化メソッド。initWithCoder:とかが指定イニシャライザになり得る。
副次的イニシャライザ(secondary initializer): 指定イニシャライザをselfを通して呼び出してるイニシャライザ。
項目17 descriptionメソッドの実装
- descriptionを実装するんだよ。
- デバッグ用にはdebugDescriptionがあるんだよ。
- NSDictionaryのdescription使うと見やすいらしいっすよ。
項目18 できるだけイミュータブルなオブジェクトを使う
- プロパティはヘッダでreadonlyにして、クラスエクステンションでreadwriteにするといいよ
- プロパティとしてミュータブルなコレクションを公開するのではなく、オブジェクトが管理するコレクションを書き換えるためのメソッドを提供しよう
※コレクションを返したい場合は、(コピーコストが少ないなら)copyしたものを返すようにする。
その際、戻り値をイントロスペクションでミュータブルかどうか調べて、内容を書き換えるようなまねはしない方が良い。
項目19 明快で首尾一貫した名前を使う
標準APIを参考にすればいいんじゃね的な内容。
項目20 非公開メソッド名にはプレフィックスを付ける
筆者はプライベートメソッドのプレフィクスにprivateのpにアンダースコアを付けたp_を使っている。
うーん、publicもpなんだよなぁ... まあプライベートメソッドの方にしか付けないから区別できるけどね。
項目21 Objective-Cエラーモデルを理解する
- 例外は、App.を狩猟させるべき致命的エラーのとき限り使う
ARCは例外セーフでない。コンパイルオプション-fobjc-arc-exceptionsを指定しておけば、例外セーフコードを生成してくれるが、例外が投げられていないときにも実行しなければならないコードが生成される。MRCでも例外を投げる前にリソースを解放させるように気をつけなければならない。
NSErrorを使う場合は↓のような感じ。domain, code等は、グローバル定数のNString*const, typedef NS_ENUM(NSUInteger, xxx)を使えば良い。
NSError *error = nil; BOOL ret = [obj doSomething:&error]; if (error/* or ret */) { /* handle error */ }
- (BOOL)doSomething:(NSError**)error { //ARCのときは(NSError*__autoreleasing*)に書き換えられる。 //do something if (/* there was an error */) { if (error) { //if error == nil, return only result (success or failure). //NSStirng *domain, NSInteger code, NSDictionary *info *error = [NSError errorWithDomain:domain code:code userInf:info]; } return NO; } return YES; }
項目22 NSCopyingプロトコルを理解する
- ミュータブル版、イミュータブル版があるオブジェクトのコピー対応には、NSCopying, NSMutableCopyingプロトコルを使う。
allocWithZoneとイニシャライザを使って以下を実装。
- (id)copyWithZone:(NSZone*)zone; //NSCopying protocol - (id)mutableCopyWithZone:(NSZone*)zone; //NSMutableCopying protocol
- シャローコピーするかディープコピーをするかを決め、可能な限りシャローコピーを採用するようにする
Foundationのすべてのコレクションクラスは、デフォルトでシャローコピー。
Effective Objective-C 2.0 第2章(オブジェクト、メッセージング、ランタイム)メモ
項目6 プロパティの理解
- 宣言プロパティ(declared property)の属性
アトミック性
デフォルト(何も書かない場合)はatomic
-
- nonatomic 排他的に実行されないアクセッサメソッド。
- atomic アクセッサにアトミック性を保証するロック機能が付く。自分でを定義する場合は、そのためのコードを書かなければならない(書くべき)。
読み書き属性
デフォルトはreadwrite。@interface部でreadonlyにして、クラスエクステンションでreadwriteにすることもできる。
-
- readwrite
- readonly
メソッド名指定
指定しなければ、getter =
-
- getter=
isというプレフィックスをつけるために、論理値のプロパティに使われることが多い。 - setter=
あまり使われない。
- getter=
値の設定方法
デフォルトはNon-ARCならassign、ARCならstrong?
-
- copy オブジェクトをcopyして設定。プロパティのクラスは、NSCopyingプロトコルを採用し、copyメソッドが利用できる必要がある。NSString等mutableなサブクラスが存在する場合に、見えないところで値が書き換えられるのを防ぐために使用する。
- weak (ARC用)弱参照。新しい値がセットされるとき、その値はretainされず、古い値はreleaseされない。プロパティが指しているオブジェクトが破棄されたときnilに書き換えられる。対応するインスタンス変数は__weakで修飾されている必要がある。
- assign 単純な代入。CGFloatやNSIntegerといった非オブジェクト型に使う。
- unsafe_unretained (ARC用)assignと同じだがターゲットがオブジェクト型。新しい値がセットされるとき、その値はretainされず、weakとは異なり破棄されたときに値がnilに書き換えられない。対応するインスタンス変数は__unsafe_unretained修飾されている必要がある。
- retain 新しい値がセットされるとき、まずその値をretainし、古い値をreleaseしてから、新しい値をセットする。
- strong (ARC用)retainと同じ。対応するインスタンス変数は、ライフタイム修飾子で修飾されていないか、___strongで参照されている必要がある。
- 自分でアクセッサを定義する場合は、指定したプロパティ属性に沿った実装をする。
- (void)setVal:(TYPE)obj { //retain指定した場合のセッタ例 if (_val != obj) { [_val release]; _val = [obj retain]; } } - (void)setVal:(TYPE)obj { //copy指定した場合のセッタ例 if (_val != obj) { [_val release]; _val = [obj copy]; } }
@dynamic指定すれば、自動合成はされない。
CoreDataのNSManagedObjectをサブクラス化するとき等に使われるプロパティがインスタンス変数ではないため。
@interface Status : NSObject @property(getter = HP, setter = setHP:) int hitPoint; @property(getter = MP, setter = setMP:) int magicPoint; @property/*(readonly)*/int level; @end @implementation Status @dynamic level; - (int)level { return (_hitPoint + _magicPoint)/2; // (self.HP + self.MP) / 2; } - (NSString*)description { return [NSString stringWithFormat:@"HP = %d, MP = %d, Lv = %d", _hitPoint, _magicPoint, self.level //@dynamic指定しているため_levelはない。自分で定義したゲッタメソッドを使う。 ]; } @end int main(int argc, const char * argv[]) { @autoreleasepool { Status *st = [[Status alloc] init]; st.HP = 10; //[st setHP:10]; st.MP = 5; //[st setMP:10]; NSLog(@"%@", st); //[st setLevel:10]; //setLevel:を実装していないので実行時エラー. readonly指定していればコンパイルエラー } return 0; }
- @property構文でアクセッサメソッドを自動合成できる
以前は、@synthesize構文を書かないと自動合成されなかったが、今は不要。
自動で生成されるインスタンス変数の名前を指定したい場合ぐらいにしか@synthesizeは使わない。
自動生成されるインスタンス変数はプロパティで指定した名前の頭に_が付く。
@synthesize firstName = _myFirstName;
- iOSでは、atomicを使うとパフォーマンスが大きく損なわれるので、nonatomicを使う。
通常、アトミック性を保証しただけなら、他のスレッドが同時書き込みをしているときに、同じプロパティ値を連続して何度も読み出すと、別の値が返される(スレッドセーフではない)。
項目7 インスタンス変数にクラス内でアクセスするときは直接アクセスする
※ オブジェクトの外からインスタンス変数にアクセスするときには、必ずプロパティを使うようにすべきだが、クラス内でインスタンス変数にアクセスするときにはどうすべきかは、色々な意見がある。
あくまで、Effective Objective-Cでは、↓のようなポリシーを推奨しているというだけ。
- クラス内での読み出しではインスタンス変数の直接読み出し、書き込みではプロパティを介した書き込みを使う。
- 直接アクセスの方が高速。
- 直接アクセスはメモリ管理属性を参照しないので注意する。copy, retain, release etc.
- 直接アクセスではKVO(Key Value Observing)通知は生成されない。(問題になる場合もそうでない場合もある)
- イニシャライザとdeallocでは、かならずインスタンス変数を介して直接データを読み書きする。
サブクラスで想定外のオーバーライドがあるかもしれないので直接アクセスした方が良いらしい。
ただし、インスタンス変数がスーパークラスで宣言されている場合は、アクセッサを使わないといけない。
うーーん、インスタンスが遅延初期化する場合とかもあるし、メモリ管理属性とか気にしないと行けないし、どうせ全て直接アクセスできないなら、イニシャライザで気をつけるより、アクセッサをオーバーライドする側で気をつけた方が良いんじゃないかな?
@interface Name : NSObject @property(nonatomic, copy) NSString *firstName, *lastName; @end @implementation Name - (instancetype)init { if (self = [super init]) self.firstName = self.lastName = @""; return self; } @end @interface Yamada : Name @end @implementation Yamada - (void)setLastName:(NSString *)lastName { if (![lastName isEqualToString:@"Yamada"]) [NSException raise:NSInvalidArgumentException format:@"Last name must be Yamada"]; self.lastName = lastName; } @end int main(int argc, const char * argv[]) { @autoreleasepool { Name *yamada = [Yamada new]; //必ず例外発生 } return 0; }
- データ遅延初期化されている場合は、プロパティを介してデータを読み出さなければならない場合がある。
セットアップコストがかかり、アクセス頻度が低い場合など、ゲッタで初期化を先延ばしする場合。
- (TYPE)val { if (!_val) _val = [TYPE new]; //重い初期化処理 return _val; }
項目8 オブジェクトが等しいとはどういうことかを理解しよう
- 同値比較したいオブジェクトでは、isEqual:とhashの2つのメソッドを用意する。
- 「2つのオブジェクトが等しい同じhash値」を守る(逆が成り立つ必要は無い)。
- hashは、高速に計算でき、衝突が起きる可能性が低くなるように実装する。
@interface Type : NSObject @property (nonatomic, copy) NSString *str; @property (nonatomic, retain) T *obj; @property (nonatomic) NSUInteger uintVal; @end @implementation Type //実装例 - (BOOL)isEqualToType:(Type*)o { if (self == o) return YES; if (![_str isEqualToString:o.str] || ![_obj isEqual:o.obj] || _uintVal != o.uintVal) { //一意な識別子があればそれを使って浅い同値比較をしても良い return NO; } return YES; } - (BOOL)isEqual:(id)object { //サブクラスを許容する場合は[self class] == [object class]を適当に書き換える return [self class] == [object class] ? [self isEqualToType:(Type*)object] : [super isEqual:object]; } - (NSUInteger)hash { return [_str hash] ^ [obj hash] ^ _uintVal; } @end
- コレクションにオブジェクトを追加したら、そのオブジェクトのハッシュ値が変更されることがあってはならない。
NSMutableSet *set = [NSMutableSet new]; NSMutableArray *arrayA = [@[@1,@2] mutableCopy]; [set addObject:arrayA]; NSLog(@"set = %@", set); //set = {((1,2))} NSMutableArray *arrayB = [@[@1,@2] mutableCopy]; [set addObject:arrayB]; NSLog(@"set = %@", set); //set = {((1,2))} NSMutableArray *arrayC = [@[@1] mutableCopy]; [set addObject:arrayC]; NSLog(@"set = %@", set); //set = {((1),(1,2))} [arrayC addObject:@2]; NSLog(@"set = %@", set); //set = {((1,2),(1,2))} 同じ要素を複数含む集合ができてしまう NSSet *setB = [set copy]; NSLog(@"set = %@", setB); //set = {((1,2))} 同じ要素を複数含む集合ができるわけではない
項目9 実装の詳細を隠すために、クラスクラスタパターンを使う
- クラスクラスタ(class cluster):同じインターフェースをもち、同じ機能を提供する複数のクラスの集合体
- パブリッククラス(public class):クラスクラスタのインターフェースを表す公開された抽象クラス
- プリミティブメソッド(primitive method):クラスクラスタにおいて、具体的なデータ構造やアルゴリズムに基づいて定義されるメソッド。パブリッククラスではサブクラスでプリミティブメソッドが実装されることを前提に、それ以外のメソッドが実装されている。
クラスクラスタのインスタンスのチェックには気をつける。ほとんどのコレクションがクラスクラスタ。
NSStringFromClass()でインスタンスのクラス名を確認できる。
void printClass(Class clazz, id ins) { printf("class = %s, \tisMemberOfClass = %s, \tisKindOfClass = %s\n", [NSStringFromClass([ins class]) UTF8String], [ins isMemberOfClass:clazz] ? "YES" : "NO", [ins isKindOfClass:clazz] ? "YES" : "NO" ); } int main(int argc, const char * argv[]) { @autoreleasepool { NSString *str = @"str"; Class clazz = [NSString class]; printClass(clazz, str); printClass(clazz, [str stringByAppendingString:@"ヽ(`Д´)ノ"]); printClass(clazz, NSHomeDirectory()); } return 0; }
class = __NSCFConstantString, isMemberOfClass = NO, isKindOfClass = YES class = __NSCFString, isMemberOfClass = NO, isKindOfClass = YES class = NSPathStore2, isMemberOfClass = NO, isKindOfClass = YES
クラスクラスタを拡張するにはカテゴリを使うのが手っ取り早い。サブクラス化する場合にはいくつか注意点がある。
クラスクラスタのパブリッククラスをサブクラス化する方法
項目10 既存のクラスにカスタムデータを追加するにはAssociated Objectを使う
- Associated Objectは、見つけにくいバグを埋め込みやすいので、ほかのアプローチでは不可能なときに使うようにしよう。
通常、オブジェクトに情報を追加したい場合は、クラスのサブクラス化を検討するが、
何か特別な手段でインスタンスが作られていて、自由にインスタンスを作るように指示することができない場合、
連想(関連)参照(associated references)を使う。
カテゴリではメソッドの追加だけで、インスタンス変数を追加することはできないので、連想参照が役立つ。
effective Objective-Cでは、UIAlertViewにボタンをタップした場合の操作をブロックオブジェクトにして、関連づけする例が示されている(delegateだとコードが分断されるため)。
※UIAlertController(iOS8以降)だと、blockオブジェクトを受け取るメソッドが用意されている。
#import <objc/message.h> //id object: オーナー、 id value: 参照オブジェクト void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) id objc_getAssociatedObject(id object, const void *key) void objc_removeAssociatedObjects(id object)
NSDictionaryのように使えるが、keyは単なるポインタ比較で区別する(isEqual:ではない)ので、staticなグローバル変数が使われることが多い(例えば static char kAssociated;としておいて、&kAssociatedを渡す).
policyにはOBJC_ASSOCIATION_RETAINといった@property属性と同じようなストレージポリシーを指定する。
objc_getAssociatedObject()で関連付けがなければnilを返す
参照の解除にobjc_removeAssociatedObjects()を使うと、指定したオーナーに関連づけられている全ての参照を解除してしまうので、通常はobjc_setAssociatedObject()でnilをセットして、個別に参照の解除を行う。
項目11 objc_msgSendの役割を理解する
送られたメッセージ(レシーバー、セレクタ、引数)は、すべて動的メッセージディスバッチシステムを通して実装をルックアップして実行する。
id returnValue = [reciever selector:arg]; id returnValue = objc_msgSend(reciever @selector(selector:), arg); //objc_msgSend family: objc_msgSend_stret(struct用), objc_msgSend_fpret(浮動小数点用), objc_msgSendSuper(super用) etc.
項目12 メッセージの転送を理解する
オブジェクトが未知のセレクタを検出したとき、
1. 動的メソッド解決で、クラスに実行時にメソッドを追加できる。
2. 自分が理解できない一定のセレクタをほかのオブジェクト(代替レシーバ)が処理できると宣言できる
3. セレクタの処理方法がどうしても見つからない場合は、本格的な転送メカニズムが実行される。
でメッセージ転送が行われる。
- 動的メソッド解決
オブジェクトが理解できないメッセージがオブジェクトに渡されたとき、最初に呼びさされるのは、
インスタンスメソッドの場合はresolveInstanceMethod:、
クラスメソッドの場合はresolveClassMethod:。
#import <Foundation/Foundation.h> @interface AddMethodSample : NSObject @property NSString *str; @property NSNumber *num; @property NSDate *date; @property id opaqueObject; @end
#import "AddMethodSample.h" #import <objc/runtime.h> @interface AddMethodSample () @property NSMutableDictionary *propertyData; @end id dicGetter(id self, SEL _cmd) { return [((AddMethodSample*)self).propertyData objectForKey:NSStringFromSelector(_cmd)]; } void dicSetter(id self, SEL _cmd, id value) { NSMutableString *key = [NSStringFromSelector(_cmd) mutableCopy]; AddMethodSample *typedSelf = (AddMethodSample*)self; //setHoge:をhogeに変えてキーにする [key deleteCharactersInRange:NSMakeRange(key.length - 1, 1)]; [key deleteCharactersInRange:NSMakeRange(0, 3)]; [key replaceCharactersInRange:NSMakeRange(0, 1) withString:[[key substringToIndex:1] lowercaseString]]; if (value) [typedSelf.propertyData setObject:value forKey:key]; else [typedSelf.propertyData removeObjectForKey:key]; } @implementation AddMethodSample @dynamic str, num, date, opaqueObject; - (id)init { if (self = [super init]) _propertyData = [NSMutableDictionary new]; return self; } + (BOOL)resolveInstanceMethod:(SEL)sel { [NSStringFromSelector(sel) hasPrefix:@"set"] ? class_addMethod(self, sel, (IMP)dicSetter, "v@:@") : class_addMethod(self, sel, (IMP)dicGetter, "@@:"); return YES; } @end
class_addMethod(Class cls, SEL name, IMP imp, const char *types)でメソッドを追加できる。
IMPは隠し引数(hidden arguments)(レシーバ、セレクタ)を含む関数へのポインタ(第一引数id self, 第二引数SEL _cmd)。tyesは、Type Encodings。
- 代替レシーバ
動的メソッド解決されなかった(resolveInstance(Class)MethodでNOを返した)場合、代替レシーバがあるかどうかを
forwardingTargetForSelector:で聞いてくる。処理する物が無い場合はnilを返す。
-(id)forwardingTargetForSelector:(SEL)aSelector { if ([self.delegate respondsToSelector:aSelector]) { return self.delegate; } return [super forwardingTargetForSelector:aSelector]; }
- 本格的な転送メカニズム
forwardingTargetForSelector:で処理されなかった(nilを返した)場合、forwardInvocation:(NSInvocation*)invocationが呼ばれる。NSInvocationは、設定されているselector, 設定されているtargetを設定し直したりできる。
NSInvocation#invokeWithTarget:で引数のオブジェクトをターゲットとして、レシーバの表すメッセージを送信する。メッセージの結果は、元のセンダに返される。
- (void)forwardInvocation:(NSInvocation *)anInvocation { [self.delegate respondsToSelector:anInvocation.selector] ? [anInvocation invokeWithTarget:self.delegate] : [super forwardInvocation:anInvocation]; }
処理しない場合は、スーパークラスの実装を呼び出さなければならない。NSObjectのforwardInvocation:が呼びさされると、doesNotRecognizeSelector:が呼び出され、未処理セレクタ例外(NSInvalidArgumentException)を発生させる。
ランタイムシステムが転送先のオブジェクトの情報を使ってNSInvocationのインスタンスを作成できるようメソッドシグネチャを返すメソッドmethodSignatureForSelector:を再定義しなければならない(あんまよく分かってない)。
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { return [super respondsToSelector:aSelector] ? [super methodSignatureForSelector:aSelector] : [self.delegate methodSignatureForSelector:aSelector]; }
ちなみにdoesNotRecognizeSelectorを使えば、メッセージの使用を積極的に禁止できる.
- (void)setVal:(id)val { //使ってほしくないセッター [self doesNotRecognizeSelector:_cmd]; //_cmdはメソッドの隠し引数で、そのメソッドのセレクタを表す。 }
項目13 不透明なメソッドのデバッグではメソッドのSwizzlingを使うことを検討する
- Method Siwizzling:実行時に既存のメソッドの実装を、自前の実装に差し替える手法。通常はもとの実装に機能を追加するために使われる。
※Effective Objective-CではSwizzlingの使用はデバッグ時だけにすることを推奨している。
@interface NSString (LogString) - (NSString*)myLogStr_lowecaseString; @end @implementation NSString (LogString) - (NSString*)myLogStr_lowecaseString { NSString *lowercase = [self myLogStr_lowecaseString]; //(奇数回)swizzle()が呼ばれた後だとlowecaseStringの実装が呼ばれる NSLog(@"%@ -> %@", self, lowercase); return lowercase; } @end static void Swizzle(Class clazz, SEL aSel, SEL bSel) { Method m1 = class_getInstanceMethod(clazz, aSel), m2 = class_getInstanceMethod(clazz, bSel); method_exchangeImplementations(m1, m2); } int main(int argc, const char * argv[]) { @autoreleasepool { Swizzle([NSString class], @selector(lowercaseString), @selector(myLogStr_lowecaseString)); [@"abcdEFGHijkLMN" lowercaseString]; //swizzlingされているのでmyLogStr_lowecaseStringの実装が呼ばれる //出力:abcdEFGHijkLMN -> abcdefghijklmn } return 0; }
項目14 クラスオブジェクトとは何かを理解する
[SomeClass instance] | -isa-> | [SomeClass class] | -isa-> | [SomeClass metaclass] |
| | | | |||
super_class | super_class | |||
↓ | ↓ | |||
[NSObject class] | -isa-> | [NSObject metaclass] |
- オブジェクトはメッセージの転送を使っている可能性があるので、可能な限りクラスオブジェクトを直接比較するのではなく、イントロスペクションメソッドを使う。
プログラミングHaskell第3章(型とクラス)まとめ
基本型
Bool (真理値)
False, True.
Char (文字)
シングルクォートで囲む。他の言語同様。
String (文字列)
ダブルクォートで囲む。他の言語同様。
Float (単精度浮動小数点)
Double (倍精度浮動小数点)
他の言語同様.
リスト型
T型の要素を持つリストの型を[T]と書く。各要素は共通の型でなければならない。
Hugsやghciで確かめるには:type の後に、式を書く。省略して:t だけでもおk。
Prelude> :t [1, 2, 3] [1, 2, 3] :: Num t => [t] Prelude> :t ['1', 2, 3] <interactive>:1:7: No instance for (Num Char) arising from the literal `2' Possible fix: add an instance declaration for (Num Char) In the expression: 2 In the expression: ['1', 2, 3]
型Tに制限は無いので、リストのリストとかもあり。リストの長さは、length関数で取得。
Prelude> :t length length :: [a] -> Int Prelude> length [] 0 Prelude> length [[]] 1 Prelude> length [[0],[1,3]] 2
タプル型
有限この要素の組。各要素の型は異なっても良い。
番目の要素が型を持つとき、そのタプルの型は、となる。
型に要素数の情報を含んでいる。要素数0のタプルは`ユニット`と呼ばれる(意味の無い値を示す)。
また、式の評価順序を明示的に指定する括弧と区別がつかないため、`要素数1のタプル型はない`。
リスト同様、タプルのタプルや、その他の型との組み合わせが可能。
関数型
Haskellでは、全ての式が型を持ち、型推論によって、式を評価する前に決定される。
fが型Aを型Bへ変換する関数であり、eが型Aの式であれば、関数適用f eは、型Bをもつ
型の引数をとり、型に変換(写像)する関数の型をと書く。
f :: A -> B e :: A f e :: B
Hugsやghciで確かめるには:type の後に、式を書く。省略して:t だけでもおk。
Prelude> :t not not :: Bool -> Bool Prelude> :t False False :: Bool Prelude> :type not False not False :: Bool Prelude> import Data.Char Prelude Data.Char> :t isDigit -- Data.Charをインポートする必要あり isDigit :: Char -> Bool
ちにみに、ghciで関数定義したい場合は、letを使って書かなければならない。(I/Oモナドの中だから?)
また、複数行にまたがって、書きたい場合は、:{ :}を使う。
以下は、二つのInt引数を取って、その和を返す関数の定義例
(厳密には、Haskellでは、引数は一つしかとらない。 二つの引数を取ると言っているが、この場合、タプル型(bigram)の引数を一つ取っているということ。 だと思う)
Prelude> :{ Prelude| let { Prelude| add :: (Int, Int) -> Int; Prelude| add (x,y) = x + y Prelude| } Prelude| :} Prelude> add (5, 6) -- add 5 6では駄目。後述する「カリー化」されていないから 11
関数には、引数の型と結果の型に制限が無い。
全ての入力に対して、出力の値が定義されている関数を全域関数というが、
Haskellでは、関数は全域関数である必要は無い。つまり、入力によっては出力の値が未定義であっても良い。
(例えば、headは空リストに対して定義はされていない)
カリー化と部分適用
※何となく分かったことを書いてるので、誤ったことを書いている可能性があります。
ググってみると、いくつか定義があるみたい。 参考:http://d.hatena.ne.jp/kazu-yamamoto/20110906/1315279311
Groovyの標準ライブラリ自体がカリー化を行うための関数ではないものにcurryとか付けてるので、
部分適用だと誤用して書かれてることが多いそうな。
↓部分適用とカリー化の違いについては、ページが分かりやすかったです。
参考:http://kmizu.hatenablog.com/entry/20091216/1260969166
プログラミングHaskellでは複数の引数という表現を使っているので、
それに習って、私も複数の引数とという表現を使うことにしますが、語弊があるかも知れません。
Haskellでは厳密には引数は一つ.
(タプルやリストで指定する複数の要素のことや、カリー化されている関数fに対して、f x y zのx,y,x etc. を複数の引数といっている場合があります。多分...)
参考 http://d.hatena.ne.jp/kazu-yamamoto/20110906/1315279311
カリー化(currying)
複数の引数をとって値を返す関数を、一引数を取る関数のチェインに直すことをカリー化するという。
参考http://qiita.com/f81@github/items/e8bfab96b4be9e404840
例:
Prelude> let { add' :: Int -> (Int -> Int); add' x y = x + y } Prelude> (add' 5) 6 11 Prelude> :t add' 1 add' 1 :: Int -> Int -- (add' 1)は(Int -> Int)型の関数を返すことが分かる Prelude> let { trimul :: Int -> (Int -> (Int -> Int)); trimul x y z = x * y * z } Prelude> ((trimul 2) 3) 4 24
上は()をつけて書いてあるが、->は右結合で、関数適用は左結合なので()は不要。
Prelude> :t add' add' :: Int -> Int -> Int Prelude> trimul 2 3 4 24
また、二つ組を取る関数をカリー化する関数が標準で用意されているので、そちらを使うともっと簡単になる。
Prelude> let { sub :: (Int, Int) -> Int; sub (x,y) = x - y } Prelude> let sub' = curry sub Prelude> :t sub' sub' :: Int -> Int -> Int Prelude> sub' 3 10 -7 Prelude> :t curry curry :: ((a, b) -> c) -> a -> b -> c
ちなみにカリー化の語源はHaskell Brooks Curry(カリー化の考案は別の人だった気がしますが)
多相型
任意の型を含む型。任意の型を含む型や式は多相型と呼ばれる。
任意の型は、小文字で始まらなければならない。通常、a,b,cを使う。
多相型の例
fst :: (a, b) -> a -- 組の1番目の要素を取り出す snd :: (a, b) -> b -- 組の2番目の要素を取り出す head :: [a] -> a -- 空でないリストの先頭要素を取り出す last :: [a] -> a -- 空でないリストの末尾要素を取り出す init :: [a] -> [a] -- 空でないリストから先頭要素を取り除く tail :: [a] -> [a] -- 空でないリストから末尾要素を取り除く take :: Int -> [a] -> [a] -- リストの先頭からn個の要素を取り出す drop :: Int -> [a] -> [a] -- リストの先頭からn個の要素を取り除く zip :: [a] -> [b] -> [(a, b)] -- 二つのリストから組のリストを作り出す id :: a -> a -- 恒等関数.引数をそのまま返す
zipの使用例
Prelude> let l1 = [1,2,3] Prelude> let l2 = [4,5] Prelude> zip l1 l2 [(1,4),(2,5)]
多重定義型
1つ以上の暮らす制約を持つ型。
(-) :: Num a => a -> a -> a -- Numクラスのインスタンスである任意の型aに対し、型a -> a -> aを持つ signum :: Num a => a -> a -- 符号反転 negate :: Num a => a -> a -- 正負の符号(正->1, 0->0, 負->-1) 3 :: Num a => a -- 数値自体も多重定義されている
基本クラス
Eq-同等クラス
==, /=で比較できる値を持つ型の集合。基本型や、要素がEqのインスタンスであるリスト、タプルは、全てEqに属す。
Prelude> 1 == 1 True Prelude> [1,2] == [1,2,3] False Prelude> ('a', False) /= ('a', True) True Prelude> "abc" == "abc" True
Ord-順序クラス
Eqのインスタンスであることに加え、値が線形的に順序つけられる型の集合。<, <=, >, >=, min, maxで比較できる。
基本型や、Ordのインスタンスを要素にもつリスト、タプルは、全てOrdに属す。文字列、リスト、タプルは辞書順で比較される。
Prelude> min "abd" "abc" "abc" Prelude> max [1,2,3] [1,3] [1,3] Prelude> 'b' > 'a' True Prelude> ('a', 2) < ('a', 3) True
Show-表示可能クラス
showメソッドを用いて値を文字列へ変換可能な型の集合。基本型や、Showのインスタンスを要素にもつリスト、タプルは、全てShowに属す。
Prelude> :t show show :: Show a => a -> String Prelude> show ('a', False) "('a',False)"
Read-読込可能クラス
readメソッドを用いて文字列から値へ変換可能な型の集合。基本型や、Readのインスタンスを要素にもつリスト、タプルは、全てReadに属す。
Prelude> :t read read :: Read a => String -> a Prelude> read "'a'"::Char 'a' Prelude> read "-1"::Int -1 Prelude> read "[1,2,3]"::[Int] [1,2,3] Prelude> read "('a',False)"::(Char,Bool) ('a',False) Prelude> read "False"::Bool False Prelude> read "False" -- 型指定がないのでエラー エラー 略 Prelude> not (read "False") -- 文脈からBoolに型推論されるのでエラーにはならない True Prelude> not (read "Hello") -- 文脈からBoolとして読み込もうとするが、Boolに変換できないためエラー *** Exception: Prelude.read: no parse
Num-数値クラス、
EqとShowのインスタンス且つ+, -, *, negate, abs, signumで処理可能な数値を値として持つ型の集合。
Numは除算のメソッドを提供していない。
Prelude> -3 + 4 1 Prelude> 6.4 *6.7 42.88 Prelude> "fasdlh" + "fds" エラー 略 Prelude> [1,2] + [3] エラー 略 Prelude> 'a' + 'b' エラー 略
プログラミングHaskell第2章(はじめの一歩)まとめ
標準ライブラリ(Prelude)
- 二項演算
除算はdiv関数を使う。通常関数は、ポーランド記号(前置記法, prefix notation)で書くが、バッククオートを入れると中置記法(infix notation)で書くことができる。
演算子の優先順位や結合の仕方は数学の慣習と同じ(累乗演算子は右結合)。
Prelude> 2 + 3 5 Prelude> 2 - 3 -1 Prelude> 2 * 3 6 Prelude> 7 `div` 2 3 Prelude> 2 ^ 3 8 Prelude> 7 `div` 0 *** Exception: divide by zero
- リストの操作
takeやdropはリストの長さ以上の値nを指定してもエラーにはならないが、負の値を指定するとエラーになる。
!!で指定したインデックスの値を返すのか..
Prelude> let xs = [1,2,3,4,5] Prelude> head xs -- 空でないリストの先頭要素を返す 1 Prelude> init xs -- 空でないリストの末尾要素を返す [1,2,3,4] Prelude> tail xs -- 空でないリストから先頭の要素を取り除いたリストを返す [2,3,4,5] Prelude> xs !! 2 -- 空でないリストn番目の要素を返す 3 Prelude> take 3 xs -- リストの先頭からn個の要素からなるリストを返す [1,2,3] Prelude> drop 3 xs -- リストの先頭からn個の要素を取り除いたリストを返す [4,5] Prelude> length xs -- リストの長さを返す 5 Prelude> sum xs -- 数値のリストの要素の総和を求める 15 Prelude> product xs -- 数値のリストの要素の積を求める 120 Prelude> [1,2,3] ++ [4,5] -- 2つのリストを連結したリストを返す [1,2,3,4,5] Prelude> reverse xs -- 逆順のリストを返す [5,4,3,2,1]
関数適用
関数適用は他の全ての演算子より優先順位が高い。
数学 | Haskell |
f(x) | f x |
f(x,y) | f x y |
f(g(x)) | f (g x) |
f(x,g(y)) | f x (g y) |
f(x)g(y) | f x * g y |
命名規則
関数、引数の名前は、小文字で始まらなければならない。それ以降は、大文字、小文字、数値、_'が使える。
シングルクォートが使えるのか... 微分とか数学的な表記をしたい場合はいいのかな。
case, class, data, default, deriving, do else, if, import, in, infix, infixl infixr, instance, let, module, newtype, of, then, type, where
Haskellの慣習として、引数がリストである場合、名前の最後にsをつける
任意の値のリストならxs, 文字のリストのリストならcssといった感じ。
レイアウト規則
あるレベルの定義は、完全に同じ絡むから始まらなければならない。行頭揃えでグループ化する。
a = b + c where b = 1 c = 2 d = a *2
{}と;を使って、明示的にグループ化することも可能。
a = b + c where { b = 1; c = 2 } d = a * 2
コメント
-- コメント {- 複数行コメント コメント1 コメント2 -}
プログラミングHaskell第1章(導入) まとめ
プログラミングHaskellでは実際のHaskellで入力する記号ではなく数学記号で記述されている。
否定とノットイコールの入力方法が慣れ親しんでるプログラミング言語とは違う。
数学記号 | 意味 | 入力方法 |
変換する | -> | |
クラス制約 | => | |
以上 | >= | |
以下 | <= | |
等しくない | /= | |
かつ | && | |
または | || | |
¬ | 否定 | not |
累乗 | ^ | |
合成 | . | |
無名関数 | \ | |
++ | 連結 | ++ |
引き出す | <- | |
順序付け | >>= | |
+++ | 選択 | +++ |
連結、選択、順序付けの記号の出し方わからなかった。。。
Haskellプログラム例1:リストの要素の総和
リストの合計を求める例。リストは[]で表現される。
はコメントアウト(これはなれないと見づらいなあ)。
xがリストの最初の要素。xsが残りの要素を示す。
= 0としているところが基底。0は加算に対して単位元だから空リストに対しては0を返すようにする。
mysum::Num a => [a] -> a -- 自動的に推論されるので書かなくても良い mysum [] = 0 mysum (x:xs) = x + mysum xs main = print $ mysum [1, 2, 3] -- sum [1..3]
通常は、標準ライブラリのsum, [..]を使えば良い。
Haskellでは、全ての関数が、型を持つ。明記しなければ、関数の型は自動的に推論される。
上の例だと、任意の数値(Num)型aに対し、型aのリストを型aの数値に変換することを示している。
Haskellプログラム例2:クイックソート
++はリストの連結を示す。
qsort [] = [] qsort (p:xs) = qsort smaller ++ [p] ++ qsort larger where smaller = [a|a <- xs, a <= p] larger = [b|b <- xs, b > p] main = print $ qsort [4,1,3,6,7,9,5,2,4,5,6,-1]
とても分かりやすい(小並感)。
javaで実装したクイックソート(http://kanetai.hatenablog.com/entry/20130226/1361895000)と比べても結構短くかける。
※ピボットの選び方や、smller, largerの分け方に違いはある。
上のhaskellの例は、リストの生成、結合をしまくるので非効率的だけど可読性は非常に高い。
この場合型は、
qsort::Ord a => [a] -> [a]
になる。順序を持つ型aに対して、型aのリストから型aのリストを返す。
従って、数値以外でも適用可能で、例えば文字のソートもできる。
main = print $ qsort "afeb"
ちなみに練習問題になってますが、<=のところを<にしたらピボットの重複要素消えますね(smaller, largerのどちらにも入らないので)。
通常、Data.listのsortで十分
import Data.List sort [4,1,3,6,7,9,5,2,4,5,6,-1]
Effective Objective-C 2.0 第1章(Objective-Cに慣れる)メモ
Effective Objective-C 2.0を買ったので、適当にまとめ。
enumの話は知らなかったけど、第1章は半分ぐらいがC/C++でも当たり前の内容。
項目1 Objective-Cのルーツを知る
ぶっちゃけなんもない。
スタック上にObjective-Cのオブジェクトを宣言できない、ヒープ上に確保する(される)。
スタックスペースを使っているようなもの(CGRectとか)を見かけるが、それは構造体とか非オブジェクト型ですよ。
動的束縛(dinamic binding)によるメッセージング構造を使っているため、型やメッセージに対する実行コードは、コンパイラではなくランタイムが判断するというような内容。
項目2 インポートされるヘッダーに含まれるヘッダーの数は最小限に抑える
- ヘッダーは可能な限り深い位置でインポートして、密結合を避ける。
ヘッダーで別のヘッダーをインポートするとクラスの相互参照問題が発生する場合があるので、クラスの先行宣言(前方宣言、前方参照)
@class myClass;
を使う。
スーパークラス、準拠しているプロトコルは、前方宣言だけでは不十分でインポートしてないと駄目。
ただし、プロトコルの場合は、ヘッダでインポートせずに実装ファイルで宣言すれば良い場合が多い。
- 準拠するプロトコルを宣言するときは、可能な限り、class extension (カテゴリ)にプロトコル準拠の宣言を移すことを検討する(前方宣言ではまずい場合がある)。そうできない場合は、プロトコルだけを定義した(小さな)ヘッダーをインポートする。
実装ファイルでプロトコル準拠の宣言をする場合は、↓こんな感じ.
@interface myClass () <myDelegate> @end
項目3 メソッドよりも同じ意味のリテラル構文を使う
int x = 1, y = 2; NSString *str = @"a"; NSNumber *intN = @1; // = [NSNumber numberWithInt:1]; NSNumber *floatN = @1.f; NSNumber *doubleN = @1.; NSNumber *boolN = @YES; NSNumber *charN = @'a'; NSNumber *exp = @(x + y); NSArray *array = @[@1, @2, @3]; // = [NSArray arrayWithObjects: @1, @2, @3, nil]; NSNumber *n = array[0]; // = [array objectAtIndex:1]; NSMutableArray *mArray = [@[@1, @2, @3] mutableCopy]; mArray[0] = @0; //[mArray replaceObjectAtIndex:0 withObject:@0]; //mArray[100] = @4 //bad access NSDictionary *dic = // = [NSDictionary dictionaryWithObjectsAndKeys: @{ @"key1" : @"value1", // @"value1", @"key1", @"key2" : @"value2", // @"value2", @"key2", @"num" : @1 }; // @1 , @"num" , nil]; n = dic[@"num"]; // = [dic objectForKey:@"num"]; NSMutableDictionary *mDic = [@{ @"num1" : @1, @"num2" : @2 } mutableCopy]; mDic[@"num1"] = @0; //[mDic setObject:@"num1" forKey:@0]; mDic[@"num3"] = @3;
式にも@()を使えばリテラル構文が使用可能。
mutableな配列や辞書はmutableCopyすればおk。無駄な(imutableな)オブジェクトが一つ作られるが可読性はこっちの方が高い。
ARC使ってないなら(mutable)copyしたときにautoreleaseを忘れずにする。
リテラル構文で配列や辞書をつくる場合は、要素にnilを含まないようにしないと下のような例外が発生する。
NSNumber *n = nil; NSArray *a; a = [NSArray arrayWithObjects:@1, @2, n, @3, nil]; //ok, but array = @[@1, @2] a = @[@1, @2, n, @3]; //error
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[1]'
ちなみにnil相当のものをNSArrayやNSDictionaryに入れたい場合は、
[NSNull null]
を使う。シングルトンなので、NSNullかどうかのチェックは==でいいはず。
項目4 プリプロセッサの#defineではなく型付き定数を使う。
#defineだと型情報がないし、再定義できてしまうから型付き定数を使いませう。
- 翻訳単位固有の定数はstatic constを使う。
グローバルシンボルテーブルに入らないから、名前空間的プレフィックスをつける必要なし。
ポインタの場合は、constの位置に注意。
NSString * const ConstStr = @"const str"; //後ろから読んでconstant pointer to an NSString //NSString const * str1 = @"str1"; だと pointer to constant NSString //const NSString * str2 = @"str2"; も pointer to constant NSString
- グローバル定数は、ヘッダーファイルでexternal(extern)宣言し、実装ファイルで定義する。定数名には名前空間的プレフィックスをつける。
項目5 状態、オプション、ステータスコードにはenumを使う
C++11にあわせて、若干の変更がある。型の指定が可能になり、そのおかげで前方宣言できるようになった。
typedef enum enum_tag : NSInteger { e1, e2, e3, }myEnum; //enum_tagは省略可能
コンパイラが選んだ型ではなく自分が選んだ型が確実に使われる。
ビット演算したい場合はNS_OPTIONSを使う。C++の場合、enumの土台の型からenum型へは明示的にキャストする必要がある。
キャストしなくても済むようにしてくれるマクロがNS_OPTIONS。
typedef NS_ENUM(NSUInteger, myEnum) { e1, e2, e3 }; typedef NS_OPTIONS(NSUInteger, myOption) { o1 = 1 << 0, o2 = 1 << 1, o3 = 1 << 2, };
ちなみにC++11では、名前を衝突させないようにenum class(scoped enumeration)が使えるようになったらしい。
enum class myEnum : uint8_t { e1, e2 }; //enum structでも同じ
参考URL
http://ramemiso.hateblo.jp/entry/2013/09/18/184145
http://d.hatena.ne.jp/spinor/20110918/1316321563
http://ja.wikipedia.org/wiki/C%2B%2B11
- enumを処理するswitch文ではdefaultを使わないようにする