<objc/runtime.h>
on the contents of the header <objc/runtime.h>
, but on some of the features of the language itself, which many developers don’t guess. Yes, you stumble upon them, reading the documentation, you note to yourself “hmm, I wonder, you have to dig a little”, but they usually quickly fly out of my head. And novice developers often even read the documentation diagonally.- (void)setSize:(CGFloat)x :(CGFloat)y
, but this can be brought to the absolute: @interface TestObject : NSObject + (id):(int)value; - (void):(int)a; - (void):(int)a :(int)b; @end // ... TestObject *obj = [TestObject :2]; [obj :4]; [obj :5 :7];
@selector(:)
and @selector(::)
. - (id)objectAtIndexedSubscript:(NSUInteger)index; - (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)index;
- (id)objectForKeyedSubscript:(id)key; - (void)setObject:(id)obj forKeyedSubscript:(id)key;
id a = obj[1]; obj[@"key"] = a;
@property
@property
ad in the header actually announces only the getter and mutator for some field. If you declare them directly, nothing will change: - (int)value; - (void)setValue:(int)newValue; obj.value = 2; int i = obj.value;
NSArray *a = @[@1, @2, @3]; NSInteger c = a.count;
@interface TestObject : NSObject - (void)setTitle:(NSString *)title; @end; //... TestObject *obj = [TestObject new]; obj.title = @"simple object";
@property
ad looks much better, and for this it was introduced. Properties are best accessed through " .
", But conventional methods are best called via " []
". Otherwise, the readability of the code starts to suffer.alloc
calloc
function, then specifying isa
manually. In principle, this is just a replacement for alloc
, and init
can be sent later. // , void *newObject = calloc(1, class_getInstanceSize([TestObject class])); // isa Class *c = (Class *)newObject; c[0] = [TestObject class]; // __bridge_transfer- ARC - obj = (__bridge_transfer TestObject *)newObject; // init - ! obj = [obj init];
object_setClass((__bridge id)newObject, [TestObject class]);
extern
: extern void _objc_autoreleasePoolPrint(void);
objc[26573]: ############## objc[26573]: AUTORELEASE POOLS for thread 0x7fff72fb0310 objc[26573]: 9 releases pending. objc[26573]: [0x100804000] ................ PAGE (hot) (cold) objc[26573]: [0x100804038] ################ POOL 0x100804038 objc[26573]: [0x100804040] 0x100204500 TestObject objc[26573]: [0x100804048] 0x100102fc0 __NSDictionaryM objc[26573]: [0x100804050] 0x1007000b0 __NSArrayI objc[26573]: [0x100804058] 0x1006000a0 __NSCFString objc[26573]: [0x100804060] 0x100600250 NSMethodSignature objc[26573]: [0x100804068] 0x100600290 NSInvocation objc[26573]: [0x100804070] 0x100600530 __NSCFString objc[26573]: [0x100804078] 0x100600650 __NSArrayI objc[26573]: ##############
@property
values@property
only generates getter and mutator signatures. But if the property is synthesized (via @synthesize
or by default), then ivar is also generated: @property NSMutableDictionary *dict; - (void)resetDict { _dict = nil; }
@interface TestObject : NSObject { @public int field; } @implementation TestObject - (void)updateWithField:(int)field { self->field = field; } @end // ... TestObject *obj = [TestObject new]; obj->field = 200;
instancetype
id
type, which is essentially NSObject *
, that is, the most basic type for objects that any other object can lead to. Convenient, but in some cases problems may arise. For example: [[MyClass sharedInstance] count];
sharedInstance
returns an id
, the code will be collected without warning even if there is no count
method in MyClass
. If sharedInstance
returns an instancetype
, then the Vorning will still appear, because the compiler clearly understands that an object is returned from the class of which sharedInstance
is called.init/new/copy
, etc.forwardingTargetForSelector:
and forwardInvocation:
- (id)forwardingTargetForSelector:(SEL)aSelector { if ([_dict respondsToSelector:aSelector]) { return _dict; } return [super forwardingTargetForSelector:aSelector]; }
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { NSMethodSignature *sig = [super methodSignatureForSelector:aSelector]; if ([NSStringFromSelector(aSelector) isEqualToString:@"allDamnKeys"]) { sig = [_dict methodSignatureForSelector:@selector(allKeys)]; } return sig; }
- (void)forwardInvocation:(NSInvocation *)anInvocation { if ([NSStringFromSelector(anInvocation.selector) isEqualToString:@"allDamnKeys"]) { anInvocation.selector = @selector(allKeys); [anInvocation invokeWithTarget:_dict]; } }
NSFastEnumeration
for..in
cycles. They are supported by all default collections, but we can support as well. To do this, you need to support the NSFastEnumeration
protocol, or rather, define the countByEnumeratingWithState:objects:count:
method countByEnumeratingWithState:objects:count:
but not everything is so simple! Here is the signature of this method: - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained [])buffer count:(NSUInteger)len
len
size) or select our own. A pointer to this buffer should be placed in the state->itemsPtr
, and the number of objects in it should be returned from the function. Just do not forget that (in the documentation there is no) the field state->mutationsPtr
should not be empty. If this is not done, then we get an unexpected SEGFAULT
. But in the state->state
field, you can write anything, but the best thing is to record the number of elements already sent. If there is nothing to give, you need to return zero. - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained [])buffer count:(NSUInteger)len { if (state->state >= _value) { return 0; } NSUInteger itemsToGive = MIN(len, _value - state->state); for (NSUInteger i = 0; i < itemsToGive; ++i) { buffer[i] = @(_values[i + state->state]); } state->itemsPtr = buffer; state->mutationsPtr = &state->extra[0]; state->state += itemsToGive; return itemsToGive; }
for (NSNumber *n in obj) { NSLog(@"n = %@", n); }
Source: https://habr.com/ru/post/210672/
All Articles