Pass self.property to the function - objective-c

I have a problem with get property. When I pass _videoRect to the drawArea function that nothing happens. But if I am change self.videoRect that work perfect.
My code:
#property (nonatomic, assign) BOOL isRecording;
#property (nonatomic) NSValue* videoRect;
#implementation Interactor
#synthesize videoRect = _videoRect;
- (id)init {
_isRecording = NO;
[self createVideoObserver];
return self;
}
- (void)record {
_isRecording = !_isRecording;
if (!_isRecording) {
[self stop];
[_presenter animateRecordButton:_isRecording];
} else {
[self drawArea:&(_videoRect) completion:nil];
}
}
- (void)drawArea:(NSValue* __strong *)rect completion:(void (^ __nullable)(void))completion {
if (!_isFullScreen) {
*rect = [NSValue valueWithRect:[self fullScreenRect]];
} else {
[self drawScreenRect];
}
if (completion != NULL) {
completion();
}
}
Setter and getter:
- (void)setVideoRect:(NSValue*)videoRect {
_videoRect = videoRect;
}
- (NSValue*)videoRect {
return _videoRect;
}
Create video observer:
- (void)createVideoObserver {
[[RACObserve(self, videoRect) skip:1] subscribeNext:^(id _) {
[self start];
[_presenter animateRecordButton:_isRecording];
}];
}
I don’t understand why observer doesn’t work. How can I pass self.videoRect to the drawArea function?

Your -drawArea:completion: method is taking a pointer to an NSValue, whereas self.videoRect is a property to an NSValue. In Swift, you can pass a property like this by reference, but in Objective-C, a property is really nothing more than a couple of methods, one of which sets an instance variable and one which returns it. So just passing the property into the method will not work. What you need to do is to read the property, pass a pointer to the resulting value, and then modify the property with the new value:
NSValue *videoRect = self.videoRect;
[self drawArea:&videoRect completion:^{
...
self.videoRect = videoRect;
}];

Related

Nonnull violation detection in Objective-C

