loadView cannot access my property, throws EXC_BAD_ACCESS - objective-c

I have a property that gets set when initiating a new view controller.
- (IBAction)headerPressed:(UIButton *)sender {
RouteViewController *route = [[RouteViewController alloc] initWithRoute:[[Route getRoute:TEST_ROUTE] autorelease]];
[[self navigationController] pushViewController:circle animated:NO];
}
in the new view, which is a UIViewController
RouteViewController.h
#interface =RouteViewController : UIViewController <MKMapViewDelegate> {
Route *r;
}
-(id)initWithRoute:(Route *)route;
#property (retain, nonatomic) Route *r;
RouteViewController.m
-(id)initWithRoute:(Route *)route{
self = [super init];
r = route;
return self;
}
- (void)loadView {
[super loadView];
NSLog(#"TEST: %d", r.Route); // throws exception. Actually ANY time I access my r property
}
r.Route is the int value that the const TEST_ROUTE contains.
NOW, when I setup my breakpoint, AT the time of the NSLog, the inspector shows my object contains
Self >
r >
Route: 1
and
r >
Route: 1
So the property is available, and it DOES contain the correct data. But when i try ACCESSING the property, it throws an EXC_BAD_ACCESS.
Thoughts? :) (This is killing me here!)

There are a few problems here:
RouteViewController *route = [[RouteViewController alloc] initWithRoute:[[Route getRoute:TEST_ROUTE] autorelease]];
I think the autorelease message should be sent to the RouteViewController here and not the Route object, as the getRoute method should return an autoreleased object due to the naming convention.
i.e.
RouteViewController *route = [[[RouteViewController alloc] initWithRoute:[Route getRoute:TEST_ROUTE]] autorelease];
You need to retain ownership of the Route object passed into the initWithRoute method:
Either:
-(id)initWithRoute:(Route *)route{
self = [super init];
r = [route retain];
return self;
}
or:
-(id)initWithRoute:(Route *)route{
self = [super init];
self.r = route;
return self;
}

It looks like you're not using ARC, but you're also not retaining route in your init method. Change this:
r = route;
to this:
r = [route retain];

Related

Where to init MutableArray?

I've tried to init/alloc it in initWithFrame but then objects wouldn't get added.
It'd only work in this method I'm calling but I call this method each time user refreshes the view so it'd init/alloc hundred times.
Not sure why it won't just work in initWithFrame.
I need to know the right way to init and add..!
-(void)queryParseMethod {
self.imageFilesArray = nil;
self.imageFilesArray = [[NSMutableArray alloc]init];
[self.imageFilesArray addObjectsFromArray:objects];
if (!error) {
for (PFObject *object in objects) {
int index = (int)[self.favArray indexOfObject:[object objectId]];
[self.imageFilesArray replaceObjectAtIndex:index withObject:object];
}
[self.favCV reloadData];
}}
Why not just:
if (self.imageFilesArray == nil) {
self.imageFilesArray = [[NSMutableArray alloc] init];
[self.imageFilesArray addObjectsFromArray:objects];
}
And make sure that imageFilesArray is a strong property.
Your most likely problem is that initWithFrame: isn't being called. If this view comes out of a storyboard, then you need to put this in awakeFromNib, since storyboard/nib-loaded objects initialize with initWithCoder:, not their designated initializer.
You generally don't want to try to do initialization in initWithCoder: because it's called too early. awakeFromNib is called after all your IBOutlets are assigned.
It is very common for experienced devs to break initialization out into its own method like this:
- (void)setup {
// Do your setup here
}
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self setup];
}
}
- (void)awakeFromNib {
[self setup];
}
Doing it this way makes sure that the object is initialized in either case.
Another common solution is lazy initialization, particularly for things like NSMutableArray:
#interface MyView
#property (nonatomic, readonly, strong) NSMutableArray *imageFilesArray;
#end
#implementation MyView
- (NSMutableArray *)imageFilesArray {
if (_imageFilesArray == nil) {
_imageFilesArray = [NSMutableArray new];
}
return _imageFilesArray;
}

ARC Objective-C: How can self be deallocated?

