Cocoa練習帳 -7ページ目

[tvOS]プロジェクトの内容

『tvOS for Developers』の情報から、tvOS用のXcodeプロジェクトの内容について探ってみよう。

DemoBotsというサンプルコードは、OSXとiOS、tvOSのプロジェクトが収められているので、tvOS固有の内容が分かりやすいのではないかと判断。

proj

当たり前だが画面の方向関連の項目がない。

file

ファイルの種類は、iOSと似ている。

複数のStoryboardがある場合、どうしてtvOSだと分かるのだろうか。

target

ターゲットに関連付けられているものということだった。

関連情報
Cocoa Advent Calendar 2015
tvOS for Developers

【Cocoa練習帳】
http://www.bitz.co.jp/weblog/
http://ameblo.jp/bitz/(ミラー・サイト)

[OSX][iOS]Objective-C

Objective-C 2.0と呼ばれるものから、大幅に機能が増えたが、当初のObjective-Cは簡素で、C言語をマスターしたプログラマーなら30分程度でマスターできる言語だった。ただし、言語の仕様を簡素というだけで、Cocoaフレームワークを使いこなせるようになるには、当初から難しかったが。

今回の投稿では、初期のCocoaでのObjective-Cについて説明する。これによって、基本的にとても簡素な仕様の言語だとうことが分かると思うので。

クラス定義は、以下の通り。通常、ヘッダーファイルに記述される。

#import <Cococa/Cocoa.h>
 
@interface Song : NSObject {
    NSString *title;
}
 
+ (id)sharedInstance;
- (id)init;
- (void)dealloc;
- (NSString *)title;
- (void)setTitle:(NSString *)aTitle;
@end

クラスは必ずNSObjectまたは、その子クラスを継承する必要がある。

クラスの実装は、以下の通り。通常、サフィックスが.mのファイルに記述される。

#import "Song.h"
 
@implementation Song
 
+ (id)sharedInstance
{
    static Song* song = nil;
    if (! song) {
        song = [[Song alloc] init];
    }
    return song;
}
 
- (id)init
{
    if (self = [super init]) {
        title = nil;
    }
    return self;
}
 
- (void)dealloc
{
    if (title) {
        [title release];
        title = nil;
    }
    [super dealloc];
}
 
- (NSString *)title
{
    return title;
}
 
- (void)setTitle:(NSString *)aTitle
{
    [title autorelease];
    title = [aTitle retain];
}
 
@end

+で始まるのはクラスメソッド。-で始まるのはインスタンスメソッド。親クラスのインスタンスはselfとなる。

既存のクラスはカテゴリーを使って拡張できる。

#import "Song.h"
 
@interface Song (Album)
 
- (NSString *)albumTitle;
 
@end
#import "Album.h"
 
@implementation Song (Album)
 
- (NSString *)albumTitle
{
    return @"Trout Mask Replica";
}
 
@end

多重継承に対応しない代わりに、プロトコルという機能を用意している。

@protocol Playable
- (void)play;
- (void)stop;
@end

Songクラスがこれに対応してみる。

#import <Cococa/Cocoa.h>
#import "Playable.h"
 
@interface Song : NSObject <Playable> {
    NSString *title;
}
 
- (void)play;
- (void)stop;


呼び出し側はプロトコルに対応しているか判定することができる。

if ([song conformsTo:@protocol(Playable)]) {
    [song play];
}
else {
}
関連情報 Cocoa Advent Calendar 2015
Cocoa in a Nutshell: A Desktop Quick Reference
【Cocoa練習帳】 http://www.bitz.co.jp/weblog/
http://ameblo.jp/bitz/(ミラー・サイト)

[OSX][iOS]Cocoaとは何ぞや

Apple Lisaコンピュータのアプリケーションは、Lisa Toolkitというライブラリを使って開発され、このToolkitライブラリはClascalと呼ばれるPascal言語を拡張したオブジェクト指向言語で書かれていたそうだ。

