My question is kind of weird, lets say I have a class inheriting from UITableViewCell called GenericTableViewCell and some more classes inheriting from GenericTableViewCell.
I want to be able to pass an argument to the GenericTableViewCell init method that will tell me which subclass of GenericTableViewCell should init this TableViewCell as.
Heres what I thought of but I know that it will fail because it has a recursive loop in it.
#implementation GenericTableViewCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier cellIdentifier: (CellIdentifier *) identifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
if ([identifier class] == [FirstIdentifier class]){
self = [[FirstTableViewCell alloc] initWithStyle:style reuseIdentifier:reuseIdentifier];
}
/// more else statements to check for other identifier cases
}
return self;
}
#end
Is there any way to do this? Or should I just check the identifier outside of the init function and by that decide which cell do I declare?
Yes, you can do this. It's a pattern you sometimes come across called a class cluster. If you are not using ARC, you must release the original value of self to stop a memory leak.
However, I would not do this. I would create a factory method in GenericTableViewCell.
+(GenericTableViewCell*) cellWithStyle: (UITableViewCellStyle)style
reuseIdentifier: (NSString *)reuseIdentifier
cellIdentifier: (CellIdentifier *) identifier
{
GenericTableViewCell* ret = nil;
if ([identifier class] == [FirstIdentifier class])
{
ret = [[FirstTableViewCell alloc] initWithStyle:style reuseIdentifier:reuseIdentifier];
}
else
{
// ....
}
return [ret autorelease];
}
You can eliminate the if statement by adding a method to CellIdentifier and overriding it in subclasseslike this:
// in CellIdentifier.m
-(id) classForCell
{
return [GenericTableViewCell class];
}
// in FirstIdentifier.m
-(id) classForCell
{
return [FirstTableViewCell class];
}
Then your factory method becomes
+(GenericTableViewCell*) cellWithStyle: (UITableViewCellStyle)style
reuseIdentifier: (NSString *)reuseIdentifier
cellIdentifier: (CellIdentifier *) identifier
{
return [[[[identifier classForCell] alloc] initWithStyle:style
reuseIdentifier:reuseIdentifier] autorelease];
}
The thing you should implement is called class cluster pattern. In your situation you shouldn't call initWithStyle:reuseIdentifier: on a subclass rather than in subclass' initializer:
In GenericTableViewCell:
- (id)initWithCustomIdentifier:(NSString *identifier) {
Class cellClass = NSClassFromString(identifier);
if (!cellClass) {
cellClass = [MyStandardTableViewCell class];
}
self = [[cellClass alloc] init];
return self;
}
In MyStandardTableViewCell (or any other subclass of GenericTableViewCell:
- (id)init {
self = [super initWithStyle:someStyle reuseIdentifier:NSStringFromClass([self class])];
if (!self) return nil;
// do extra setup here
return self;
}
Related
I have a custom non-ARC init, and I wonder if I should call [super init] before releasing self.
Solution A, not calling [super init] before [self release]:
- (instancetype)initWithInteger:(NSInteger)anInteger
{
if (anInteger == 0)
{
[self release];
return nil;
}
self = [super init];
if (!self)
return nil;
// Initialization code
self.i = anInteger;
return self;
}
Solution B, calling [super init] before [self release]:
- (instancetype)initWithInteger:(NSInteger)anInteger
{
self = [super init];
if (!self)
return nil;
if (anInteger == 0)
{
[self release];
return nil;
}
// Initialization code
self.i = anInteger;
return self;
}
I would go with the second pattern. The reason for this is that super's dealloc might possibly rely on something in super's init having been done to work properly.
Here is a (very) contrived example, this is the init and dealloc method in a class that you are subclassing.
#implementation ASuperClass
{
char* foo;
}
-(id) init
{
self = [super init];
if (self != nil)
{
foo = strdup("blah blah blah");
}
return self;
}
-(void) dealloc
{
if (foo[1] == 'l')
{
NSLog(#"Second character was l");
}
free(foo);
}
In the above, if this is the class you inherited from, your first pattern will throw an EXC_BAD_ACCESS on a null pointer dereference.
I'm trying to subclass NSTokenField to intercept some of the keyboard events. I wrote subclasses for NSTokenField, NSTokenFieldCell and NSTextView. In the NSTokenField subclass I swap the regular cell with my custom cell and in the custom cell I override -(NSTextView*)fieldEditorForView:(NSView *)aControlView to provide my textview as a custom field editor. All the initialisation methods are called as expected but for some reason my custom token field is not drawn.
Here is the code for the NSTokenField subclass:
#synthesize fieldEditor = _fieldEditor;
-(JSTextView *)fieldEditor
{
if (!_fieldEditor) {
_fieldEditor = [[JSTextView alloc] init];
[_fieldEditor setFieldEditor:YES];
}
return _fieldEditor;
}
- (void)awakeFromNib {
JSTokenFieldCell *newCell = [[JSTokenFieldCell alloc] init];
[self setCell:newCell];
}
+ (Class) cellClass
{
return [JSTokenFieldCell class];
}
- (id)initWithFrame:(NSRect)frameRect
{
self = [super initWithFrame:frameRect];
if (self) {
JSTokenFieldCell *newCell = [[JSTokenFieldCell alloc] init];
[self setCell:newCell];
}
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
JSTokenFieldCell *newCell = [[JSTokenFieldCell alloc] initWithCoder:aDecoder];
[self setCell:newCell];
}
return self;
}
And here is the code for the subclass of NSTokenFieldCell:
-(NSTextView*)fieldEditorForView:(NSView *)aControlView
{
if ([aControlView isKindOfClass:[JSTokenField class]]) {
JSTokenField *tokenField = (JSTokenField *)aControlView;
return tokenField.fieldEditor;
}
return nil;
}
- (id)initWithCoder:(NSCoder *)decoder
{
return [super initWithCoder:decoder];
}
- (id)initTextCell:(NSString *)aString
{
return [super initTextCell:aString];
}
- (id)initImageCell:(NSImage *)anImage
{
return [super initImageCell:anImage];
}
Addition
After further digging I found this post which says that the only way to have an NSTokenField with a custom text view is by overriding private methods. Is it true? If so, is there any other way I can intercept keyboard events without subclassing NSTextView?
i have a question about initializing a custom delegate.
Within MyScrollView initWithFrame method, there is the first position where i need to send my delegate. But it´s still unknown there, because i set the delegate within MyCustomView after the initializer.
How can i fix that, so the delegate gets called even within init?
Thanks for your help..
MyCustomView.m
self.photoView = [[MyScrollView alloc] initWithFrame:frame withDictionary:mediaContentDict];
self.photoView.delegate = self;
//....
MyScrollView.h
#protocol MyScrollViewDelegate
-(void) methodName:(NSString*)text;
#end
#interface MyScrollView : UIView{
//...
__unsafe_unretained id <MyScrollViewDelegate> delegate;
}
#property(unsafe_unretained) id <MyScrollViewDelegate> delegate;
MyScrollView.m
-(id) initWithFrame:(CGRect)frame withDictionary:(NSDictionary*)dictionary{
self.content = [[Content alloc] initWithDictionary:dictionary];
self = [super initWithFrame:frame];
if (self) {
//.... other stuff
// currently don´t get called
[self.delegate methodName:#"Test delegate"];
}
return self;
}
I am sure you have defined a:
- (id)initWithFrame:(CGRect)frame withDictionary:(NSDictionary *)dictionary;
Then, just pass the delegate, too:
- (id)initWithFrame:(CGRect)frame withDictionary:(NSDictionary *)dictionary withDelegate:(id<MyScrollViewDelegate>)del;
In the Implementation File:
- (id)initWithFrame:(CGRect)frame withDictionary:(NSDictionary *)dictionary withDelegate:(id<MyScrollViewDelegate>)del {
// your stuff...
self.delegate = del;
[self.delegate methodName:#"Test delegate"];
}
Use it:
self.photoView = [[MyScrollView alloc] initWithFrame:frame withDictionary:mediaContentDict withDelegate:self];
One option might be to pass in your delegate in your custom class's initializer:
-(id)initWithFrame:(CGRect)frame withDictionary:(NSDictionary*)dictionary delegate:(id)delegate
{
self = [super initWithFrame:frame];
if (self == nil )
{
return nil;
}
self.content = [[Content alloc] initWithDictionary:dictionary];
self.delegate = delegate;
//.... other stuff
// Delegate would exist now
[self.delegate methodName:#"Test delegate"];
return self;
}
I have been reading about an objective-c singleton example from http://getsetgames.com/2009/08/30/the-objective-c-singleton/.
The .m code looks like
+(MySingleton*)sharedMySingleton{
#synchronized([MySingleton class])
{if (!_sharedMySingleton)
if(!_sharedMySingleton)
return _shareMySingleton;
}
....
-(id)init{
self = [super init];
if(self != nil){}
return self;
}
....
Thanks for your example, but I have a confused place, in your code, I am wondering in which place the static MySingleton* _sharedMySingleton is initialized. for example if we would have to have some implementation like
-(id)init{
self = [super init];
if(self != nil){
_sharedMySingleton = self
}
return self;
}
You miscopied a part of the code from the link you posted by mistake.
#implementation MySingleton
static MySingleton* _sharedMySingleton = nil;
+(MySingleton*)sharedMySingleton
{
#synchronized([MySingleton class])
{
if (!_sharedMySingleton)
[[self alloc] init];
return _sharedMySingleton;
}
return nil;
}
+(id)alloc
{
#synchronized([MySingleton class])
{
NSAssert(_sharedMySingleton == nil, #"Attempted to allocate a second instance of a singleton.");
_sharedMySingleton = [super alloc];
return _sharedMySingleton;
}
return nil;
}
-(id)init {
self = [super init];
if (self != nil) {
// initialize stuff here
}
return self;
}
-(void)sayHello {
NSLog(#"Hello World!");
}
#end
looking at the code from the post you are asking about it actually makes sense.
What he is doing in the static method +(MySingleton*)sharedMySingleton is that he is checking if the _sharedMySingletonobject has a value he is returning it, if not it gets initialized.
The alloc method is the one setting the singleton object, its not being set in the initializer. technically its the same, since its going to be point on the same object that will be initialized a moment after.
I hope that clarifies your confusion.
you can use macro from http://code.google.com/p/google-toolbox-for-mac/source/browse/trunk/Foundation/GTMObjectSingleton.h it is vvveeerryyy easy to use
in implementation file (m)
GTMOBJECT_SINGLETON_BOILERPLATE(ClassName, sharedInstance)
and header file
+ (ClassName *) sharedInstance;
If I create a custom initWith for an object do I essentially include the code I would add should I want to override init?
-(id) init {
self = [super init];
if (self) {
NSLog(#"_init: %#", self);
}
return(self);
}
e.g.
-(id) initWithX:(int) inPosX andY:(int) inPosY {
self = [super init];
if(self) {
NSLog(#"_init: %#", self);
posX = inPosX;
posY = inPosY;
}
return(self);
}
gary
You can create one designated initializer that accepts all parameters that you want to make available in initialization.
Then you call from your other -(id)init your designated initializer with proper parameters.
Only the designated initializer will initialize super class [super init].
Example:
- (id)init
{
return [self initWithX:defaultX andY:defaultY];
}
- (id)initWithPosition:(NSPoint)position
{
return [self initWithX:position.x andY:position.y];
}
- (id)initWithX:(int)inPosX andY:(int)inPosY
{
self = [super init];
if(self) {
NSLog(#"_init: %#", self);
posX = inPosX;
posY = inPosY;
}
return self;
}
The designated initializer is -(id)initWithX:andY: and you call it from other initializers.
In case you want to extend this class you call your designated initializer from subclass.
I'd suggest creating one main initializer that handles most of the work. You can then create any number of other initializers that all call this main one. The advantage of this is if you want to change the initialization process, you'll only have to change one spot. It might look like this:
-(id) initWithX:(float)x {
if (self = [super init]) {
/* do most of initialization */
self.xVal = x;
}
return self;
}
-(id) init {
return [self initWithX:0.0f];
}
In this example initWithX: is our main initializer. The other initializer (init) simply calls initWithX: with a default value (in this case 0).
Yes, that's exactly how I do it. One slight change will cut out a line of code:
if (self = [super init]) {
As opposed to:
self = [super init];
if(self) {
For modern Objective-C ...
UDFile.h
#import <Foundation/Foundation.h>
#interface UDFile : NSObject
#property (nonatomic, strong) NSString *name;
- (instancetype)initWithName:(NSString *)name NS_DESIGNATED_INITIALIZER;
#end
UDFile.m
#import "UDFile.h"
#implementation UDFile
- (instancetype)initWithName:(NSString *)name {
self = [super init];
if (self) {
_name = [name copy];
}
return self;
}
- (instancetype)init {
return [self initWithPathname:#""];
}
Sometimes, you want to reuse some initialisation code and modify the behaviour only slightly for specific initialisers. In this case, I do the following:
- (id) init
{
self = [super init];
if (!self) return nil;
// These values are always initialised this way
ivar1 = 10;
ivar2 = #"HellO";
ivar3 = [[NSMutableArray alloc] initWithCapacity:10];
ivar4 = 22;
return self;
}
- (id) initWithIvar4:(int) aValue
{
// call -init on self, which will call -init on super for us, and set
// up ivar1, ivar2, ivar3, and ivar4.
self = [self init];
if (!self) return nil;
// Change ivar4 from the default 22 to whatever aValue is.
ivar4 = aValue;
return self;
}