I am creating a workflow to navigate through websites, every step of the workflow has to load n frames and then knows its ready (I have to implement the timeout).
I don't understand why [self next] is giving me this error:
* -[WebWorkflow next]: message sent to deallocated instance 0x105796ef0
Considering this delegate function:
- (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
frameCounter++;
NSInteger frames = [(WebWorkflowStep *)[steps objectAtIndex:index] frames];
NSLog(#"Frame counter %ld of %ld", frameCounter, frames);
[self next];
}
And this next method:
-(void) next
{
if ( index < [steps count])
{
frameCounter = 0;
index = index + 1;
WebWorkflowStep *step = [steps objectAtIndex:index-1];
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:step forKey:#"selector"];
[[NSNotificationCenter defaultCenter] postNotificationName:EVENT_WORKFLOW_NEXT object:nil userInfo:userInfo];
}
}
Notes:
- WebWorflow a.k.a 'self' has been created/binded by another class with strong
Like so:
#interface AController : NSObject <APIProtocol>
{
WebView *webview;
NSMutableArray *accounts;
WebWorkflow *workflow;
}
#property (strong) WebWorkflow *workflow;
...
I do create the workflow like this:
workflow = [[WebWorkflow alloc] initWithWebView:webview];
NSArray *getPicturesWorkflow = [NSArray arrayWithObjects:
[[WebWorkflowStep alloc] initWithSelector:#"open" andLoadFrames:0],
[[WebWorkflowStep alloc] initWithSelector:#"login" andLoadFrames:2],
[[WebWorkflowStep alloc] initWithSelector:#"getPictures" andLoadFrames:8],
nil];
[workflow setSteps:getPicturesWorkflow];
And it gets initialized like:
-(id)initWithWebView:(WebView *)webview
{
self = [ super init];
if(self) {
timeout = 10;
index = 0;
web = webview;
frameCounter = 0;
[web setFrameLoadDelegate:self];
}
return self;
}
The AController instance owns a web view and is the web view's delegate. The AController instance is getting released (for some reason...we'd need to see how it's owner manages it). Since it might get released during a load, it should clean up after itself as follows:
- (void)dealloc {
[web stopLoading:self]; // or webView, not sure what you call it
}
This will prevent the crash. It will also abandon the load. If you don't want to do that, you'll need to figure out why the AController instance is being released.
The first step in doing that would be a breakpoint in the dealloc method.

why is this OCUnit test failing?

It's stepping into the ViewDidLoad of the main view controller, and hitting the line calling get all tweets, but I put a breakpoint in the getAllTweets of both the base and derived to see if it just wasn't hitting the derived like I expected.
#implementation WWMainViewControllerTests {
// system under test
WWMainViewController *viewController;
// dependencies
UITableView *tableViewForTests;
WWTweetServiceMock *tweetServiceMock;
}
- (void)setUp {
tweetServiceMock = [[WWTweetServiceMock alloc] init];
viewController = [[WWMainViewController alloc] init];
viewController.tweetService = tweetServiceMock;
tableViewForTests = [[UITableView alloc] init];
viewController.mainTableView = tableViewForTests;
tableViewForTests.dataSource = viewController;
tableViewForTests.delegate = viewController;
}
- (void)test_ViewLoadedShouldCallServiceLayer_GetAllTweets {
[viewController loadView];
STAssertTrue(tweetServiceMock.getAllTweetsCalled, #"Should call getAllTweets on tweetService dependency");
}
- (void)tearDown {
tableViewForTests = nil;
viewController = nil;
tweetServiceMock = nil;
}
The base tweet service:
#implementation WWTweetService {
NSMutableArray *tweetsToReturn;
}
- (id)init {
if (self = [super init]) {
tweetsToReturn = [[NSMutableArray alloc] init];
}
return self;
}
- (NSArray *)getAllTweets {
NSLog(#"here in the base of get all tweets");
return tweetsToReturn;
}
#end
The Mock tweet service:
#interface WWTweetServiceMock : WWTweetService
#property BOOL getAllTweetsCalled;
#end
#implementation WWTweetServiceMock
#synthesize getAllTweetsCalled;
- (id)init {
if (self = [super init]) {
getAllTweetsCalled = NO;
}
return self;
}
- (NSArray *)getAllTweets {
NSLog(#"here in the mock class.");
getAllTweetsCalled = YES;
return [NSArray array];
}
The main view controller under test:
#implementation WWMainViewController
#synthesize mainTableView = _mainTableView;
#synthesize tweetService;
NSArray *allTweets;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
allTweets = [tweetService getAllTweets];
NSLog(#"was here in view controller");
}
- (void)viewDidUnload
{
[self setMainTableView:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
}
Since you're able to break in the debugger in viewDidLoad, what's the value of the tweetService ivar? If it's nil, the getAllTweets message will just be a no op. Maybe the ivar isn't being set properly or overridden somewhere else.
You should probably use the property to access the tweetService (call self.tweetService) rather than its underlying ivar. You should only ever access the ivar directly in getters, setters, and init (also dealloc if aren't using ARC for some crazy reason).
You also should not call loadView yourself, rather just access the view property of the view controller. That will kick off the loading process and call viewDidLoad.
Also, if you're doing a lot of mocking, I highly recommend OCMock.

Memory leak in objective-c class init method

I'm having problems with a leak in the init method of a class I have created. To keep it simple, I have the following (simplified) problem:
ViewController initialises an instance of
ClipData class which initialises an instance of
AnimationData class which initialise a string
ViewController:
myClipData = [[ClipData alloc] init];
ClipData:
- (id)init
{
self = [super init];
if (self) {
animData = [[AnimationData alloc] init]; //LEAK HERE
}
return self;
}
AnimationData:
- (id)init
{
self = [super init];
if (self) {
name = [NSString string];
}
return self;
}
All the objects in the classes are declared as (nonatomic, retain). I'm aware that doing this bumps up the retain count, but how do I initialise the AnimationData without leaking the animData???
Probably a very stupid question, so any help much appreciated.
Thanks,
Duncs
You are never releasing the animData. You need to add dealloc to your class:
- (void)dealloc {
[animData release];
[super dealloc];
}
Similarly, you need to add a similar dealloc to AnimationData.
On a related note, you need to retain and later release the string created in -[AnimationData init], what you are doing right now is essentially a noop, except that it leaves behind a garbled pointer.
When you have an alloc you must also have a release.
You should also reference the properties through self so you access the properties rather than the underlying members.
So you should really do :
ClipData *clip = [[ClipData alloc] init];
self.myClipData = clip;
[clip release];
And
if (self) {
AnimationData *data = [[AnimationData alloc] init];
self.animData = data;
[data release];
}
Make sure you also release the properties in the dealloc of the class by setting them to nil.
self.myClipData = nil;
self.animData = nil;

NSMutabledictionary not working

I am new to Iphone programming. Please correct me where am I going wrong.
I have two viewcontrollers
viewcontroller1 viewcontroller2
In viewcontroller1,
-(IBAction) getQuestions:(id)sender
{
NSLog(#"In get questions..");
[[self viewcontroller2] initWithData:userInfo];
[self presentModalViewController:viewcontroller2 animated:YES];
[quesions autorelease];
}
In viewController2 I have the following code.
-(id)initWithData:(NSMutableDictionary*)data
{
self = [super init];
if(self)
{
userInfo = [[NSMutableDictionary alloc] initWithDictionary:data];
}
return self;
}
-(IBAction) getQuesionAfterPopUp:(id) sender
{
NSLog(#"In get question..After popup...%#",userInfo);
}
For some reason "userInfo" is null. Why is it null even after using init with data.
You didn't initialize correctly, therefore the instance variable userInfo is not accessible.
Initialization should look like this:
- (id)initWithData:(NSMutableDictionary *)data
{
self = [super init]; // 'self' and it's instance variables are accessible at this point ...
if (self)
{
userInfo = // etc...
}
return self;
}
You might want to read up on this link for more information on how to implement designated initializers. Especially focus on the topic Implementing an Initializer.
in the above code you don't retain your userInfo object. Try
userInfo = [[[NSMutableDictionary alloc] initWithDictionary:data]]retain];
Or if userInfo is a property like:
#property (nonatomic, retain)
Try:
self.userInfo = [[NSMutableDictionary alloc] initWithDictionary:data]];
Which will automatically retain the userInfo. Please note by not calling self you are assigning straight to the instance variable.
Finally, make sure that the data object is not empty