『オブジェクト指向プログラミング for the Macintosh』によると、Clascalのコードは以下のようになる。

{TViewクラスの子クラス}
TQuadView = SUBCLASS OF TView
    {インスタンス変数}
    window:TQuadWindow;
    {作成メソッド}
    FUNCTION TQuadView.CREATE(object:TObject;heap:THead;itsExtent:LRect;itsPanel:TPanel):TQuadView;
    {選択メソッド}
    PROCEDURE TQuadView.MousePress(mouseLPt:LPoint);OVERRIDE;
    FUNCTION TQuad TQuadView.NewSelection(quad:TQuad):TQuadSelection;ABSTRACT;
END;{TQuadView}

Pascal派生の言語ということでC言語とは異なる部分があるが、iOSしか知らなくてもコードの内容が理解できると思うし、Swiftなど、逆に近づいているように感じる部分があって面白いと思ったが、どうだろうか?

このLisa ToolkitとClascalは、Macintoshの開発環境に影響を与えている。MacintoshのAPIはオブジェクト指向から手続き型のToolboxライブラリになっているが、MacAppクラスライブラリが用意され、このMacAppはObject Pascalというオブジェクト指向プログラミング言語で記述されていた。

こちらもコードを『オブジェクト指向プログラミング for the Macintosh』から引用してみる。

TYPE TFoo = OBJECT(TSnark)
    fFirstObject: TEmployee;
    fNumber: INTEGER;
    PROCEDURE TFoo.IFoo;
    PROCEDURE TFoo.Free;OVERRIDE;
END;
 
PROCEDURE TFoo.IFoo;
VAR tempObject:TEmployee;
BEGIN
    SELF.ISnark;
    NEW(tempObject);
    tempObject.IEmployee;
    SELF.fFirstObject:=tempObject;
END;
 
PROCEDURE TFoo.Free;
BEGIN
    FreeObject(SELF.fFirstObject);
    INHEFITED Free;
END;

Lisaコンピュータが発売されたのが1983年。Macintoshコンピュータが1984年。MacAppクラスライブラリは1985年。NeXT創業が1985年。NeXTワークステーションとNeXTSTEPオペレーティングシステムが発表されたのが1988年。

アプリケーションは、Application Kitソフトウェアキットを利用して開発され、Objective-Cで記述されていた。『Xウィンドウとその仲間たち』によると以前のクラス名は頭にNXが付いていて、その後のNSと異なっている。NeXTSTEPからOPENSTEPに変わる際に変わったのかな?(NSはNeXT Softwareの略と聞いたことがある)

C++でなくObjective-Cが選択された理由の一つに、当時、C++は研究室で利用されるもので、実際の現場で利用可能になっていたのはObjective-Cだったからという話を聞いたことがあるが、今となってみれば、C++でなくてよかったのではないか!?

『オブジェクト指向プログラミング for the Macintosh』に古い形式のObjective-Cのコードが掲載されていたので引用する。

= Point:Object (Geometry, Primitive) { short, xLoc, yLoc; }
+ x:(int)anX y:(int)aY {
    id myNewPoint = [self new];
    [myNewPoint x:anX];
    [myNewPoint y:aY];
    return myNewPoint;
}
 
- x:(int)anX { xLoc = anX; return self; }
- y:(int)aY { yLoc = aY; return self; }
=:

1996年、NeXTはAppleに買収された。OPENSTEPはRhapsodyというコードネームでMacintoshに移植された。RhapsodyにはMatintosh ToolboxをエミュレーションするBlue Boxと、OPENSTEPのYellow Boxで構成されていた。

RhapsodyはMac OS X Server 1.0としてリリースされたのち、2001年にMac OS Xとしてリリースされ、v10.8 (Mountain Lion) からMacが取れてOS Xとなった。そして、RhapsodyのBlue BoxはCarbonに、Yellow BoxはCocoaという名前となり、現在でも、OS XやiOS、watchOS、tvOSの主要なフレームワークとなっている。

