ARCでUIButtonをタップするとEXC_BAD_ACCESSとなる問題

 プログラミング  Comments Off on ARCでUIButtonをタップするとEXC_BAD_ACCESSとなる問題
Jul 292012
 

Objective-CでARCをONにした状態で、UIButtonを設置。ボタンをタップするとメソッドが呼び出されずEXC_BAD_ACCESSとなる場合の解決法。Xcodeの問題でもiOSの問題でもない。ARCが主流になると新たな問題も出て来る一例。

基本の設置コード

ボタンを画面に一つ貼付けるコード。タップするとログを出す。

UIViewControllerのインスタンスを生成し、viewControllerのviewをwindowに貼付ける。viewControllerではviewDidLoadの時点でUIButtonのインスタンスをviewに貼付ける。

FTAppDelegate.m

#import "FTButtonViewController.h"
@implementation FTAppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.
    FTButtonViewController *c = [[FTButtonViewController alloc] init];
    [self.window addSubview:c.view];
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    return YES;
}

FTButtonViewController.m

- (void)tapped:(id)sender
{
    NSLog(@"thx tapped");
}
- (void)viewDidLoad
{
    [super viewDidLoad];
	// Do any additional setup after loading the view.
    UIButton * button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [button setFrame:CGRectMake(50, 100, 100, 20)];
    [button addTarget:self action:@selector(tapped:) forControlEvents:UIControlEventTouchUpInside];
    [button setTitle:@"hello" forState:UIControlStateNormal];
    [self.view addSubview:button];
}

このボタンをタップするとメソッドが呼び出されずEXC_BAD_ACCESSとなる。ARCでは、この例の場合AppDelegateでviewはaddSubViewされているためretainCountは増えているが、viewControllerをstrong(retain)参照するプロパティが存在しない上、代入されていない。既にメモリ解放されている。

解決法

viewControllerがメモリから解放されないようにstrong(retain)参照(強い参照)を作成しておく。

#import "FTButtonViewController.h"
@implementation FTAppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.
    FTButtonViewController *c = [[FTButtonViewController alloc] init];
    [self.window addSubview:c.view];
    self.controller = c;//UIViewControllerをstrong(retain)参照するメンバ
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    return YES;
}

参考サイト

http://stackoverflow.com/questions/9103705/uibutton-touches-up-ibaction-causing-exc-bad-access-with-arc

 Posted by at 15:43
Jul 292012
 

iPhoneの画面の様々な高さのポイント数を一覧できるシート(Cheatsheet)の紹介。ステータスバーを含まないビューの高さ。ナビゲーションバーの高さ。タブの高さ、ツールバーの高さ等。プログラムで取得する方法と定数のまとめPDFファイル。

iosのビューの高さ一覧

iPhone用の画面サイズの様々な高さが収録されています。UIKeyboard(キーボード)の高さ等。A4で印刷するとちょうどいいです。

ダウンロード(PDFファイル)は以下。

iOSの画面サイズ一覧図解PDF

コピー印刷改変は自由です。

 Posted by at 03:21
Jul 092012
 

初期化に使われるイニシャライザー(initializer)のinitWithCoderとinitWithNibNameの違い。initからloadViewまでUIViewControllerの初期化から表示されるまでのそれぞれのメソッドの動作まとめ。nibを使う場合と使わない場合の違いを図解で解説。

iOS初期化メソッド(イニシャライザー)の図解

initWithNibName: bundle:

  • UIViewControllerのためのイニシャライザーである。
  • コードでUIViewControllerを作成する時に呼び出されるイニシャライザーである。
  • nibをロードできるようUIViewControllerを設定するイニシャライザーである。
このメソッドが呼ばれた時点では…
  • outletやactionは設定されていない状態
viewにアクセスするとnibをロードし、outletやactionの設定が開始される。

initWithCoder:

  • アーカイブオブジェクトのためのイニシャライザーである。
  • nibからオブジェクトをロードする時に呼び出されるイニシャライザーである。
  • nibの中に保存されたオブジェクトはアーカイブオブジェクトである。

このメソッドが呼ばれた時点では…

  • nibからオブジェクトが取り出されている(unarchive/deserialize)過程にある
  • outletやactionは設定されていない状態
  • UIViewControllerのcontextの中で、nibからUIViewControllerが生成される

initやユーザ定義の(initWith)イニシャライザー

  • nibを使わずコードのみでUIViewControllerを生成する場合に使う。

その他viewのライフサイクルで登場するメソッド

loadView

  • nibを使わない場合はオーバーライドする。
  • viewプロパティに指定するオブジェクトをここで生成する。
  • nibを使う場合はこのメソッドを利用しない。
