为什么 NSTimer 在 UIViewController 之外崩溃?
Why does NSTimer crash outside UIViewController?
我试图通过重新排列相关方法(包括我之前放在 UIViewController
中的 NSTimer
来提高代码的可读性。我现在需要将它们重新定位到自定义 class,以便它们可以独立于 ViewController
工作。
但是在我尝试这样做的过程中,我引入了一个 NSTimer 的问题,即使代码看起来是正确的,以前也没有这个问题。发生崩溃并出现以下错误日志:
Thread 1: EXC_BAD_ACCESS (code=1, address=0x1)
当我尝试使用断点并逐步执行代码时 Xcode 似乎陷入了此语句的循环中
timer = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:@selector(nextClock)
userInfo:nil
repeats:YES];
这是我的代码的精简版。 NSTimer
在一个名为 ConcertController
的 class 中,它在 PlayViewController.h
中有一个前向声明
PlayViewController.h
#import <UIKit/UIKit.h>
#import "ConcertController.h"
@interface PlayViewController : UIViewController
{
ConcertController *concertStateMachine;
}
@end
并在 PlayViewController.m
中从 viewDidLoad
调用
PlayViewController.m
#import "PlayViewController.h"
@implementation PlayViewController {
}
@synthesize lastEventChangeTime;
...
...
- (void)viewDidLoad
{
[super viewDidLoad];
selectedFamily = [parent getSelectedFamily];
selectedPlayerID = [parent getSelectedPlayerID];
concertStateMachine = [[ConcertController alloc] initConcertStateMachine:(int)selectedFamily
forPlayer:(int)selectedPlayerID];
CGRect rect = [UIScreen mainScreen].bounds;
float statusBarHeight = [[UIApplication sharedApplication] statusBarFrame].size.height;
screenFrame = CGRectMake(0,statusBarHeight,rect.size.width,rect.size.height - statusBarHeight);
self.view = [[UIView alloc] initWithFrame: screenFrame];
}
ConcertController
在 PlayViewController.h
中有前向声明
ConcertController.h
#import <UIKit/UIKit.h>
@class PlayViewController;
@interface ConcertController : NSObject
{
int currentState;
NSUInteger clockCount;
int totalMinutes;
int totalSeconds;
…
…
NSDate* lastEventChangeTime;
NSTimer* timer;
}
- (id)initConcertStateMachine:(int)selectedFamily
forPlayer:(int)selectedPlayerID;
@property (nonatomic, retain) NSDate *lastEventChangeTime;
@end
ConcertController.m
#import "PlayViewController.h"
@implementation ConcertController
@synthesize lastEventChangeTime;
- (id)initConcertStateMachine:(int)selectedFamily
forPlayer:(int)selectedPlayerID
{
[self findEntryPointsFor:(int)selectedFamily
andPlayer:(int)selectedPlayerID];
[self startClock];
return self;
}
- (void)startClock
{
lastEventChangeTime = [[NSDate alloc] init];
currentState = 0; // CLOCK_Init_CurrentState;
clockCount = 24; // number of seconds per state
totalMinutes = 0 // CLOCK_Init_totalMinutes;
totalSeconds = 0; // CLOCK_Init_totalSeconds;
timer = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:@selector(nextClock)
userInfo:nil
repeats:YES];
}
- (void)nextClock
{
lastEventChangeTime = [NSDate date];
clockCount++;
[self masterClockReadout];
if ((clockCount % (int)24) == 0)
{
// [self nextState]; // other (i.e. non-UIView) code goes here
}
}
编辑#2
ConcertController.m 中的初始化已按照建议进行了标准化。
- (id)initConcertStateMachine:(int)selectedFamily
forPlayer:(int)selectedPlayerID
{
self = [super init];
if (self) {
[self findEntryPointsFor:(int)selectedFamily
andPlayer:(int)selectedPlayerID];
[self startClock];
}
return self;
}
编辑#1。这是要求的日志。注意:日志还会显示一些在发布的代码示例中未找到的项目(为清楚起见已删除)。格雷格
2017-07-25 17:06:30.809 SatGam2[5476:1809452] FamilySelectViewController loaded
2017-07-25 17:06:34.361 SatGam2[5476:1809452] PlayerIDSelectViewController loaded
2017-07-25 17:06:36.250 SatGam2[5476:1809452] SyncViewController loaded (Family 1 PlayerID 1)
2017-07-25 17:06:38.376 SatGam2[5476:1809452] Initialising MotionListener
2017-07-25 17:06:38.674444+1000 SatGam2[5476:1811506] [aqme] 254: AQDefaultDevice (1): skipping input stream 0 0 0x0
2017-07-25 17:06:38.692 SatGam2[5476:1809452] PlayViewController init called and AudioSession active
2017-07-25 17:06:38.693 SatGam2[5476:1809452] MIDI Event [tuningTransposition: 1 assignedPitches: 1]
2017-07-25 17:06:38.694 SatGam2[5476:1809452] Selected octave is 2
2017-07-25 17:06:38.694 SatGam2[5476:1809452] Dekany : index MIDI Note Number Frequency
2017-07-25 17:06:38.694 SatGam2[5476:1809452] 52 63 662.2421
2017-07-25 17:06:38.695 SatGam2[5476:1809452] 53 64 708.2311
2017-07-25 17:06:38.695 SatGam2[5476:1809452] 54 65 772.6157
2017-07-25 17:06:38.695 SatGam2[5476:1809452] 55 66 809.407
2017-07-25 17:06:38.695 SatGam2[5476:1809452] 56 67 882.9894
2017-07-25 17:06:38.695 SatGam2[5476:1809452] 57 68 910.5828
2017-07-25 17:06:38.696 SatGam2[5476:1809452] 58 69 993.3631
2017-07-25 17:06:38.696 SatGam2[5476:1809452] 59 70 1030.154
2017-07-25 17:06:38.696 SatGam2[5476:1809452] 60 73 1158.924
2017-07-25 17:06:38.696 SatGam2[5476:1809452] 61 74 1214.11
2017-07-25 17:06:38.697 SatGam2[5476:1809452]
(
"662.2421",
"708.2311",
"772.6157",
"809.407",
"882.9894",
"910.5828",
"993.3631",
"1030.154",
"1158.924",
"1214.11"
)
2017-07-25 17:06:38.697 SatGam2[5476:1809452] concert sequence for selectedFamily 1 and selectedPlayerID 1
2017-07-25 17:06:38.697 SatGam2[5476:1809452] entryPoints
2017-07-25 17:06:38.697 SatGam2[5476:1809452] 1 1 0 0
2017-07-25 17:06:38.698 SatGam2[5476:1809452] 2 1 0 0
2017-07-25 17:06:38.698 SatGam2[5476:1809452] 3 1 0 0
2017-07-25 17:06:38.698 SatGam2[5476:1809452] 4 1 0 0
2017-07-25 17:06:38.698 SatGam2[5476:1809452] 5 0 -1 48
2017-07-25 17:06:38.698 SatGam2[5476:1809452] 6 0 0 24
2017-07-25 17:06:38.699 SatGam2[5476:1809452] 7 1 1 0
2017-07-25 17:06:38.699 SatGam2[5476:1809452] 8 1 0 0
2017-07-25 17:06:38.699 SatGam2[5476:1809452] 9 0 -1 48
2017-07-25 17:06:38.699 SatGam2[5476:1809452] 10 0 0 24
2017-07-25 17:06:38.700 SatGam2[5476:1809452] 11 1 1 0
2017-07-25 17:06:38.700 SatGam2[5476:1809452] 12 1 0 0
2017-07-25 17:06:38.700 SatGam2[5476:1809452] 13 1 0 0
2017-07-25 17:06:38.700 SatGam2[5476:1809452] 14 0 -1 96
2017-07-25 17:06:38.700 SatGam2[5476:1809452] 15 0 0 72
2017-07-25 17:06:38.700 SatGam2[5476:1809452] 16 0 0 48
2017-07-25 17:06:38.701 SatGam2[5476:1809452] 17 0 0 24
2017-07-25 17:06:38.701 SatGam2[5476:1809452] 18 1 1 0
2017-07-25 17:06:38.701 SatGam2[5476:1809452] 19 1 0 0
2017-07-25 17:06:38.701 SatGam2[5476:1809452] 20 1 0 0
2017-07-25 17:06:38.701 SatGam2[5476:1809452] 21 1 0 0
2017-07-25 17:06:38.702 SatGam2[5476:1809452] 22 0 -1 144
2017-07-25 17:06:38.702 SatGam2[5476:1809452] 23 0 0 120
2017-07-25 17:06:38.702 SatGam2[5476:1809452] 24 0 0 96
2017-07-25 17:06:38.702 SatGam2[5476:1809452] 25 0 0 72
2017-07-25 17:06:38.702 SatGam2[5476:1809452] 26 0 0 48
2017-07-25 17:06:38.702 SatGam2[5476:1809452] 27 0 0 24
2017-07-25 17:06:38.703 SatGam2[5476:1809452] 28 1 1 0
2017-07-25 17:06:38.703 SatGam2[5476:1809452] 29 1 0 0
2017-07-25 17:06:38.703 SatGam2[5476:1809452] 30 0 -1 48
2017-07-25 17:06:38.703 SatGam2[5476:1809452] 31 0 0 24
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x1)
frame #0: 0x03144383 libobjc.A.dylib`objc_release + 19
* frame #1: 0x00081c1c SatGam2`-[ConcertController startClock](self=0x786bb480, _cmd="startClock") at ConcertController.m:46 [opt]
frame #2: 0x00081b87 SatGam2`-[ConcertController initConcertStateMachine:forPlayer:](self=0x786bb480, _cmd="initConcertStateMachine:forPlayer:", selectedFamily=1, selectedPlayerID=1) at ConcertController.m:25 [opt]
frame #3: 0x00093a16 SatGam2`-[PlayViewController viewDidLoad](self=0x7aa49c00, _cmd="viewDidLoad") at PlayViewController.m:154 [opt]
frame #4: 0x014e2878 UIKit`-[UIViewController _sendViewDidLoadWithAppearanceProxyObjectTaggingEnabled] + 38
frame #5: 0x014e7201 UIKit`-[UIViewController loadViewIfRequired] + 1434
frame #6: 0x014e776c UIKit`-[UIViewController view] + 29
frame #7: 0x00085c29 SatGam2`-[MultiviewViewController displayView:](self=<unavailable>, _cmd="displayView:", intNewView=<unavailable>) at MultiviewViewController.m:45 [opt]
frame #8: 0x000825ed SatGam2`-[MultiviewAppDelegate displayView:](self=0x793a1360, _cmd="displayView:", intNewView=4) at MultiviewAppDelegate.m:17 [opt]
frame #9: 0x0008828e SatGam2`-[SyncViewController fromSyncButton:](self=<unavailable>, _cmd="fromSyncButton:", button=0x7b67deb0) at SyncViewController.m:65 [opt]
frame #10: 0x03146220 libobjc.A.dylib`-[NSObject performSelector:withObject:withObject:] + 63
frame #11: 0x0131fca0 UIKit`-[UIApplication sendAction:to:from:forEvent:] + 91
frame #12: 0x0131fc3a UIKit`-[UIApplication sendAction:toTarget:fromSender:forEvent:] + 41
frame #13: 0x014c7f67 UIKit`-[UIControl sendAction:to:forEvent:] + 64
frame #14: 0x014c82d1 UIKit`-[UIControl _sendActionsForEvents:withEvent:] + 469
frame #15: 0x014c7207 UIKit`-[UIControl touchesEnded:withEvent:] + 666
frame #16: 0x01396526 UIKit`-[UIWindow _sendTouchesForEvent:] + 3066
frame #17: 0x01397dea UIKit`-[UIWindow sendEvent:] + 4445
frame #18: 0x0133e1b0 UIKit`-[UIApplication sendEvent:] + 363
frame #19: 0x01bbac2f UIKit`__dispatchPreprocessedEventFromEventQueue + 2973
frame #20: 0x01bb20ff UIKit`__handleEventQueue + 1255
frame #21: 0x01bb3663 UIKit`__handleHIDEventFetcherDrain + 66
frame #22: 0x0360aa5f CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 15
frame #23: 0x035f01c4 CoreFoundation`__CFRunLoopDoSources0 + 500
frame #24: 0x035ef69c CoreFoundation`__CFRunLoopRun + 1084
frame #25: 0x035eefd4 CoreFoundation`CFRunLoopRunSpecific + 372
frame #26: 0x035eee4b CoreFoundation`CFRunLoopRunInMode + 123
frame #27: 0x0516aa7a GraphicsServices`GSEventRunModal + 71
frame #28: 0x0516a95f GraphicsServices`GSEventRun + 80
frame #29: 0x0131dbc9 UIKit`UIApplicationMain + 148
frame #30: 0x00080f74 SatGam2`main(argc=1, argv=0xbff82818) at main.m:12 [opt]
frame #31: 0x05f0e779 libdyld.dylib`start + 1
(lldb)
如果您不使用 ARC,
然后将其更改为:
lastEventChangeTime = [NSDate date];
尝试为其添加保留
lastEventChangeTime = [[NSDate date] retain];
并且当您完成 lastEventChangeTime 后,设置
lastEventChangeTime = nil;
我不完全确定是不是这样,但我认为您不应该在初始化程序中调用 startClock
。该 init 方法通常不遵循合适的初始化程序的正确结构,即
- (instancetype)init... {
self = [super init];
if (self) {
// initialize properties (and ivars in your case)
}
return self;
}
我认为问题在于您正在安排定时器,它包含对 self
的引用,即初始化器完成之前的 ConcertController
实例,即实际上没有self
还没有。特别是因为您也从未调用过 super
的初始化程序(除非您在那个 findEntryPointsFor:andPlayer:
方法中这样做,这完全违反了任何约定)。
如果您稍后调用 startClock
(例如从视图控制器),它可能已经工作了,但我 真的 建议修复您的 init
以满足约定。不要忘记,尤其是在不仅仅是编码美学的 ARC 下,ARC 还依赖于某些东西来正确推断要保留和释放的内容等。
除此之外,您直接定义 ivars 的事实有点可疑。我猜这是来自 MRC 的迁移?我建议在这里也使用属性(这并不是真正的性能损失,正如许多人似乎错误地相信的那样)。唯一要记住的是,在 initializer 中,您可以使用 _ivarName
(即下划线符号)访问它们并依赖其他地方的 getters 和 setters(即通常点语法,除了一些边缘情况,在这些情况下你需要避免一些键值观察的东西,但正如我从这里看到的那样,你甚至没有那个)。这确实是更清洁、更安全的方法,尤其是当您想要更换计时器等时。如果您担心保持 class 的 public 界面干净,请使用 class 扩展,这仍然比 ivars 好。
我猜你的 ConcertController 声明不好。
为您的 PlayVC 试试这个 header:
#import <UIKit/UIKit.h>
#import "ConcertController.h"
@interface PlayViewController : UIViewController
{
// remove this line.
}
@property (nonatomic, strong) ConcertController *concertStateMachine;
@end
如果不是属性,object通常会在需要内存时由iOS自由释放。在您的 ConcertController 中,您还应该更改 NSTimer* 计时器;也是属性。试一试。
我试图通过重新排列相关方法(包括我之前放在 UIViewController
中的 NSTimer
来提高代码的可读性。我现在需要将它们重新定位到自定义 class,以便它们可以独立于 ViewController
工作。
但是在我尝试这样做的过程中,我引入了一个 NSTimer 的问题,即使代码看起来是正确的,以前也没有这个问题。发生崩溃并出现以下错误日志:
Thread 1: EXC_BAD_ACCESS (code=1, address=0x1)
当我尝试使用断点并逐步执行代码时 Xcode 似乎陷入了此语句的循环中
timer = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:@selector(nextClock)
userInfo:nil
repeats:YES];
这是我的代码的精简版。 NSTimer
在一个名为 ConcertController
的 class 中,它在 PlayViewController.h
PlayViewController.h
#import <UIKit/UIKit.h>
#import "ConcertController.h"
@interface PlayViewController : UIViewController
{
ConcertController *concertStateMachine;
}
@end
并在 PlayViewController.m
viewDidLoad
调用
PlayViewController.m
#import "PlayViewController.h"
@implementation PlayViewController {
}
@synthesize lastEventChangeTime;
...
...
- (void)viewDidLoad
{
[super viewDidLoad];
selectedFamily = [parent getSelectedFamily];
selectedPlayerID = [parent getSelectedPlayerID];
concertStateMachine = [[ConcertController alloc] initConcertStateMachine:(int)selectedFamily
forPlayer:(int)selectedPlayerID];
CGRect rect = [UIScreen mainScreen].bounds;
float statusBarHeight = [[UIApplication sharedApplication] statusBarFrame].size.height;
screenFrame = CGRectMake(0,statusBarHeight,rect.size.width,rect.size.height - statusBarHeight);
self.view = [[UIView alloc] initWithFrame: screenFrame];
}
ConcertController
在 PlayViewController.h
ConcertController.h
#import <UIKit/UIKit.h>
@class PlayViewController;
@interface ConcertController : NSObject
{
int currentState;
NSUInteger clockCount;
int totalMinutes;
int totalSeconds;
…
…
NSDate* lastEventChangeTime;
NSTimer* timer;
}
- (id)initConcertStateMachine:(int)selectedFamily
forPlayer:(int)selectedPlayerID;
@property (nonatomic, retain) NSDate *lastEventChangeTime;
@end
ConcertController.m
#import "PlayViewController.h"
@implementation ConcertController
@synthesize lastEventChangeTime;
- (id)initConcertStateMachine:(int)selectedFamily
forPlayer:(int)selectedPlayerID
{
[self findEntryPointsFor:(int)selectedFamily
andPlayer:(int)selectedPlayerID];
[self startClock];
return self;
}
- (void)startClock
{
lastEventChangeTime = [[NSDate alloc] init];
currentState = 0; // CLOCK_Init_CurrentState;
clockCount = 24; // number of seconds per state
totalMinutes = 0 // CLOCK_Init_totalMinutes;
totalSeconds = 0; // CLOCK_Init_totalSeconds;
timer = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:@selector(nextClock)
userInfo:nil
repeats:YES];
}
- (void)nextClock
{
lastEventChangeTime = [NSDate date];
clockCount++;
[self masterClockReadout];
if ((clockCount % (int)24) == 0)
{
// [self nextState]; // other (i.e. non-UIView) code goes here
}
}
编辑#2
ConcertController.m 中的初始化已按照建议进行了标准化。
- (id)initConcertStateMachine:(int)selectedFamily
forPlayer:(int)selectedPlayerID
{
self = [super init];
if (self) {
[self findEntryPointsFor:(int)selectedFamily
andPlayer:(int)selectedPlayerID];
[self startClock];
}
return self;
}
编辑#1。这是要求的日志。注意:日志还会显示一些在发布的代码示例中未找到的项目(为清楚起见已删除)。格雷格
2017-07-25 17:06:30.809 SatGam2[5476:1809452] FamilySelectViewController loaded
2017-07-25 17:06:34.361 SatGam2[5476:1809452] PlayerIDSelectViewController loaded
2017-07-25 17:06:36.250 SatGam2[5476:1809452] SyncViewController loaded (Family 1 PlayerID 1)
2017-07-25 17:06:38.376 SatGam2[5476:1809452] Initialising MotionListener
2017-07-25 17:06:38.674444+1000 SatGam2[5476:1811506] [aqme] 254: AQDefaultDevice (1): skipping input stream 0 0 0x0
2017-07-25 17:06:38.692 SatGam2[5476:1809452] PlayViewController init called and AudioSession active
2017-07-25 17:06:38.693 SatGam2[5476:1809452] MIDI Event [tuningTransposition: 1 assignedPitches: 1]
2017-07-25 17:06:38.694 SatGam2[5476:1809452] Selected octave is 2
2017-07-25 17:06:38.694 SatGam2[5476:1809452] Dekany : index MIDI Note Number Frequency
2017-07-25 17:06:38.694 SatGam2[5476:1809452] 52 63 662.2421
2017-07-25 17:06:38.695 SatGam2[5476:1809452] 53 64 708.2311
2017-07-25 17:06:38.695 SatGam2[5476:1809452] 54 65 772.6157
2017-07-25 17:06:38.695 SatGam2[5476:1809452] 55 66 809.407
2017-07-25 17:06:38.695 SatGam2[5476:1809452] 56 67 882.9894
2017-07-25 17:06:38.695 SatGam2[5476:1809452] 57 68 910.5828
2017-07-25 17:06:38.696 SatGam2[5476:1809452] 58 69 993.3631
2017-07-25 17:06:38.696 SatGam2[5476:1809452] 59 70 1030.154
2017-07-25 17:06:38.696 SatGam2[5476:1809452] 60 73 1158.924
2017-07-25 17:06:38.696 SatGam2[5476:1809452] 61 74 1214.11
2017-07-25 17:06:38.697 SatGam2[5476:1809452]
(
"662.2421",
"708.2311",
"772.6157",
"809.407",
"882.9894",
"910.5828",
"993.3631",
"1030.154",
"1158.924",
"1214.11"
)
2017-07-25 17:06:38.697 SatGam2[5476:1809452] concert sequence for selectedFamily 1 and selectedPlayerID 1
2017-07-25 17:06:38.697 SatGam2[5476:1809452] entryPoints
2017-07-25 17:06:38.697 SatGam2[5476:1809452] 1 1 0 0
2017-07-25 17:06:38.698 SatGam2[5476:1809452] 2 1 0 0
2017-07-25 17:06:38.698 SatGam2[5476:1809452] 3 1 0 0
2017-07-25 17:06:38.698 SatGam2[5476:1809452] 4 1 0 0
2017-07-25 17:06:38.698 SatGam2[5476:1809452] 5 0 -1 48
2017-07-25 17:06:38.698 SatGam2[5476:1809452] 6 0 0 24
2017-07-25 17:06:38.699 SatGam2[5476:1809452] 7 1 1 0
2017-07-25 17:06:38.699 SatGam2[5476:1809452] 8 1 0 0
2017-07-25 17:06:38.699 SatGam2[5476:1809452] 9 0 -1 48
2017-07-25 17:06:38.699 SatGam2[5476:1809452] 10 0 0 24
2017-07-25 17:06:38.700 SatGam2[5476:1809452] 11 1 1 0
2017-07-25 17:06:38.700 SatGam2[5476:1809452] 12 1 0 0
2017-07-25 17:06:38.700 SatGam2[5476:1809452] 13 1 0 0
2017-07-25 17:06:38.700 SatGam2[5476:1809452] 14 0 -1 96
2017-07-25 17:06:38.700 SatGam2[5476:1809452] 15 0 0 72
2017-07-25 17:06:38.700 SatGam2[5476:1809452] 16 0 0 48
2017-07-25 17:06:38.701 SatGam2[5476:1809452] 17 0 0 24
2017-07-25 17:06:38.701 SatGam2[5476:1809452] 18 1 1 0
2017-07-25 17:06:38.701 SatGam2[5476:1809452] 19 1 0 0
2017-07-25 17:06:38.701 SatGam2[5476:1809452] 20 1 0 0
2017-07-25 17:06:38.701 SatGam2[5476:1809452] 21 1 0 0
2017-07-25 17:06:38.702 SatGam2[5476:1809452] 22 0 -1 144
2017-07-25 17:06:38.702 SatGam2[5476:1809452] 23 0 0 120
2017-07-25 17:06:38.702 SatGam2[5476:1809452] 24 0 0 96
2017-07-25 17:06:38.702 SatGam2[5476:1809452] 25 0 0 72
2017-07-25 17:06:38.702 SatGam2[5476:1809452] 26 0 0 48
2017-07-25 17:06:38.702 SatGam2[5476:1809452] 27 0 0 24
2017-07-25 17:06:38.703 SatGam2[5476:1809452] 28 1 1 0
2017-07-25 17:06:38.703 SatGam2[5476:1809452] 29 1 0 0
2017-07-25 17:06:38.703 SatGam2[5476:1809452] 30 0 -1 48
2017-07-25 17:06:38.703 SatGam2[5476:1809452] 31 0 0 24
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x1)
frame #0: 0x03144383 libobjc.A.dylib`objc_release + 19
* frame #1: 0x00081c1c SatGam2`-[ConcertController startClock](self=0x786bb480, _cmd="startClock") at ConcertController.m:46 [opt]
frame #2: 0x00081b87 SatGam2`-[ConcertController initConcertStateMachine:forPlayer:](self=0x786bb480, _cmd="initConcertStateMachine:forPlayer:", selectedFamily=1, selectedPlayerID=1) at ConcertController.m:25 [opt]
frame #3: 0x00093a16 SatGam2`-[PlayViewController viewDidLoad](self=0x7aa49c00, _cmd="viewDidLoad") at PlayViewController.m:154 [opt]
frame #4: 0x014e2878 UIKit`-[UIViewController _sendViewDidLoadWithAppearanceProxyObjectTaggingEnabled] + 38
frame #5: 0x014e7201 UIKit`-[UIViewController loadViewIfRequired] + 1434
frame #6: 0x014e776c UIKit`-[UIViewController view] + 29
frame #7: 0x00085c29 SatGam2`-[MultiviewViewController displayView:](self=<unavailable>, _cmd="displayView:", intNewView=<unavailable>) at MultiviewViewController.m:45 [opt]
frame #8: 0x000825ed SatGam2`-[MultiviewAppDelegate displayView:](self=0x793a1360, _cmd="displayView:", intNewView=4) at MultiviewAppDelegate.m:17 [opt]
frame #9: 0x0008828e SatGam2`-[SyncViewController fromSyncButton:](self=<unavailable>, _cmd="fromSyncButton:", button=0x7b67deb0) at SyncViewController.m:65 [opt]
frame #10: 0x03146220 libobjc.A.dylib`-[NSObject performSelector:withObject:withObject:] + 63
frame #11: 0x0131fca0 UIKit`-[UIApplication sendAction:to:from:forEvent:] + 91
frame #12: 0x0131fc3a UIKit`-[UIApplication sendAction:toTarget:fromSender:forEvent:] + 41
frame #13: 0x014c7f67 UIKit`-[UIControl sendAction:to:forEvent:] + 64
frame #14: 0x014c82d1 UIKit`-[UIControl _sendActionsForEvents:withEvent:] + 469
frame #15: 0x014c7207 UIKit`-[UIControl touchesEnded:withEvent:] + 666
frame #16: 0x01396526 UIKit`-[UIWindow _sendTouchesForEvent:] + 3066
frame #17: 0x01397dea UIKit`-[UIWindow sendEvent:] + 4445
frame #18: 0x0133e1b0 UIKit`-[UIApplication sendEvent:] + 363
frame #19: 0x01bbac2f UIKit`__dispatchPreprocessedEventFromEventQueue + 2973
frame #20: 0x01bb20ff UIKit`__handleEventQueue + 1255
frame #21: 0x01bb3663 UIKit`__handleHIDEventFetcherDrain + 66
frame #22: 0x0360aa5f CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 15
frame #23: 0x035f01c4 CoreFoundation`__CFRunLoopDoSources0 + 500
frame #24: 0x035ef69c CoreFoundation`__CFRunLoopRun + 1084
frame #25: 0x035eefd4 CoreFoundation`CFRunLoopRunSpecific + 372
frame #26: 0x035eee4b CoreFoundation`CFRunLoopRunInMode + 123
frame #27: 0x0516aa7a GraphicsServices`GSEventRunModal + 71
frame #28: 0x0516a95f GraphicsServices`GSEventRun + 80
frame #29: 0x0131dbc9 UIKit`UIApplicationMain + 148
frame #30: 0x00080f74 SatGam2`main(argc=1, argv=0xbff82818) at main.m:12 [opt]
frame #31: 0x05f0e779 libdyld.dylib`start + 1
(lldb)
如果您不使用 ARC,
然后将其更改为:
lastEventChangeTime = [NSDate date];
尝试为其添加保留
lastEventChangeTime = [[NSDate date] retain];
并且当您完成 lastEventChangeTime 后,设置
lastEventChangeTime = nil;
我不完全确定是不是这样,但我认为您不应该在初始化程序中调用 startClock
。该 init 方法通常不遵循合适的初始化程序的正确结构,即
- (instancetype)init... {
self = [super init];
if (self) {
// initialize properties (and ivars in your case)
}
return self;
}
我认为问题在于您正在安排定时器,它包含对 self
的引用,即初始化器完成之前的 ConcertController
实例,即实际上没有self
还没有。特别是因为您也从未调用过 super
的初始化程序(除非您在那个 findEntryPointsFor:andPlayer:
方法中这样做,这完全违反了任何约定)。
如果您稍后调用 startClock
(例如从视图控制器),它可能已经工作了,但我 真的 建议修复您的 init
以满足约定。不要忘记,尤其是在不仅仅是编码美学的 ARC 下,ARC 还依赖于某些东西来正确推断要保留和释放的内容等。
除此之外,您直接定义 ivars 的事实有点可疑。我猜这是来自 MRC 的迁移?我建议在这里也使用属性(这并不是真正的性能损失,正如许多人似乎错误地相信的那样)。唯一要记住的是,在 initializer 中,您可以使用 _ivarName
(即下划线符号)访问它们并依赖其他地方的 getters 和 setters(即通常点语法,除了一些边缘情况,在这些情况下你需要避免一些键值观察的东西,但正如我从这里看到的那样,你甚至没有那个)。这确实是更清洁、更安全的方法,尤其是当您想要更换计时器等时。如果您担心保持 class 的 public 界面干净,请使用 class 扩展,这仍然比 ivars 好。
我猜你的 ConcertController 声明不好。
为您的 PlayVC 试试这个 header:
#import <UIKit/UIKit.h>
#import "ConcertController.h"
@interface PlayViewController : UIViewController
{
// remove this line.
}
@property (nonatomic, strong) ConcertController *concertStateMachine;
@end
如果不是属性,object通常会在需要内存时由iOS自由释放。在您的 ConcertController 中,您还应该更改 NSTimer* 计时器;也是属性。试一试。