[Objective-C] 強制メモリリーク&メモリ警告発生

実機でメモリを使い切るために作ったもの。シミュレータでやるとMacのリソースを持ってってくれていろいろメンドクサイので、実機で実行する。

/*!
 @header MemoryLeaker.h
 @abstract 実機でメモリ警告を出したいときとか、メモリリークでクラッシュさせたいときにどうぞ。
 @author Fernweh8000
 @copyright 2013 fernweh.jp
 @discussion
 ARC    : YES
 Lisence: MIT License
 */

#import <Foundation/Foundation.h>

#import "F8VmStatistics.h"

@protocol MemoryLeakerDelegate <NSObject>

/*!
 @abstract メモリリーク実行中に呼ばれまくる。
 好きに実装してくダサい。
 @param F8VmStatistics メモリの情報
 @return void
 */
- (void) logWithVmStatistics:(F8VmStatistics *)vmStatistics;

@end

@interface MemoryLeaker : NSObject<UIAlertViewDelegate, UIApplicationDelegate, MemoryLeakerDelegate>

/*!
 @abstact
 ログ出力用のデリゲートを指定して初期化を行う
 @param delegate
 nilを指定した場合は [self init] と等価
 @return self
 @discussion
 単に [self init] を使用した場合は [self initWithDelegate:self] と等価
 */
- (id)initWithDelegate:(id<MemoryLeakerDelegate>)delegate;

/*!
 @abstract どんどんメモリリークしちゃおうね
 @param delay 指定した時間後にメモリリークし始める
 @param logged ログ表示を行うか否かのフラグ
 YESの場合は 現在のメモリの状態をログに吐き出す
 @return void
 @discussion
 initWithDelegate で指定したデリゲートは
 ログ出力用のデリゲート関数 logWithTaskBasicInfo: を
 実装したオブジェクトを指定する。
 指定しなかった場合は デフォルトのログを吐き出す。
 */
- (void)forceLeakMemoryWithDelay:(NSTimeInterval)delay logged:(BOOL)logged;

@end
#import "MemoryLeaker.h"

@interface MemoryLeaker (Priate)

- (void)initializeCommon;
- (void)memoryLeak;
- (void)stopLeakingMemory;

@end

@implementation MemoryLeaker {
  BOOL runFlag_;
  BOOL logFlag_;
  __weak id<MemoryLeakerDelegate> delegate_;
}

#pragma mark - Public Methods

- (void)forceLeakMemoryWithDelay:(NSTimeInterval)delay logged:(BOOL)logged
{
  CFRetain((__bridge CFTypeRef)self);

  runFlag_ = YES;
  logFlag_ = logged;
  [self performSelector:@selector(memoryLeak) withObject:nil afterDelay:delay];
}

#pragma mark - Life cycle

- (id) init {
  self = [super init];
  if ( self ) {
    [self addMemoryWarningNotificationObserver];
    delegate_ = self;
  }
  return self;
}

- (id) initWithDelegate:(id<MemoryLeakerDelegate>)delegate {
  self = [super init];
  if ( self ) {
    [self addMemoryWarningNotificationObserver];
    delegate_ = delegate ? delegate : self ;
  }
  return self;
}

//
// メモリ警告通知のオブザーバーを解除
//
- (void)dealloc
{
  NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
  [notificationCenter removeObserver:self
                                name:UIApplicationDidReceiveMemoryWarningNotification
                              object:nil];
}

//
// メモリ警告通知のオブザーバーを登録
//
- (void) addMemoryWarningNotificationObserver {
  NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
  [notificationCenter addObserver:self
                         selector:@selector(applicationDidReceiveMemoryWarning:)
                             name:UIApplicationDidReceiveMemoryWarningNotification
                           object:nil];
}

#pragma mark - Feature

- (void)stopLeakingMemory {
  [delegate_ logWithVmStatistics:[[F8VmStatistics alloc] init]];

  runFlag_ = NO;
  [[[UIAlertView alloc] initWithTitle:@"メモリリーク停止"
                              message:@"再開する場合は「再開するボタン」を押してください。メモリリークを再開して少し待つとクラッシュします。停止する場合もメモリの解放は行われず、メモリリークしたままの状態で停止します。"
                             delegate:self
                    cancelButtonTitle:@"再開する"
                    otherButtonTitles:@"停止する", nil] show];
}

- (void)memoryLeak {
  if ( ! runFlag_ ) {
    return;
  }

  [delegate_ logWithVmStatistics:[[F8VmStatistics alloc] init]];

  for(NSInteger i = 0; i < 1024 * 128; i++) {
    CFMutableArrayRef leak = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
    CFRetain(leak);
  }

  [self performSelector:@selector(memoryLeak)
             withObject:nil
             afterDelay:0.01];
}

- (void)logWithVmStatistics:(F8VmStatistics *)vmStatistics {
  NSLog(@"FreeSize:%.2f MB", vmStatistics.freeSize / 1024.0 / 1024.0);
}

#pragma mark - Event Handler

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
  switch (buttonIndex) {
    case 0:
      [self forceLeakMemoryWithDelay:1.0 logged:logFlag_];
      break;

    default:
      break;
  }
}

- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application
{
  static bool firstRun = true;
  if ( firstRun ) {
    firstRun = false;
    [self stopLeakingMemory];
  }
}

@end
Share