関連情報
Cocoa Advent Calendar 2015
Kurt J. Schmucker(1986)『オブジェクト指向プログラミング for the Macintosh [上巻]』大谷和利訳, 日本ソフトバンク.
Kurt J. Schmucker(1986)『オブジェクト指向プログラミング for the Macintosh [下巻]』大谷和利訳, 日本ソフトバンク.
Xウィンドウとその仲間たち
【Cocoa練習帳】 http://www.bitz.co.jp/weblog/
http://ameblo.jp/bitz/(ミラー・サイト)

Cocoa Advent Calendar 2015

CocoaフレームワークのAdvent Calendarを作成しました。

Cocoa Advent Calendar 2015

興味がある方は、どうぞ参加してください!

【Cocoa練習帳】
http://www.bitz.co.jp/weblog/
http://ameblo.jp/bitz/(ミラー・サイト)

[OSX][OSX]Swift標準入出力

スクリプト言語の代わりにcommand-line環境でSwiftを利用するため、引数の取得や標準入出力について調べてみた。

引数の取得は以下の通り。

let args = Process.arguments
for arg in args {
	
}

標準入力からreturnされるまでを読み込むのは以下の通り。

let standardInput = NSFileHandle.fileHandleWithStandardInput()
let input = standardInput.availableData
let datastring = String(NSString(data:input, encoding:NSUTF8StringEncoding))
print(datastring)

return単位で読み込み続ける場合は、以下となる。

let standardInput = NSFileHandle.fileHandleWithStandardInput()
while true {
	
	
	
	
}

上記は、空行が来るとループを抜ける。

標準出力への印字は以下の通り。

let standardOutput = NSFileHandle.fileHandleWithStandardOutput()
let outputString: NSString = "hello\n"
let dataout: NSData? = outputString.dataUsingEncoding(NSUTF8StringEncoding)
if let dout = dataout {
	
}

同じ文字列を標準エラー出力に印字する場合。

let standardError = NSFileHandle.fileHandleWithStandardError()
if let dout = dataout {
	
}

後で流用できるよう、これらを全部つなげた完全なスクリプトを載せる。

#!/usr/bin/env xcrun swift
 
import Foundation
 
let args = Process.arguments
for arg in args {
	
}
 
let standardInput = NSFileHandle.fileHandleWithStandardInput()
//while true {
	
	
	
	
//}
 
let standardOutput = NSFileHandle.fileHandleWithStandardOutput()
let outputString: NSString = "hello\n"
let dataout: NSData? = outputString.dataUsingEncoding(NSUTF8StringEncoding)
if let dout = dataout {
	
}
 
let standardError = NSFileHandle.fileHandleWithStandardError()
if let dout = dataout {
	
}
 
/* End Of File */
関連情報 Swift Blog
Swift Programming Series (iBooks Store)
The Swift Programming Language
Swift Standard Library Reference
Start Developing iOS Apps
Using Swift with Cocoa and Objective-C
【Cocoa練習帳】 http://www.bitz.co.jp/weblog/
http://ameblo.jp/bitz/(ミラー・サイト)

[Android]logcat

NDKの利用などの理由で、統合環境を利用しないでAndroidの開発を行わざるおえないことがある。その際、困るのがデバッグログの確認。自分は以下のシャルスクリプトでデバッグログの表示と保存を行っている。

#!/bin/sh
 
today=`date +%Y%m%d`
 
adb logcat -v time | tee -a ${today}.txt
 
# End Of File

ログは実行した日付のログに出力され、停止し実行した場合も、既存のログに追加される形で記録される。

関連情報 Android Developers
【Cocoa練習帳】 http://www.bitz.co.jp/weblog/
http://ameblo.jp/bitz/(ミラー・サイト)

[OSX]Gitのブランチ名をプロンプトに表示する

ターミナルでGitを操作する際、現在のブランチ名が分かると嬉しい。著者は、以下の方法でプロンプトにブランチ名を表示させるようにしている。

