Key-Value Coding with custom getter, setter and ivar - objective-c

I'm wondering how Key-Value Coding, to access a property value, works in Objective-C when this property has a custom getter, setter and ivar defined. According to Accessor Search Patterns the runtime will first search for a getter method and fall back to finding an ivar using the reflection string.
According to the search pattern, when neither a getter nor an ivar are found, an exception should be thrown.
However, when I run the following code:
#import <Foundation/Foundation.h>
#interface Class1 : NSObject {
NSInteger prop;
}
#property (getter=customGetter,setter=customSetter:) NSInteger prop;
#end
#implementation Class1
#synthesize prop = customIvar;
#end
int main() {
Class1 *class1;
// Create and give the properties some values with KVC...
class1 = [[Class1 alloc] init];
class1.prop = 9;
NSLog(#"Set value to 9 with direct access");
// Directly access value, should return 9.
NSLog(#"Direct access: %ld", class1.prop);
// Set with setValue:forKey: to 20.
NSLog(#"Set value to 20 with KVC");
[class1 setValue:[NSNumber numberWithInt:20] forKey:#"prop"];
// Directly access value.
NSLog(#"Direct access: %ld", class1.prop);
// Access value using KVC
NSNumber *propVal = [class1 valueForKey:#"prop"];
NSLog(#"ValueForKey access: %d", [propVal intValue]);
}
I get this output:
Set value to 9 with direct access
Direct access: 9
Set value to 20 with KVC
Direct access: 9
ValueForKey access: 20
It seems that I get two different values: values that are set by directly accessing the property are retrieved when reading directly from the property (9). Values that are set using Key-Value Coding are retrieved by using Key-Value Coding (20).
Does anyone know how this works internally? Is this behaviour expected and am I missing something?

Add the following method to your Class1:
- (void) showVars
{
NSLog(#"->prop %ld | ->customIVar %ld", prop1, customIvar);
}
and call it after you alter your property and you will see what is going on.
Read the reference you linked to, what does it say about the search for a getter and setter?
You've found your answer.
Is it well documented? Probably not, Objective-C & Cocoa have no formal semantic description :-(
HTH

Related

How to initialise private member variable

In the below code I want to declare a private instance variable and initialise it to zero
But when I try to initialise it to zero I receive irrelevant error “; comma expected at the end of the statement”
As an attempt to solve the issue, I tried to find out to which value this variable is initialised:
-(void) startRecursion: (UIView *) uiviews {
if (!uiviews) {
return;
}
NSLog(#"numOfMethodCalls: %d", ++self->numOfMethodCalls);
NSUInteger *countOfSubViews = [uiviews.subviews count];
}
However, the log printed :
2019-11-10 14:08:25.711847+0100 UIEnhancement_00[10065:383388] numOfMethodCalls: 8
Please let me know how to solve this issue
code:
#interface UIEnhancements : NSObject {
NSUInteger *numOfMethodCalls;
}
+(id) initSelf;
Instance variables are automatically initialised to zero in Objective-C.
Your code is printing 8 as you declared your variable as NSUInteger * – a pointer (*) to an NSUInteger. Now on a 64-bit platform an NSUInteger is 8 bytes long.
(Objective-)C defines the operator ++ on a pointer to increment it by the size of whatever type it points at. So numOfMethodCalls was initialised to 0 automatically and then the ++ added 8 to it.
Instance variables are best private, as you state you intended, but you have declared it publicly in the #interface. The best way is to declare it in the implementation:
#implementation UIEnhancements
{
NSUInteger numOfMethodCalls;
}
...
#end
Lastly though you don't show an implementation you declare:
+(id) initSelf;
in your interface. Only methods which are initialisation methods should start with init, they are instance methods, and in modern Objective-C are normally declared to return instancetype (rather than id) which improves compile-time type checks. In summary:
- (instancetype) initSelf;
though if it is your only initialisation method you should call it simply init.
HTH

How to clone a class instance and change just one field in Objective C or Swift?

Scala provides a way to change just one property when copying an immutable object. Does Objective C or Swift for that matter have a equivalent or is there any library that adds similar functionality?
https://coderwall.com/p/8wofhq/update-property-value-of-immutable-object-while-maintaining-immutability
NOTE: This question is about only referencing a single property not about updating an array of objects.
There's nothing built in, but it's easy enough to write a method that does this, using KVC to set the value:
#implementation NSObject (WSSCopyMutating)
- (instancetype)WSSCopyChangingValue:(id)val forKey:(NSString *)key
{
id newObj = [self copy];
[newObj setValue:val forKey:key];
return newObj;
}
#end
This requires that the class you're copying conforms to NSCopying. setValue:forKey: will find the field you've named regardless of its public visibility or writeability.

ios, KVC, why doesn't it invoke countOf<Key> when i invoke [[MyObject valueForKey:"MyArray" ] count]

..................
Environment:OSX10.8, Xcode4.5
Reference:
https://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual
/KeyValueCoding/Articles/SearchImplementation.html#//apple_ref/doc/uid
/20000955-CJBBBFFA
If the countOf method and at least one of the other two possible methods are
found, a collection proxy object that responds to all NSArray methods is
returned. Each NSArray message sent to the collection proxy object will result
in some combination of countOf, objectInAtIndex:, and AtIndexes: messages being
sent to the original receiver of valueForKey:.
My steps:
1) Create a property for NSArray* arrs in MyObject.h.
2) in MyObject.m
#implementation MyObject
- (void)setArrs:(NSArray*)arrs
{ _arrs=arrs; }
- (NSUInteger) countOfArrs
{ NSLog("Im here");
return 0;}
- (id) objectInArrsAtIndex:(NSUInteger)index
{ NSLog(#"objectInArrs");
return nil;
}
#end
3) Testing code
MyObject* obj=[[MyObject alloc] init];
NSArray* arr=[NSarray arrayWithObjects:#"abc",nil];
[obj setArrs:arr];
NSLog(#"%d",[[obj valueForKey:#"arrs"]count]);
NSLog(#"%#",[[obj valueForKey:#"arrs"] objectobjectAtIndex:0])
My Question:
I expect it to invoke countOfArrs and objectInArrsAtIndex: automatically,
however, it didn't. All it does is return the normal NSArray, shows count quantity by 1
and 'abc'.
I didn't find any helpful samples,or maybe i misunderstand what the doc says, don't I?
My tangue language is not English, hope i didn't make any ambitious issues.
You have implemented more functionality than what is needed. Your #property declaration is adding the -<key> getter and -set<Key> accessor. As you were able to find out, your getter (called by valueForKey:#"arrs") is returning the actual NSArray assigned to the property which returns its count and object.
The guide you linked to has the reason why this didn't work in step 1 and step 2 of the Default Search Pattern for valueForKey: section.
Basically, your -countOf<Key> and -objectIn<Key>AtIndex: methods are only called when there isn't either a -<key>, -get<Key> or -is<Key> methods.
In order to get this to work, you need to remove the getter by removing the #property declaration, and then you'll need to add an instance variable to save your array.
Make this your interface and it should work
#interface MyObject : NSObject {
NSArray *_arrs;
}
#end

How do I get the Objective-C class of an ivar?

I have a bunch of simple NSManagedObjects I create in a unit test. They just have a single name attribute of type NSString *. I always give my NSManagedObject the same entityName and Class name.
I want to avoid having to write the following code 30 times to set up a unit test:
#interface FooTest : GHTestCase {
Foo *foo;
}
#end
#implementation FooTest
- (void) setUp {
[super setUp];
foo = [NSEntityDescription insertNewObjectForEntityForName:#"Foo"
inManagedObjectContext:managedObjectContext];
foo.name = #"foo";
}
#end
Since foo is an ivar, I would think I should be able to write a macro to grab the type of foo (Foo), and use to create my Foo:
#define InsertManagedObjectByVariable(variable) \
do { \
variable = [NSEntityDescription insertNewObjectForEntityName:NSStringFromClass([typeof(variable) class])]; \
variable.name = (NSString *) CFSTR(#variable);
} while(0)
However, this causes the following warning in clang:
variable = [NSEntityDescription insertNewObjectForEntityName:NSStringFromClass([typeof(variable) class])];
^
Expected expression
I also thought I could try to determine the type using the objective-c runtime IVar from Ivar class_getInstanceVariable(Class cls, const char* name), but the only IVar type information available from the type encoding from ivar_getTypeEncoding is id, which isn't enough.
Can someone think of a way to obtain the type information of an IVar either at compile time or runtime?
I haven't tried obtaining class information from an ivar, but I know that #property declarations do encode information about the class. For instance, this property declaration:
#property (copy) NSString *normalString;
results in this attribute string (retrieved using property_getAttributes()) at runtime:
T#"NSString",C,VnormalString
I've written some open source parsing code for this information.
Once you have the class name, you can convert it into an actual Class object using NSClassFromString(), and message the result from there.
Disclaimer: This probably shouldn't be depended upon for production applications, as it is undocumented.
An id is an id. At runtime, all Objective-C objects have the same type (objc_object). This is tied up in the dynamic nature of ObjC. For example, an object can change classes at runtime, new classes can be created, and the class hierarchy can change. You can ask a specific instance what its type is (since this is stored in objc_object), but a pointer to an object is just a pointer to an object. Even less than that: it's really just a pointer to a C struct that happens to have extra memory allocated at the end (to hold subclass ivars).
Your macro seems interesting, but you'll probably need to pass the classname as the second parameter rather than autodetecting it.
Maybe i misunderstand what you are trying to achieve.
To get the class of an iVar, can't you use the class method of the iVar?
like:
NSString *aString = #"random string";
NSLog(#"%#",NSStringFromClass([aString class]));

Custom property attributes in Objective-c

Can custom property attributes be created in Objective-C just like in VB.NET? For example, in VB.NET you can create the "Browsable" attribute and read it at runtime to determine whether you should display a property or not.
Public Class Employee
<Browsable(True)> _
Public Property Property1() As String
Get
End Get
Set(ByVal Value As String)
End Set
End Property
<Browsable(False)> _
Public Property Property2() As String
Get
End Get
Set(ByVal Value As String)
End Set
End Property
End Class
I would like to do the same in Objective-C, even if it is a fixed attribute that can only be set at compile time and cannot be changed at all.
What I'm trying to do is to add an attribute to properties of my class to determine whether the properties should be serialized or not.
I know the standard Objective-C attributes (readonly, nonatomic, etc.), but those don't help me... unless you have a creative way of using them. I also looked into using C attributes with the __attribute__(("Insert attribute here")) keyword, but C has specific attributes that serve specific purposes, and I'm not even sure you can read them at runtime. If I missed one that can help me, let me know.
I tried using typdef. For example:
typdef int serializableInt;
serializableInt myInt;
and use the property_getAttributes() Objective-C runtime function, but all it tells me is that myInt is an int. I guess typedef is pretty much like a macro in this case... unless I can create a variable of type serializableInt at runtime. Anyhow, here's Apple's documentation on the values you get from property_getAttributes().
The other requirement is that this attribute has to work with NSObject sub-classes as well as primitive data types. I thought about the idea of adding to the class a black lists or white lists as an ivar that would tell me which properties to skip or serialize, which is basically the same idea. I'm just trying to move that black/white list to attributes so it's easy to understand when you see the header file of a class, it's consistent across any class I create and it's less error prone.
Also, this is something to consider. I don't really need the attribue to have a value (TRUE or FALSE; 1, 2, 3; or whatever) because the attribute itself is the value. If the attribute exists, then serialize; otherwise, skip.
Any help is appreciated. If you know for sure that this is not possible on Objective-C, then let me know. Thanks.
If you want to add attribute to property, class, method or ivar, you can try to use github.com/libObjCAttr. It's really easy to use, add it via cocoapods, and then you can add attribute like that:
#interface Foo
RF_ATTRIBUTE(YourAttributeClass, property1 = value1)
#property id bar;
#end
And in the code:
YourAttributeClass *attribute = [NSDate RF_attributeForProperty:#"bar" withAttributeType:[YourAttributeClass class]];
// Do whatever you want with attribute, nil if no attribute with specified class
NSLog(#"%#", attribute.property1)
unless i've missed your point…
i'd recommend declaring a protocol. then using instances of objc objects as variables in your objc classes which adopt the protocol.
#interface MONProtocol
- (BOOL)isSerializable;
- (BOOL)isBrowsable;
/* ... */
#end
#interface MONInteger : NSObject <MONProtocol>
{
int value;
}
- (id)initWithInt:(int)anInt;
#end
#interface MONIntegerWithDynamicProperties : NSObject <MONProtocol>
{
int value;
BOOL isSerializable;
BOOL isBrowsable;
}
- (id)initWithInt:(int)anInt isSerializable:(BOOL)isSerializable isBrowsable:(BOOL)isBrowsable;
#end
// finally, a usage
#interface MONObjectWithProperties : NSObject
{
MONInteger * ivarOne;
MONIntegerWithDynamicProperties * ivarTwo;
}
#end
if you want to share some implementation, then just subclass NSObject and extend the base class.
you'd then have a few variants to write for the types/structures you want to represent.
The deficiency with the other answers I've seen so far is that they are implemented as instance methods, i.e., you need to have an instance already before you can query this metadata. There are probably edge cases where that's appropriate, but metadata about classes should be implemented as class methods, just as Apple does, e.g.:
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString*)key { }
We could imagine our own along similar lines:
+ (BOOL)keyIsBrowsable:(NSString*)key { }
or
+ (NSArray*)serializableProperties { }
Let's imagine our class is called FOOBar, and we want to know whether the baz key is browsable. Without having to create a FOOBar we can just say:
if ([FOOBar keyIsBrowsable:#"baz"]} { ... }
You can do pretty much anything with this technique that can be done with custom attributes. (Except for things like the Serializable attribute which require cooperation from the compiler, IIRC.) The nice thing about custom attributes, though, is that it is easy to distinguish at a glance what is metadata and what is intrinsic to that class's actual functionality, but I think that's a minor gain.
(Of course, you may have to check for the existence of the keyIsBrowsable: selector, just as you'd have to check for the existence of a specific custom attribute. Again, custom attributes have a slight leg up here, since we can tell the .NET runtime to give them all to us.)
I've come across a similar issue whe serializing objects. My solution is to add a #property (nonatomic, readonly) NSArray *serialProperties; which has a custom getter that returns the names (as NSString*) of the properties of this (sub-)class that should be serialized.
For example:
- (NSArray *)serialProperties {
return #[#"id", #"lastModified", #"version", #"uid"];
}
Or in a subclass:
- (NSArray *)serialProperties {
NSMutableArray *sp = [super serialProperties].mutableCopy;
[sp addObject:#"visibleName"];
return sp;
}
You can then easily get all properties and their values via [self dictionaryWithValuesForKeys:self.serialProperties].
You can't add custom properties other than what sdk has provided..
.
But there is a work around to attain your objective...
#interface classTest:NSObject
#property(strong,nonatomic)NSString *firstName;
#property(strong,nonatomic)NSString *lastName;
#property(strong,nonatomic)NSMutableDictionary *metaData;
#end
#implementation classTest
- (id) init
{
self = [super init];
//Add meta data
metaData=[[NSmutableDictionary alloc]init];
//
if( !self ) return nil;
return self;
}
#end
so use the dictionary to add and retrieve meta data...
i hope it helps....