Is this how one method calls another method? - iphone

I'm doing some programming and I'm also reading some code at the same time and I come across this code
-(id)init
{
if ((self = [super init]))
{
some code....
[self initEnemy];
}
return self;
}
and below that it has
-(void)initEnemy
{
more code....
[self resetEnemy];
}
and then..
-(void)resetEnemy
{
more code.. etc..
}
The way I see it is the first method called init calls on the method initEnemy and that in turn calls on resetEnemy. Basically one method brings on the other and so on.
Successfully forming an algorithm (you can't really tell because i've shown little code). Am I looking at it the right way?
Also, could I have an explanation on what happens inside the -(id)init method when return self; is performed.

Yup, you're reading that sequence of execution correctly.
When "return" happens in a method, control returns to the "caller" method (whoever originally called it in the first place). The "self" is there to indicate that the value of "self" should be handed back to the caller. (In this case, "self" refers to the instance of the object being initialized in the -init. If you want to know more about initializers, you can break it down into more specific questions.)

Related

Making Xcode complain about a missing parameter

I am designing a new application by modernizing code I wrote in the past. This old code uses the class/delegate model and I am trying to transform them to use blocks as callbacks, not the delegate stuff.
What I do is to create a property like
#property (nonatomic, copy) void (^onTouch)(NSInteger index);
That would pass to the object using that class a block where code can be inserted and in this case executed on touch.
But my problem is this. When you use delegates and you have a method on the delegate protocol, Xcode will warn if you use that class and forget to implement the delegate protocols. Is that a way to do that with blocks? Or in other words: is there a way to make Xcode complain if a callback block is not defined by the caller?
I mean this would be the correct:
MyClass *obj = [[MyClass alloc] init];
obj.onTouch = ^(NSInteger *index){ //call back code to be executed };
This would be OK too
MyClass *obj = [[MyClass alloc] init];
obj.onTouch = nil;
but this would generate a message
MyClass *obj = [[MyClass alloc] init];
// no callback block defined.
Is this possible?
If you want to enforce setting a certain parameter, I would include it in the initializer.
MyClass *obj = [[MyClass alloc] initWithBlock:^(NSInteger *index) { /* code*/ }];
Then, in MyClass:
- (id)init {
// This will result in a runtime error if you use the wrong initializer.
NSAssert(NO, #"Use initWithBlock instead.");
}
- (id)initWithBlock(initWithBlock:^(NSInteger *)block) {
self = [super init];
if (self) {
self.onTouch = block;
}
return self;
}
Also note, attempting to execute a NULL block results in a crash, so make sure to do:
if (self.onTouch) { self.onTouch(); }
Wherever you run the block.
First, I strongly recommend defining types to represent your blocks - makes them a lot easier to work with, especially if you need to refactor the parameters.
You can't write code that distinguishes between "I set this property to nil" or "the runtime initialized this property to nil", at least not without some crazy runtime code to check the stack. Only option I can think of would be to use the null object pattern. Before I elaborate, bear in mind that I haven't actually tried to test this, but it should work. Define a block that means 'has no value' and set your property to point to that block on init. Then you can compare to that NullBlock at runtime to identify if someone explicitly set the property to nil (because it would be nil at that point) or gave it a real non-nil value.
Alternatively, if you don't mind manually writing your set accessors, you could have a BOOL that tracks if someone set the property explicitly. Then when you call the block just check if someone actually set the value or not.
#synthesize onTouchBlock=_onTouchBlock;
MyBlock _onTouchBlock;
BOOL _onTouchBlockWasSet;
- (void)setOnTouchBlock:(MyBlock)block {
_onTouchBlockWasSet = YES;
_onTouchBlock = block;
}
I would not recommend passing the value in the initializer because that makes it tied to the creation of that object type. If you wanted to change the block in code based on some condition, you'd be back to square one. Also, it prevents you from using storyboards which create that object.

custom setter for NSString

I have a NSString called fontType
and I am trying to have a custom setter for it:
- (void) setFontType:(NSString *) fType
{
if (self.fontType != fType){
[fontType release];
self.fontType = [fType retain];
//some more custom code
}
}
Is there any issue with this?
A few things that stand out for me:
do not use self. inside of custom accessors. access the variable directly
it's better use copy semantics for properties of a type that has a
mutable subtype
be careful with whatever is // some more custom code
My personal style preferences are like so:
-(void)setFontType:(NSString *)fontType_ {
if (fontType == fontType_) return; // get out quick, flatten the code
[fontType release];
fontType = [fontType_ copy];
// some more code
}
Cocoa with Love has a good article on this topic. It's worth a read.
When you do self.fontType = newFontType, you are doing [self setFontType:newFontType] (just with a different syntax), this means you are calling the method inside itself.
This is called recursion, but in this case, you don't have a base case in which the method will stop calling itself, so my guess is that this method will call itself until the app crashes. In short, recursion is not something you want here.
Just replace self.fontType = [fType retain] with fontType = [fType retain] (Assuming the var linked to the fontType property is called fontType as well).
PS. At the end of the question you asked
Is there any issue with this?
If you didn't try this, then you shouldn't even be asking that here on StackOverflow, and if you did try it, then you should have realized that this method didn't work, so that last line is pretty meaningless. Just saying.

Problem with reinitializing of string in iPhone

I have an interface like this:
#interface MacCalculatorAppDelegate:NSObject
<UIApplicationDelegate> {
// ...
UIButton *operatorPressed;
NSString *waitingOperation;
}
And I am initializing waitingOperation variable in my implementation like this:
- (id)init {
if (self = [super init]) {
waitingOperation = #"not set";
}
return self;
}
And I want to reinitialize this variable in a function. This is calculator program and when user clicks on operators button the following function will be invoked:
- (IBAction)operatorPressed:(UIButton *)sender {
if([#"+" isEqual:operand]) {
waitingOperation = #"+";
}
}
But after the check in if statement, my program won't do anything and this happens when I am trying to reinitialize waitingOperation variable.
I am new to objective-c, please help me understand what's wrong here.
Thanks in advance.
There are several things to note here.
waitingOperation=#"not set";
As it stands, this will eventually crash your program. Objective C string literals are autoreleased instances of NSString, which means unless you assign it to a retained property or retain it manually, the memory will be deallocated, leaving a dangling pointer.
-(IBAction) operatorPressed:(UIButton *)sender {
Have you verified that this method is actually being called? You have to assign the IBAction in Interface Builder. Step through it in the debugger or use NSLog to verify that this method is being called.
if([#"+" isEqual:operand])
Where is operand coming from?
waitingOperation=#"+";
Same problem as above, this will get deallocated behind the scenes, leaving you with a dangling pointer.
Also, note that if you know that both variables are NSStrings, using isEqualToString: is faster than using isEqual:.
Finally, this stuff shouldn't be part of your app delegate. This is view controller logic.
If your operand is also a string, then check for isEqualToString instead of isEqual

Change classes instantiated with loadNibNamed

I am trying to change the class of objects created with a nib with the iPhone SDK.
The reason for this is; i dont know until runtime what the class is that i want the nib object to be (though they will have the same UIView based super class), and i dont want to create a different nib for every eventuality - as the .nib will be the same for each, apart from the class of one object.
I have been successful, with a couple of methods, but either have some knock on effects or am unsure of how safe the methods I have used are:
Method 1: Override alloc, on the super class and set a c variable to the class I require:
+ (id) alloc {
if (theClassIWant) {
id object = [theClassIWant allocWithZone:NSDefaultMallocZone()];
theClassIWant = nil;
return object;
}
return [BaseClass allocWithZone:NSDefaultMallocZone()];
}
this works well, and i assume is 'reasonably' safe, though if have a nib with the correct class as the class identity in the Nib, or I alloc a subclass myself (without setting 'theClassIWant') - an object of the base class is created. I also dont really like the idea of overriding alloc...
Method 2: use object_setClass(self,theClassIWant) in initWithCoder (before calling initWithCoder on the super class):
- (id) initWithCoder:(NSCoder *)aDecoder {
if (theClassIWant) {
// the framework doesn't like this:
//[self release];
//self = [theClassIWant alloc];
// whoa now!
object_setClass(self,theClassIWant);
theClassIWant = nil;
return [self initWithCoder:aDecoder];
}
if (self = [super initWithCoder:aDecoder]) {
...
this also works well, but not all the subclasses are necessarily going to be the same size as the super class, so this could be very unsafe! To combat this i tried releasing and re-allocing to the correct type within initWithCoder, but i got the following error from the framework:
"This coder requires that replaced objects be returned from initWithCoder:"
dont quite get what this means! i am replacing an object in initWithCoder...
Any comments on the validity of these methods, or suggestions of improvements or alternatives welcome!
While I'm curious to see if you can pull this off using your approach, you may want to consider using custom placeholder objects.

If a method is called at the exact same time twice, how to only execute it once?

We have a method in the iPhone SDK that is a delegate method. Problem is, the OS calls this method twice at the exact same time. This method does some heavy lifting so I don't want to execute the logic twice. What is a good way to detect this and prevent one of the two from running?
Forgot to mention that, it is called from different threads.
One method is a BOOL member that you set when entering the method and clear on leaving it. If the variable is set upon entry, you know it's already executing and can just return.
Assuming you're being called from multiple threads, you'll want to lock access to this critical area of checking/setting. An NSLock is good for this.
The code below has two implementations: myMethod1 which uses NSLock and myMethod2 which shows using #synchronize.
#interface MyClass : NSObject
{
NSLock* theLock;
BOOL isRunning;
}
#end
#implementation MyClass
-(id)init
{
self = [super init];
if(self != nil)
{
theLock = [[NSLock alloc] init];
isRunning = NO;
}
return self;
}
-(void)dealloc
{
[theLock release];
[super dealloc];
}
// Use NSLock to guard the critical areas
-(void)myMethod1
{
[theLock lock];
if(isRunning == YES)
{
[theLock unlock]; // Unlock before returning
return;
}
isRunning = YES;
// Do fun stuff here
isRunning = NO;
[theLock unlock];
}
// This method uses #synchronize
-(void)myMethod2
{
#synchronized(self)
{
if(isRunning == YES)
{
return;
}
isRunning = YES;
// Do stuff here.
isRunning = NO;
}
}
#end
Wow. That answer is correct, but way over-engineered. Just use #synchronized().
Foo.h:
#interface Foo
{
id expensiveResult;
}
- (void) bar;
#end
Foo.m:
#implementation Foo
- (void) bar
{
#synchronized(self) {
if (expensiveResult) return expensiveResult;
.... do expensive stuff here ....
expensiveResult = [theResult retain];
}
return expensiveResult;
}
#end
If you have multiple instances of Foo and want to guarantee exclusivity across all instances, create a global variable in +(void)initialize -- an NSString will do fine -- and #synchronized() on that.
However, your question raises a much more important question. In particular, there is never a case where the same method is going to be called twice simultaneously unless you quite explicitly configured your application to cause exactly that to happen.
The answer(s) provided sound more like a fix to a symptom and not a fix for the real problem.
Note: This is relying on expensiveResult being nil, which it will be as all iVars are nil on instantiation. Obviously, reset the ivar to nil if you want to recalculate.
simplest is to set a flag.
- (void)action {
if (flag_is_set == NO) {
flag_is_set = YES;
do stuff
flag_is_set = NO;
}
}
this is not 100% safe though as you may get some rare cases of interlocking.
If you can handle some sleeps on the thread, use a nslock
- (id)init {
theLock = [[NSLock alloc] init];
}
- (void)action {
[theLock lock];
doStuff
[theLock unlock];
}
When thread 2 comes to the lock call and thread 1 already has it, it will sit in the execution loop until the lock is released, then it will start again. If you have UI on this thread, you app will appear to freeze
Some of the given answers are acceptable solutions to the problem of multiple "producer" threads calling the same function at the same time but you might be better off figuring out why multiple threads are calling this same block of code at the same time. It could be that you are assigning this delegate to multiple event handlers or something like that. You have probably noticed that this is occurring because some shared state is being mangled or the output of the function is not correct for the "global" state at the end of the function. Putting a bandaid over the fact 2 threads are in a given function (when its clear that threading was not a primary concern when this was written) is not going to necessarily give you the right results. Threading is not trivial and shared state makes it very tricky to get right, make sure that you completely understand why this is occurring before just trying to patch over it.
That being said, if you do take the bandaid approach, its probably better to do one with a lock where every thread eventually gets to execute the function rather than having them bail out if the function is allready started because to the client code it would look like that long and "heavy-lifting" process has completed and they may check for the results.
If these calls are synchronized, so only one happens at a time, you can just have a variable on your class, called something like "initialized", which starts off false and is set when initialized:
if (!initialized) {
// handle delegated call
initialized = 1;
}
If the delegate is called from multiple threads, you need to put all this in a mutex block.
Here's how you can use objective-c locks as mutexes.
http://rosettacode.org/wiki/Mutex#Objective-C
Mutexes exist to allow mutually exclusive access to a certain block of code. Within the method you can do something like:
[methodLock lock]; //Blocks and waits until the lock is released
//...do stuff
[methodLock unlock]; //Releases the lock once the code has executed.
This will ensure that only one thread will be allowed within the //do stuff block of code.
EDIT: I read the question again; within the lock I'd check the flag to see if it's run (using a BOOL)
Use pthread_once() -- it was explicitly designed to do exactly this. The only problem is that the function you pass it can't take any arguments, so you have to use global variables to pass information to it.
If they are called at the exact same time, I guess they are called in threads?
What you can do is define a BOOL #property (called isRunning for example) with the attribute atomic (set by default). This way this property can be accessed safely from different threads, and then you can do something like:
if (isRunning)
return ;
isRunning = YES;
// ...
// Your code here
// ...
usRunning = NO;
You might also make sure that you are doing the right thing. If your method is called twice, maybe you're doing something wrong (or maybe it's normal, I don't know what you are doing ;))