Pattern for determining if object is fully or partially loaded - ios

The question does not strictly tied with iOS, but since I encountered this in iOS app I will speak in terms of Objective-C.
My iOS app is a client, which gets some data from server. The data from server is json, and it is mapped with my classes. The problem appears when the server send only the necessary part of the object.
Lets say the full object is
{
"a" : 1,
"b" : 2,
"c" : 3
}
My class to which it is mapped is
#class MyObject
{
int a, b, c;
}
#property (nonatomic) int a, b, c;
-(id) initFromDictionary:(NSDictionary*)dict
#end
#implementation MyObject
-(id) initFromDictionary:(NSDictionary*)dict
{
self = [super init];
if (self)
{
a = [dict[#"a"] intValue];
b = [dict[#"b"] intValue];
c = [dict[#"c"] intValue];
}
return self;
}
#end
The server can send
{
"a" : 1,
"c" : 3
}
for request getAandC and
{
"a" : 1,
"b" : 2
}
for another - getAandB (these requests are not dependent, the only thing they are similar is the object they use). I do not need any information about b in the first one and about c in the second one.
The problem is the following. When I write code for these requests I surely know which fields are returned and do not use empty field, but after some time I may forget about which request returned partial object or full, and try to use empty field. So there can be a lot of errors, which could be hard to find.
Is there any practices for that case or maybe some pattern for determining if the object is fully or partially loaded and warning the developer about it?

You Can Implement it as :
#implementation MyObject
-(id) initFromDictionary:(NSDictionary*)dict
{
self = [super init];
if (self)
{
a = ([dict objectForKey: #"a"]) ? [[dict objectForKey: #"a"] intValue] : 0;
b = ([dict objectForKey: #"b"]) ? [[dict objectForKey: #"b"] intValue] : 0;
c = ([dict objectForKey: #"c"]) ? [[dict objectForKey: #"c"] intValue] : 0;
// Here you can replace 0 with nil in case if variable a , b , c
are object type i.e. (id) type or You can use default value here as per your convenience so that you can easily track it if it is partially or fully Loaded
if ((a == 0) || (b == 0) || (c == 0))
{
NSLog(#"object is Partially loaded with values a : %d , b : %d , c : %d", a,b,c);
}else{
NSLog(#"object is Completely loaded with values a : %d , b : %d , c : %d", a,b,c);
}
}
return self;
}
#end
Or
#implementation MyObject
-(id) initFromDictionary:(NSDictionary*)dict
{
self = [super init];
if (self)
{
NSArray *keys = [dict AllKeys];
for(NSString * key in keys)
{
[self setValueToItsVariableForKey:key fromDictionary: dict];
}
}
return self;
}
- (void)setValueToItsVariableForKey:(NSString *)key fromDictionary: (NSDictionary *)dict
{
switch ([key intValue])
{
case : a
a = [[dict objectForKey: key] intValue];
break;
case : b
b = [[dict objectForKey: key] intValue];
break;
case : c
c = [[dict objectForKey: key] intValue];
break;
}
}
#end

I would approach this using the KVO pattern. What you can do is setup a counter where you increase the count by adding a KVO on the property that you want to observe. You can then set a observer on the counter and if the conter has reached the predetermined count (of all properties to be loaded) you can consider the object to be fully loaded.

Related

Objective C - How check if all elements (NSMutableString) of NSMutableArray has a value?

I store user's answers inside NSMutableString element of array. i want to show a pop up (call popGoResultsAlert) when all of the array's element has the value and user would complete the questionary , i search over the internet but didn't succeed to find the solution for that.
here is my code :
in first class
[UserData updateUserAnswerValues:[userData objectForKey:#"userAnswerValues"]];
//second class ,part of the userData.m
static NSMutableArray *userAnswerValues;
+ (NSMutableArray *)getAnswersCompleted {
return userAnswerValues;
}
+ (void)updateUserAnswerValues:(NSArray *)newAnswerValues
{
userAnswerValues = [[NSMutableArray alloc] init];
[userAnswerValues addObjectsFromArray:newAnswerValues];
}
+ (void)updateUserAnswerValueWithIndex:(NSUInteger)index andValue:(NSMutableString*)value
{
[userAnswerValues setObject:value atIndexedSubscript:index];
}
in 3rd class:
- (void)viewWillAppear:(BOOL)animated
{
NSMutableArray *userAnswerValues = [UserData getAnswersCompleted];
for (int i=0; i<userAnswerValues.count; i++) {
id object = userAnswerValues[i];// similar to [myArray objectAtIndex:0]
if(object && [object length] !=0 && i!=0)
{
[self popGoResultsAlert];
break;
} else if (!object) {
continue;
}
}
}
Edit :
//4th class , here seems the origin data is populated to the userAnswerValues base on what user pick from PickerView , that index has the value and that value is put by selectedOptionValue variable ... I have debug the userAnswerValues and notice the value has 56 elements like userAnswerValues = ( "", "", "", "", "", "", "", "", 4, 3, 4, 3, "", ... (fill the rest with ,"" until 56 items> , "" );
so whenever a user select a value in PickerView one of the above element filled out with index number , so my question is how search through these mutable array and see if all item has value...
NSUInteger index = [self.currentQuestion.index integerValue];
[UserData updateUserAnswerWithIndex:index andValue:self.selectedOptionIndex];
Option *selectedOption = [self.currentOptions objectAtIndex:[self.selectedOptionIndex integerValue]];
NSString *selectedOptionValue = selectedOption.value;
[UserData updateUserAnswerValueWithIndex:index andValue:selectedOptionValue];
there is 3 section of questionary that store all the answer's value in a single and same array. with above code , the alert pop up whenever each of 3 section would be completed , my intend is it would only show pop up when all 3 section is completed....
I have test [object length] !=0 , ![object isEqual:[NSNull null]] , ![object isEqualToString:#""] with no luck , all of the 3 has same behaviour (show pop up after each section is completed ,
These project has not written by me from the scratch so i am trying to debug and fix some of it's bugs , when i debug userAnswerValues , i see that the items of NSMutableArray had different type in different elements type, as you see item 32th is _NSFConstantString with empty value , while the item 33th is NSTagedPointerString... please see the attached file for more details.
What I think you need is very similar to the previous answer. Here, I'll translate to your data names.
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
BOOL showPopup = YES; // Assume success
NSMutableArray *userAnswerValues = [UserData getAnswersCompleted];
// Try to go all the way through the array without finding an empty string
for (int i = 0; i < userAnswerValues.count; i++) {
id object = userAnswerValues[i];
if ([object length] == 0) {
showPopup = NO; // Something is not filled in
break;
}
}
if (showPopup) {
[self popGoResultsAlert];
}
}
Bool nilBool = 0;
for (int i = 0 ; i < yourArray.count; i ++)
{
NSString *value = [yourArray ObjectAtIndex:i];
if([value isEqual: #""] || value == nil)
{
nilBool = 1;
}
}
if(nilBool)
{
//one if your values is nil!
}
else
{
//all values are filled, you are good to go
}

Get NSDictionary with variable names as keys

Say I have n variables
NSNumber* A = #(1);
NSNumber* B = #(2);
NSNumber* C = #(3);
NSNumber* D = #(4);
NSNumber* E = #(5);
...
I need a dictionary like
{#"A":#(1), #"B":#(2), #"C":#(3), #"D":#(4), ... }
One can imagine a more cumbersome example that would be tedious to type out
I think saw a C style function for it but I can't remember. Something like NSDictionaryForVariables()
The C preprocessor macro (not a function) you're looking for is NSDictionaryOfVariableBindings. However, outside of Auto Layout (and, debatably, even there), it's not really a great idea to be setting up dependencies between runtime and compile-time identifiers like that.
Depending on what you're trying to actually accomplish, Key-Value Coding might be a better solution.
It's not a good approach you may find another way to solve your issue but if you want to have an idea about your requested solution here here my try
Our properties
#interface TestyViewController ()
#property (nonatomic) NSNumber* a;
#property (nonatomic) NSNumber* b;
#property (nonatomic) NSNumber* c;
#property (nonatomic) NSNumber* d;
#end
Set the values
- (void)viewDidLoad {
[super viewDidLoad];
self.a=#(1);
self.b=#(2);
self.c=#(3);
self.d=#(4);
}
Get our instance variables
-(NSArray *)propertyNames{
unsigned int propertyCount = 0;
objc_property_t * properties = class_copyPropertyList([self class], &propertyCount);
NSMutableArray * propertyNames = [NSMutableArray array];
for (unsigned int i = 0; i < propertyCount; ++i) {
objc_property_t property = properties[i];
const char * name = property_getName(property);
[propertyNames addObject:[NSString stringWithUTF8String:name]];
}
free(properties);
return propertyNames;
}
Create the dictionary
- (IBAction)buttonClicked:(id)sender
{
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
for (NSString* varName in [self propertyNames])
{
[dict setObject:[self valueForKey:varName] forKey:varName];
}
NSLog(#"%#",dict);
}
result
2015-07-15 20:30:56.546 TestC[879:27973] {
a = 1;
b = 2;
c = 3;
d = 4;
}

Return the first number which occurs only once in an NSArray

I would like to know what's the best or most appropriate approach for this question: Given a list of numbers example [2, 3, 4, 2, 3], return the first number that occurs only once in the list.
I have followed some algorithms approach and came up with this, but not sure if there are any built-in helper functions in Objective-C that will allow me to do this with a better performance..
If there are not built-ins solutions, is there is any improvements that can be made to my approach or any other solution that could be better in terms of performance?
This is my updated solution for this:
For testing:
#import "NSArray+Addons.h"
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSArray<NSNumber *> *array = #[#(2), #(7), #(3), #(2), #(3), #(2), #(7), #(3), #(2), #(3), #(4), #(7), #(5), #(5), #(9)];
NSLog(#"Unique number: %#", [array firstUniqueNumber]);
}
#end
NSArray category:
#import "NSArray+Addons.h"
#import "NSMutableDictionary+Addons.h"
#implementation NSArray (Addons)
- (NSNumber *)firstUniqueNumber
{
if (!self.count)
{
return nil;
}
NSMutableDictionary<NSNumber *, NSNumber *> *myUniqueNumbers = [[NSMutableDictionary alloc] init];
return [myUniqueNumbers uniqueValueFromArray:self];
}
#end
NSMutableDictionary category:
#import "NSMutableDictionary+Addons.h"
#implementation NSMutableDictionary (Addons)
- (NSNumber *)uniqueValueFromArray:(NSArray<NSNumber *> *)array
{
if (!array.count)
{
return nil;
}
for (NSNumber *number in array)
{
if (!self[number])
{
self[number] = [NSNumber numberWithInteger:1];
}
else
{
NSInteger count = [self[number] integerValue];
count++;
self[number] = [NSNumber numberWithInteger:count];
}
}
return [self uniqueNumberWithArray:array];
}
- (NSNumber *)uniqueNumberWithArray:(NSArray<NSNumber *> *)array
{
if (!array.count)
{
return nil;
}
NSNumber *uniqueNumber = nil;
for (NSInteger index = array.count - 1; index > 0; index--)
{
NSNumber *key = array[index];
if (self[key] && [self[key] integerValue] == 1)
{
uniqueNumber = key;
}
}
return uniqueNumber;
}
#end
NSCountedSet* set = [[NSCountedSet alloc] initWithArray:array];
NSUInteger index = [array indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop){
return [set countForObject:obj] == 1;
}];
return index == NSNotFound ? nil : [array objectAtIndex:index];
This problem can be reduced to element distinctness problem, so there is no linear time solution, without using hashing and extra space.
One simple solution in O(n) time on average + space is:
Build a hash based histogram of the data, that maps each value to the number of its occurances.
Find the first number in the array that its value in the histogram is 1.
Pseudo code:
map = new hashmap
for each element x:
if map contains x is a key:
map.put(x,map.get(x)+1)
else:
map.put(x,1)
for each element x in array:
if map.get(x) == 1:
return x
//if reached here - no distinct element
Example:
array = [2, 3, 4, 2, 3]
create histogram: {[2=2] [3=2], [4=1]}
iterate the array:
check 2, it has value of 2 in histogram. continue
check 3, it has value of 2 in histogram. continue
check 4, it has value of 1 in histogram. Return it and finish.
-(NSNumber *)returnFirstUniqueFromArray: (NSArray *)array{
//put the numbers in a set
NSCountedSet *numbers = [[NSCountedSet alloc] initWithArray:array];
for(NSNumber *number in array){
//if it only occurs once return
if([numbers countForObject:number]==1) return number;
}
return nil;
}
They key being here you need a good way to keep track of how many times something occurs so take advantage of NSCountedSet's "count" method. Will tell you how many times an object occurs.

Transform (or copy) an object to a subclass instance in Objective-C

I want to transform an instance of an object into an instance of a subclass of that object class, so that I can use the additional methods and properties of that subclass, in Objective-C.
How can I do this in a way that does not require me to hardcode the properties of that object class in a copy method?
It is not possible to transform an object into an instance of a subclass in Objective-C. However, with the class below you can supply an instance of both the object and the subclass and have the values of all properties copied to the subclass instance. This implementation works with both Objective-C object types and C primitives. You do not have to specify (or indeed even determine) the properties that need to be copied, providing you know that the important variables are visible and can be set (i.e., there are no properties that are exposed as "read only" or not exposed at all, whose values cannot be recalculated by the class). This method is thus relatively robust for known classes and will not require updating to support future changes you make in your object class that fit these parameters. It is iOS 8 compatible.
This class provides four class methods:
+ (id) copyObject:(id)object toSubclassObject:(id)subclassObject
Copies all properties of object to subclassObject. If the subclassObject is not a subclass of object, nil is returned.
+ (NSDictionary *) propertiesOfObject:(id)object;
Returns a dictionary of all visible properties of an object, including those from all its superclasses (other than NSObject).
+ (NSDictionary *) propertiesOfClass:(Class)class;
Returns a dictionary of all visible properties of a class, including those from all its superclasses (other than NSObject).
+ (NSDictionary *) propertiesOfSubclass:(Class)class;
Returns a dictionary of all visible properties that are specific to a subclass. Properties for its superclasses are not included.
Header:
// SYNUtilities.h
#import <Foundation/Foundation.h>
#interface SYNUtilities : NSObject
+ (id) copyObject:(id)object toSubclassObject:(id)subclassObject;
+ (NSDictionary *) propertiesOfObject:(id)object;
+ (NSDictionary *) propertiesOfClass:(Class)class;
+ (NSDictionary *) propertiesOfSubclass:(Class)class;
#end
Implementation:
#import "SYNUtilities.h"
#import <objc/runtime.h>
#import <objc/message.h>
#implementation SYNUtilities
+ (id) copyObject:(id)object toSubclassObject:(id)subclassObject
{
if (![[subclassObject class] isSubclassOfClass:[object class]]) {
return nil;
}
NSDictionary * properties = [self propertiesOfObject:object];
NSLog(#"Properties of %#:\n%#", [object class], properties);
for (NSString * property in properties) {
SEL selector = NSSelectorFromString(property);
if (selector) {
id value = [object valueForKey:property];
[subclassObject setValue:value forKey:property];
}
}
return subclassObject;
}
+ (NSDictionary *) propertiesOfObject:(id)object
{
Class class = [object class];
return [self propertiesOfClass:class];
}
+ (NSDictionary *) propertiesOfClass:(Class)class
{
if (class == NULL) {
return nil;
}
NSMutableDictionary * properties = [NSMutableDictionary dictionary];
[self propertiesForHierarchyOfClass:class onDictionary:properties];
return [NSDictionary dictionaryWithDictionary:properties];
}
+ (NSDictionary *) propertiesOfSubclass:(Class)class
{
if (class == NULL) {
return nil;
}
NSMutableDictionary *properties = [NSMutableDictionary dictionary];
return [self propertiesForSubclass:class onDictionary:properties];
}
+ (NSMutableDictionary *)propertiesForHierarchyOfClass:(Class)class onDictionary:(NSMutableDictionary *)properties
{
if (class == NULL) {
return nil;
}
if (class == [NSObject class]) {
// On reaching the NSObject base class, return all properties collected.
return properties;
}
// Collect properties from the current class.
[self propertiesForSubclass:class onDictionary:properties];
// Collect properties from the superclass.
return [self propertiesForHierarchyOfClass:[class superclass] onDictionary:properties];
}
+ (NSMutableDictionary *) propertiesForSubclass:(Class)class onDictionary:(NSMutableDictionary *)properties
{
unsigned int outCount, i;
objc_property_t *objcProperties = class_copyPropertyList(class, &outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property = objcProperties[i];
const char *propName = property_getName(property);
if(propName) {
const char *propType = getPropertyType(property);
NSString *propertyName = [NSString stringWithUTF8String:propName];
NSString *propertyType = [NSString stringWithUTF8String:propType];
[properties setObject:propertyType forKey:propertyName];
}
}
free(objcProperties);
return properties;
}
static const char *getPropertyType(objc_property_t property) {
const char *attributes = property_getAttributes(property);
char buffer[1 + strlen(attributes)];
strcpy(buffer, attributes);
char *state = buffer, *attribute;
while ((attribute = strsep(&state, ",")) != NULL) {
if (attribute[0] == 'T' && attribute[1] != '#') {
// A C primitive type:
/*
For example, int "i", long "l", unsigned "I", struct.
Apple docs list plenty of examples of values returned. For a list
of what will be returned for these primitives, search online for
"Objective-c" "Property Attribute Description Examples"
*/
NSString *name = [[NSString alloc] initWithBytes:attribute + 1 length:strlen(attribute) - 1 encoding:NSASCIIStringEncoding];
return (const char *)[name cStringUsingEncoding:NSASCIIStringEncoding];
}
else if (attribute[0] == 'T' && attribute[1] == '#' && strlen(attribute) == 2) {
// An Objective C id type:
return "id";
}
else if (attribute[0] == 'T' && attribute[1] == '#') {
// Another Objective C id type:
NSString *name = [[NSString alloc] initWithBytes:attribute + 3 length:strlen(attribute) - 4 encoding:NSASCIIStringEncoding];
return (const char *)[name cStringUsingEncoding:NSASCIIStringEncoding];
}
}
return "";
}
#end
I needed to create a subclass of NSTextFieldCell, used in an NSTableView, and wanted to keep the properties intact that were set for the cell in Interface Builder.
I solved the task by using NSKeyedArchiver, which is made to store and restore an object's properties.
Since NSTextFieldCell implements initWithCoder, it supports the archiver functions, and therefore I could use this code to init my subclass from the other's properties:
- (id)initWithCell:(NSCell *)cell {
// Use NSArchiver to copy the NSCell's properties into our subclass
NSMutableData *data = [NSMutableData data];
NSKeyedArchiver *arch = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[cell encodeWithCoder:arch];
[arch finishEncoding];
NSKeyedUnarchiver *ua = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
self = [self initWithCoder:ua];
// Here I'd set up additional properties of my own class
return self;
}

NSDictionary, CustomKeys, Comparison

SOLVED
It seemed that both -referenceDictionary were not containing exact same information… Dang it ! Fixed it and now it works perfectly.
Initial question
It's been a few hours since I started struggling with this problem, here it is :
I have a CustomClassA which I use as NSDictionary key (obviously for the Object:ForKey: method).
At first it did not work at all, then I read this, that and a few other stuffs around the web…
So I tried overriding both -isEqual: and -hash by doing as said (maybe a little less :p) :
- (BOOL)isEqual:(id)anObject
{
if([anObject isKindOfClass:[self class]])
{
NSLog(#"HASH = %#", [anObject hash] == [self hash] ? #"YES" : #"NO");
NSLog(#"DICT = %#", [[self referenceDictionary] isEqualToDictionary:[anObject referenceDictionary]] ? #"YES" : #"NO");
return [[self referenceDictionary] isEqualToDictionary:[anObject referenceDictionary]];
}
return false;
}
- (NSUInteger)hash
{
NSUInteger prime = 31;
NSUInteger result = 1;
NSArray* values = [referenceDictionary allValues];
for(id k in values)
{
result = prime * result + [k hash];
}
return result;
}
Please, note that my NSDictionary will never contain nil values because I do take care of having these away.
Anyway, both of these methods do return YES on one of the NSDictionary entry when calling :[myDict objectForKey:myObject].
But still, it seems [myDict objectForKey:myObject] returns nil, even after "setting" it myself…
If anyone has time to help me solving my pb I'd be thankful !
Regards,
Cehm
EDIT:
Ok, for the purpose of being a bit more clear about this issue. Lets assume two classes : Artist and Single.
Then I want to create a NSDictionary which contains an Artist as the key and this Artist key points to a NSArray of "Single"s.
For instance I'd have the following dictionary :
#{
artistA : #[song1, song2, song3],
artistB : #[songZ],
artistC : #[songF]
}
But now the thing is I want to create this dictionary like this :
for(Single* aSong in ArrayOfSingle)
{
Artist* anArtist = [[Artist alloc] initWithArtistName:aSong.artistName];
if([data objectForKey: anArtist] == nil)
{
[data setObject:[[NSMutableArray alloc] init] forKey: anArtist];
} else
{
NSLog(#"exists");
}
[[data objectForKey: anArtist] addObject:aSong];
}
Taken into account that data is mutable (Simple NSMutableDictionary) and that I won't ever change anArtist (assuming ARC still keeps its instance…).
EDIT 2:
I overrode NSCopying :
- (id)copyWithZone:(NSZone *)zone {
return [[[self class] allocWithZone:zone] initWithDictionary:referenceDictionary];
}
As my CustomClass uses initWithDictionary: I assumed it would work just fine by doing this.
EDIT 3:
Here's the initWithDictionary:
-(id) initWithDictionary:(NSDictionary*) dictionary
{
self = [super initWithDictionary:dictionary];
if(self)
{
referenceDictionary = dictionary;
title = [super notNullObjectForKey:#"title"];
name = [super notNullObjectForKey:#"name"];
category = [super notNullObjectForKey:#"category"];
artist = [super notNullObjectForKey:#"artist"];
ad_city = [super notNullObjectForKey:#"ad_city"];
}
return self;
}

Resources