Delegation question - iphone

code structure
#protocal A_Delegate
{
-(void)doIt:(BOOL)isDone;
}
Super Class A // has properties of set delegate
-(void) setDelegate:(id<A_Delegate>)_delegate
{
/*self.delegate = _delegate*/ error, compiler stops right there and doesn't assigns the value from '_delegate'
self.delegate = _delegate.
//should be
delegate = _delegate;
}
Sub Class B : A // want to call and define the delegation for the super class of B which is A
-(void) acquireDelegation:(id<A_Delegate>)_delegate
{
[[super delegate] setDelegate];
}
Now, the Class C want to use the Class B and want to know its state,
Class C : NSObject <A_Delegation>
-(void) doSomething
{
B *b = [[B alloc]init];
[b aquireDelegation:self];
}
-(void)doIt:(BOOL)isDone
{
if(isDone)
// Do Something
}
Does any body know What I have done wrong and why super can't delegation?
Is it possible to fix?
resolved.

This would lead to infinite loop (until the stack overflow):
-(void) setDelegate:(id<A_Delegate>)_delegate
{
self.delegate = _delegate; // error, compiler stops right there and doesn't assigns the value from '_delegate'
}
cause self.delegate = _delegate; is calling setDelegate:. You have to assign to the ivar itself.
And I think this is not compiler. It is everything in runtime...

There are a couple issues. First, that protocol definition, well, isn't one. The syntax is all wrong. Secondly, what is "Super Class A" is that meant to be a comment?
Anyhoo, the major problem is in the setDelegate: method. If you have defined the delegate property then the line:
self.delegate = _delegate;
is equal to the line:
[self setDelegate:_delegate];
So, in the method -setDelegate you are calling -setDelegate.

Related

Calling [[super allocWithZone:nil] init], messaging mechanism

f. e. (just for understanding messages mechanism more clear) I have class
MyClass.h
#interface MyClass : NSObject {
int ivar1;
int ivar2;
}
+ (id)instance;
#end
MyClass.m
static MyClass* volatile _sInstance = nil;
#implementation MyClass
+ (id)instance {
if (!_sInstance) {
#synchronized(self) {
if (!_sInstance) {
_sInstance = [[super allocWithZone:nil] init];
}
}
}
return _sInstance;
}
#end
What will be send in objc_msgSend in fact when calling [super allocWithZone:nil] ?
objc_msgSend([MyClass class], "allocWithZone", nil) or objc_msgSend([NSObject class], "allocWithZone", nil) ?
In practice I think that called objc_msgSend(self, "allocWithZone", nil) and in that case self == [MyClass class];
I want to be sure that memory for ivar1 and ivar2 will be allocated.
Is it true, that when we call super in class method, in objc_msgSend() function the "self" argument is passed, that in our case is class object of child? And allocWithZone will "look" at the child class object to see how much memory should be allocated for ivar1 and ivar2.
Thanks!
Any message to super is translated by the compiler to objc_msgSendSuper (not objc_msgSend). The first argument is a pointer to a struct. The struct contains a pointer to the super class of the current implementation and the a pointer to the receiver. The former is needed during runtime to search for the overridden implementation, the latter is used as the first argument.
In the case of a class method the receiver is again a class pointer, yet not the same as the super_class. In your case the receiver is a MyClass pointer while the super_class pointer would be NSObject.
Two side notes: I recommend against putting energy in writing the fanciest Singleton. Better leave it up to the developer to create his own instances or use the provided shared instance. And please note that double-checked locking is broken.

Call a method from another class when shake is detected

HERE IS THE CODE: http://min.us/mWdMO0n14
I'm a Obj C newbie, so I've searched quite a bit, but haven't found anything that can solve my problem.
I have CalculatorViewController.h and .m and then CalculatorBrain.h and.m (Stanford Lectures)
in CalculatorBrain.m, I have the following method, with all of the variables defined as private in the CalculatorBrain header.
- (void)clearEverythingOnShakeGesture{
operand = 0;
waitingOperation = #"";
waitingOperand = 0;
}
Then in CalculatorBrain.m , I have everything set up to detect shakes, as follows. I've included some of the code above the shake detection just so you have a general idea.
#interface CalculatorViewController()
#property(nonatomic, retain) CalculatorBrain *brain;
#end
#implementation CalculatorViewController
#synthesize brain;
- (CalculatorBrain *)brain {
if (!brain) {
brain = [[CalculatorBrain alloc] init];
}
return brain;
}
-(BOOL)canBecomeFirstResponder{
return YES;
}
-(void)viewDidAppear: (BOOL) animated{
[super viewDidAppear:animated];
[self becomeFirstResponder];
}
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if (event.subtype == UIEventSubtypeMotionShake)
{
NSLog(#"SHAKE IT!");
[brain clearEverythingOnShakeGesture]; //********** not sure how to call this.
}
}
I'm not sure how to call [brain clearEverythingOnShakeGesture]; , because I get the error "Class method +clearEverythingOnShakeGesture not found, defaults to return type id". However, if I make it a class method, the variables inside are instance variables, which provides another error. Any help greatly appreciated.
The project's AppDelegate posted in the comment above is building the calculator view controller from a nib, then releasing it immediately. The app functions partially, but the UILabel property to be cleared on the shake gesture is nulled at that point.
Also, it's a good practice to declare private properties in the private category, synthesize them with _underscore aliases, and refer to them as self.property outside of synthesized methods.
Are you #import-ing the CalculatorBrain.h? Also, you're using a nice lazy initialization pattern by building the CalculatorBrain in the getter, but you're not calling the getter in the motionBegan: method. Try [self.brain clearEverything ...] to get the brain instance.
I don't see anything in the code that would make the compiler think you have a class method. So that's mysterious. Please double check about the header import. You are correct that the clearEverything... should be an instance method.

