Singleton(dispatch_once版)

On 2011年12月1日, in Objective-C, by タカ

Objective-Cのシングルトンは以前書いたけど、

dispatch_onceを使うとより簡単に書けるのでメモ。


Singleton.h

#import <Foundation/Foundation.h>

@interface Singleton : NSObject
+ (Singleton *)instance;
@end

Singleton.m

#import "Singleton.h"

@implementation Singleton

+ (Singleton *)instance {
  static Singleton *instance_ = nil;
  static dispatch_once_t predicate;
  dispatch_once(&predicate, ^{
    instance_ = [[self alloc] init];
  });
  return instance_;
}

@end

fullScreenImageメソッドで取得できる画像がちょっとおかしい。

initWithCGImage:scale:orientation:

でUIImage作って表示させると右に90度回転してる。

iOS4だとこの問題は起きない。

initWithCGImage

にすると、ちゃんと表示される。

でも、今度はiOS4系だと回転している。

iTunesで同期した画像はiOS5でもこの問題は起きない。

クラスリファレンスみるとiOS5で修正が入ったぽいが…。

まぁ、iOS4とiOS5の時で使い分ければいいんだけど何か釈然としない…。

Tagged with:  

NSString+Escape.h

#import <Foundation/Foundation.h>

@interface NSString (Escape)
- (NSString *)urlEncode;
- (NSString *)urlDecode;
- (NSString *)escapeHtml;
- (NSString *)unescapeHtml;
@end

NSString+Escape.m

#import "NSString+Escape.h"

@implementation NSString (Escape)