Xcodeとは別に、個別にコマンドライン・ツールを入手し、インストールする。
https://developer.apple.com/downloads/

.bashrcに以下の記述を追加する。

# Git
 
if [ -f /Library/Developer/CommandLineTools/usr/share/git-core/git-completion.bash ]; then
	
fi
 
if [ -f /Library/Developer/CommandLineTools/usr/share/git-core/git-prompt.sh ]; then
	
fi
 
GIT_PS1_SHOWDIRTYSTATE=true
GIT_PS1_SHOWUNTRACKEDFILES=true
GIT_PS1_SHOWSTASHSTATE=true
GIT_PS1_SHOWUPSTREAM=auto
 
PS1="\h:\W \u\$(__git_ps1)$ "
 
# End Of File

.bash_profileに以下の記述を追加する。

if [ -f ~/.bashrc ]; then
	
fi

すると、プロンプトにブランチ名が表示されるようになる。

macintosh:workspace who (master *%=)$ 
関連情報 Shell Scripting Primer
Bourne Shell自習テキスト
Bourne Shell自習テキスト
【Cocoa練習帳】 http://www.bitz.co.jp/weblog/
http://ameblo.jp/bitz/(ミラー・サイト)

[iOS]Connector/ResponseParserパターンの改良

ずっと利用し続けている『iOS開発におけるパターンによるオートマティズム』のConnector/ResponseParserパターンに独自の改良を施したので説明する。説明の材料となるとは、以前から公開していたRFCViewerだ。


Connector/ResponseParserパターンで課題と考えていたのは、ResponseParserの種類が増えるたびにConnectorに手を加えないといけないことと、Connectorに記述するResponseParser関連のコードは、似た内容になるのだが、共通化出来ないことだ。


ResponseParserは多種多様になるので、今回の改良が全ての場合に適用できるかは、その状況にならないと分からないが、今回、以下の方法で共通化してみた。


ResponseParserの共通の定義をプロトコルとし、ConnecterはResponseParserプロトコルを扱うようにする。ResponseParserプロトコルの定義は以下のとおり。


@protocol ResponseParserProtocol;
 
#define kResponseParserNoError       0
#define kResponseParserGenericError  1
 
typedef enum _ResponseParserNetworkState {
    ResponseParserNetworkSateNotConnected = 0,
    ResponseParserNetworkSateInProgress,
    ResponseParserNetworkSateFinished,
    ResponseParserNetworkSateError,
    ResponseParserNetworkSateCanceled,
} ResponseParserNetworkSate;
 
typedef void (^ResponseParserCompletionHandler)(id<ResponseParserProtocol> parser);
 
@protocol ResponseParserDelegate <NSObject>
- (void)parser:(id<ResponseParserProtocol>)parser didReceiveResponse:(NSURLResponse*)response;
- (void)parser:(id<ResponseParserProtocol>)parser didReceiveData:(NSData *)data;
- (void)parserDidFinishLoading:(id<ResponseParserProtocol>)parser;
- (void)parser:(id<ResponseParserProtocol>)parser didFailWithError:(NSError*)error;
- (void)parserDidCancel:(id<ResponseParserProtocol>)parser;
@end
 
@protocol ResponseParserProtocol <NSObject>
 
@property (assign, readonly, nonatomic) ResponseParserNetworkSate   networkState;
@property (strong, nonatomic) NSError                               *error;
@property (strong, nonatomic) NSOperationQueue                      *queue;
@property (weak, nonatomic) id<ResponseParserDelegate>              delegate;
@property (copy, nonatomic) ResponseParserCompletionHandler         completionHandler;
 
- (void)parse;
- (void)cancel;
 
@end

Connecterの要求メソッドも、要求の種類を辞書型にすることによって、共通化できた。


extern NSString * const ConnectorDidBegin;
extern NSString * const ConnectorInProgress;
extern NSString * const ConnectorDidFinish;
extern NSString * const ConnectorParser;
extern NSString * const ConnectorParsers;
extern NSString * const ConnectorNetworkAccessing;
 