I have some code like this where I declare an object with nonnull properties
#interface HelloObject : NSObject
#property (nonatomic,strong,nonnull) NSString *foo;
#property (nonatomic,strong,nonnull) NSString *bar;
-(instancetype _Nullable)initWithJson:(nonnull NSDictionary*)json;
#end
and then to initialize the object from the JSON I make use this code:
-(instancetype _Nullable)initWithJson:(nonnull NSDictionary*)json
{
if( (self = [super init]) ) {
_bar = json[#"bar"];
_foo = json[#"foo"];
}
return self;
}
The server may have sent me malformed JSON. For example, the "foo" field might be missing. It would be easy enough to check for nil and return nil, but I have a lot of this code and it would be inelegant and error prone.
Is there a easy and elegant way to check to see if an object violates its nonnull declarations at runtime? For example, I don't want to write code like this:
barX = json[#"bar"];
if (barX) {
_bar = barX;
} else {
return nil;
}
That's ugly and boilerplate (and therefore prone to error). I'd much rather have something like:
if (![self ValidForNonNulls]) {
return nil;
}
but I can't think of a way to write ValidForNonNulls for a general object.
I don't think that trying to work around the need to test for nil is practical in Objective-C.
If I were having to verify many such incoming terms, and I needed to check them for validity or return nil, then I would rewrite my initWithJson method to check, with a category to keep the code clean and readable.
- (instancetype _Nullable)initWithJSON:(nonnull NSDictionary *)json
{
if ( (self = [super init]) ) {
if ( ![json hasValuesForKeys:#[#"foo", #"bar"]] ) {
//Consider logging this as an error
return nil;
}
_bar = json[#"bar"];
_foo = json[#"foo"];
}
return self;
}
...
#interface NSDictionary (hasValuesForKeys)
- (BOOL)hasValuesForKeys:(NSArray *)keys;
#end
#implementation
- (BOOL)hasValuesForKeys:(NSArray *)keys
{
for (NSString *key in keys) {
if ( !self[key] || [self[key] isEqual:[NSNull null]] ) {
return NO;
}
}
return YES;
}
#end
You could make more specific tests for each value if you need to validate whether they are NSNumber for example.

How to set default values for IBInspectable in Objective-C?

I know default values of IBInspectable-properties can be set as:
#IBInspectable var propertyName:propertyType = defaultValue in Swift. But how do I achieve a similar effect in Objective-C so that I can have default value of some property set to something in Interface Builder?
Since IBInspectable values are set after initWithCoder: and before awakeFromNib:, you can set the defaults in initWithCoder: method.
#interface MyView : UIView
#property (copy, nonatomic) IBInspectable NSString *myProp;
#property (assign, nonatomic) BOOL createdFromIB;
#end
#implementation MyView
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if(self != nil) {
self.myProp = #"foo";
self.createdFromIB = YES;
}
return self;
}
- (void)awakeFromNib {
if (self.createdFromIB) {
//add anything required in IB-define way
}
NSLog(#"%#", self.myProp);
}
#end
I wrote my code like this. It works pretty well for me, both when designing in the interface builder or running as a app.
#interface MyView : UIView
#property (copy, nonatomic) IBInspectable propertyType *propertyName;
#end
- (void)makeDefaultValues {
_propertyName = defaultValue;
//Other properties...
}
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self makeDefaultValues];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
[self makeDefaultValues];
}
return self;
}
I'm using like that
#IBInspectable var propertyNameValue:propertyType?
var propertyName:propertyType { return propertyNameValue ?? defaultValue }
if propertyNameValue has nil, propertyName will return defaultValue.
Why don't use use the macro such as:
#if TARGET_INTERFACE_BUILDER
// .....IB only specific code when being rendered in IB
#endif
???
The prepareForInterfaceBuilder selector may also help you to implement IB specific code.
More info about those 2 points here:
https://developer.apple.com/library/ios/recipes/xcode_help-IB_objects_media/chapters/CreatingaLiveViewofaCustomObject.html
Firstly I tried to override getter and did something like this:
- (UIColor *)borderColor {
return _borderColor ?: [UIColor greenColor];
}
But in that case I received issue about undeclared identifier _borderColor.
Ok, I tried to avoid this issue via custom getter.
- (UIColor *)getBorderColor {
return _borderColor ?: [UIColor greenColor];
}
Actually it's not a proper getter, as we don't point this method as getter. In case we point we'll receive issue about undeclared identifier, that's why we won't.
Then we use this method for getting property value in updateUI method.
Also we have to override setter:
- (void)setBorderColor:(UIColor *)borderColor {
_borderColor = borderColor;
[self updateUI];
}

objc_setAssociatedObject in a category sets for all subclasses

I have a custom container view controller that manages the view hierarchy of my app. I know that every controller is some sort of child of this container controller. I thought it would be nice to have a category on UIViewController that would allow me to access the container controller, no matter where I am in the hierarchy.
This involves a recursive walk up the controller hierarchy, so I thought it would be good to try and only do that walk once per controller. So with objc_setAssociatedObject, I set the container once I've found it and set a flag so that I know whether or not I need to walk the hierarchy on subsequent calls (I planned to invalidate that if the viewcontroller ever moved, but that's probably overkill, and I didn't get that far).
Anyway, that works fine for the most part except that my flag for whether or not the hierarchy has been walked seems to be attached to UIViewController, and not specific subclasses of UIViewController.
I swizzled +load to try to set default values on my associated objects to no avail.
Any ideas? How to I get associated objects in a category to associate with the subclasses of the class the category is defined on?
Here's my code, for good measure.
#import "UIViewController+LMPullMenuContainer.h"
#import <objc/runtime.h>
static char const * const CachedKey = "__LM__CachedBoolPullMenuAssociatedObjectKey";
static char const * const PullMenuKey = "__LM__PullMenuAssociatedObjectKey";
#implementation UIViewController (LMPullMenuContainer)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
SEL initSelector = #selector(initWithCoder:);
SEL pullViewInitSelector = #selector(init__LM__Swizzled__WithCoder:);
Method originalMethod = class_getInstanceMethod(self, initSelector);
Method newMethod = class_getInstanceMethod(self, pullViewInitSelector);
BOOL methodAdded = class_addMethod([self class],
initSelector,
method_getImplementation(newMethod),
method_getTypeEncoding(newMethod));
if (methodAdded) {
class_replaceMethod([self class],
pullViewInitSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, newMethod);
}
});
}
- (instancetype)init__LM__Swizzled__WithCoder:(NSCoder *)coder {
self = [self init__LM__Swizzled__WithCoder:coder];
if (self != nil)
{
objc_setAssociatedObject(self, CachedKey, [NSNumber numberWithBool:NO], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_setAssociatedObject(self, PullMenuKey, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return self;
}
- (LMPullMenuContainerViewController*)pullMenuContainerController {
BOOL isCached = [objc_getAssociatedObject(self, CachedKey) boolValue];
if (isCached) {
return objc_getAssociatedObject(self, PullMenuKey);
} else {
return [self pullMenuParentOf:self];
}
}
- (LMPullMenuContainerViewController *)pullMenuParentOf:(UIViewController *)controller {
if (controller.parentViewController) {
if ([controller.parentViewController isKindOfClass:[LMPullMenuContainerViewController class]]) {
objc_setAssociatedObject(self, CachedKey, [NSNumber numberWithBool:YES], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_setAssociatedObject(self, PullMenuKey, controller.parentViewController, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
return (LMPullMenuContainerViewController *)(controller.parentViewController);
} else {
return [self pullMenuParentOf:controller.parentViewController];
}
} else {
objc_setAssociatedObject(self, CachedKey, [NSNumber numberWithBool:YES], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_setAssociatedObject(self, PullMenuKey, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
return nil;
}
}
For now I've resigned to setting the property manually where necessary.
As it happens, the above code works just fine. My container controller was loading all of the controllers it manages when it was first initialized and not when the controllers were first displayed, so to me it looked as though the flag had been set before it should have been.

iOS Store random property in Component e.g. UITextField without Subclass

Does anyone know if there is a way to set a property like a string in the User Defined Runtime Atributes sections of Interface Builder without creating a subclass of said component? For example, I want to store a metadata value for each component in my interface that I use later. I just don't want to have to create a subclass or each component to add a metadata property.
This is one approach I came up with. Opinions?
#import <UIKit/UIKit.h>
#import <objc/runtime.h>
#interface UIControl(MetaData)
#property (nonatomic, retain) id entityProperty;
#end
#implementation UIControl(MetaData)
static char const * const EntityPropertyKey = "EntityProperty";
#dynamic entityProperty;
- (id)entityProperty {
return objc_getAssociatedObject(self, EntityPropertyKey);
}
- (void)setEntityProperty:(id)newEntityProperty {
objc_setAssociatedObject(self, EntityPropertyKey, newEntityProperty, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
#end
...
if (textField.entityProperty)
[managedObject setValue: textField.text forKey:textField.entityProperty];
You could keep an NSDictionary somewhere, perhaps in a singleton object that has methods for issuing unique ids for objects and storing metadata by the id keys in the dictionary. The UI objects have a tag property that you can use, if your ids are just incremented integers. Then the dictionary keys would just be NSNumbers for those unique integers.
Like this:
#import <Foundation/Foundation.h>
#interface ACLMetadataManager : NSArray
+(ACLMetadataManager*) sharedMetadataManager;
-(NSUInteger) getUniqueId;
-(void) setObject: (id) object forId:(NSUInteger) theId;
-(id) objectForId:(NSUInteger) theId;
#end
And:
#import "ACLMetadataManager.h"
#implementation ACLMetadataManager { // Private variables
NSMutableDictionary *_metadata;
NSUInteger _ids;
}
- (id)init {
self = [super init];
if (self) {
_metadata = [[NSMutableDictionary alloc] init];
}
return self;
}
+(ACLMetadataManager*) sharedMetadataManager { // Singleton getter
static ACLMetadataManager *instance;
if (instance != nil) {
return instance;
}
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0
static dispatch_once_t oneTimeThread;
dispatch_once(&oneTimeThread, ^(void) {
instance = [[ACLMetadataManager alloc] init];
});
#else
#synchronized(self) {
instance = [[ACLMetadataManager alloc] init];
}
#endif
return instance;
}
-(NSUInteger) getUniqueId { // Increment unique id when getter is called.
return ++_ids; // Start from 1 because tag is 0 by default.
}
-(void) setObject: (id) object forId:(NSUInteger) theId {
[_metadata setObject:object forKey:[NSNumber numberWithInteger:theId]];
}
-(id) objectForId:(NSUInteger) theId {
return [_metadata objectForKey:[NSNumber numberWithInteger:theId]];
}
// Override some methods to ensure singleton stays instantiated.
- (id) retain {
return self;
}
- (oneway void) release {
// Does nothing here.
}
- (id) autorelease {
return self;
}
- (NSUInteger) retainCount {
return INT32_MAX;
}
#end
Usage:
ACLMetadataManager *metadataManager = [ACLMetadataManager sharedMetadataManager];
myControl.tag = [metadataManager getUniqueId];
[metadataManager setObject:myMetadata forId:myControl.tag];

Shared UITableViewDelegate

I'm writting a subclass of UITableView and I want my subclass to handle some of the UITableViewDelegate methods itself before passing them along to the "real" delegate as well as forward all the UITableViewDelegate methods not implemented by my subclass.
In the subclass I have a private property:
#property (nonatomic, assign) id <UITableViewDelegate> trueDelegate;
which holds the "real delegate" that all the unimplemented methods should forward to. In both my init methods I set
self.delegate = self;
and I override - (void)setDelegate:(id) like this
-(void)setDelegate:(id<UITableViewDelegate>)delegate {
if (delegate != self) {
_trueDelegate = delegate;
} else {
[super setDelegate:self];
}
}
Then I override these to handle the message forwarding
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
NSMethodSignature *sig;
sig = [[self.delegate class] instanceMethodSignatureForSelector:aSelector];
if (sig == nil) {
sig = [NSMethodSignature signatureWithObjCTypes:"#^v^c"];
}
return sig;
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
SEL selector = anInvocation.selector;
if ([self respondsToSelector:selector]) {
[anInvocation invokeWithTarget:self];
} else {
[anInvocation invokeWithTarget:_trueDelegate];
}
}
The problem is that the unimplemented delegate methods never get called on the tableview, therefore they are not given a chance to be forwarded along to the _trueDelegate object.
I tried checking for them here:
- (BOOL)respondsToSelector:(SEL)aSelector {
}
but that method is never called for the UITableViewDelegate methods although it catches other methods just fine.
For performance, UITableView checks and remembers which delegate methods are available as soon as the delegate is set. You set the delegate self first, then the trueDelegate. So at the time the delegate is set on the UITableView, trueDelegate is nil, and so -respondsToSelector: on that one always returns NO.
To fix that, set the delegate after trueDelegate is set. Also, you can simplify the forwarding code. Remove all the code you have above except for the property and replace it with:
- (void)setDelegate:(id <UITableViewDelegate>)delegate
{
if (delegate == self) return;
self.trueDelegate = delegate;
[super setDelegate:self];
}
- (BOOL)respondsToSelector:(SEL)aSelector
{
if ([super respondsToSelector:aSelector]) return YES;
return [self.trueDelegate respondsToSelector:aSelector];
}
- (id)forwardingTargetForSelector:(SEL)aSelector
{
return self.trueDelegate;
}