Objective-C++でクラス
JSONを使った受け渡しを想定して書いてみた。非効率かも。
#import <Foundation/Foundation.h> @class Engine; typedef NSDictionary JSON; @protocol EngineProtocol <NSObject> - (void)engine:(nonnull Engine *)engine message:(nonnull JSON *)message; @end @interface Engine : NSObject + (nonnull instancetype)engine:(nonnull id <EngineProtocol>)delegate; - (void)tellMessage:(nonnull JSON *)message; @end #include <stdio.h> #include <iostream> class EngineCpp; class EngineCpp { public: EngineCpp(); ~EngineCpp(); void hello(); typedef std::shared_ptr<EngineCpp> Ptr; std::string message; std::string proc(std::string src); }; EngineCpp::EngineCpp() { std::cout << "EngineCpp new" << std::endl; } EngineCpp::~EngineCpp() { std::cout << "EngineCpp delete" << std::endl; } void EngineCpp::hello() { std::cout << "EngineCpp hello" << std::endl; } std::string EngineCpp::proc(std::string src) { return "{\"Hello\":" + src + "}"; } @interface NSJSONSerialization(String) + (NSString *)jsonString:(NSDictionary *)dic; + (NSDictionary *)jsonString_r:(std::string &)str; @end @implementation NSJSONSerialization(String) + (NSString *)jsonString:(NSDictionary *)dic { NSData *d = [NSJSONSerialization dataWithJSONObject:dic options:NSJSONWritingPrettyPrinted error:nil]; NSString *sd = [[NSString alloc] initWithData:d encoding:NSUTF8StringEncoding]; return sd; } + (NSDictionary *)jsonString_r:(std::string &)str { NSData *data = [NSData dataWithBytes:str.c_str() length:str.length()]; NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil]; return dic; } @end @interface Engine() @property (nonatomic) id <EngineProtocol> delegate; @property (nonatomic, assign) EngineCpp::Ptr engineCpp; @end @implementation Engine { } - (void)dealloc { NSLog(@"Engine dealloc"); } + (instancetype)engine:(id<EngineProtocol>)delegate { Engine *engine = [Engine new]; engine.delegate = delegate; engine.engineCpp = std::make_shared<EngineCpp>(); return engine; } - (void)tellMessage:(JSON *)message { std::string str = [NSJSONSerialization jsonString:message].UTF8String; std::string result = self.engineCpp->proc(str); NSDictionary *dic = [NSJSONSerialization jsonString_r:result]; [self.delegate engine:self message:dic]; } @end
リーダー選出アルゴリズム
リーダー選出のアルゴリズム
後続(successor)がダウンしている場合、飛ばして次のプロセスにElectionメッセージを送信する。
このメッセージには自分のプロセス番号をつける。
後続者は次々と自分の後続者にElectionメッセージを自分のプロセス番号を追加してまわしていく。
一周してElectionメッセージがもどってきたら(メッセージに自分のプロセスIDが入っているかどうかで判断する)、メッセージのタイプをCoordinatorに変更、これを回覧する。
すべてのプロセス番号がそろっているのでCoordinatorが誰かがわかる。
結果、2周する。
EventManager
登録方法は2種類。
EventListenerInterfaceを実装してonで自身を登録するか、onでキーとともに無名関数を登録しておくか。
Eventを作ってdispatchすることも可能。
登録したものの削除はoffでできる。
使いすぎて密結合に注意。
一文字づつ区切る
NSStringEnumerationByComposedCharacterSequencesを用いる。
NSString *string= @"ハローワールド🗾"; NSMutableArray *list = [NSMutableArray array]; [string enumerateSubstringsInRange:NSMakeRange(0,string.length) options:NSStringEnumerationByComposedCharacterSequences usingBlock:^(NSString * _Nullable substring, NSRange substringRange, NSRange enclosingRange, BOOL * _Nonnull stop) { [list addObject:substring]; }]; for (NSString *sub in list) { NSLog(@"%@", sub); for (int i=0;i<sub.length;i++) { NSLog(@"%04x", [sub characterAtIndex:i]); } }
Unicode block
Basic Latin
UTF8では1バイトで表現される。
Basic Latin (Unicode block) - Wikipedia
Latin-1 Supplement
U+0080–U+00FF
Halfwidth and fullwidth forms
Halfwidth and fullwidth forms - Wikipedia
U+FF00–FFEF
- U+FF01–FF5EはASCIIの全角
- U+FF65–FFDCはカタカナ、ハングルの半角
Hello World 4回目
アプリ作る。cakephp 3.5
composer create-project --prefer-dist cakephp/app MyApp2017
中に入る
cd MyApp2017/
sqlite3のdb作成
sqlite3 MyApp2017.db
sqlite> .tables sqlite> .q
コンフィグでデータベースをsqlite3に。atomは好きなエディタで。
atom ./config/app.php
'Datasources' => [ 'default' => [ 'className' => 'Cake\Database\Connection', 'driver' => 'Cake\Database\Driver\Sqlite', 'persistent' => false, 'database' => 'MyApp2017.db', 'encoding' => 'utf8', 'timezone' => 'UTC', 'flags' => [], 'cacheMetadata' => true, 'log' => false,
ここでサーバを立ち上げて確認する。
./bin/cake server
http://localhost:8765/にアクセス。databaseにつながっていることを確認。
ここでDBのテーブルを考える。
ログインしたユーザが書き込める掲示板という発想。
ユーザーテーブルを作ってみる。
./bin/cake bake migration CreateUsers name:string description:text created modified
/config/Migrationsフォルダに20170911144034_CreateUsers.phpファイルができる。
public function change() { $table = $this->table('users'); $table->addColumn('name', 'string', [ 'default' => null, 'limit' => 255, 'null' => false, ]); $table->addColumn('description', 'text', [ 'default' => null, 'null' => false, ]); $table->addColumn('created', 'datetime', [ 'default' => null, 'null' => false, ]); $table->addColumn('modified', 'datetime', [ 'default' => null, 'null' => false, ]); $table->create(); }
実際に作るには
./bin/cake migrations migrate
確認する。
sqlite3 MyApp2017.db .tables .schema
たしかにusersができている。idというautoincrementな主キーは自動で追加されるようだ。
次にUsersのモデル(テーブルとエンティティ)を作る。
./bin/cake bake model Users
これで
src/Model/Table/UsersTable.php
src/Model/Entity/User.php
ができた。
class UsersTable extends Table { /** * Initialize method * * @param array $config The configuration for the Table. * @return void */ public function initialize(array $config) { parent::initialize($config); $this->setTable('users'); $this->setDisplayField('name'); $this->setPrimaryKey('id'); $this->addBehavior('Timestamp'); } /** * Default validation rules. * * @param \Cake\Validation\Validator $validator Validator instance. * @return \Cake\Validation\Validator */ public function validationDefault(Validator $validator) { $validator ->integer('id') ->allowEmpty('id', 'create'); $validator ->scalar('name') ->allowEmpty('name'); $validator ->scalar('description') ->allowEmpty('description'); return $validator; } }
class User extends Entity { /** * Fields that can be mass assigned using newEntity() or patchEntity(). * * Note that when '*' is set to true, this allows all unspecified fields to * be mass assigned. For security purposes, it is advised to set '*' to false * (or remove it), and explicitly make individual fields accessible as needed. * * @var array */ protected $_accessible = [ '*' => true, 'id' => false ]; }
調子に乗ってユーザーコントローラを作る。
./bin/cake bake controller Users
これで
/src/Controller/UsersController.php ができた。
<?php namespace App\Controller; use App\Controller\AppController; /** * Users Controller * * @property \App\Model\Table\UsersTable $Users * * @method \App\Model\Entity\User[] paginate($object = null, array $settings = []) */ class UsersController extends AppController { /** * Index method * * @return \Cake\Http\Response|void */ public function index() { $users = $this->paginate($this->Users); $this->set(compact('users')); $this->set('_serialize', ['users']); } /** * View method * * @param string|null $id User id. * @return \Cake\Http\Response|void * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found. */ public function view($id = null) { $user = $this->Users->get($id, [ 'contain' => [] ]); $this->set('user', $user); $this->set('_serialize', ['user']); } /** * Add method * * @return \Cake\Http\Response|null Redirects on successful add, renders view otherwise. */ public function add() { $user = $this->Users->newEntity(); if ($this->request->is('post')) { $user = $this->Users->patchEntity($user, $this->request->getData()); if ($this->Users->save($user)) { $this->Flash->success(__('The user has been saved.')); return $this->redirect(['action' => 'index']); } $this->Flash->error(__('The user could not be saved. Please, try again.')); } $this->set(compact('user')); $this->set('_serialize', ['user']); } /** * Edit method * * @param string|null $id User id. * @return \Cake\Http\Response|null Redirects on successful edit, renders view otherwise. * @throws \Cake\Network\Exception\NotFoundException When record not found. */ public function edit($id = null) { $user = $this->Users->get($id, [ 'contain' => [] ]); if ($this->request->is(['patch', 'post', 'put'])) { $user = $this->Users->patchEntity($user, $this->request->getData()); if ($this->Users->save($user)) { $this->Flash->success(__('The user has been saved.')); return $this->redirect(['action' => 'index']); } $this->Flash->error(__('The user could not be saved. Please, try again.')); } $this->set(compact('user')); $this->set('_serialize', ['user']); } /** * Delete method * * @param string|null $id User id. * @return \Cake\Http\Response|null Redirects to index. * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found. */ public function delete($id = null) { $this->request->allowMethod(['post', 'delete']); $user = $this->Users->get($id); if ($this->Users->delete($user)) { $this->Flash->success(__('The user has been deleted.')); } else { $this->Flash->error(__('The user could not be deleted. Please, try again.')); } return $this->redirect(['action' => 'index']); } }
最後にtemplateをbakeする。
./bin/cake bake template Users
src/Template/Users/index.ctp
src/Template/Users/view.ctp
src/Template/Users/add.ctp
src/Template/Users/edit.ctp
が生成された。
このあたりでもう一度サーバーを立ち上げみてみる。
http://localhost:8765/users にアクセスするとユーザー一覧が出てくる。
続く。
型を消す
親クラスはジェネリクスをもたない。メソッドがジェネリクスになっている。
子クラスはジェネリクスになっている。
書いてみた。実行時エラーになるのはいやだ。。
import Foundation class A { let b:B init<T>(_ v:T) { self.b = C(v) } /// 問題点 なんでも良いことになってしまっている。 func get<T>() -> T { return b.get() } } class B { func get<T>()->T { let this = self as! C<T> return this.get() } } class C<T> : B { let v:T init(_ v:T) { self.v = v } func get()->T { return v } } func sample() { let a = A("Hello") let b = A(3) let c:String = a.b.get() let d:Int = b.b.get() let e:Int = a.b.get() // 実行時エラーになる。。orz }
メソッド交換
デバッグではまったのでメソッドの交換を試してみた。以下はそのコード。
extension CLLocationManager { func my_requestWhenInUseAuthorization() { print("requestWhenInUseAuthorization") } func my_requestAlwaysAuthorization() { print("requestAlwaysAuthorization") } func switchReq() { sw(from:#selector(requestAlwaysAuthorization), to:#selector(my_requestWhenInUseAuthorization)) sw(from:#selector(requestWhenInUseAuthorization), to:#selector(my_requestWhenInUseAuthorization)) } func sw(from:Selector, to:Selector) { let method = class_getInstanceMethod(CLLocationManager.self, from) let method2 = class_getInstanceMethod(CLLocationManager.self, to) method_exchangeImplementations(method, method2) } }
framework作成
フレームワーク作成
1. CocoaTouch frameworkを作る。Objective-C, 名前はSampleと仮定。
2. クラスを作成。(MyServiceとする。)
3. 公開するヘッダーをProjectのTargetsのBuild PhasesのHeaderにHeadersのPublicに加える。
4. Sample.h(アンブレラヘッダー)にそのpublic headerをimport (#import
他のプロジェクト(Swift想定)から使う。
ここではプロジェクトに依存関係を作り、フレームワークもビルドしつつ使用することを仮定。
1. プロジェクトのtarget依存性にフレームワークのプロジェクト追加 → 違) embeded projectかも。
2. フレームワーク追加(これは必要?)
3. Objective-Cのフレームワークなのでブリッジヘッダーを作る。
4. ブリッジヘッダーでimport (#import
5. swiftファイルで自由に使えるようになる。
Swiftの場合、クラスなどにpublicをつけておかないとみえない。ファイルにて適宜importして使うことになる。
lipo 複数アーキテクチャのライブラリをまとめる
iOSシミュレータ用と実機用の2つのスタティックライブラリを一つにまとめる。
lipo -output sample.a -create ./Debug-iphoneos/libSample.a ./Debug-iphonesimulator/libSample.a
labs.torques.jp
daybysay.hatenablog.com
結合後、ファイルタイプを見る
file sample.a sample.a: Mach-O universal binary with 2 architectures: [x86_64: current ar archive random library] [arm64: current ar archive random library] sample.a (for architecture x86_64): current ar archive random library sample.a (for architecture arm64): current ar archive random library