How to swizzle initialization method? - objective-c

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>

Related

Cannot add CCNode to Scene

I have the IntroScene, and I wanna add a node, but it doesn't seem to work. Here are two different ways I tried doing it, and BOTH failed.
First way, failed:
in hearts2.h
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#interface Hearts2 : CCNode {
}
#end
in hearts2.m
#import "Hearts2.h"
#implementation Hearts2
#end
in IntroLayer.m
- (id)init
{
// Apple recommend assigning self with supers return value
self = [super init];
if (!self) return(nil);
heart2 *heart;
[self addChild:heart z:2];
// done
return self;
}
I didn't expect that to work (actually I was desperate and tried it that way as the second way just to see if it would work). The actual first attempt I tried to do was this, and it also Failed:
in hearts1.h
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#interface Hearts1 : CCNode
+ (Hearts1 *)node;
- (id)init;
-(void)selfAnimate;
#end
in hearts1.m
#import "Hearts1.h"
#implementation Hearts1 {
}
+ (Hearts1 *)node
{
return [[self alloc] init];
}
- (id)init
{
self = [super init];
if (!self) return(nil);
return self;
}
- (void)dealloc
{
}
- (void)onEnter
{
[super onEnter];
}
- (void)onExit
{
// always call super onExit last
[super onExit];
}
- (void)selfAnimate
{
}
#end
in IntroLayer.m
- (id)init
{
// Apple recommend assigning self with supers return value
self = [super init];
if (!self) return(nil);
heart1 *heart;
[self addChild:heart z:2];
// done
return self;
}
Please, I would do anything if someone could help me figure this out thanks everyone very much. I always get the SigABRT so I have no idea what is going wrong. I'm sure I'm just stupid and don't know how to code and missing something simple.
heart2 *heart;
You named your class Hearts2 so use the exact same name, including uppercase.
Secondly you created a variable but this will be nil. If you aren't using ARC (which you should) this will create an uninitialized object.
This will create an instance of Hearts2, assign it to the local var heart and add it as a child:
Hearts2 *heart = [Hearts2 node];
[self addChild:heart z:2];

Objective C - deallocated variable

I have this static/singelton class that has a member variable.
Everything works just fine, however, when I quit the app, and the come back, that member variable is causing the app to crash.
I tried reinitializing the member variable, but doesn't seem to do the trick.
I am including samples of my code below, has anyone seen this before?
The header (.h) file:
#interface Metrics : NSObject {
Metrics *metrics;
Distance *distance;
}
The implementation (.m) file:
#implementation Metrics
static Metrics *metrics = nil;
+ (Metrics *)sharedInstance {
#synchronized(self) {
if (metrics == nil) {
metrics = [[Metrics alloc] init];
}
}
return metrics;
}
- (id)init {
self = [super init];
if (self) {
}
return self;
}
-(void) setupDistance
{
distance = [[Distance alloc] init];
distance.test; // it dies here after I quit the app and come back
}
And where it is used, AppDelegate.m
- (void)applicationDidBecomeActive:(UIApplication *)application
{
[[Metrics sharedInstance] setupDistance];
}
Thank you,
Tee
When you say "quit the app" you almost certainly mean "send the app to the background." This does not terminate your application. When you become active again, you're leaking distance and assigning a new one. I suspect that something in test is actually the problem.
First, don't access your ivars directly, except in init and dealloc. Use accessors. This will make most of your problems go away.
Second, don't call setupDistance in applicationDidBecomeActive. You mean to call it in applicationDidFinishLaunchingWithOptions:. But better yet, just initialize it during Metrics init. Why have this internal Metrics detail in the app delegate?
applicationDidBecomeActive will get called when it comes back from sleep calling to setup in the distance again. It's wierd that you're calling a property (.test) and not assigning. Is test doing work? Should it be a method? What's it doing?
Also, in a singleton you have to overload quite a few things. Here's a good template for a singleton that I use.
static MySingleton *sharedInstance = nil;
#implementation MySingleton
#pragma mark -
#pragma mark class instance methods
#pragma mark -
#pragma mark Singleton methods
+ (MySingleton*)sharedInstance
{
#synchronized(self)
{
if (sharedInstance == nil)
sharedInstance = [[MySingleton alloc] init];
}
return sharedInstance;
}
+ (id)allocWithZone:(NSZone *)zone {
#synchronized(self) {
if (sharedInstance == nil) {
sharedInstance = [super allocWithZone:zone];
return sharedInstance;  // assignment and return on first allocation
}
}
return nil; // on subsequent allocation attempts return nil
}
- (id)copyWithZone:(NSZone *)zone
{
return self;
}
- (id)retain {
return self;
}
- (unsigned)retainCount {
return UINT_MAX;  // denotes an object that cannot be released
}
- (void)release {
//do nothing
}
- (id)autorelease {
return self;
}
#end

