ObjC : difference between self.obj and obj - objective-c

What is the difference between self.obj and obj in objective C?
I'm asking this because when I write [view method] it's fine
but when trying [self.view method] it's an infinite loop
In fact the code is the following:
//---create a UIView object---
UIView *view = [[UIView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame];
view.backgroundColor = [UIColor lightGrayColor];
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[view addSubview:button];
self.view = view;
The problem is that [self.view addSubview:button] gives an infinite loop.

Using obj is just what it looks like: direct variable access.
However, self.obj is not what it looks like. Instead, it is context-dependent syntactic sugar for a call to an accessor method. By convention, the setter method would be setObj: and the getter obj, though it is possible to override this. So typically self.obj is equivalent to either [self obj] or [self setObj:x] depending on whether you're reading from the value or assigning to it.
In your example, when you put [self.view method], what you are really doing is [[self view] method]. Without knowing more about the context in which you're using this, it's hard to say why it's giving you an infinite loop, although one obvious case that would do that is if you were making this call inside the accessor method for view.

When you're using '.' you're accessing property. And when you're typing [view method] you're accessing the variable named view. See my answer for this question.
for example:
-(void) doSmth {
UIView* view = [[UIView alloc] init];
[view method]; //sends message 'method' to view variable declared in this function
[self.view method]; //sends message to instance variable of self (accessing it via generated accessor)
}

Related

EXEC_BAD_ACCESS while popping out a new View inside existing View

I am trying to call a new view inside my existing view
TransactionFinish *childView= [[TransactionFinish alloc] initWithNibName:#"TransactionFinish" bundle:nil];
childView.view.frame = self.view.frame;
childView.view.frame=CGRectMake(10, 10, self.view.frame.size.width-20, self.view.frame.size.height-20);
childView.view.alpha = 0.0f;
[self.view addSubview:childView.view];
[UIView animateWithDuration:0.5 delay:0.0 options:UIViewAnimationCurveEaseOut
animations:^{
childView.view.alpha = 1.0f;
}
completion:^(BOOL finished) {
}];
It is going inside ViewDidLoad() of TransactionFinish(I have tried debugging it) but it gives me Thread EXEC_BAD_ACCESS(code=1,address=0x31f54e62) with green color
The issue lays with the lifetime of childView. You instantiate and store a reference to it into a local variable:
TransactionFinish *childView= [[TransactionFinish alloc] initWithNibName:#"TransactionFinish" bundle:nil];
If you are using ARC, when the local variable goes out of scope, the object referenced by it (childView) is deallocated.
If you are not using ARC, I suppose you are doing:
[childView release];
somewhere to avoid childView to be leaked (as the code you pasted above would imply).
Either hypothesis would explain why you get the crash: when viewDidLoad is called, the controller has already been deallocated.
Adding childView view to self.view:
[self.view addSubview:childView.view];
will retain childView.view but not childView. So the controller is deallocated, while its view is not.
A fix to this is creating a strong property in your class to store a reference to your childView controller:
#property(nonatomic, strong) TransactionFinish *childView;
Another possibility is using controller containment; you could do something li
[vc willMoveToParentViewController:self];
[self addChildViewController:childView];
[self.view addSubview:childView.view]; // or something like this.
[childView didMoveToParentViewController:self];
but this will only work on iOS5+.

Getting a NSViewController's View if it is a custom class?

I use the following code to get my View out of my controller:
CollectionItemView *myView = [self view];
This works pretty well, but I get the warning Incompatible pointer types initializing CollectionItemView __strong with an expression of type NSView. I understand why i get this but is it okay to ignore it or should I overwrite the view property ?
chuck
If you are sure that [self view] is CollectionItemView just do:
CollectionItemView *myView = (CollectionItemView*)[self view];
or (which is better) you can use:
id myView = [self view];
There is no need to overwrite it. troolee already suggested two working solutions.
However, just to be save I'd rather code it differently.
CollectionItemView *myView = nil;
if ([[self view] isKindOfClass:[CollectionItemView class])
self.view = (CollectionItemView*)[self view];
The shorter version without isKindOfClass test is ok when you know for sure from the context that the object must be of type CollectionItemView or any of its subclasses.

Setting Bool in different classes

I have the following code where after a bool is true I want to add a drawing to my rect. here is the code I have but for some reason it is either not setting the bool or calling the setNeedsDisplay. Am I referencing to the other class properly? thanks
//in AppController.m
-(IBAction)colorToggle:(id)sender
{
if ([colorFilter state] == NSOnState)
{
CutoutView *theView = [[CutoutView alloc] init];
[theView setFilterEnabled:YES];
}
}
//in cutoutView.m
- (void)drawRect:(NSRect)dirtyRect
{
[[[NSColor blackColor]colorWithAlphaComponent:0.9]set];
NSRectFill(dirtyRect);
//this is what i want to be drawn when my bool is true and update the drawRect
if (filterEnabled == YES) {
NSRectFillUsingOperation(NSMakeRect(100, 100, 300, 300), NSCompositeClear);
[self update];
}
}
-(void)update
{
[self setNeedsDisplay:YES];
}
OK, you know how not every UILabel is the same? Like, you can remove one UILabel from a view without all the others disappearing too? Well, your CutoutView is the same way. When you write CutoutView *theView = [[CutoutView alloc] init]; there, that creates a new CutoutView that isn't displayed anywhere. You need to talk to your existing CutoutView (probably by hooking up an outlet, but there are any number of perfectly valid designs that will accomplish this goal).
You are forgetting to call the drawRect: method, it should looks like this:
CutoutView *theView = [[CutoutView alloc] init];
[theView setFilterEnabled:YES];
[theView setNeedsDisplay];
From the docs:
When the actual content of your view changes, it is your
responsibility to notify the system that your view needs to be
redrawn. You do this by calling your view’s setNeedsDisplay or
setNeedsDisplayInRect: method of the view.

Can't access UIView in external method

I'm new to Objective-C, so the way I'm going about this might be ludicrous, but here goes:
I have a login form in my iPhone application. When the user has entered their credentials, they hit Done in the top right corner, which triggers an IBAction and a custom progress indicator pops up. I've created this indicator by using a class containing an instance method named showProgressIndicator. showProgressIndicator creates and returns a UIView, which I then add to my view like so:
ProgressIndicatorElement *ProgressIndicator = [[ProgressIndicatorElement alloc] init];
box = [ProgressIndicator showProgressIndicator];
[self.view addSubview:box];
I have of course declared box as a UIView in my header file. The progress indicator pops up beautifully and in the meantime I'm doing a behind-the-scenes URL request that, when finished, calls another method in my view controller named receivedServerResponse. Now, what I want to do is to remove the progress indicator, which is why I'm doing this:
- (void)receivedServerResponse {
[box removeFromSuperview];
}
But nothing happens at all. I'm not getting any errors or warnings, and the code is being highlighted just as if everything was running smoothly. I've tried retaining the indicator in my IBAction, but that doesn't help either.
Hope you can help out!
Updated:
Here is the showProgressIndicator method:
- (UIView *)showProgressIndicator {
box = [[UIView alloc] initWithFrame:CGRectMake(85, 190, 210, 140)];
box.backgroundColor = [UIColor colorWithRed:0.0 / 255 green:0.0 / 255 blue:0.0 / 255 alpha:.6];
box.layer.cornerRadius = 8;
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
spinner.layer.frame = CGRectMake((box.layer.bounds.size.width - spinner.layer.bounds.size.width)/2, 20, spinner.layer.bounds.size.width, spinner.layer.bounds.size.height);
[spinner startAnimating];
[box addSubview:spinner];
UILabel *titleInBox = [[UILabel alloc] initWithFrame:CGRectMake(0, 65, 150, 20)];
titleInBox.font = [UIFont boldSystemFontOfSize:16];
titleInBox.textColor = [UIColor whiteColor];
titleInBox.textAlignment = UITextAlignmentCenter;
titleInBox.backgroundColor = [UIColor clearColor];
titleInBox.text = #"Authorizing...";
[box addSubview:titleInBox];
return box;
}
Second update:
#Deepak just pointed out in the comments that I might be running two different instances of my view controller, which actually seems to be the case. In the external class that handles the aforementioned URL request, I get back to the view controller's receivedServerResponse method by doing this:
- (void)requestFinished:(ASIHTTPRequest *)request {
NSString *responseString = [request responseString];
SignInViewController *viewController = [[SignInViewController alloc] init];
[viewController receivedServerResponse];
}
Without spreading myself too thin (probably too late ;)), ASIHTTPRequest is set up so that if you call one method that performs an asynchronous URL request, a predefined method called requestFinished (above) is called, which is why I've had to call my view controller this way, because I can't access the returned value in an easier way (that I know of).
Creating a new instance of SignInViewController is not the correct way. It only seems correct to maintain a weak reference (assigned property) of the SignInViewController object. Say your class is RequestHandler.
#interface RequestHandler: [..] {
}
#property (nonatomic, assign) SignInViewController * signInViewController;
#end
#implementation RequestHandler
#synthesize signInViewController;
[..]
- (void)requestFinished:(ASIHTTPRequest *)request {
NSString *responseString = [request responseString];
[signInViewController receivedServerResponse];
}
#end
So when you're creating a RequestHandler object within the SignInViewController instance, you do,
RequestHandler * requestHandler = [[RequestHandler alloc] init];
requestHandler.signInViewController = self;
[..]
Note, you can also look at delegation and notifications.
I think part of the problem may be with memory management. If showProgressIndicator does not return an autoreleased object, try releasing box after adding it as a subview, like so:
[self.view addSubview:box];
[box release];
box may not disappear if box is not deallocated when removed from the superview.
My other recommendation is that instead of doing it the way you are doing, creating a view, adding it, and then trying to removing it, you might want to try adding box as a subview when the login view is created and setting its hidden property to YES then unhiding it later when necessary.
Based on your update: You have some memory management issues in showProgressIndicator. Whenever you alloc an object, you should release it. In this case, release all of your variables after adding them as subviews as I mentioned above. box however should be returned as an autoreleased object since showProgressIndicator does not know when it will need to be released. For that you should replace return box; with return [box autorelease];
You need to send the activity indicator a stopAnimating message when you want the animation to stop. There's no need to remove it from its superview; instead, simply make sure that its hidesWhenStopped property is set to YES.
How about adding box view on window in appDelgate? Give a tag to your boxView and in the remove method get the boxView back by using tag. For example if you give tag 99
- (void)receivedServerResponse {
UIView *box = [window viewWithTag:99];
[box removeFromSuperview];
}
also you don't need to declare an instance variable in header file. and you can access progress indicator anywhere in the application.
Without spreading myself too thin (probably too late ;)), ASIHTTPRequest is set up so that if you call one method that performs an asynchronous URL request, a predefined method called requestFinished (above) is called, which is why I've had to call my view controller this way, because I can't access the returned value in an easier way (that I know of).
ASIHTTPRequest calls -requestFinished: on the object you set as the request's delegate. You should design your classes such that this delegate object either has a reference to the view controller you want it to act on or has some means of notifying that view controller to take action.
The easiest solution might be to make the controller the request's delegate.

Objective-c How can add one UIview at the main view directly from a class?

I have this class
- (void) initWithFrame:(CGRect)frame withString:(NSString *)html{
//CGRect rectFrame = [UIScreen mainScreen].applicationFrame;
news = [[UIView alloc] initWithFrame:frame];
web = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, 200, 300)];
web.scalesPageToFit = YES;
//web.delegate = self;
web.scalesPageToFit = YES;
[web loadHTMLString:html baseURL:nil];
[news addSubview:web];
[[self view] addSubview:news];
}
I receive Sigabrt at line [[self view] addSubview:news];
How can add the UIview at the main view directly from a class? is it possibile?
1) init<...> methods should return initialized object - declare it returning value of id type. Return self in implementation or nil if it's failed.
2) init<...> methods have to initialize this object. Read about initializing objects here - The Objective-C Programming Language - Allocating and Initializing Objects. Basically you have to call one of the initialization methods of superclass, assign self to returned value (self = [super init]) and check if this value is nil.
Is this class a controller? If so:
3) You have to load it's view - either by loading it from nib on initialization (init or initWithNibName:bundle: methods) or by implementing method loadView. See View Controller Programming Guide for iOS
4) You can be sure your view is available only when viewDidLoad is called on your controller. Implement this method if you want to add something to controller's view.
The reason you are receiving the abort signal is that your view is not initialized. Instead try adding to awakeFromNib call.