, , Objective-C ++. :
:
( ++: Reference to non-const type 'id' with no explicit ownership). __strong, __weak __autoreleasing
. ++ Objective-C, , " " , . , , , .
swizzling . , swizzling, , .
++. , ++ runtime, Objective-C, . OBJC_IVAR, ++. .
#line , .
- 1.0:
- 1.1:
OBJC_IVAR_NAME, . __func__.
, , :
OBJC_IVAR.hpp
#ifndef OBJC_IVAR_HPP
#define OBJC_IVAR_HPP
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import "NSValue+CppObject.h"
#define __NARG(_1, _2, _3, _4, _5, VAL, ...) VAL
#define NARG(...) __NARG(__VA_ARGS__, 5, 4, 3, 2, 1, 0)
#define __OBJC_IVAR(N, ...) _OBJC_IVAR_ ## N (__VA_ARGS__)
#define _OBJC_IVAR(N, ...) __OBJC_IVAR(N, __VA_ARGS__)
#define OBJC_IVAR(...) _OBJC_IVAR(NARG(__VA_ARGS__), __VA_ARGS__)
#define __OBJC_IVAR_STRINGIFY_NAME(file, line, name, counter) @file ":" #line " " #name ":" #counter
#define _OBJC_IVAR_NAME(file, line, name, counter) __OBJC_IVAR_STRINGIFY_NAME(file, line, name, counter)
#define OBJC_IVAR_NAME(name) _OBJC_IVAR_NAME(__FILE__, __LINE__, name, __COUNTER__)
#define _OBJC_IVAR_0(...) _Pragma("message \"Cannot call OBJC_IVAR with 0 params!\"")
#define _OBJC_IVAR_1(Name) _OBJC_IVAR_2(__strong id, Name)
#define _OBJC_IVAR_2(Type, Name) Type& Name = (_OBJC_IVAR::IMPL<Type>(self, OBJC_IVAR_NAME(Name)))
static NSString *_OBJC_IVAR_CUR_NAME;
#define _OBJC_IVAR_3(Type, Name, Default) Type& Name = (_OBJC_IVAR::IS_SET(self, (_OBJC_IVAR_CUR_NAME = OBJC_IVAR_NAME(Name))) ? _OBJC_IVAR::IMPL<Type>(self, _OBJC_IVAR_CUR_NAME) : _OBJC_IVAR::IMPL<Type>(self, _OBJC_IVAR_CUR_NAME, Default))
namespace _OBJC_IVAR
{
static NSMutableSet *_names = [NSMutableSet set];
template<typename T>
class Wrapper {
private:
T _value;
T& _ref;
public:
Wrapper() : _value(), _ref(_value) { }
Wrapper(T val) : _value(val), _ref(_value) { }
operator T& () {
return _ref;
}
T& get() {
return _ref;
}
};
inline NSString *name_intern(NSString *name)
{
if (id tmpName = [_names member:name])
{
name = tmpName;
}
else
{
[_names addObject:name];
}
return name;
}
BOOL IS_SET(id target, NSString *name)
{
name = name_intern(name);
id val = objc_getAssociatedObject(target, (__bridge const void *) name);
return val != nil;
}
template<typename T>
Wrapper<T>& IMPL(id target, NSString *name)
{
name = name_intern(name);
Wrapper<T> *reference = nullptr;
NSValue *result = objc_getAssociatedObject(target, (__bridge const void *) name);
if (result == nil)
{
reference = new Wrapper<T>();
result = [NSValue valueWithCppObject:reference onDealloc:^(void *) {
delete reference;
}];
objc_setAssociatedObject(target, (__bridge const void *) name, result, OBJC_ASSOCIATION_RETAIN);
}
reference = static_cast<Wrapper<T> *>([result cppObjectValue]);
return *reference;
}
template<typename T>
Wrapper<T>& IMPL(id target, NSString *name, const T& defVal)
{
name = name_intern(name);
Wrapper<T> *reference = nullptr;
NSValue *result = objc_getAssociatedObject(target, (__bridge const void *) name);
if (result == nil)
{
reference = new Wrapper<T>(defVal);
result = [NSValue valueWithCppObject:reference onDealloc:^(void *) {
delete reference;
}];
objc_setAssociatedObject(target, (__bridge const void *) name, result, OBJC_ASSOCIATION_RETAIN);
}
reference = static_cast<Wrapper<T> *>([result cppObjectValue]);
return *reference;
}
}
#endif
NSValue + CppObject.h
#import <Foundation/Foundation.h>
@interface NSValue (CppObject)
+(id) valueWithCppObject:(void *) ptr onDealloc:(void (^)(void *)) deallocBlock;
-(id) initWithCppObject:(void *) ptr onDealloc:(void (^)(void *)) deallocBlock;
-(void *) cppObjectValue;
@end
NSValue + CppObject.m
#import "NSValue+CppObject.h"
@interface ConcreteCppObject : NSValue
{
void *_object;
void (^_deallocBlock)(void *);
}
@end
@implementation ConcreteCppObject
+(id) valueWithCppObject:(void *)ptr onDealloc:(void (^)(void *))deallocBlock
{
return [[self alloc] initWithCppObject:ptr onDealloc:deallocBlock];
}
-(id) initWithCppObject:(void *)ptr onDealloc:(void (^)(void *))deallocBlock
{
if (self = [super init])
{
_object = ptr;
_deallocBlock = deallocBlock;
}
return self;
}
-(const char *) objCType
{
return @encode(void *);
}
-(void) getValue:(void *)value
{
*((void **) value) = _object;
}
-(BOOL) isEqual:(id)compare
{
if (![compare isKindOfClass:[self class]])
return NO;
return [compare cppObjectValue] == [self cppObjectValue];
}
-(void) dealloc
{
_deallocBlock(_object);
}
-(void *) cppObjectValue
{
return _object;
}
@end
@implementation NSValue (CppObject)
+(id) valueWithCppObject:(void *)ptr onDealloc:(void (^)(void *))deallocBlock
{
return [[ConcreteCppObject alloc] initWithCppObject:ptr onDealloc:deallocBlock];
}
-(id) initWithCppObject:(void *)ptr onDealloc:(void (^)(void *))deallocBlock
{
return [[self class] valueWithCppObject:ptr onDealloc:deallocBlock];
}
-(void *) cppObjectValue
{
[self doesNotRecognizeSelector:_cmd];
return nil;
}
@end
#import "OBJC_IVAR.hpp"
@interface SomeObject : NSObject
-(void) doSomething;
@end
@implementation SomeObject
-(void) doSomething
{
OBJC_IVAR(__strong id, test, @"Hello World!");
OBJC_IVAR(int, test2, 15);
NSLog(@"%@", test);
NSLog(@"%i", test2 += 7);
{
OBJC_IVAR(int, test, 100);
NSLog(@"%i", ++test);
}
[self somethingElse];
}
-(void) somethingElse
{
OBJC_IVAR(int, newVar, 7);
NSLog(@"%i", newVar++);
}
@end
int main()
{
SomeObject *obj = [SomeObject new];
[obj doSomething];
[obj doSomething];
[obj doSomething];
}