Adding object to NSMutableArray as an object's associative reference - objective-c

I have the following:
static char associated_obj_key;
id anObj;
NSMutableArray *a = [[NSMutableArray alloc] init];
objc_setAssociatedObject(anObj, &associated_obj_key, a, OBJC_ASSOCIATION_RETAIN);
[a release];
And I'd like to know: how to add objects to the NSMutableArray inside an associative reference for the anObj object? Is it such a thing even possible?
Can I even do something like [objc_getAssociatedObject(anObj, &associated_obj_key) addObject:something];?

Yes, but you would have to have the same key all over the places. As using an address of some static variable is indeed a recommended way, I suggest factoring out the single method for obtaining a reference of an associated object (creating if necessary). I use the following:
- (NSMutableArray*) getOrCreateArray
{
static const char key;
NSMutableArray* arr = objc_getAssociatedObject(self, &key);
if (!arr)
{
arr = [NSMutableArray array];
objc_setAssociatedObject(self, &key, arr, OBJC_ASSOCIATION_RETAIN);
}
return arr;
}
- (void) someMethod
{
id something = // ....
[[self getOrCreateArray] addObject: something];
}

Related

Using method parameters check to see if array object is equal to another string (objective-C)?

I'm all self taught so please keep the techincal jargin to a minimum. Theoretically if I had a method and it had a parameter that is the name of an array. How do I check to see if that array index 5 is equal to #"Yes" or #"No". I know it's one of these because its testing to see if the picture is appearing in the veiw controller. Here is an example:
-(void)methodName :(NSMutableArray *)arrayNameInMethod {
if ( [NSMutableArray *(arrayNameInMethod) indexOfObject:5] == #"Yes"){
//Hide a different picture assocciated with the Array
} else {
//Unhide a different picture assocciated with the Array
};
Also how do you do use the parameter "arrayNameInMethod" to replace the object. Basically:
if(Picture Clicked and picture is Unhidden) {
[NSMutableArray *(differentArrayNameInMethod) replaceObjectAtIndex:5 withObject: #"True)
};
(this is all in another method)
Comment #2: You can't use the parameters the same way because it's a string. You can't access an array with a name as a string.
Thank you so much!
I think what you will need is a dictionary, mapping the name to the array. I will give a barebones implementation:
#interface YourClass()
{
NSMutableDictionary *_arrayMap;
}
#end
#interface YourClass
- (instancetype)init
{
self = [super init];
if (self) {
// You might do this somewhere else, like viewDidLoad:
_arrayMap = [NSMutableDictionary new];
}
return self;
}
- (void)someMethod
{
// Some functionality to add a new array:
// Note the array contains NSNumber and NSString objects:
_arrayMap[#"sampleName"] = [#[ #(0), #"one", #(2.0), #"three", #(4), #(YES)] mutableCopy];
}
-(BOOL)checkForConditionInArrayNamed:(NSString *)arrayName
{
BOOL retval = NO;
NSMutableArray *array = _arrayMap[arrayName];
if ([array count] > 5) {
id obj = array[5];
if ([obj isKindOfClass:[NSNumber class]])
retval = [obj boolValue];
}
return retval
};
#end
You then call checkForConditionInArrayNamed: to check for the condition of the named array and act accordingly.

iOS - Storing groups of UILabels into a NSMutableArray

I'm creating UILabels dynamically in a for each loop. Every loop that is run creates 1-4 UILabels.
What I want is that I put these UILabels into my NSMutableArray and being able later to easy retrieve the data.
My original thought was to put these UILabels into a NSDictionary and use [dictGroupLabels setValue:uiLabel1 forKey:#"uiLabel1"] and then [dictGroupLabels setValue:uiLabel2 forKey:#"uiLabel2"] and so on. And then put this dictionary into my NSMutableArray for each loop. Later on I could access the values like UILabel *label = [[myArray objectAtIndex:0] valueForKey:#"uiLabel1"] BUT that unfortunately doesn't work since UILabels don't conform to the NSCopying protocol.
So with this in mind how would you solve this?
this question provided more information on what you are trying to accomplish. Since you know for a fact, the possible set of labels you are trying to create in each case, I would highly recommend using mutable dictionaries instead of arrays.
To illustrate, given the following hypothetical class definition:
#interface MyClass: NSObject {
NSMutableDictionary * _labelDict;
}
#property (nonatomic, retain) NSMutableDictionary * labelDict;
- ( void )methodA;
- ( void )methodB;
- (NSMutableDictionary *) labelsForRunLoop: (NSUInteger) loopIdx;
#end
You would have the following, hypothetical, class implementation:
#implementation MyClass
#synthesize labelDict = _labelDict;
- ( id ) init {
if( ( self = [ super init ] ) ) {
[self setLabelDict: [NSMutableDictionary dictionaryWithCapacity: 8]];
}
}
- ( void ) dealloc {
[ self.labelDict release ];
[ super dealloc ];
}
- ( void ) methodA {
for(NSUInteger i = 0; i < some index; i++) {
[self.labelDict setObject: [self labelsForRunLoop: i] forKey: [NSString stringWithFormat: #"%d", i]];
}
}
- ( void ) methodB {
// Locate the label you need to work with. Example based on this crude pseudo code
NSMutableDictionary * subDict = (NSMutableDictionary *) [self.labelDict objectForKey: #"0"];
UILabel * theLabel = (UILabel * ) [subDict objectForKey: #"UILabel.Z"];
theLabel.text = #"Label 1";
}
- (NSMutableDictionary *) labelsForRunLoop: (NSUInteger) loopIdx {
NSMutableDictionary * dictionary = [NSMutableDictionary dictionaryWithCapacity: 4] ;
[dictionary setObject: create-w-label forKey: #"UILabel.W"];
[dictionary setObject: create-x-label forKey: #"UILabel.X"];
[dictionary setObject: create-y-label forKey: #"UILabel.Y"];
[dictionary setObject: create-z-label forKey: #"UILabel.Z"];
return [dictionary retain];
}
#end
This is basically pseudo code and will not successfully compile. However it will serve as a good starting point. You probably want to store each label dictionary under some key that makes sense, instead of just using the loop's index. Hope this helps.
They don’t need to adhere to NSCopying to be added to an array. It sounds like you just need to do something like this:
NSMutableArray *mainArray = [NSMutableArray array];
for(int i = 0; i < 5; i++)
{
NSMutableArray *subArray = [[NSMutableArray alloc] initWithCapacity:5];
for(int j = 0; j < 4; j++)
{
UILabel *label = [[UILabel alloc] init];
// etc.
[subArray addObject:label];
[label release];
}
[mainArray addObject:subArray];
[subArray release];
}
// then, to get one of the labels:
UILabel *someSpecificLabel = [[mainArray objectAtIndex:2] objectAtIndex:1];

How can I create an NSMutableArray of structs?

I created a structure like this
typedef struct Node {
NSString* Description;
NSString* AE;
NSString* IP;
NSString* Port;
} Node;
I need to create NSMutableArray of this Node structure I need to know how create object of node path it to the NSMutableArray retrieve it and read for example the port.
After running into this problem, I came across this thread which helped, but was more complicated than the solution I ended up with.
Basically the NSValue is the wrapper for your struct, you don't need to create a new class yourself.
// To add your struct value to a NSMutableArray
NSValue *value = [NSValue valueWithBytes:&structValue objCType:#encode(MyStruct)];
[array addObject:value];
// To retrieve the stored value
MyStruct structValue;
NSValue *value = [array objectAtIndex:0];
[value getValue:&structValue];
I hope this answer will save the next person a bit of time.
You can actually create a custom class (since it holds only NSString pointers) with struct values as instance variables. I think it'd even make more sense.
You can also create an array of NSValues which hold these structures:
NSValue *structValue = [NSValue value:&myNode objCType:#encode(Node *)];
NSMutableArray *array = [[NSMutableArray alloc] initWithObject:structValue];
You can then retreive these structs as follows:
NSValue *structValue = [array objectAtIndex:0];
Node *myNode = (Node *)[structValue pointerValue];
// or
Node myNode = *(Node *)[structValue pointerValue];
You can only store Objective-C objects in an NSMutableArray.
One route you can take is to use a standard C array:
unsigned int array_length = ...;
Node** nodes = malloc(sizeof(Node *) * array_length);
Another route is to wrap the structure in an Objective-C object:
#interface NodeWrapper : NSObject {
#public
Node *node;
}
- (id) initWithNode:(Node *) n;
#end
#implementation NodeWrapper
- (id) initWithNode:(Node *) n {
self = [super init];
if(self) {
node = n;
}
return self;
}
- (void) dealloc {
free(node);
[super dealloc];
}
#end
Then, you'd add NodeWrapper objects to your NSMutableArray like this:
Node *n = (Node *) malloc(sizeof(Node));
n->AE = #"blah";
NodeWrapper *nw = [[NodeWrapper alloc] initWithNode:n];
[myArray addObject:nw];
[nw release];
To retrieve the Node from the NodeWrapper, you'd simply do this:
Node *n = nw->node;
or
Node n = *(nw->node);

Is there anything like an NSSet that allows retrieving by hash value?

I'm trying to create an NSDictionary that stores objects with keys based on IDs. I know I can use NSNumber objects, but why can't I just use an int instead? Is there some class that supports this? Something like an NSSet almost works, except I can't access it by it's hash value (I've overridden - (NSUInteger) hash to return the object's ID, which always unique)
I'm basically trying to turn this:
//objects is an NSMutableDictionary
- (id) objectForId:(NSUInteger)id {
return [objects objectForKey:[NSNumber numberWithInt:id]];
}
- (void) addObject:(Object *)foo {
[objects setObject:foo forKey:[NSNumber numberWithInt:id]];
}
into this:
//objects is an NSSet
- (id) objectForId:(NSUInteger)id {
return [objects objectForHash:id];
}
- (void) addObject:(Object *)foo {
[objects addObject:foo];
}
You'll want to use the C API to NSMapTable after configuring an NSMapTable instance to use integer keys. An example:
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSMapTable *mt = [NSMapTable mapTableWithKeyOptions: NSPointerFunctionsIntegerPersonality | NSPointerFunctionsOpaqueMemory
valueOptions: NSPointerFunctionsObjectPersonality];
for(NSUInteger i = 0; i<10; i++)
NSMapInsert(mt, (void *) i, [NSNumber numberWithInt: i]);
for(NSUInteger j = 0; j<10; j++)
NSLog(#"retrieved %#", (id) NSMapGet(mt, (void *) j));
[pool drain];
return 0;
}
Note that there appears to be a bug in NSMapTable() where it does not allow 0 to be a key. Oops.
Better documentation of the functional API to NSMapTable is requested in <rdar://problem/7228605>.
A fix for the 0 key problem is documented in <rdar://problem/7228618>
I ended up creating a category on NSDictionary to store objects based on int keys:
#implementation NSMutableDictionary (IntKeyDictionary)
- (void) setObject:(id)obj forId:(int)id {
[self setObject:obj forKey:[NSNumber numberWithInt:id]];
}
- (void) removeObjectForId:(int)id {
[self removeObjectForKey:[NSNumber numberWithInt:id]];
}
- (id) objectForId:(int)id {
return [self objectForKey:[NSNumber numberWithInt:id]];
}
#end
NSDictionary's setObject:forKey: already takes the key by id, which is what it looks like you're doing with your NSSet equivalent. What about NSDictionary is insufficient for your purposes?

deep mutable copy of a NSMutableDictionary

I am trying to create a deep-copy of a NSMutableDictionary and assign it to another NSMutableDictionary. The dictionary contains a bunch of arrays, each array containing names, and the key is an alphabet (the first letter of those names). So one entry in the dictionary is 'A' -> 'Adam', 'Apple'. Here's what I saw in a book, but I'm not sure if it works:
- (NSMutableDictionary *) mutableDeepCopy
{
NSMutableDictionary * ret = [[NSMutableDictionary alloc] initWithCapacity: [self count]];
NSArray *keys = [self allKeys];
for (id key in keys)
{
id oneValue = [self valueForKey:key]; // should return the array
id oneCopy = nil;
if ([oneValue respondsToSelector: #selector(mutableDeepCopy)])
{
oneCopy = [oneValue mutableDeepCopy];
}
if ([oneValue respondsToSelector:#selector(mutableCopy)])
{
oneCopy = [oneValue mutableCopy];
}
if (oneCopy == nil) // not sure if this is needed
{
oneCopy = [oneValue copy];
}
[ret setValue:oneCopy forKey:key];
//[oneCopy release];
}
return ret;
}
should the [onecopy release] be there or not?
Here's how I'm going to call this method:
self.namesForAlphabets = [self.allNames mutableDeepCopy];
Will that be ok? Or will it cause a leak? (assume that I declare self.namesForAlphabets as a property, and release it in dealloc).
Because of toll-free bridging, you can also use the CoreFoundation function CFPropertyListCreateDeepCopy:
NSMutableDictionary *mutableCopy = (NSMutableDictionary *)CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFDictionaryRef)originalDictionary, kCFPropertyListMutableContainers);
Assuming all elements of the array implement the NSCoding protocol, you can do deep copies via archiving because archiving will preserve the mutability of objects.
Something like this:
id DeepCopyViaArchiving(id<NSCoding> anObject)
{
NSData* archivedData = [NSKeyedArchiver archivedDataWithRootObject:anObject];
return [[NSKeyedUnarchiver unarchiveObjectWithData:archivedData] retain];
}
This isn't particularly efficient, though.
IMPORTANT: The question (and my code below) both deal with a very specific case, in which the NSMutableDictionary contains only arrays of strings. These solutions will not work for more complex examples. For more general case solutions, see the following:
Tom Dalling's answer
dreamlax's answer
Source from yfujiki on GitHub Gist
Answer for this specific case:
Your code should work, but you will definitely need the [oneCopy release]. The new dictionary will retain the copied objects when you add them with setValue:forKey, so if you do not call [oneCopy release], all of those objects will be retained twice.
A good rule of thumb: if you alloc, retain or copy something, you must also release it.
Note: here is some sample code that would work for certain cases only. This works because your NSMutableDictionary contains only arrays of strings (no further deep copying required):
- (NSMutableDictionary *)mutableDeepCopy
{
NSMutableDictionary * ret = [[NSMutableDictionary alloc]
initWithCapacity:[self count]];
NSMutableArray * array;
for (id key in [self allKeys])
{
array = [(NSArray *)[self objectForKey:key] mutableCopy];
[ret setValue:array forKey:key];
[array release];
}
return ret;
}
Another technique that I have seen (which is not at all very efficient) is to use an NSPropertyListSerialization object to serialise your dictionary, then you de-serialise it but specify that you want mutable leaves and containers.
NSString *errorString = nil;
NSData *binData =
[NSPropertyListSerialization dataFromPropertyList:self.allNames
format:NSPropertyListBinaryFormat_v1_0
errorString:&errorString];
if (errorString) {
// Something bad happened
[errorString release];
}
self.namesForAlphabets =
[NSPropertyListSerialization propertyListFromData:binData
mutabilityOption:NSPropertyListMutableContainersAndLeaves
format:NULL
errorDescription:&errorString];
if (errorString) {
// something bad happened
[errorString release];
}
Again, this is not at all efficient.
Trying to figure out by checking respondToSelector(#selector(mutableCopy)) won't give the desired results as all NSObject-based objects respond to this selector (it's part of NSObject). Instead we have to query if an object conforms to NSMutableCopying or at least NSCopying. Here's my answer based on this gist mentioned in the accepted answer:
For NSDictionary:
#implementation NSDictionary (MutableDeepCopy)
// As seen here (in the comments): https://gist.github.com/yfujiki/1664847
- (NSMutableDictionary *)mutableDeepCopy
{
NSMutableDictionary *returnDict = [[NSMutableDictionary alloc] initWithCapacity:self.count];
NSArray *keys = [self allKeys];
for(id key in keys) {
id oneValue = [self objectForKey:key];
id oneCopy = nil;
if([oneValue respondsToSelector:#selector(mutableDeepCopy)]) {
oneCopy = [oneValue mutableDeepCopy];
} else if([oneValue conformsToProtocol:#protocol(NSMutableCopying)]) {
oneCopy = [oneValue mutableCopy];
} else if([oneValue conformsToProtocol:#protocol(NSCopying)]){
oneCopy = [oneValue copy];
} else {
oneCopy = oneValue;
}
[returnDict setValue:oneCopy forKey:key];
}
return returnDict;
}
#end
For NSArray:
#implementation NSArray (MutableDeepCopy)
- (NSMutableArray *)mutableDeepCopy
{
NSMutableArray *returnArray = [[NSMutableArray alloc] initWithCapacity:self.count];
for(id oneValue in self) {
id oneCopy = nil;
if([oneValue respondsToSelector:#selector(mutableDeepCopy)]) {
oneCopy = [oneValue mutableDeepCopy];
} else if([oneValue conformsToProtocol:#protocol(NSMutableCopying)]) {
oneCopy = [oneValue mutableCopy];
} else if([oneValue conformsToProtocol:#protocol(NSCopying)]){
oneCopy = [oneValue copy];
} else {
oneCopy = oneValue;
}
[returnArray addObject:oneCopy];
}
return returnArray;
}
#end
Both methods have the same internal to-copy-or-not-to-copy logic and that could be extracted into a separate method but I left it like this for clarity.
For ARC - note kCFPropertyListMutableContainersAndLeaves for truly deep mutability.
NSMutableDictionary* mutableDict = (NSMutableDictionary *)
CFBridgingRelease(
CFPropertyListCreateDeepCopy(kCFAllocatorDefault,
(CFDictionaryRef)someNSDict,
kCFPropertyListMutableContainersAndLeaves));
Thought I'd update with an answer if you're using ARC.
The solution Weva has provided works just fine. Nowadays you could do it like this:
NSMutableDictionary *mutableCopy = (NSMutableDictionary *)CFBridgingRelease(CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFDictionaryRef)originalDict, kCFPropertyListMutableContainers));
Useful answers here, but CFPropertyListCreateDeepCopy doesn't handle [NSNull null] in the data, which is pretty normal with JSON decoded data, for example.
I'm using this category:
#import <Foundation/Foundation.h>
#interface NSObject (ATMutableDeepCopy)
- (id)mutableDeepCopy;
#end
Implementation (feel free to alter / extend):
#implementation NSObject (ATMutableDeepCopy)
- (id)mutableDeepCopy
{
return [self copy];
}
#end
#pragma mark - NSDictionary
#implementation NSDictionary (ATMutableDeepCopy)
- (id)mutableDeepCopy
{
return [NSMutableDictionary dictionaryWithObjects:self.allValues.mutableDeepCopy
forKeys:self.allKeys.mutableDeepCopy];
}
#end
#pragma mark - NSArray
#implementation NSArray (ATMutableDeepCopy)
- (id)mutableDeepCopy
{
NSMutableArray *const mutableDeepCopy = [NSMutableArray new];
for (id object in self) {
[mutableDeepCopy addObject:[object mutableDeepCopy]];
}
return mutableDeepCopy;
}
#end
#pragma mark - NSNull
#implementation NSNull (ATMutableDeepCopy)
- (id)mutableDeepCopy
{
return self;
}
#end
Example extensions – strings are left as normal copies. You could override this if you want to be able to in place edit them. I only needed to monkey with a deep down dictionary for some testing, so I've not implemented that.