Skip to content

Instantly share code, notes, and snippets.

@satoshin2071
Last active February 25, 2019 13:17
Show Gist options
  • Save satoshin2071/08daecc13c9e293e70d3 to your computer and use it in GitHub Desktop.
Save satoshin2071/08daecc13c9e293e70d3 to your computer and use it in GitHub Desktop.
CoreFoundation入門 Toll-free bridge

#CoreFoundation入門 Toll-free bridge

##概要 CoreFoundation入門 CFStringとその周辺の続き。

CoreFoundation入門 イントロで説明したFoundationとCoreFoundationの互換性を実現するToll-free bridgeの仕組みを確認していく。

##Tool-free bridgeとは

CocoaのオブジェクトをCoreFoundationから、そしてCoreFoundationのオブジェクトをCocoaから呼ぶ事ができる仕組み。

##NSStringとCFString

実はcocoaのFoundationの実装はCarbonのCoreFoudationでなされている。本当にFoundationのNSStringはCoreFoundationのCFStringということになるかを確認。

まずはclass-dumpを実行してNSCFStringで検索 (class-dumpなければbrew install class-dumpで入手できる)

% class-dump /System/Library/Frameworks/CoreFoundation.framework/CoreFoundation > ./checkFoundation.h

@interface __NSCFString : __NSCFType
{
}
//メソッドは省略
@end

文字列を扱うクラスなのにインスタンス変数の定義がひとつもない、つまり NSCFStringは何かのラッパークラスであるということが推測として立てられる。

続いてNSStringとCFStringのインスタンスを作って中身を確認。

int main(int argc, const char * argv[]) {
    NSString *str0 = @"string";
    NSString *str1 = @"string";
    CFStringRef str2 = CFSTR("string");
    NSLog(@"str0 %p, str1 %p, str2 %p", str0, str1, str2);
    return 0;
}

同じ文字列の3つのインスタンスは同じアドレスになるのでNSStringとCFStringの実体は同じだということがわかる。

##isaと_cfisa

上記のようにCoreFoundationのAPIにCocoaのオブジェクトが渡された時、これはCoreFoundationか?Cocoaか?を判別するかは __CFRuntimeBaseのcfisaとobjc_objectのisaポインタを使用している。

CFRuntime.h

typedef struct __CFRuntimeBase {
    uintptr_t _cfisa;
    uint8_t _cfinfo[4];
#if __LP64__
    uint32_t _rc;
#endif
} CFRuntimeBase;

finderでobjc.hを検索

struct objc_object {
    Class isa  OBJC_ISA_AVAILABILITY;
};

/// A pointer to an instance of a class.
typedef struct objc_object *id;

id型はobjc_object構造体のポインタだということ、objc_objectはClass型のisaをもっているということがわかる。

両方の先頭にisaポインタがあるので、Objective-Cから呼んだ時にメソッド呼び出しとしてisa先のクラスが処理をする。Cから呼ばれたときは無視する。なのでキャストし合っても問題なくアクセスできるという仕組みになっている模様。

cfisaの中身はどこで設定が行われているかについてを確認。昔はCFRuntime.cの_CFRuntimeCreateInstance関数内で行われていたようだが、現在はCFBase.cで行われていた。

CFBase.c

static void _CFAllocatorSetInstanceTypeIDAndIsa(struct __CFAllocator *memory) {
    _CFRuntimeSetInstanceTypeID(memory, __kCFAllocatorTypeID);
    memory->_base._cfisa = __CFISAForTypeID(__kCFAllocatorTypeID);
}

引数で渡されたCFAllocatorからCFRuntimeBase、cfisaと辿り、__CFISAForTypeID関数を使用して値を設定していることがわかる。

_CFAllocatorSetInstanceTypeIDAndIsa以外の関数でも同様の処理が行われているで要確認。

__CFISAforTypeIDはCFInternal.hにて確認できる

CFInternal.h
#define __CFISAForTypeID(t) (0)

##Toll-free bridgeでのObjective-Cメソッド処理の流れ

NSStringのオブジェクト作成 イコール CFStringRefが作られ、_cfisaフィールドはNSCFString__を指す。
↓
上記のCFStringRefに対してメソッドを投げると_cfisaに設定されているクラスがこのメソッドを処理する

CoreFoundationの世界とObjective-Cの世界を行き来しながら処理を行っている。注目すべきところはどちらの世界からも扱うオブジェクトは一つだということ。なので余計な変換処理が発生しないところがToll-free bridgeの利点の一つ。

##CFType

CoreFoundationのオブジェクトにはToll-free bridgeで結ばれるオブジェクトが存在しないものもある。その場合の対応を行っているのがCFTypeというクラス。ドキュメントを確認。

CFType Reference

All other Core Foundation opaque types derive from CFType. 

CFTypeを除くすべてのCoreFoundationのopaque型はCFTypeから派生している。

つまりCFTypeはルートクラスに近い存在ということになる。CFTypeの以下の関数はCoreFoundation全てのオブジェクトで使用できる。

Memory Management

  • CFGetAllocator
  • CFGetRetainCount
  • CFMakeCollectable
  • CFRelease
  • CFRetain

Determining Equality

  • CFEqual

Hashing

  • CFHash

Miscellaneous Functions

  • CFCopyDescription
  • CFCopyTypeIDDescription
  • CFGetTypeID
  • CFShow

DataTypes

  • CFHashCode
  • CFTypeID
  • CFTypeRef

CFTypeの詳細はCoreFoundation入門 メモリ管理で確認。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment