Objective-C : adding attribute to a category - objective-c

I have built a category for NSDate and I would like to encapsulate an attribute in this category to hold some data. But I can't achieve adding this attribute, only methods.
Is there any way to achieve this ?
Thank you.

Here some Code:
Filename: NSObject+dictionary.h
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#interface NSObject (dictionary)
- (NSMutableDictionary*) getDictionary;
#end
Filename: NSObject+dictionary.m
#import "NSObject+dictionary.h"
#implementation NSObject (dictionary)
- (NSMutableDictionary*) getDictionary
{
if (objc_getAssociatedObject(self, #"dictionary")==nil)
{
objc_setAssociatedObject(self,#"dictionary",[[NSMutableDictionary alloc] init],OBJC_ASSOCIATION_RETAIN);
}
return (NSMutableDictionary *)objc_getAssociatedObject(self, #"dictionary");
}
Now every instance (of every class) has a dictionary, where you can store your custom attributes.
With Key-Value Coding you can set a value like this:
[myObject setValue:attributeValue forKeyPath:#"dictionary.attributeName"]
And you can get the value like this:
[myObject valueForKeyPath:#"dictionary.attributeName"]
That even works great with the Interface Builder and User Defined Runtime Attributes.
Key Path Type Value
dictionary.attributeName String(or other Type) attributeValue

You can't add instance variables in categories.
However, you can add storage for your attribute to an object using associative references. Note that if you need to add more than one attribute, rather than adding an associative reference for each, you're probably better off adding a single reference to (say) an NSMutableDictionary, CFMutableDictionaryRef, or NSMapTable and using that for all of your attributes.

objc_setAssociatedObject() and objc_getAssociatedObject()

If you want to add attribute to class, you can try to use github.com/libObjCAttr. It's really easy to use, add it via pods, and then you can add attribute like that:
RF_ATTRIBUTE(YourAttributeClass, property1 = value1)
#interface NSDate (AttributedCategory)
#end
And in the code:
YourAttributeClass *attribute = [NSDate RF_attributeForClassWithAttributeType:[YourAttributeClass class]];
// Do whatever you want with attribute
NSLog(#"%#", attribute.property1)

Related

Objective-C: Property not found on object of type

So I am very new to Objective-C and I was trying to expand on a working program I made from a lesson book.
So, I have a working class names "Item". Here is Item.h:
#interface Item : NSObject
{
NSString *_itemName;
NSString *_serialNumber;
int _valueInDollars;
NSDate *_dateCreated;
}
+(instancetype)randomItem;
It has a few functions to manipulate those variables, initialize them in a costume way and print them in a formatted manner.
Then I made a sub-class of "Item" called "Container". I wanted it to be similar to "Item" but have a NSMUtableArray variable that could hold an Array of "Item"s. Here is Container.h:
#import "Item.h"
#interface Container : Item
{
NSString *_containerName;
NSDate *_containerDatecreated;
int _containerValueinDollars;
NSMutableArray *_subItems;
}
+(instancetype) randomContainer;
Now in my main. I do a number of things with the "Item" class and they work fine. But when I do this:
Container *container=[Container randomContainer];
for (NSString *item in container.containerName) {
NSLog(#"%#", item);
}
It says "Property 'containerName' not found on object of type 'Container *'". But I clearly added that variable to the "Container" subclass.
Note: randomContainer creates a random container instance and does some initializing to it. When I debug in Xcode it seems to be working fine.
What is my problem?
container.containerName is called dot syntax. It is the equivalent of [container containerName]. But you don't have a property or method on your class named containerName so the compiler is complaining.
Now, that is likely entirely baffling in the context of the book you are using. And that is because the book you are using is way way way out of date.
If it were up to date, your Item class would look more like:
#interface Item:NSObject
#property(copy) NSString *containerName;
#property(copy) NSDate *containerDateCreated;
#property(assign) NSInteger containerValueInDollars;
... etc ...
#end
Get yourself an up to date set of tutorials and/or books before learning anything else. It'll save a lot of frustration.

How to use #property correctly (Setters) within another class

another question i'm trying to use a setter within another class but I seem to get this odd error here is the code below:
AppDataSorting.h
#import <Foundation/Foundation.h>
#interface AppDataSorting : NSObject{
NSString *createNewFood;
NSNumber *createNewFoodCarbCount;
}
#property (readwrite) NSString *createNewFood;
#end
AppDelegate.m
#import "AppDelegate.h"
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
}
- (IBAction)saveData:(id)sender {
NSLog(#"%#", self.foodName.stringValue);
self.createNewFood = self.foodName.stringValue;
NSLog(#"%.1f", self.carbAmount.floatValue);
}
#end
I get the error message in AppDelegate.m which is: Property 'createNewFood' not found on object of type 'AppDelegate *'
Could someone please explain the issue here?
You declare this property:
#property (readwrite) NSString *createNewFood;
In AppDataSorting.h so you can access it like self.createNewFood in AppDataSorting.m file not AppDelegate.m. If you want to call it as you do in AppDelegate.m you have move this line:
#property (readwrite) NSString *createNewFood;
to AppDelegate.h file.
Or if you want to use property from AppDataSorting class in AppDelegate you have to create object and call it on that object:
- (IBAction)saveData:(id)sender {
NSLog(#"%#", self.foodName.stringValue);
AppDataSorting *dSorting = [[AppDataSorting alloc] init];
dSorting.createNewFood = self.foodName.stringValue;
NSLog(#"%.1f", self.carbAmount.floatValue);
}
In -saveData:, self refers to an instance of NSAppDelegate. The createNewFood property is defined on instances of the class AppDataSorting.
Also note that Cocoa/CF naming conventions give special meaning to methods that start with "init", "new" and (to a lesser degree) "create". You probably want to avoid such things in your property names. Details here.
In general, properties should represent conceptual "properties" of an object. So if you had a Person class, it might have a name property, but it wouldn't have a createNewOutfit property.
You need to access createNewFood on an instance of AppDataSorting - but you're trying to access the property on the AppDelegate-class which clearly doesn't implement it.
So you would need to create an instance of AppDataSorting and then access the property like so:
AppDataSorting *instance = [[AppDataSorting alloc] init];
instance.createNewFood = self.foodName.stringValue;
Final notes:
The docs provide a good base of information
If you don't need atomicity you should always declare properties with the nonatomic attribute
createNewFood is not a good name for a property since it suggests a method which creates new food - yet it's only meant to store data (in this case an NSString instance)

Subclassing iOS Model Objects - Appropriate Design Pattern

I fear this is a rather simple question, but after much googling I think I have overshot my intended result. I believe my question to be related to a design pattern, but alas I could be wrong.
My application calls an RESTful API and gets back what amounts to a list of model objects represented by an NSDictionary. Each of which I will call NNEntity. There are (conceptually) multiple different subtypes of NNEntity. All subtypes of NNEntity share the property of entityID, but each have their own unique properties as well. All instances of NNEntity have a method called readFromDict:(NSDictionary *)d that populates their respective properties. This method is enforced by a protocol that all NNEntity subtypes conform to. It looks like this:
//NNEntity.h
#interface NNEntity : NSObject <NNReadFromDictProtocol>
#property (nonatomic, strong) NSString *entityID;
#end
//NNEntity.m
#implementation NNEntity
- (void)readFromDict:(NSDictionary *)d {
//set common properties from values in d
self.entityID = [d objectForKey:#"ID"];
}
#end
//NNSubEntity1.h
#interface NNSubEntity1 : NSEntity <NNReadFromDictProtocol>
#property (nonatomic, strong) NSString *favoriteColor;
#end
//NNSubEntity1.m
#implementation NNSubEntity1
- (void)readFromDict:(NSDictionary *)d {
[super readFromDict:d];
//set unique properties from values in d
self.favoriteColor = [d objectForKey:#"colorPreference]:
}
#end
//NNSubEntity2.h
#interface NNSubEntity2 : NSEntity <NNReadFromDictProtocol>
#property (nonatomic, strong) NSString *middleName;
#end
//NNSubEntity2.m
#implementation NNSubEntity2
- (void)readFromDict:(NSDictionary *)d {
[super readFromDict:d];
//set unique properties from values in d
self.middleName = [d objectForKey:#"middleName]:
}
#end
I have read various pieces on the use of a Factory or Builder Desing pattern for similar use cases but I am curious if that is necessary in this rather simple case. For example, does my current code end up creating both and instance of NNEntity and NNSubEntity2 if I were to call something like this:
NNEntity *newEntity = [[NNSubEntity2 alloc] init];
//assume dict exists already and is properly keyed
[newEntity readFromDict:dict];
I assume not, but would newEntity have both the common property of entityID as well as the unique property of middleName set correctly? Also, much appreciated if you have thoughts on a better or more efficient design approach.
This looks like exactly how you should be doing it. You have a base class which read in the common attributes, and subclasses which read in their specific attributes.
For example, does my current code end up creating both and instance of NNEntity and NNSubEntity2? NNEntity *newEntity = [[NNSubEntity2 alloc] init];
Nope. When you run this, you instantiate NNSubEntity2 and store the result in a variable typed by it's superclass, which is totally valid. This allows you to call any methods defined on the superclass, but the actual instance is still of the subclass.
Would newEntity have both the common property of entityID as well as the unique property of middleName set correctly?
It sure would. It inherits the instance variables, properties and methods in the superclass.
Rest assured, as far as I can tell this looks sound and is a pattern I've used before.
I do it like this.
// NNEntity.h
#interface NNEntity : NSObject
#property (nonatomic, retain) NSString *entityId;
#end;
// NNEntity.m
#implementation NNEntity
#end;
// NNEntity+KVC.h
#interface NNEnity (KVC)
-(void)setValue:(id)value forUndefinedKey:(NSString *)key {
#end
// NNEntity+KVC.m
#implementation NNEntity (KVC)
-(void)setValue:(id)value forUndefinedKey:(NSString *)key {
// Handle this as appropriate to your app.
// A minimal implementation will throw an exception.
}
#end
And similarly for your various subclasses. You don't (necessarily) need the category on your subclasses.
Then, given NSDictionary *dict with your stuff in it:
NNEntity *entity = [[NNEntity alloc] init];
[entity setValuesForKeysWithDictionary:dict];
Violá! You're done. There are some criticisms of this method, but given a strong implementation of setValue:forUndefinedKey:, I think it's safe and incredibly flexible.
The secrets are in Apple's beautiful Key-Value Coding technology. Essentially, setValuesForKeysWithDictionary: iterates the keys the dict you give it, and for eachinvokes setValue:forKey: in its receiver. It looks something like this (though I'm sure Apple optimizes it under the hood):
-(void)setValuesForKeysWithDictionary:(NSDictionary *)dictionary {
NSArray *keys = [dictionary allKeys];
for (NSString* key in keys) {
[self setValue:[dictionary valueForKey:key] forKey:key];
}
}
I also like this approach because a conversion to CoreData is simple; when you tell CoreData to 'render' your model, it simply overwrites your stubbed model classes, keeping your KVC Category intact. What is more, if your implementation of setValue:forUndefinedKey: is smooth, you can make model changes to your backend without crashing the app (this is a bit of a no-no, but it's not much different from your factory solution).
Of course, I have not addressed your need to selectively choose which class to instantiate. But that is a larger design issue that could be affected even by the design of your API and backend. So I defer.
Also, as you noted in your comment below, the property names must match up. This is a show-stopper for some developers, especially so if you cannot control both the backend and the client.
Give it a try. Feedback is welcome.

What is the actual use of categories instead of inheritance?

I'm trying to understand the actual use of categories as opposed to inheritance in Objective-C. When should I prefer to use a category? A real-life example with code would be helpful.
When you need to add functionality to an existing class.
For example, your app is working with NSDate or NSString instances and you want to add some functionality. You cannot add the functionality to a subclass because you can't force system methods to return the subclass.
Just look into the examples in the API:
1/ NSDictionary is used to hold file attributes and has a method that returns file size.
2/ NSString has a UI category that extends it with drawing. Note that you don't want a separate subclass of string that can be drawn. You want all strings to have the ability to be drawn.
I used categories when I need to add some convenient functions that I will use repeatedly to the existing class without having a need to subclass to overwrite some existing functions of that class.
For example, when I want to check for an empty string, or remove all leading and trailing spaces of a string:
.h file:
#interface NSString (Extension)
-(BOOL)isEmptyString;
-(NSString *)trimLeadingAndTrailingWhiteSpaces;
#end
.m file:
#implementation NSString (Extension)
-(BOOL)isEmptyString
{
NSString *myString = [self stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
if (myString.length == 0)
return TRUE;
else
return FALSE;
}
-(NSString *)trimLeadingAndTrailingWhiteSpaces
{
NSString *myString = [self stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
return myString;
}
#end
To use it:
someString = [someString trimLeadingAndTrailingWhiteSpaces];
if ([someString isEmptyString])
{
//someString is empty, do whatever!
}
I see categories as a sort of easier way of achieving some of the functionality provided by inheritance. There are other things categories do which are not provided by inheritance. Categories allow you to extend the functionality of a existing class without subclassing them. You can also use them to replace existing methods in classes. A category method is sort of bolted onto the existing class unlike a subclass which direct descendant of the original class. Once category method is added to a objective c class it is available to all instances of the class including the ones not created by you.
For example , if you have to encryption function which you need to use on all data in your project and say most your data uses only NString for saving and manipulation. One of the ways you can go about it is to create a category for NSString
NSString-Encryption.h
#interface NSString (Encryption)
-(NSString*) encrypt;
#end
NSString-Encryption.m
#import "NSString-Encryption.h"
#implementation NSString (Encryption)
-(NSString*) encrypt
{
// your encryption method here
return encryptedString;
}
#end
UseNSString-encryption.m
NSString *testString = #"this is test";
NSString *encryptedString = [testString encrypt];
As you can see that the category is easier to use than subclass. This method can be called from NSMutableString also as it inherits from NSString. So it is powerful.
You can also use category to replace existing methods on classes.
Another use of a category is that it can be used for private methods as objective c does not have a formal private designation for methods. This done by putting a category in a .m file instead of .h file.
Phone.h
#interface Phone:NSObject
-(void) call;
#end
Phone.m
#interface Phone(Private)
-(void) validatePhoneEntry:(NSString*) phoneNumber;
#end
#implementation Phone
-(void) validatePhoneEntry:(NSString*) phoneNumber
{
}
-(void) call
{
}
#end
The disadvantage of categories is that you cant use them if you need to add member variable to classes.
Sulthan explains it well. Here is a code example of extending the functionality of NSString by adding a category. You can use categories this way to extend classes that you don't have the implementations for without subclassing them.
If you want to add methods to a class, use a category. If you want to change functionality of existing methods in a class, create a subclass.
It's kinda/sorta possible to use a category to replace an existing method, sort of like overriding in a subclass, but you shouldn't use categories for this. There are three reasons: 1) you lose access to the original method; 2) you won't break existing code; 3) if more than one category attempts to "override" the same method, the method that the class will end up with is undefined. Basically, the class will end up with the method from the last category applied to the class, but the order in which categories are applied can't be relied on. It's a recipe for a mess, so just don't do it.
So, you could use a category to add methods to NSData such as +(NSData*)dataWithCryptographicallyRandomBytes:(NSUInteger)length or -(void)base64Decode. But if you wanted to change the behavior of an existing method, like -writeToFile:atomically:, you should create a subclass of NSData and implement your new functionality there. For one thing, you might still want to use the existing behavior in your override, and subclassing lets you do that by calling [super writeToFile:file atmoically:YES]. Also, using a subclass here means that your new behavior will only affect your own code -- it won't affect uses of NSData elsewhere in the framework that may not expect your new functionality.
Categories are similar to Java interfaces (in java the implementation is not optional) - a way to group methods and make objects respond to a particular API - independent of the class type. Inheritance makes sense if you need to add additional ivars and the new class matches a isa relation (student isa person). Adding a few utility methods does not make an isa case - this is usualy done with protocols. Categories are often used with delegates where the methods in question are optional.

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....