Problem when accessing an object (UIView) with another class object!

I have two classes A and B.
Class A contains a UIView named myView and also a method MyMethod to set the position of the myView.
-(void)MyMethod:(NSString *)justCheck
{
[self.view addSubview:myView];
[self.view bringSubviewToFront:myView];
CGRect mframe = [myView frame];
NSLog(#"------------> MyMethod Called = %#",justCheck);
// EDIT: the following NSLogs are added later--------------------
NSLog(#"------------> MyMethod Called:mframe:x = %g",mframe.origin.x); //0
NSLog(#"------------> MyMethod Called:mframe:y = %g",mframe.origin.y); //42
NSLog(#"------------> MyMethod Called:mframe:H = %g",mframe.size.height); //317
NSLog(#"------------> MyMethod Called:mframe:W = %g",mframe.size.width); //320
//---------------------------------------------------------------
mframe.origin.y = 42;
[myView setFrame:mframe];
}
When a button in the class A named buttonOfA calls this MyMethod, it works perfectly and I can see the myView in position 42.
code is as below,
-(IBAction)buttonOfA:(id)sender
{
[self MyMethod:#"I am A"];
}
But, when the button of class B named buttonOfB tries to call this method, NSLog works but I cannot see the myView in position 42. Code as below,
-(IBAction)buttonOfB:(id)sender
{
[objOfA MyMethod:#"I am B"]; //objOfA is the object of class A
}
What is happening here??
I have been trying hard to figure out the problem, But I couldn't. Plz help me.
Thanx :)
EDIT: four NSLogs are added in myMethod()
Make sure the myView instance variable in the class A object is initialized and added to a super view when class B calls MyMethod: on it. Otherwise setting the frame won't have any effect.
Hold a reference to the object of type A, but it should be the same object which created the UIView. Hence it is better to return the singleton instance of the object.
Using the returned singleton for the Class A object call your method.
because myView is not a property just make it property of class A and do this work
-(void)MyMethod:(NSString *)justCheck
{
CGRect mframe = [self.myView frame];
NSLog(#"------------> MyMethod Called = %#",justCheck);
mframe.origin.y = 42;
[self.myView setFrame:mframe];
}
To make it a property instead of declaring it as
UIView* myView;
Do it as like this in .h file
#property(nonatomic,retain) UIView* myView;
and in .m of class A do it like this
#synthesize myView;
and you don't have to change anything in code just replace all myView with self.myView

Cocoa -- change class of an object in app update?

I have two objects of class WidgetClass in my stored model. They are saved each time the app exits and reloaded each time it starts. I want to update my model to make one of them a WidgetSubclass object. WidgetSubclass will be a subclass of WidgetClass.
WidgetClass has quite a lot of ivars. WidgetSubclass will add few or none.
What is the most efficient way to accomplish the update? I am not using core data.
Couple of things.
If the subclass does not add any ivars to the superclass, you can actually get away with the following:
WidgetSubclass* widget = (WidgetSubclass*)[[WidgetClass alloc]initWithCoder: someCoder];
Class object_setClass(widget, [WidgetSubclass class]);
There is some risk that changes in the runtime could break the above code. So here is a safer way:
Foo.m:
-(void) copyIvarsTo: (Foo*) foo {
[super copyIvarsTo: foo];
foo.ivar1 = [self.objectIvar1 copy];
foo.ivar2 = [self.objectIvar2 copy];
foo.floatIvar = self.floatIvar;
// etc. Method works fine if foo is actually a member of a subclass.
}
-(Foo*) copy {
Foo* clone = [[self class]alloc];
[self copyIvarsTo: clone];
return clone;
}
Now I can have the following NSObject category method:
-(NSObject*) wj_copyWithSubclass: (Class) subclass {
if (![self respondsToSelector: #selector(copyIvarsTo:)])
return nil;
NSAssert([subclass isSubclassOfClass: [self class]], #"call copyWithSubclass only on subclasses");
NSObject* clone = [subclass alloc];
[self copyIvarsTo: clone];
return clone; // at this point, clone has copied all the ivars that are members of the receiver's class. Other ivars have their default values. Calling code needs to handle that.
}

Dealing with objects from different classes

I have 3 classes of objects. All 3 classes share some properties in common, as color, text, etc.
For example, I can have this
Class1 *objectA = [[Class1 alloc] init];
objectA.myColor = [UIColor redColor];
Class2 *objectB = [[Class2 alloc] init];
objectA.myColor = [UIColor redColor];
Class3 *objectC = [[Class3 alloc] init];
objectA.myColor = [UIColor redColor];
... etc.
Now I need, for example, to create a method that can change the color of a given object, whatever class it represents.
A typical method would be
- (void) changeColor:(Class1*) myOBJ toColor:(UIColor*)myColor {
myOBJ.color = myColor;
}
when in fact I need this
- (void) changeColor:(???) myOBJ toColor:(UIColor*)myColor {
myOBJ.color = myColor;
}
// what to put on ??? to make it generic? Is this a "whatever" kind?
thanks
EDIT
the problem of using this approach
- (void) changeColor:(id)myOBJ toColor:(UIColor*)myColor {
if ([myOBJ respondsToSelector:#selector(setColor:)]) {
myOBJ.color = myColor;
}
}
is this. Imagine I want to set the frame of the object.
Then I will have to have this:
- (void) changeColor:(id)myOBJ newFrame:(CGRect)myFrame {
if ([umID isKindOfClass:[Class1 class]]) {
Class1 *oneObj = (Class1 *)myObj;
oneObj.frame = myFrame;
}
if ([umID isKindOfClass:[Class2 class]])
Class2 *oneObj = (Class2 *)myObj;
oneObj.frame = myFrame;
}
if ([umID isKindOfClass:[Class3 class]])
Class3 *oneObj = (Class3 *)myObj;
oneObj.frame = myFrame;
}
}
in other words, I will have to repeat the same stuff 3 times... right?
in other words, the problem is not solved as this is the same of having 3 methods, one for each class.
Maybe you can use protocols? Make Class1, Class2 and Class3 conform to a protocol with a property myColor. Then you could have a method like this (assuming your classes are of type UIView and your protocol is called ColorProtocol):
- (void) changeColor:(UIView<ColorProtocol>*) myOBJ toColor:(UIColor*)myColor {
myOBJ.color = myColor;
myOBJ.frame = ...;
}
Here is what your protocol definition could look like:
#protocol ColorProtocol
#property (nonatomic, retain) UIColor *myColor;
#end
Change your class definition files (.h) as follows to specify that you will conform to the protocol:
interface Class1 : UIView <ColorProtocol> {...}
In the implementation files (.m) you must simply synthesize the myColor property to conform to the ColorProtocol:
#synthesize myColor;
If your classes are very similar, using inheritance might be even simpler though. Check out Philip Regan's answer.
You have a couple options. The simplest, and "most dangerous" approach is to use a type id. This will let you pass in any object, but you'll want to test that it actually has a color property before you try and set it.
- (void) changeColor:(id)myOBJ toColor:(UIColor*)myColor {
if ([myOBJ respondsToSelector:#selector(setColor:)]) {
myOBJ.color = myColor;
}
}
(That said, with the responds to selector check, this approach isn't all that dangerous, and it's much more flexible than the next idea.)
Another approach is to have all your objects inherit from a shared base class that has a color property. Then your parameter type would be the base class. This approach could be considered "safer" as the compiler would check that you're passing in the correct type of object. This approach also requires considerably more code.
If you want to use the first approach, but set something other than color, adjust the respondsToSelector: call appropriately.
- (void) changeFrame:(id)myOBJ newFrame:(CGRect)myFrame {
if ([myOBJ respondsToSelector:#selector(setFrame:)]) {
myOBJ.frame = myFrame;
}
}
In general, if you want to know if an object supports propertyX, use [myOBJ respondsToSelector:#selector(setPropertyX:)]. If the passed in object is declared as id, you can then call [myOBJ setPropertyX:newPropertyValue] or myObj.propertyX = newPropertyValue.
If you have multiple classes that share characteristics, then, if at all possible, I suggest refactoring the class structure so that those characteristics are contained in an umbrella parent class, we'll call it ClassZ. ClassZ's subclasses can override things as needed. Otherwise, let the method in the parent class handle it for you. Then, your method turns back into this...
- (void) changeColor:(ClassZ *) myOBJ toColor:(UIColor*)myColor {
myOBJ.color = myColor; // note, myObj is ClassZ, not the subclasses.
}
Otherwise, you are stuck with id and testing the individual classes.
use [object setFrame:newFrame]; instead of object.frame = newFrame;
and instead of oldFrame = object.frame; use oldFrame = [object frame];
??? will be 'id'.