- (NSString *)escapeHtml
{
  NSMutableString *string = [[self mutableCopy] autorelease];
  [string replaceOccurrencesOfString:@"&" withString:@"&amp;" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  [string replaceOccurrencesOfString:@"<" withString:@"&lt;" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  [string replaceOccurrencesOfString:@">" withString:@"&gt;" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  [string replaceOccurrencesOfString:@"\"" withString:@"&quot;" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  return [NSString stringWithString: string];
}

- (NSString *)unescapeHtml
{
  NSMutableString *string = [[self mutableCopy] autorelease];
  [string replaceOccurrencesOfString:@"&quot;" withString:@"\"" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  [string replaceOccurrencesOfString:@"&gt;" withString:@">" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  [string replaceOccurrencesOfString:@"&lt;" withString:@"<" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  [string replaceOccurrencesOfString:@"&amp;" withString:@"&" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  return [NSString stringWithString: string];
}

- (NSString *)urlEncode
{
  NSMutableString *string = [[self mutableCopy] autorelease];
  [string replaceOccurrencesOfString:@"%" withString:@"%25" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  [string replaceOccurrencesOfString:@";" withString:@"%3B" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  [string replaceOccurrencesOfString:@"/" withString:@"%2F" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  [string replaceOccurrencesOfString:@"?" withString:@"%3F" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  [string replaceOccurrencesOfString:@":" withString:@"%3A" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  [string replaceOccurrencesOfString:@"@" withString:@"%40" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  [string replaceOccurrencesOfString:@"&" withString:@"%26" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  [string replaceOccurrencesOfString:@"=" withString:@"%3D" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  [string replaceOccurrencesOfString:@"+" withString:@"%2B" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  [string replaceOccurrencesOfString:@"$" withString:@"%24" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  [string replaceOccurrencesOfString:@"," withString:@"%2C" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  [string replaceOccurrencesOfString:@"[" withString:@"%5B" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  [string replaceOccurrencesOfString:@"]" withString:@"%5D" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  [string replaceOccurrencesOfString:@"#" withString:@"%23" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  [string replaceOccurrencesOfString:@"!" withString:@"%21" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  [string replaceOccurrencesOfString:@"'" withString:@"%27" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  [string replaceOccurrencesOfString:@"(" withString:@"%28" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  [string replaceOccurrencesOfString:@")" withString:@"%29" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  [string replaceOccurrencesOfString:@"*" withString:@"%2A" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  return [NSString stringWithString: string];
}

- (NSString *)urlDecode
{
  NSMutableString *string = [[self mutableCopy] autorelease];
  [string replaceOccurrencesOfString:@"%2A" withString:@"*" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  [string replaceOccurrencesOfString:@"%29" withString:@")" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  [string replaceOccurrencesOfString:@"%28" withString:@"(" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  [string replaceOccurrencesOfString:@"%27" withString:@"'" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  [string replaceOccurrencesOfString:@"%21" withString:@"!" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  [string replaceOccurrencesOfString:@"%23" withString:@"#" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  [string replaceOccurrencesOfString:@"%5D" withString:@"]" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  [string replaceOccurrencesOfString:@"%5B" withString:@"[" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  [string replaceOccurrencesOfString:@"%2C" withString:@"," options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  [string replaceOccurrencesOfString:@"%24" withString:@"$" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  [string replaceOccurrencesOfString:@"%2B" withString:@"+" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  [string replaceOccurrencesOfString:@"%3D" withString:@"=" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  [string replaceOccurrencesOfString:@"%26" withString:@"&" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  [string replaceOccurrencesOfString:@"%40" withString:@"@" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  [string replaceOccurrencesOfString:@"%3A" withString:@":" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  [string replaceOccurrencesOfString:@"%3F" withString:@"?" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  [string replaceOccurrencesOfString:@"%2F" withString:@"/" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  [string replaceOccurrencesOfString:@"%3B" withString:@";" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  [string replaceOccurrencesOfString:@"%25" withString:@"%" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
  return [NSString stringWithString: string];
}

main.m

NSString *string1 = @"<strong>strong</strong>";
NSLog(@"%@, %@, %@", string1, [string1 escapeHtml], [[string1 escapeHtml] unescapeHtml]);

NSString *string2 = @"%;/=?&";
NSLog(@"%@, %@, %@", string2, [string2 urlEncode], [[string2 urlEncode] urlDecode]);
Tagged with:  

キュー

On 2010年12月21日, in Objective-C, by タカ

Queue.h

#import <Foundation/Foundation.h>

#define kDefaultQueueSize 20

@interface Queue : NSObject <NSFastEnumeration>
{
 @private
  NSMutableArray *tasks_;
  int capacity_;
}

@property (nonatomic, readonly) int capacity;

- (id)init;
- (id)initWithCapacity:(int)capacity;
- (void)dealloc;
- (void)add:(id)object;
- (id)remove;
- (int)count;
@end

Queue.m

#import "Queue.h"

@implementation Queue

@synthesize capacity = capacity_;

- (id)init
{
  return [self initWithCapacity:kDefaultQueueSize];
}

- (id)initWithCapacity:(int)capacity
{
  if (![super init]) return nil;
  capacity_ = capacity;
  tasks_ = [[NSMutableArray alloc] initWithCapacity:capacity];
  return self;
}

- (void)dealloc
{
  [tasks_ release]; tasks_ = nil;
  [super dealloc];
}

- (void)add:(id)object
{
  [tasks_ addObject:object];
  if ([tasks_ count] > capacity_) {
    [tasks_ removeObjectAtIndex:0];
  }
}

- (id)remove
{
  if ([tasks_ count] < 1) return nil;
  id object = [tasks_ objectAtIndex:0];
  [object retain];
  [tasks_ removeObjectAtIndex:0];
  return [object autorelease];
}

- (int)count
{
  return [tasks_ count];
}

- (NSString *)description
{
  return [tasks_ description];
}

- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len
{
  return [tasks_ countByEnumeratingWithState:state objects:stackbuf count:len];
}

@end

使い方

  Queue *queue = [[Queue alloc] initWithCapacity:3];
  [queue add:@"1"];
  [queue add:@"2"];
  [queue add:@"3"];
  [queue add:@"4"];
  [queue add:@"5"];
  NSLog(@"%@, %d", queue, [queue count]);
  [queue remove];
  NSLog(@"%@, %d", queue, [queue count]);

  for (id object in queue) {
    NSLog(@"%@", object);
  }
Tagged with:  

目次


ひとまずまとめてみた。

間違っているかもしれないので、つっこみ大歓迎!

メモリ解放の仕組み

  1. alloc,initすると参照カウンタが1になる
  2. retain, copy すると参照カウンタが1増える
  3. releaseすると参照カウンタが1減る
  4. 参照カウンタが0になるとdeallocが呼ばれる
  5. allocWithZone,copyWithZone,mutableCopy,mutableCopyWithZoneも参照カウンタを1増やす

ルール

  1. retainはアドレスコピー
  2. copyは別領域にメモリを確保してコピー
  3. NSString,NSArray,NSDictionaryなどのMutableでないクラスはcopyしてもアドレスコピーになってしまう!
  4. retain, copyをした人が責任を持つ
  5. auto releaseをしておけば責任放棄できる
  6. auto releaseが設定されている変数はreleaseしてはいけない
  7. auto release設定した変数は予期せぬタイミングでreleaseされる可能性があるので注意!
  8. +で始まるメソッドはauto release設定済みなのでreleaseしてはダメ(stringWithXXXとかarrayXXX)
  9. NSMutableArrayなどにaddObjectした変数はreleaseしないとメモリリークする。
  10. initXXXメソッドは、呼び出した側がreleaseする事。(auto release設定されていない)

ハマった点

  1. allocの時点では参照カウンタが1にならないクラスもある(alloc,initをセットでやる事!)
  2. シングルトンなクラスで保持しているNSStringの変数は他のクラス内変数にしない

どうやらアドレスを共有してしまうらしく、
対象クラスのdealloc時にメモリ内容がクリアされ次にその変数を見に行く時に落ちる。

参考

Google Objective-Cスタイルガイド 日本語訳

http://www.textdrop.net/google-styleguide-ja/objcguide.xml

iPhoneアプリ開発時のメモリ管理で気をつけること

http://d.hatena.ne.jp/glass-_-onion/20090831/1251723900

Tagged with:  

要因は沢山あると思うんだけど俺がはまったケース。

原因

addSubviewする前に、不正なCGSize(マイナスの値)を設定してしまった

という事で起きました。

調べ方

オブジェクト表示する時に、サイズが正しいか表示してみてはどうでしょう?

NSLog(@"%@", NSStringFromCGRect(frame));

とやると情報が見れます。

Tagged with:  
iOS 4.2.1から出るようになった??

viewが表示された時点で、UITextViewにフォーカスを当てる為に、
viewDidLoadでbecomeFirstResponderをしてました。

どうやらこれが原因みたい…。
コメントアウトすると起きない…。

この警告が出る時は、動作が重くなる。
(UITextViewにフォーカスが当たってキーボードが立ち上がるまでに一瞬間がある)
標準アプリも似たような動きをしてる気がするんだよな…。

もしかして、iOSのバグ?

要追加調査。

Tagged with:  

両端の空白文字削除

On 2010年10月20日, in Objective-C, by タカ

Rubyだとstrip、PHPだとtrim

XMLデータのパースをやっていて空白文字が結構邪魔だった…。

API側で削れ!!でもいいんだけど…。

Objective-Cではどうやるのか調べた

結論から言うと、

stringByTrimmingCharactersInSet

というメソッドでできる。

[str stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];

とやると、空白&改行を削ってくれる。

って、メソッド名長いでしょ…。

面倒だからNSStringを拡張

NSStringExtensions.h
#import <Foundation/Foundation.h>

@interface NSString(TrimMethods)
- (NSString *)trim;
- (NSString *)strip;
@end
NSStringExtensions.m
#import "NSStringExtensions.h"

@implementation NSString(TrimMethods)
- (NSString *)trim
{
    return [self stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
- (NSString *)strip
{
    return [self trim];
}
@end

短くなった!

[str trim];
[str strip];

で使えるよ!

Tagged with:  

方位角を求める

On 2010年10月20日, in Objective-C, by タカ

地点Aから見た地点Bの方位を求めたかったので、
色々調べて実装してみた。

CLLocationを拡張。

参考は、こちら


CLLocationExtensions.h

#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>
#import <math.h>

@interface CLLocation(Extensions)
- (CLLocationDirection)azimuthToLocation:(const CLLocation *)target;
@end

CLLocationExtensions.m

#import "CLLocationExtensions.h"

#define kEquatorialRadius 6378137 // 赤道半径

@implementation CLLocation(Extensions)

- (CLLocationDirection)azimuthToLocation:(const CLLocation *)target
{
    CLLocationDirection dx = kEquatorialRadius * (target.coordinate.latitude - self.coordinate.latitude) * cos(self.coordinate.longitude);
    CLLocationDirection dy = kEquatorialRadius * (target.coordinate.longitude - self.coordinate.longitude);
    return atan2(dy, dx);
}

@end

使い方

CLLocation *own = [[CLLocation alloc] initWithLatitude:緯度 longitude:経度];
CLLocation *target = [[CLLocation alloc] initWithLatitude:緯度 longitude:経度];

NSLog("[own => target] %f°", [own azimuthToLocation:target]);

みたいに使えます。

Tagged with:  

CLLocationの拡張

On 2010年10月4日, in Objective-C, by タカ

独自クラス作るより使い回しが楽そうなので、メモ。


CLLocationExtensions.h

#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>

@interface CLLocation (Extensions)
- (CLLocationDegrees)latitude;
- (CLLocationDegrees)longitude;
- (BOOL)isBetterAccuracyThan:(CLLocation *)location;
- (BOOL)isBetterHorizontalAccuracyThan:(CLLocation *)location;
- (BOOL)isBetterVerticalAccuracyThan:(CLLocation *)location;
- (NSString *)description;
@end

CLLocationExtensions.m

#import "CLLocationExtensions.h"

@implementation CLLocation (Extensions)

- (CLLocationDegrees)latitude
{
    return self.coordinate.latitude;
}

- (CLLocationDegrees)longitude
{
    return self.coordinate.longitude;
}

- (BOOL)isBetterAccuracyThan:(CLLocation *)location
{
    if (![self isBetterHorizontalAccuracyThan:location]) return NO;
    if (![self isBetterVerticalAccuracyThan:location]) return NO;
    return YES;
}

- (BOOL)isBetterHorizontalAccuracyThan:(CLLocation *)location
{
    if (signbit(location.horizontalAccuracy)) return NO;
    if (self.horizontalAccuracy <= location.horizontalAccuracy) return YES;
    return NO;
}

- (BOOL)isBetterVerticalAccuracyThan:(CLLocation *)location
{
    if (signbit(location.verticalAccuracy)) return NO;
    if (self.verticalAccuracy <= location.verticalAccuracy) return YES;
    return NO;
}

- (NSString *)description
{
    return [NSString stringWithFormat:@"緯度 = %f\n経度 = %f\n高度 = %f\n精度(水平) = %f\n精度(垂直) = %f", [self latitude], [self longitude], self.altitude, self.horizontalAccuracy, self.verticalAccuracy];
}

@end
Tagged with: