I have a Square class i'm trying to override an init method, the .m looks like this:
#implementation Square
{
Rectangle *rect;
}
#synthesize side;
-(int) area
{
return side * side;
}
-(int) peremiter
{
return side * 4;
}
-(id) init
{
return [self init];
}
-(Square *) initWithSide:(int)s
{
self = [super init];
if (self)
[self initWithSide:s];
return self;
}
#end
The error im getting is in the initWithSide methode that say:
The result of a delegate init must be immediately returned or assigned to self
These lines:
if (self)
[self initWithSide:s];
Make no sense, and basically means you will call the initializer over and over again. You should change it to do something useful (like set the value of s, for example). On a side note, your no-arg init method is also wrong. It should be:
-(id) init {
return [super init];
}
But since there is no specialized implementation in it, you don't actually even need it at all.
drop off your current init methods and replace them with these (don't forget to update them at the interface scope):
- (id)init {
if (self = [super init]) {
// do whatever you like to to do here
}
return self;
}
- (id)initWithSide:(int)s { // but an NSInteger would be more elegant, and the parameter name 's' also would be better to be like 'side'
if (self = [super init]) {
[self setSide:s];
}
return self;
}
This is the correct syntax:
- (id) initWithSide:(int)s{
self = [super init];
if (self){
//init elements
}
return self;
}
Related
I have one class MyOldController with init method
-(instancetype) initWithMyController: (MyController *) myController {
if((self = [self init])) {
_myController = myController;
}
return self;
}
I want swizzle this initialization method to another and this my swizzle code
#implementation MyOldController(Swizzle)
+ (void)load {
[MyOldController swizzleMethods];
}
+ (void)swizzleMethods {
method_exchangeImplementations(class_getInstanceMethod(self, #selector(initWithMyController)), class_getInstanceMethod(self, #selector(swizzle_ initWithMyController)));
}
I try write this
-(instancetype) swizzle_initWithMyController: (MyController *) myController {
if((self = [self init])) {
_myController = myController;
}
return self;
}
But it drops error
Then I renamed init method to this and updated (void)swizzleMethods
-(instancetype) initWithMyController_swizzle: (MyController *) myController {
if((self = [self init])) {
_myController = myController;
}
return self;
}
Error message disappeared but swizzle doesn't works. It just calls old initialization method, not my new.
Which point i missed? Is swizzling of initialization method have some special way to do it?
(Starting with the required caveat: this is incredibly dangerous and should never be used in production code. Swizzling initializers is particularly dangerous given designated initializer chaining, and should definitely never be done for anything but exploration and debugging without first confirming the implementation of the swizzled initializer. OK, got that out of the way.)
I can't reproduce your issue. And initializer should always start with with init, so your second approach is correct. I suspect you've just made a small mistake, perhaps in your #selector (which has a typo in your question, which suggests maybe there's a mistake in your actual code). Here is code that does what you're describing.
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#interface MyOldController: NSObject
- (instancetype)initWithInt:(NSInteger)x
#end
#implementation MyOldController
- (instancetype)initWithInt:(NSInteger)x
{
self = [super init];
if (self) {
NSLog(#"init");
}
return self;
}
#end
#implementation MyOldController(Swizzle)
+ (void)load {
[MyOldController swizzleMethods];
}
+ (void)swizzleMethods {
method_exchangeImplementations(class_getInstanceMethod(self, #selector(initWithInt:)), class_getInstanceMethod(self, #selector(initWithInt_swizzle:)));
}
- (instancetype)initWithInt_swizzle:(NSInteger)x
{
self = [super init];
if (self) {
NSLog(#"init_swizzle");
}
return self;
}
#end
int main(int argc, const char * argv[]) {
#autoreleasepool {
MyOldController *controller = [[MyOldController alloc] initWithInt:1];
NSLog(#"%#", controller);
}
return 0;
}
This prints, as expected:
2018-06-21 12:23:14.431936-0400 test[30981:401466] init_swizzle
2018-06-21 12:23:14.432172-0400 test[30981:401466] <MyOldController: 0x10051ee10>
I'm designing a class that MUST only have one instance of itself at any time. I'm trying to avoid the common pattern of a shared singleton that is globally accessible, I only want a local object that can only be allocated once but can also be set to nil. How does this look?
static BOOL isInitialized = NO;
#implementation Single
-(instancetype) init
{
if (isInitialized == NO)
{
if (self = [super init])
{
}
isInitialized = YES;
return self;
}
else
{
NSAssert(FALSE, #"Only one instance allowed");
return nil;
}
}
-(void) dealloc
{
isInitialized = NO;
}
#end
I'm not concerned about thread safety as I only plan to use class on main thread. When the object is deferrenced the overridden dealloc should ensure a new instance can be created. Anybody see any issues with this or improvements? Cheers
It's a corner case, but as written if the call to [super init] fails then isInitialized will be set to YES (Note that in this case the passed in self is deallocated before the flag is set) - this means that no instance of the class will ever be created as there is nothing to deallocate to reset your flag. Maybe you want:
if (self = [super init])
{
isInitialized = YES;
}
return self;
Otherwise, given that you are not concerned over thread safety and you want a second allocation attempt to be a failure (your use of NSAssert), your code looks fine.
Would it be better to return the existing instance instead of crashing?
#implementation Single
- (instancetype)init {
static __weak Single *weakInstance;
Single *strongInstance = weakInstance;
if (strongInstance) {
self = strongInstance;
} else {
if (self = [super init]) {
weakInstance = self;
}
}
return self;
}
You don't need to do anything special in dealloc because the system will clear the __weak reference automatically when the instance is deallocated.
#Rob
Can the strong reference be removed from your method?
- (instancetype)init
{
static __weak id weakInstance;
if (weakInstance)
{
self = weakInstance;
}
else
{
if (self = [super init])
{
weakInstance = self;
}
}
return self;
}
seems to work.
How come Apple changed the built-in init method snippet from:
- (id)init
{
self = [super init];
if (self) {
}
return self;
}
to:
- (instancetype)init
{
self = [super init];
if (self) {
}
return self;
}
?
instancetype is the best return type for Objective-C init methods. If you write id instead of instancetype in an init method then the compiler will assume you really meant instancetype (at least in ARC mode).
I have a classes:
BasicObject : NSObject
AdvObject : BasicObject
in other class i make an instance by:
BasicObject *bObj = [[BasicObject alloc] initWithSomething:propertyOne andSomethingElse:propertyTwo];
BasicObject have two properties:
#interface BasicObject : NSObject
-(id)initWithSomething:propertyOne andSomethingElse:propertyTwo;
#property (strong,nonatomic) NSString* propertyOne;
#property (strong,nonatomic) NSArray* propertyTwo;
And then in initializer:
-(id)initWithSomething:propertyOne andSomethingElse:propertyTwo
{
if (self = [super init])
{
_propertyOne = propertyOne;
_propertyTwo = propertyTwo;
if(!propertyTwo) //this is not valid condition i know, not important here
{
AdvObject *aObj = [[AdvObject alloc] initWithBasic:self]; //here it what i'm more concern about
return aObj;
}
}
return self;
}
So there in AdvObject class in initializer i have:
#implementation AdvObject
#synthesize basics = _basics;
-(id)initWithBasic:(BasicObject *)bObj
{
if(self = [super init]) {
_basics = bObj;
}
return self;
}
And after that when i return this object of course i have a object.basics filled properly, but why i can't access object.propertyOne? (this is nil). What i'm doing wrong? Is this a right design?
Or, you could avoid this entire pattern as being overly clever and create a class factory method that returns either a BasicObject or an AdvObject depending on the parameters that were passed to it.
Your init... method would need to do a couple of things differently, as shown below:
- (id)initWithSomething:propertyOne andSomethingElse:propertyTwo
{
if (self = [super init])
{
if (!propertyTwo)
{
self = [[AdvObject alloc] initWithBasic:self];
}
_propertyOne = propertyOne;
_propertyTwo = propertyTwo;
}
return self;
}
I haven't actually tried this with ARC, so you'd need to test this carefully.
I have two classes:
BaseClass : NSObject
AdvanceClass : BaseClass
And in AdvanceClass i have an initializer:
-(id)initWithBaseObject:(BaseClass *)bObj
{
if(self = [super init]) {
self = (AdvanceClass*)bObj;
}
return self;
}
And then when i get TRUE when i'm calling:
[myObject isKindOfClass:[BaseClass class]]
Why? I'm casting bObj to AdvanceClass object.
What i want to do here is assign all of the properties from BaseClass with properties from bObj object. How can i do that?
-(id)initWithBaseObject:(BaseClass *)bObj
{
if(self = [super init]) {
self = (AdvanceClass*)bObj; // this line of code discards the self = [super init]; and makes self a reference to a casted BaseClass object
self.property1 = bObj.property1; // this is what you need to do for each property and remove the line with the cast
}
return self;
}
I've just realize that the best way is write a public method in BaseClass and call it from initializer. In that case you can only write this once, and it is simply to edit.
-(id)initWithBaseObject:(BaseClass *)bObj
{
if(self = [super init]) {
[self setBaseProperties:bObj];
}
return self;
}
And in BaseClass.m
-(void)setBaseProperties:(BaseClass*)bObj
{
_prop1 = bObj.prop1;
_prop2 = bObj.prop2;
.
.
.
}
This is obvious solution, silly me.