Objective-c class initializer executes multiple times - iphone

Unless I misunderstood it, this LINK from Apple's documentation clearly states that the class initializer, "+ (void)initialize)", only executes once per class.
Here is the excerpt:
Special Considerations
initialize it is invoked only once per class. If you want to perform independent initialization for the class and for categories of the class, you should implement load methods.
However, I'm getting a weird behavior on my project and the initializer is being executed twice. So I have to check if _classContext is null. I only got one class that has this method. What are the possible reasons why this is happening?
I'm using XCode 4.5.2 and OS X 10.8.2. I got multiple iOS simulators, iPhone 5.1 and 6.0.
+ (void) initialize
{
num++;
NSLog([NSString stringWithFormat:#"Times: %i", num]);
if(_classContext == nil)
_classContext = [[myClass alloc] init];
}

This will happen if you have a subclass of this class. The initialize method will be called for the class and each subclass.
The proper way to code the initialize method is:
+ (void)initialize {
// Replace ThisClass with the actual class name
if (self == [ThisClass class]) {
// do initialization here
}
}

+initialize can be called more than once and one should use caution when overriding +initialize.
Please read the blog post +initialize Can Be Executed Multiple Times (+load not so much) at bbum's weblog-o-mat.

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.

How to tell if a Class inherits from NSObject (Objective-C)

I'm working in Objective-C on the iPhone and need to know whether a 'Class' inherits from 'NSObject'.
I tried checking if it responds to an NSObject selector:
bool success = [myClass respondsToSelector:#selector(class)];
but you can guess what happened... it didn't even respond to "respondsToSelector:" so it throws a "does not implement doesNotRecognizeSelector:" exception.
I tried to catch that exception, but it looks like it can't be caught with a #try-#catch.
Any ideas?
Go direct to the Objective-C runtime:
#import <objc/runtime.h>
/* originally posted version — works because eventually class_getSuperclass(class)
returns nil, and class_getSuperclass(nil) does so also. */
BOOL classDescendsFromClass(Class classA, Class classB)
{
while(1)
{
if(classA == classB) return YES;
id superClass = class_getSuperclass(classA);
if(classA == superClass) return (superClass == classB);
classA = superClass;
}
}
/* shorter version; exits straight after classA turns into nil */
BOOL classDescendsFromClassShorter(Class classA, Class classB)
{
while(classA)
{
if(classA == classB) return YES;
classA = class_getSuperclass(classA);
}
return NO;
}
...
if(classDescendsFromClass(classToTest->isa, [NSObject class]) ...
class_getSuperclass does what it says, and it's safe to compare metaclasses by pointer in the Objective-C runtime because there is only exactly one instance of the metaclass for each class. The isa pointer is the only thing that's definitely in struct objc_object.
EDIT: additionally, there are known bugs in the iPhone simulator that cause some exceptions not to be caught by try/catch blocks. I've reported them as a bug to Apple and been told that mine was a duplicate, so they are definitely aware. Did you try your code on a real device or just in the simulator?
EDIT2: from the wider context given elsewhere in this conversation, something like this might be smarter:
#import <objc/runtime.h>
BOOL classRespondsToSelector(Class classA, SEL selector)
{
return class_getInstanceMethod(classA, selector) ? YES : NO;
}
....
if(classRespondsToSelector(instance->isa, #selector(respondsToSelector:))
{
// great, we've got something that responds to respondsToSelector:; do the
// rest of our querying through there
}
You can use the methods isKindOfClass: and isMemberOfClass: to determine whether a class is a subclass of another class or if it is a particular class.
respondsToSelector: is itself an NSObject-defined selector, so you can't use it. I don't believe there's a way to do this without getting very deep into the internals of Objective-C.
May I ask why you have objects that aren't descendants of NSObject? Apple very strongly recommends you don't attempt to create them, and with good reason.
The class 'Class' does not inherit from NSObject. That means methods defined by NSObject (such as isKindOfClass or respondsToSelector) cannot be used on it.
What are you trying to do with it in the first place?

+(void) initialize in objective-c class static variables constructor

I found some sample code from here.
static UIImage *backgroundImageDepressed;
/**
*
*/
#implementation DecimalPointButton
+ (void) initialize {
backgroundImageDepressed = [[UIImage imageNamed:#"decimalKeyDownBackground.png"] retain];
}
Is it something like this - +(void) initialize method initialize static variables of a class ( interface ) in Objective C? I have never seen this before.
This +initialize method is described in The Objective-C Programming Language.
The runtime system sends an initialize message to every class object before the class receives any other messages and after its superclass has received the initialize message. This gives the class a chance to set up its runtime environment before it’s used. If no initialization is required, you don’t need to write an initialize method to respond to the message.
For example, when [DecimalPointButton alloc] is called, the runtime will check if [DecimalPointButton initialize] has been called. If not, it will +initialize the class. This ensure the backgroundImageDepressed image is ready before any instances of DecimalPointButton are constructed.

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.

Should +initialize/+load always start with an: if (self == [MyClass class]) guard?

When implementing an +initialize or +load method in one of your Objective-C classes, should you always start with this kind of guard?:
#implementation MyClass
+ (void)initialize {
if (self == [MyClass class]) {
...
}
}
...
#end
Seems like code in +load and +initialize usually only wants to be executed once. So this would help avoid dupe execution when subclasses load/initialize.
I guess I'm just wanting some reinforcement from some ObjC wizards that this is necessary/common practice...
What's the common wisdom on this? would you recommend always doing this?
Is your advice the same for both +load and +initialize, or is there a difference in they way they should be handled?
thanks.
The quick answer is: No.
An in-depth discussion of this matter can be found on the Apple developer mailing list.
The gist of it is that:
The runtime will actually call +initialize on super classes before it is called on subclasses.
If you do include the guard, subclasses of your class that have their own +initialize method will not trigger dependent KVO notifications.
For an example of point #2, be sure to read this post in the thread mentioned above.
Yes, you should do this in your intialize and load methods if you are initializing globals that should only be initialized once.
That said, there are a number of cases where you may avoid it...
You shouldn't wrap with this conditional if the work needs to be performed on every inheritant of every class:
For example, adding all inherited class names for each class to a set.
edited addition: or you're establishing KVO dependencies (as mentioned by eJames)
There are also situations where you just needn't bother:
If the actions you perform are idempotent (don't change values if repeated)
The class is "sealed" (has no descendants by design)
The "idempotent" part is relevant. An initializer should just be setting the initial state (which should be the same each time). In a good initializer, repetition shouldn't matter. Although I suppose that if you forget to wrap the method in the conditional when it does matter, this might be annoying.
edited addition: A different approach, that properly reflects any initialize-only-once requirements would be to test if your properties to initialize are initialized already. i.e.
id myGlobalObject = nil;
+(void)initialize
{
if (myGlobalObject == nil)
{
myGlobalObject = [[MyGlobalClass alloc] init];
}
}
YES!!!!
Because the initialize method of a class may be invoked many times. e.g. when you implement initialize in parent class, and don't implement in sub class, then you call sub class first, the initialize of parent will invoked twice.
#implementation BaseClass
+ (void)initialize
{
NSLog(#"BaseClass initialize self=%#, class=%#", self, [BaseClass class]);
}
#end
#interface SubClass : BaseClass
#end
#implementation SubClass
// don't implement the initialize method
#end
==================
now when you call SubClass first, just like
[SNSBaseSubLogic alloc]
look the debug console, output:
BaseClass initialize self=BaseClass, class=BaseClass
BaseClass initialize self=SubClass, class=BaseClass
so, you must use
+ (void)initialize
{
if (self == [BaseClass class]) {
NSLog(#"BaseClass initialize self=%#, class=%#", self, [BaseClass class]);
}
}
to ensure the method body execute once.