I use NSProxy to mock a class, and want to hook all invocation of the class. But only methods called outside the class are hooked, without methods called inside the class. Below is something like my code:
In my AppDelegate.m, TBClassMock is subclass of NSProxy
TBClassMock *mock = [[TBClassMock alloc] init];
TBTestClass *foo = [[TBTestClass alloc] init];
mock.target = foo;
foo = mock;
[foo outsideCalled];
In my TBTestClass.m
- (void)outsideCalled
{
[self insideCalled];
}
- (void)insideCalled
{
}
In my TBClassMock.m
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
{
NSLog(#"Signature: %#", NSStringFromSelector(selector));
return [self.target methodSignatureForSelector:selector];
}
-(void)forwardInvocation:(NSInvocation*)anInvocation
{
//... Do other things
[anInvocation invokeWithTarget:self.target];
}
Then I can log the invocation of [foo outsideCalled], but can't log the invocation of [self insideCalled].
I aim to do something in all invocations of the class in //... Do other things, and this way seems failed. Any explanations about this and any other method to implement my requirement? I just don't want to swizzle all methods of the class using method_exchangeImplementations as I think it's too fussy and not a good way.
I guess you've misunderstood the concept of NSProxy. The NSProxy stands for a class, it isn't anything in itself. The way a NSProxy is supposed to be used is, you just call all the methods on proxy as if it were the class it is standing for.
In your code, instead of:
foo = mock;
[foo outsideCalled];
Do:
[mock outsideCalled];
This is my entire code:
#import <Foundation/Foundation.h>
#interface TBTestClass : NSObject
- (void)outsideCalled;
- (void)insideCalled;
#end
#implementation TBTestClass
- (void)outsideCalled;
{
NSLog(#"TBTestClass:outsideCalled");
[self insideCalled];
}
- (void)insideCalled;
{
NSLog(#"TBTestClass:insideCalled");
}
#end
#interface TBClassMock : NSProxy
#property (retain) id target;
#end
#implementation TBClassMock
#synthesize target;
- (void)dealloc
{
self.target = nil;
}
- (id)init
{
self.target = nil;
return self;
}
-(void)forwardInvocation:(NSInvocation *)anInvocation
{
if([self.target respondsToSelector:[anInvocation selector]]){
[anInvocation invokeWithTarget:self.target];
}
}
- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector
{
return [self.target methodSignatureForSelector:selector];
}
#end
int main(int argc, const char **argv)
{
#autoreleasepool {
TBClassMock *mock = [[TBClassMock alloc] init];
TBTestClass *foo = [[TBTestClass alloc] init];
mock.target = foo;
[mock outsideCalled];
[mock release];
[foo release];
}
return 0;
}
Output:
2014-10-23 05:35:06.043 a.out[25483:507] TBTestClass:outsideCalled
2014-10-23 05:35:06.047 a.out[25483:507] TBTestClass:insideCalled
Related
I've been trying to implement a global NSMutableArray from what I think to be a singleton class that I've implemented.
I can enter ViewController # 2, add and remove objects to the array.
However, when I leave ViewController #2 and come back, the data does not persist, and I have an array with 0 objects.
What do you think I'm doing wrong?
.h
// GlobalArray.h
#interface GlobalArray : NSObject{
NSMutableArray* globalArray;
}
+(void)initialize;
.m
#import "GlobalArray.h"
#implementation GlobalArray
static GlobalArray* sharedGlobalArray;
NSMutableArray* globalArray;
+(void)initialize{
static BOOL initalized = NO;
if(!initalized){
initalized = YES;
sharedGlobalArray = [[GlobalArray alloc] init];
}
}
- (id)init{
if (self = [super init]) {
if (!globalArray) {
globalArray = [[NSMutableArray alloc] init];
}
}
return self;
}
View Controller #2
GlobalArray* myGlobalArray;
myGlobalArray = [[GlobalArray alloc] init];
//Various add and remove code
Thank you for your input.
Following is best approach to share data Globally at Application level. Singleton Class is a key. Singleton is only initialised once, rest of times shared data is returned.
#interface Singleton : NSObject
#property (nonatomic, retain) NSMutableArray * globalArray;
+(Singleton*)singleton;
#end
#implementation Singleton
#synthesize globalArray;
+(Singleton *)singleton {
static dispatch_once_t pred;
static Singleton *shared = nil;
dispatch_once(&pred, ^{
shared = [[Singleton alloc] init];
shared.globalArray = [[NSMutableArray alloc]init];
});
return shared;
}
#end
Following is the way to access/use shared data.
NSMutableArray * sharedData = [Singleton singleton].globalArray;
You create separate instance of GlobalArray in your ViewController#2 with this code:
GlobalArray* myGlobalArray;
myGlobalArray = [[GlobalArray alloc] init];
Instead, you should create accessor method to return your shared instance, something like this:
// GlobalArray.h
#interface GlobalArray : NSObject{
NSMutableArray* globalArray;
}
+(void)initialize;
+(GlobalArray*)sharedInstance;
with implementation:
// GlobalArray.m
// ... your existing code
// accessor method
+(GlobalArray*)sharedInstance
{
return sharedGlobalArray;
}
and then call it from your ViewController#2:
GlobalArray* myGlobalArray = [GlobalArray sharedInstance];
However, using global variables to transfer data between view controllers is bad practice; I suggest you to use more safe methods, create a delegate, for example.
To create a shared global array, if that's really what you want, just put this in the header file:
extern NSMutableArray *myGlobalArray;
and this in your main source file:
NSMutableArray *myGlobalArray;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
myGlobalArray = [NSMutableArray new];
}
Use this code for set and get the array views, for adding and removing do it separate in controller itself.
// GlobalArray.h
#interface GlobalArray : NSObject
#property (nonatomic, strong) NSMutableArray* globalArray;
+ (id)sharedManager;
-(NSMutableArray *) getGlobalArray;
-(void) setGlobalArray:(NSMutableArray *)array;
#end
/*-----------------------------------------*/
#import "GlobalArray.h"
#implementation GlobalArray
+ (id)sharedManager {
static GlobalArray *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
});
return sharedMyManager;
}
- (id)init{
if (self = [super init]) {
if (!globalArray) {
globalArray = [[NSMutableArray alloc] init];
}
}
return self;
}
-(NSMutableArray *) getGlobalArray{
return self.globalArray;
}
-(void) setGlobalArray:(NSMutableArray *)array{
_globalArray = globalArray;
}
#end
-------------------------
//get array
NSArray * array = [[GlobalArray sharedManager] getGlobalArray];
//set array
[[GlobalArray sharedManager] setGlobalArray:array]
-------------------------
Is it possible to swizzle the addObject: method of NSMutableArray?
Here is what I am trying.
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#implementation NSMutableArray (LoggingAddObject)
+ (void)load {
Method addObject = class_getInstanceMethod(self, #selector(addObject:));
Method logAddObject = class_getInstanceMethod(self, #selector(logAddObject:));
method_exchangeImplementations(addObject, logAddObject);
Method original = class_getInstanceMethod(self, #selector(setObject:atIndexedSubscript:));
Method swizzled = class_getInstanceMethod(self, #selector(swizzled_setObject:atIndexedSubscript:));
method_exchangeImplementations(original, swizzled);
}
- (void)logAddObject:(id)anObject {
[self logAddObject:anObject];
NSLog(#"Added object %# to array %#", anObject, self);
}
-(void)swizzled_setObject:(id)obj atIndexedSubscript:(NSUInteger)idx
{
NSLog(#"This gets called as expected!!-----");
[self swizzled_setObject:obj atIndexedSubscript:idx];
}
I am able to swizzle some of the methods like setObject:atIndexedSubscript: but I am worried that I cant do it do the addObject: and others.
I think the below can not be swizzled? Can someone explain why ? what I am doing wrong and or a way around this?
/**************** Mutable Array ****************/
#interface NSMutableArray : NSArray
- (void)addObject:(id)anObject;
- (void)insertObject:(id)anObject atIndex:(NSUInteger)index;
- (void)removeLastObject;
- (void)removeObjectAtIndex:(NSUInteger)index;
- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject;
#end
You can try this with NSProxy, but I don't suggest you to use it on production code because:
it will break something (some framework may require NSMutableArray to throw exception when add nil into it to prevent more serious error later. i.e. Fail fast)
it is slow
If you really want to avoid nil checking, I suggest you to make a subclass of NSMutableArray and use it everywhere in your code. But really? There are so many ObjC code using NSMutableArray, most of them doesn't need this feature. So why you are so special?
#import <objc/runtime.h>
#interface XLCProxy : NSProxy
+ (id)proxyWithObject:(id)obj;
#end
#implementation XLCProxy
{
id _obj;
}
+ (void)load
{
Method method = class_getClassMethod([NSMutableArray class], #selector(allocWithZone:));
IMP originalImp = method_getImplementation(method);
IMP imp = imp_implementationWithBlock(^id(id me, NSZone * zone) {
id obj = ((id (*)(id,SEL,NSZone *))originalImp)(me, #selector(allocWithZone:), zone);
return [XLCProxy proxyWithObject:obj];
});
method_setImplementation(method, imp);
}
+ (id)proxyWithObject:(id)obj
{
XLCProxy *proxy = [self alloc];
proxy->_obj = obj;
return proxy;
}
- (void)forwardInvocation:(NSInvocation *)invocation
{
[invocation setTarget:_obj];
[invocation invoke];
const char *selname = sel_getName([invocation selector]);
if ([#(selname) hasPrefix:#"init"] && [[invocation methodSignature] methodReturnType][0] == '#') {
const void * ret;
[invocation getReturnValue:&ret];
ret = CFBridgingRetain([XLCProxy proxyWithObject:CFBridgingRelease(ret)]);
[invocation setReturnValue:&ret];
}
}
-(NSMethodSignature *)methodSignatureForSelector:(SEL)sel
{
return [_obj methodSignatureForSelector:sel];
}
- (Class)class
{
return [_obj class];
}
- (void)addObject:(id)obj
{
[_obj addObject:obj ?: [NSNull null]];
}
- (BOOL)isEqual:(id)object
{
return [_obj isEqual:object];
}
- (NSUInteger)hash {
return [_obj hash];
}
// you can add more methods to "override" methods in `NSMutableArray`
#end
#interface NSMutableArrayTests : XCTestCase
#end
#implementation NSMutableArrayTests
- (void)testExample
{
NSMutableArray *array = [NSMutableArray array];
[array addObject:nil];
[array addObject:#1];
[array addObject:nil];
XCTAssertEqualObjects(array, (#[[NSNull null], #1, [NSNull null]]));
}
#end
You can iterate over all registered classes, check if current class is a subclass of NSMutableArray, if so, swizzle.
I would advice against it, rather act on case-by-case basis to have more predictable behavior - you never know which other system frameworks rely in this particular behavior (e.g. I can see how CoreData might rely on this particular behavior)
You can swizzle any NSMutableArray method in the following way:
#implementation NSMutableArray (Swizzled)
+ (void)load
{
Method orig = class_getInstanceMethod(NSClassFromString(#"__NSArrayM"), NSSelectorFromString(#"addObject:"));
Method override = class_getInstanceMethod(NSClassFromString(#"__NSArrayM"), #selector(addObject_override:));
method_exchangeImplementations(orig, override);
}
- (void)addObject_override:(id)anObject
{
[self addObject_override:anObject];
NSLog(#"addObject called!");
}
I am currently writing a helper class for my app. The helper class will return globally accessible variables. I created a simple helper as shown below:
#interface MyHelper : NSObject
{
}
+(id) sharedHelper;
+(NSMutableArray *) employers;
+(id) sharedHelper
{
static MyHelper *sharedHelper = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken,^{
sharedHelper = [[self alloc] init];
});
return sharedHelper;
}
+(NSMutableArray *) employers
{
return _employers;
}
Now I can access the employers like this:
[MyHelper employers] and I can also access it like this [[MyHelper sharedHelper] employers] What is the benefit for each approach or they they both same.
I suppose that employers array is a property of your class MyHelper.
If you call [MyHelper employers] without call [MyHelper sharedHelper] you can get incorrect result (the value of employers array is garbage).
Maybe the best practice here is to use lazy loading in +(NSMutableArray *) employers and get static variable out of +(id) sharedHelper:
static MyHelper *sharedHelper = nil;
static dispatch_once_t onceToken;
+(id) sharedHelper
{
dispatch_once(&onceToken,^{
sharedHelper = [[self alloc] init];
});
return sharedHelper;
}
+(NSMutableArray *) employers
{
if(!sharedHelper)
[MyHelper sharedHelper];
return _employers;
}
You employers class method is referencing _employers, which is presumably an instance variable for your class. You cannot do that. Furthermore, even if you did some sloppy workaround, like a global variable, you have no assurances that _employers has been instantiated unless the employers class method also ensures that the sharedHelper is called.
So, a couple of thoughts:
You really should have some class property for employers (I assume you did, but omitted it for brevity, but let's include it here to eliminate ambiguity):
#interface MyHelper : NSObject
#property (nonatomic, strong) NSMutableArray *employers;
#end
I presume your init method would initialize this employers object
- (id)init
{
self = [super init];
if (self) {
_employers = [[NSMutableArray alloc] init];
}
return self;
}
Your existing sharedHelper method is perfectly fine.
If you're going to have an employers class method, though, should access the employers instance method:
+ (NSMutableArray *)employers
{
return [[MyHelper sharedHelper] employers];
}
Having done all of that, your class would look like:
#interface MyHelper : NSObject
#property (nonatomic, strong) NSMutableArray *employers;
+ (id)sharedHelper;
+ (NSMutableArray *)employers;
#end
#implementation MyHelper
+ (id)sharedHelper
{
static MyHelper *sharedHelper = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken,^{
sharedHelper = [[self alloc] init];
});
return sharedHelper;
}
- (id)init
{
self = [super init];
if (self) {
_employers = [[NSMutableArray alloc] init];
}
return self;
}
+ (NSMutableArray *)employers
{
return [[MyHelper sharedHelper] employers];
}
#end
That works, but having said that, as a matter of style, I personally wouldn't recommend a class method, employers, when you have a getter method for a property of the same name. Seems a little confusing. I would excise that employers class method, and stick with the standard getter method that will be synthesized for you, resulting in just:
#interface MyHelper : NSObject
#property (nonatomic, strong) NSMutableArray *employers;
+ (id)sharedHelper;
#end
#implementation MyHelper
+ (id)sharedHelper
{
static MyHelper *sharedHelper = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken,^{
sharedHelper = [[self alloc] init];
});
return sharedHelper;
}
- (id)init
{
self = [super init];
if (self) {
_employers = [[NSMutableArray alloc] init];
}
return self;
}
#end
And then other code using this MyHelper class can simply refer to [[MyHelper sharedHelper] employers] themselves.
*I definitely need a break... cause was simple - array was not allocated... Thanks for help. Because of that embarrassing mistake, I flagged my post in order to delete it. I do not find it useful for Users ;) *
I have just tried to create a singleton class in iOS, but I probably I am making a mistake. Code (no ARC is a requirement):
#import "PeopleDatabase.h"
#import "Person.h"
#import <Foundation/Foundation.h>
#interface PeopleDatabase : NSObject{objetive
NSMutableArray* _arrayOfPeople;
}
+(PeopleDatabase *) getInstance;
#property (nonatomic, retain) NSMutableArray* arrayOfPeople;
#end
--
#implementation PeopleDatabase
#synthesize arrayOfPeople = _arrayOfPeople;
static PeopleDatabase* instance = nil;
-(id)init{
if(self = [super init]) {
Person* person = [[[Person alloc] initWithName:#"John" sname:#"Derovsky" descr:#"Some kind of description" iconName:#"johnphoto.png" title:Prof] retain];
[_arrayOfPeople addObject:person];
NSLog(#"array count = %d", [_arrayOfPeople count]); // <== array count = 0
[person release];
}
return self;
}
+(PeopleDatabase *)getInstance {
#synchronized(self)
{
if (instance == nil)
NSLog(#"initializing");
instance = [[[self alloc] init] retain];
NSLog(#"Address: %p", instance);
}
return(instance);
}
-(void)dealloc {
[instance release];
[super dealloc];
}
#end
When invoking getInstance like here:
PeopleDatabase *database = [PeopleDatabase getInstance];
NSLog(#"Adress 2: %p", database);
Address 2 value the same value as in getInstance.
The standard way of creating a singleton is like...
Singleton.h
#interface MySingleton : NSObject
+ (MySingleton*)sharedInstance;
#end
Singleton.m
#import "MySingleton.h"
#implementation MySingleton
#pragma mark - singleton method
+ (MySingleton*)sharedInstance
{
static dispatch_once_t predicate = 0;
__strong static id sharedObject = nil;
//static id sharedObject = nil; //if you're not using ARC
dispatch_once(&predicate, ^{
sharedObject = [[self alloc] init];
//sharedObject = [[[self alloc] init] retain]; // if you're not using ARC
});
return sharedObject;
}
#end
Check this apple doc on how to create singleton instance:
https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaObjects/CocoaObjects.html
#synchronized(self)
{
if (instance == nil)
NSLog(#"initializing");
instance = [[[self alloc] init] retain];
NSLog(#"Address: %p", instance);
}
You appear to be missing your braces for that if statement. As written, the only thing you do different when instance == nil is emit a log message.
After web reading and personal practicing, my current singleton implementation is:
#interface MySingleton
#property myProperty;
+(instancetype) sharedInstance;
#end
#implementation MySingleton
+ (instancetype) sharedInstance
{
static dispatch_once_t pred= 0;
__strong static MySingleton *singletonObj = nil;
dispatch_once (&pred, ^{
singletonObj = [[super allocWithZone:NULL]init];
singletonObj.myProperty = initialize ;
});
return singletonObj;
}
+(id) allocWithZone:(NSZone *)zone
{
return [self sharedInstance];
}
-(id)copyWithZone:(NSZone *)zone
{
return self;
}
this is a thread safe implementation and avoids the risk to create new objects by calling "alloc init" on your class. Attributes initialization has to occur inside the block, not inside "init" override for similar reasons.
This is an error that can be avoided by some disziplined convention which is to always use curly brackets followed by if and else.
+(PeopleDatabase *)getInstance {
#synchronized(self)
{
if (instance == nil)
NSLog(#"initializing");
instance = [[[self alloc] init] retain];
NSLog(#"Address: %p", instance);
}
return(instance);
}
If instance is nil then the very next statement and only that is executed. And that is the nslog and not the allocation. Then instance is allocated anyway, regardless wether it was used before or not. This will provide you with a new singleton on each call. BTW that causes a leak.
+(PeopleDatabase *)getInstance {
#synchronized(self)
{
if (instance == nil) {
NSLog(#"initializing");
instance = [[[self alloc] init] retain];
NSLog(#"Address: %p", instance);
}
}
return(instance);
}
But this error came in while debugging. It may confuse you but does not solve your original problem. Please add an alloc and init and retain for _arrayOfPeople as well.
-(id)init{
if(self = [super init]) {
Person* person = [[[Person alloc] initWithName:#"John" sname:#"Derovsky" descr:#"Some kind of description" iconName:#"johnphoto.png" title:Prof] retain];
_arrayOfPeople = [[[NSMutableArray alloc] init] retain]; //dont forget the release
[_arrayOfPeople addObject:person];
NSLog(#"array count = %d", [_arrayOfPeople count]); // <== array count = 1 !!!
[person release];
}
return self;
}
In your code _arrayOfPeople is nil and addObject is sent to nil which does not cause an abort but does not do anything either. Then count is sent to nil wich returns 0/nil.
in this function +(PeopleDatabase *)getInstance i think you need to place curly Braces correctly : like this
+(PeopleDatabase *)getInstance {
#synchronized(self)
{
if (instance == nil)
{
NSLog(#"initializing");
instance = [[[self alloc] init] retain];
NSLog(#"Address: %p", instance);
}
return instance ;
}
}
While implementing a subclass of NSArray (a class cluster), I was surprised to see that my overridden description method was not called. Can somebody explain what is happening here?
#interface MyArrayClassCluster : NSArray
#end
#implementation MyArrayClassCluster
{
NSArray *_realArray;
}
// Implement the class cluser stuff here
- (NSUInteger)count
{
return [_realArray count];
}
- (id)objectAtIndex:(NSUInteger)index
{
return [_realArray objectAtIndex:index];
}
// lifeCycle
- (id)initWithItems:(NSArray *)items
{
self = [super init];
_realArray = [items retain];
return self;
}
- (void)dealloc
{
[_realArray release];
[super dealloc];
}
- (NSString *)description
{
return [NSString stringWithFormat:#"My Custom Array: %p, objs:%#", self, _realArray];
}
#end
int main(int argc, const char * argv[])
{
#autoreleasepool {
NSArray *a = #[#1, #2, #3];
NSLog(#"a: %#", a);
MyArrayClassCluster *clzCluster = [[MyArrayClassCluster alloc] initWithItems:a];
NSLog(#"clzCluster: %#", clzCluster);
}
return 0;
}
Output
2013-01-29 18:52:38.704 ClassClusterTester[31649:303] a: (
1,
2,
3
)
2013-01-29 18:52:38.707 ClassClusterTester[31649:303] clzCluster: (
1,
2,
3
)
The link #Rob pointed to had the correct answer at the bottom. An obscure fact: if something implements descriptionWithLocale:, NSLog will call that instead. Since my class is a subclass of NSArray, and NSArray implements that, my version of description was not called.