- (void)loadView
{
    MyUIView *rootView = [[MyUIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.view = rootView;
}

viewDidLoad

  • viewが設定されている。
  • outletやactionの接続が設定されている。
  • nibを使う場合はnibが読み込まれた後に呼び出される。
  • nibを使わない場合はここでsubviewの設定をする。
  • (メモリの問題で)viewが解放されている場合、再度呼ばれる。

viewWillAppear

  • 画面の表示される直前に呼ばれる。
  • モーダル等のアニメが始まる前である。
  • (入れ替えの場合)他のUIViewControllerのviewWillDisappearが実行された後である。

viewDidAppear

  • 画面の表示が行われた直後に呼ばれる。
  • モーダル等のアニメが終わった後である。
  • 実際のframeサイズとなる。
 Posted by at 04:48

UIView の drawRect と setNeedsDisplay の関係

 プログラミング  Comments Off on UIView の drawRect と setNeedsDisplay の関係
Jul 092012
 

Objective-CでUIViewを使う場合、drawRect:とsetNeedsDisplay等の描画に関係するメソッドがある。関係のまとめ。手始めにUIViewControllerのライフサイクル、init初期化メソッド(initializer)からloadViewへと順に考えていく。

ライフサイクルの流れ(表示まで)

  1. init(initWith)
  2. loadView
  3. viewDidLoad
  4. viewWillAppear
  5. viewDidAppear
各メソッドの詳細のまとめ記事(http://blog.f60k.com/initwithcoder_…hnibnamebundle/)

drawRect

システムにおいて、ビジュアルの画面描画は負荷の高い処理となる。再描画を最小限にすることが表示を高速化するコツとなる。

いつどのようにdrawRectは呼ばれるか?

通常、UIViewはCALayerのdelegateであり、アニメーションなど表示更新のタイミングでdelegateメソッドを呼び出し、UIViewに実装されているdelegateメソッドdrawLayer:inContext:メソッドが呼び出される。drawLayer:inContext:メソッドは、デフォルトでdrawRectを呼び出す。つまり、ユーザ定義でサブクラス内でdrawLayer:inContext:メソッドを実装していない場合、単純にdrawRectが呼ばれる。

layer階層やsubviewで分割

drawRectメソッドは最も一般的な描画メソッドで描画のカスタマイズに使われる。描画範囲を最小にするためにはlayer階層やsubviewで分割し、細かい単位で行うのが良いとされている。drawRectはViewというのが最小単位となるためview全体のredrawになるとされているためあまりよい方法ではない、一般的(大衆的)な方法ですらある。なおlayer単位で描画する場合にdrawLayer:inContext:に記述する。

全体のビュー更新を避ける

setNeedsDisplayでもsetNeedsDisplayInRectでも全体のビュー更新を行うようにマークされる。例えば、画像(UIImage)をアニメーションさせたり移動や回転などさせる場合、画像用のviewやlayerを作成して行うのが良い。これによりiOSがscreenを更新する時点で、viewのdrawRectで呼ばずに済み親ビュー全体の更新を避けることができ、つまりCGContextDrawImageや-drawInRectも呼ばずに済む。

drawRect

  • このメソッドはビューの描画を更新する。
  • UIViewを一つの要素と扱う。
  • 一つの要素の単位で再描画される。
  • 描画のキャッシュ(pixels)がされる。
  • 更新が必要な場合に呼び出される。

setNeedsDisplay/setNeedsDisplayInRect

  • drawRectを呼び出す予約をする。

display

  • 即座にdrawRectを呼び出す。

 

遅い場合

UIViewの中から、つまり[self setneedsdisplay]になっているとselfに対応したUIView全体に更新(drawrect:)が呼び出される。親ビューの上にsubviewが乗っているケースでは、subviewの背景が透過でなければ親は見えない。この重なった状態でselfとsubviewにsetneedsdisplayを送るとちらつくので注意。

  • opaqueプロパティを見直す(半透明合成は重いのはパソコン全般昔からの通説)
  • layerに細分化する(描画範囲を狭める)
  • subviewにして更新はsubviewに対してのみsetneedsdisplayする(描画範囲を狭める)

 

参考サイト https://developer.apple.com/library/ios/#qa/qa1708/_index.html#//apple_ref/doc/uid/DTS40010245 http://developer.apple.com/library/ios/#DOCUMENTATION/WindowsViews/Conceptual/ViewPG_iPhoneOS/WindowsandViews/WindowsandViews.html#//apple_ref/doc/uid/TP40009503-CH2-SW10

 Posted by at 04:31