extern NSString * const ConnectorRequestTypeKey;
extern NSString * const ConnectorRequestTypeRFCIndex;
extern NSString * const ConnectorRequestTypeRFC;
 
extern NSString * const ConnectorRFCIndexKey;
 
@interface Connector : NSObject
 
@property (assign, readonly, nonatomic, getter=isNetworkAccessing) BOOL networkAccessing;
 
+ (Connector *)sharedConnector;
- (void)requestWithParams:(NSDictionary *)params completionHandler:(ResponseParserCompletionHandler)completionHandler;
- (void)cancelWithResponseParser:(id<ResponseParserProtocol>)aParser;
- (void)cancelWithParams:(NSDictionary *)params;
- (void)cancelAll;
 
@end

ただ、ConnectorでResponseParserを生成するメソッドの共通化は無理で、ここだけ、ResponseParserの種類が増えるたびに修正という事になる。


- (id<ResponseParserProtocol>)_parserWithParams:(NSDictionary *)params
                                          queue:(NSOperationQueue *)queue
                                       delegate:(id<ResponseParserDelegate>)delegate
                              completionHandler:(ResponseParserCompletionHandler)completionHandler
{
    id<ResponseParserProtocol> parser = nil;
    
    if (params) {
        NSString    *request = params[ConnectorRequestTypeKey];
        if (request && [request isEqualToString:ConnectorRequestTypeRFCIndex]) {
            RFCResponseParser *rfcResponseParser = [[RFCResponseParser alloc] init];
            rfcResponseParser.index = 0;
            rfcResponseParser.queue = self.queue;
            rfcResponseParser.delegate = self;
            rfcResponseParser.completionHandler = completionHandler;
            parser = rfcResponseParser;
        }
        else if (request && [request isEqualToString:ConnectorRequestTypeRFC]) {
            RFCResponseParser *rfcResponseParser = [[RFCResponseParser alloc] init];
            NSUInteger  index = 1;
            NSNumber    *indexNumber = params[ConnectorRFCIndexKey];
            if (indexNumber && [indexNumber isKindOfClass:[NSNumber class]]) {
                index = indexNumber.unsignedIntegerValue;
            }
            rfcResponseParser.index = index;
            rfcResponseParser.queue = self.queue;
            rfcResponseParser.delegate = self;
            rfcResponseParser.completionHandler = completionHandler;
            parser = rfcResponseParser;
        }
    }
    return parser;
}

ソースコード
GitHubからどうぞ。

関連情報
【Cocoa練習帳】
http://ameblo.jp/bitz/(ミラー・サイト)

[OSX]PHPビルトインウェブサーバ

テスト用にWebサイトとして公開したいディレックトリで、以下のコマンドを実行する。

$ php -S localhost:8000
【Cocoa練習帳】 http://www.bitz.co.jp/weblog/
http://ameblo.jp/bitz/(ミラー・サイト)

[OSX][iOS]ゲーム開発(フレームワーク)

OSX/iOSでゲームを開発するとなると、どんなフレームワークを使うのか思いつくものをあげてみる。

  • NSView/UIView
  • SpriteKit
  • SceneKit
  • cocos2d-x
  • Unity

Android版も開発となると、後ろ二つのcocos2d-xとUnityという話も出てくるが、ネイティブ部の開発にそれなりの知識が必要という課題もあるので、何を選択するのかが難しい。

今回は、特定のフレームワークの利用に偏らず、WWDC2015でもGameplayKitというフレームワークから独立したゲーム開発が楽になるフレームワークが発表されたということもあり、ゲーム開発に関係することについて調べてことを発表したいと思う。

関連情報
GameplayKit Programming Guide
Introducing GameplayKit

【Cocoa練習帳】
http://www.bitz.co.jp/weblog/
http://ameblo.jp/bitz/(ミラー・サイト)