factorial( 0 ) -> 1
factorial( 1 ) -> 1
factorial( n ) -> n * factorial( n - 1 )
typedef id ( ^ PatternMatchingBlock ) ( id obj ) ;
@interface NSObject ( PatternMatching )
- ( void ) method : ( SEL ) sel_ withParameter : ( id ) object_ useBlock : ( PatternMatchingBlock ) block_;
@end
objc_method
from which we are interested in the field method_imp
type IMP
. IMP
is a pointer to the C function of the method implementation. The meaning of the method substitution is in replacing these pointers in 2 methods. Create a class that will hold a pointer to the original implementation of the method and a dictionary, the keys of which are the objects of “images”, and the values are the implementation blocks:@interface PMImplementation : NSObject
@property ( nonatomic, retain ) NSMutableDictionary * impls;
@property ( nonatomic, retain ) NSValue * defaultImpl;
+ ( id ) implementationWithDefaultImpl : ( IMP ) impl_;
- ( id ) forObject : ( id ) object_ invokeWithParameter : ( id ) parameter_;
@end
static char * PMimplsKey = nil ;
@implementation PMImplementation
@synthesize impls = _impls;
@synthesize defaultImpl = _default_impl;
- ( void ) dealloc {
[ _impls release ] ;
[ _default_impl release ] ;
[ super dealloc ] ;
}
- ( id ) initWithDefaultImpl : ( IMP ) impl_ {
if ( ! ( self = [ super init ] ) )
return nil ;
self.defaultImpl = [ NSValue valueWithPointer : impl_ ] ;
self.impls = [ NSMutableDictionary dictionary ] ;
return self;
}
+ ( id ) implementationWithDefaultImpl : ( IMP ) impl_ {
return [ [ [ self alloc ] initWithDefaultImpl : impl_ ] autorelease ] ;
}
- ( id ) forObject : ( id ) object_ invokeWithParameter : ( id ) parameter_ {
for ( id key_ in [ self.impls allKeys ] )
if ( [ key_ isEqual : parameter_ ] ) {
PatternMatchingBlock block_ = [ self.impls objectForKey : key_ ] ;
return block_ ( parameter_ ) ;
}
IMP impl_ = [ self.defaultImpl pointerValue ] ;
return impl_ ( object_, _cmd, parameter_ ) ;
}
@end
@implementation NSObject ( PatternMatching )
- ( NSMutableDictionary * ) impls {
NSMutableDictionary * impls_ = objc_getAssociatedObject ( self, & PMImplsKey ) ;
if ( ! impls_ ) {
impls_ = [ NSMutableDictionary dictionary ] ;
objc_setAssociatedObject ( self, & PMImplsKey, impls_
, OBJC_ASSOCIATION_RETAIN_NONATOMIC ) ;
}
return impls_;
}
- ( void ) method : ( SEL ) sel_ withParameter : ( id ) object_
useBlock : ( PatternMatchingBlock ) block_ {
NSString * selector_key_ = NSStringFromSelector ( sel_ ) ;
PMImplementation * impl_ = [ self.impls objectForKey : selector_key_ ] ;
if ( ! impl_ ) {
Method default_ = class_getInstanceMethod ( [ self class ] , sel_ ) ;
IMP default_impl_ = method_getImplementation ( default_ ) ;
impl_ = [ PMImplementation implementationWithDefaultImpl : default_impl_ ] ;
[ self.impls setObject : impl_ forKey : selector_key_ ] ;
Method swizzed_method_ = class_getInstanceMethod ( [ self class ]
, @selector ( swizzledMethod :) ; ) ;
method_setImplementation ( default_, method_getImplementation ( swizzed_method_ ) ) ;
}
[ impl_.impls setObject : block_ forKey : object_ ] ;
}
- ( id ) swizzledMethod : ( id ) obj_ {
PMImplementation * impl_ = [ self.impls objectForKey : NSStringFromSelector ( _cmd ) ] ;
return [ impl_ forObject : self invokeWithParameter : obj_ ] ;
}
@end
@interface Factorial : NSObject
- ( NSDecimalNumber * ) factorial : ( NSDecimalNumber * ) number_;
@end
@implementation Factorial
- ( id ) init {
if ( ! ( self = [ super init ] ) )
return nil ;
NSDecimalNumber * zero_ = [ NSDecimalNumber numberWithInteger : 0 ] ;
[ self method : @selector ( factorial :) withParameter : zero_ useBlock : ^ ( id obj_ ) {
return ( id ) [ NSDecimalNumber numberWithInteger : 1 ] ;
} ] ;
NSDecimalNumber * one_ = [ NSDecimalNumber numberWithInteger : 1 ] ;
[ self method : @selector ( factorial :) withParameter : one_ useBlock : ^ ( id obj_ ) {
return ( id ) [ NSDecimalNumber numberWithInteger : 1 ] ;
} ] ;
return self;
}
- ( NSDecimalNumber * ) factorial : ( NSDecimalNumber * ) number_ {
return [ number_ decimalNumberByMultiplyingBy :
[ self factorial : [ number_ decimalNumberBySubtracting :
[ NSDecimalNumber numberWithInteger : 1 ] ] ] ] ;
}
Factorial * factorial_ = [ [ Factorial new ] autorelease ] ;
NSNumber * number_ = [ NSDecimalNumber numberWithInteger : 10 ] ;
NSLog ( @ "factorial% @ =% @" , number_, [ factorial_ factorial : number_ ] ) ;
factorial 10 = 3628800
#define PMCLASS (x) [[[PMClass alloc] initWith: [x class]] autorealese]
@interface PMClass : NSObject <NSCopying>
@property ( nonatomic, retain ) Class class;
- ( id ) initWith : ( Class ) class_;
@end
@implementation PMClass
@synthesize class = _class;
- ( void ) dealloc {
[ _class release ] ;
[ super dealloc ] ;
}
- ( id ) initWith : ( Class ) class_ {
if ( ! ( self = [ super init ] ) )
return nil ;
self.class = class_;
return self;
}
- ( BOOL ) isEqual : ( id ) object_ {
return [ self.class isEqual : [ object_ class ] ] ;
}
- ( id ) copyWithZone : ( NSZone * ) zone_ {
return [ [ PMClass alloc ] initWith : self.class ] ;
}
@end
@interface Test : NSObject
- ( void ) test : ( id ) obj_;
@end
@implementation Test
- ( id ) init {
if ( ! ( self = [ super init ] ) )
return nil ;
[ self method : @selector ( test :) withParameter : PMCLASS ( NSNull ) useBlock : ^ ( id obj_ ) {
NSLog ( @ "implementation for Null:% @" , [ obj_ class ] ) ;
return ( id ) nil ;
} ] ;
return self;
}
- ( void ) test : ( id ) obj_ {
NSLog ( @ "default impl for Object:% @" , [ obj_ class ] ) ;
}
@end
Test * test_ = [ [ Test new ] autorealese ] ;
[ test_ test : @ "String" ] ;
[ test_ test : [ NSNull null ] ] ;
default impl for Object: NSCFString
implementation for Null: NSNull
Source: https://habr.com/ru/post/123187/
All Articles