Should I "Pull Up" Refactor

I have some very small classes that I feel should be "pulled up" but the methods are so small I'm not sure. For example, the only thing that's meaningfully different is the body of the buildFromJSON: selector.
I acknowledge that this is similar to:
Pull-up refactoring, Objective-C
but I feel my question is specific to refactoring very small classes/methods.
Also, not sure it relates to my particular code example, but I'm wondering if a child class says it conforms to a protocol, whether it's enough that it's parent actually supply the implementation of required selector(s)?
#implementation AsyncFoo
-(void)dealloc {
[clientDelegate release];
[super dealloc];
}
- (id)initWithDelegate: (id <ServiceClientProtocol>) delegate {
if((self = [super init])) {
clientDelegate = [delegate retain];
}
return self;
}
- (void)buildFromJSON:(NSString*)jsonResponseString {
[clientDelegate serviceComplete:[RestAdapter buildFooArray: jsonResponseString]];
}
#end
#implementation AsyncBar
-(void)dealloc {
[clientDelegate release];
[super dealloc];
}
- (id)initWithDelegate: (id <ServiceClientProtocol>) delegate {
if((self = [super init])) {
clientDelegate = [delegate retain];
}
return self;
}
- (void)buildFromJSON:(NSString*)jsonResponseString {
[clientDelegate serviceComplete:[RestAdapter buildBarArray:jsonResponseString]];
}
#end
Answers including code example would be great.
EDIT: Post accepted answer I'd like to add that since I was able to subclass, the derived classes did not need to declare that they conformed to protocol:
#interface Async : NSObject <ModelBuilderProtocol> {
id <ServiceClientProtocol> clientDelegate;
}
- (void)buildFromJSON:(NSString*)jsonResponseString;
#end
#interface AsyncArtistById : Async
#end
You don't normally retain your delegates as this can cause a retain cycle.
Knowing what I know from looking at your example I would probably implement like this:
The super class
// Async.h
#interface Async : NSObject
#property (nonatomic, assign) id<ServiceClientProtocol> delegate;
- (void)buildFromJSON:(NSString *)jsonResponseString;
#end
// Async.m
#implementation Async
#synthesize delegate = _delegate;
- (id)initWithDelegate:(id<ServiceClientProtocol>)delegate
{
self = [super init];
if(self) {
_delegate = delegate;
}
return self;
}
- (void)buildFromJSON:(NSString *)jsonResponseString
{
// This will ensure that we over ride this method in a sub class
[NSException raise:NSInternalInconsistencyException
format:#"You must override %# in a subclass", NSStringFromSelector(_cmd)];
}
#end
Concrete subclass AsyncFoo
// AsyncFoo.h
#interface AsyncFoo : Async
#end
// AsyncFoo.m
#implementation AsyncFoo
- (void)buildFromJSON:(NSString *)jsonResponseString
{
[self.delegate serviceComplete:[RestAdapter buildFooArray: jsonResponseString]];
}
#end
Concrete subclass AsyncBar
// AsyncBar.h
#interface AsyncBar : Async
#end
// AsyncBar.m
#implementation AsyncBar
- (void)buildFromJSON:(NSString *)jsonResponseString {
[self.delegate serviceComplete:[RestAdapter buildBarArray:jsonResponseString]];
}
#end

View of custom class

I have the following class:
#interface Gamer {
...
}
+(id) CreatePlayer;
#end
#implementation Gamer
+(id) CreatePlayer
{
return [[[self alloc] init]autorelease];
}
#end
I need to use the Gamer in an another class as instance variable.
For example like this:
#interface Layer{
Gamer * mCenterGamer;
}
#end
#implementation
-(void) init{
mCenterGamer = [Gamer CreatePlayer];
}
-(void) exampleFuncForUseGamer{
[mCenterGamer ...]// some methods of the Gamer class
}
#end
Is it correct? (I think autorelease freed the mCenterGamer after exiting from the init function)
You need to retain mCenterGamer (and make sure to release it in the Layer's -dealloc method). Also, -init needs id as its return type:
- (id)init {
if (self = [super init])
mCenterGamer = [[Gamer CreatePlayer] retain];
return self;
}
- (void)dealloc {
[mCenterGamer release];
[super dealloc];
}
Your -exampleFuncForUseGamer should be fine, depending on what you're trying to do there.

Objective-C :: using a method to change an object

I have a class called "CardSet", containing an NSMutableArray* cardSet to hold "cards", which I extend to make "DeckCards". I'd like "CardSet" to have a method called "(void)addCard:(Card*)" (and similarly a method "removeCard"). I'd like "addCard" to some how have access to and set cardSet. Even better I'd like to use the "addCard" method to initialise cardSet. The class file "CardSet.h" reads:
#import < Cocoa/Cocoa.h >
#import < Card.h >
#interface CardSet : NSObject {
NSMutableArray* cardSet;
}
-(id)init;
-(NSMutableArray*)getCardSet;
-(void)setCardSet:(NSMutableArray *)new_cardset;
-(Card*)getCard:(NSInteger) index;
**-(void)addCard:(Card*) new_card;**
-(void)removeCard:(Card*) old_card;
-(void)dealloc;
#property (readwrite, retain, getter=getCardSet, setter=setCardSet) NSMutableArray* cardSet;
#end
and the method file reads:
#import "CardSet.h"
#implementation CardSet
-(id)init{
if( self = [super init] ){} //will add initialisations here later
return self;
}
-(NSMutableArray*)getCardSet{
return cardSet;
}
-(void)setCardSet:(NSMutableArray *)new_cardSet{
cardSet = new_cardSet;
}
-(Card*)getCard:(NSInteger)index{
return [cardSet objectAtIndex:index];
}
**-(void)addCard:(Card *)new_card{
[cardSet addObject:new_card];
}**
-(void)removeCard:(Card *)old_card{
[cardSet removeObject:old_card];
}
-(void)dealloc{
[cardSet release];
[super dealloc];
}
#synthesize cardSet;
#end
This compiles just fine. I'd like to initialise a "DeckCards" instance using its "addCard" method 52 times. When I call addCard 52 times in a DeckCards setter method, and ask for the size of its "cardSet", I'm returned 0.
This appears to be a scope or privileges problem? Can the "addCard" method have any setter privileges? Must a setter argument be the same as the return and respective member type?
[I can work around the above by creating an NSMutableArray object "deck_cards_temp" outside of "DeckCard", add 52 cards to this, and pass it to set the member of my "DeckCards" instance via the setter inherited from "CardSet". This is not very satisfactory!]
What do you advise? Many thanks in advance for your help and patience.
You are never actually creating the cardSet object. You should be creating it in your -init method:
-(id)init
{
if( self = [super init] )
{
cardSet = [[NSMutableArray alloc] init];
}
return self;
}
Because you never actually create the array, all the calls to -addCard: are being sent to a nil object.
When you pass in an array to -setCardSet:, you are passing in an initialized array so the array is no longer nil and the -addCard: calls work fine.
CardSet.h
#import <Cocoa/Cocoa.h>
// For know we just need to know there is a class named "Card" being used but implemented later
#class Card;
#interface CardSet : NSObject {
NSMutableArray *cardSet;
}
// Here are the methods according to "correct" naming conventions
- (Card *)cardAtIndex:(NSInteger)index;
- (void)addCard:(Card *)card;
- (void)removeCard:(Card *)card;
// This will help us and forget about writing the setter/getter
#property (nonatomic, retain) NSMutableArray *cardSet;
#end
CardSet.m
#import "CardSet.h"
// Now we tell the compiler what "Card" is and what methods etc. it has
#import "Card.h"
#implementation CardSet
#synthesize cardSet;
- (id)init {
if (self = [super init]) {
// If we don't create the cardSet, how are we able to work with it!?
NSMutableArray *anArray = [[NSMutableArray alloc] init];
self.cardSet = anArray;
[anArray release];
}
return self;
}
- (Card *)cardAtIndex:(NSInteger)index {
return [cardSet objectAtIndex:index];
}
- (void)addCard:(Card *)card {
[cardSet addObject:card];
}
- (void)removeCard:(Card *)card {
[cardSet removeObject:card];
}
- (void)dealloc {
[cardSet release];
[super dealloc];
}
#end
As Abizern already noted: Naming the array the same as your class is a bad thing.
I would shorten that init method:
- (id)init {
if (self = [super init]) {
// If we don't create the cardSet, how are we able to work with it!?
self.cardSet = [NSMutableArray array];
}
return self;
}