Having problem with MPSMatrixMultiplication encodeToBuffer - objective-c

I am using MPSMatrixMultiplication, and when I call encodeToCommandBuffer it is stating error "The starting matrix must be contained within each of the MPSMatrix objects." I am not sure what it means.
I am just trying to do a simple calculation and it seems if I apply similar code and pattern in swift, it works perfectly, but in Objective-c it is giving me error.
#import <Foundation/Foundation.h>
#import <Metal/Metal.h>
#import <MetalKit/MetalKit.h>
#import <MetalPerformanceShaders/MetalPerformanceShaders.h>
NS_ASSUME_NONNULL_BEGIN
//Interface
#interface KernelCalculator: NSObject
///Properties
#property id<MTLDevice> device;
#property id<MTLBuffer> bufferA;
#property id<MTLBuffer> bufferB;
#property id<MTLBuffer> bufferC;
#property MTKView *view;
#property id<MTLCommandQueue> commandQueue;
#property id<MTLCommandBuffer> commandBuffer;
#property MPSMatrixMultiplication *mmKernel;
#end
NS_ASSUME_NONNULL_END
#import "KernelCalculator.h"
#implementation KernelCalculator
- (instancetype)init
{
self = [super init];
if (self)
{
_view = [[MTKView alloc] init];
_view.device = MTLCreateSystemDefaultDevice();
if(!_view.device){
NSLog(#"Metal is not supported on this device");
return self;
}
_device = _view.device;
_commandQueue = [_device newCommandQueue];
_commandBuffer = [_commandQueue commandBuffer];
//Float array A
float _matrix_A[] = {
2.0, 3.0, 4.0
};
//Float array B
float _matrix_B[] = {
2.0, 2.0, 2.0
};
//size of each array
int matrix_A_length= sizeof(_matrix_A)/sizeof(float);
int matrix_B_length= sizeof(_matrix_B)/sizeof(float);
///<A>
int totalBytesA = sizeof(_matrix_A);
_bufferA = [_device newBufferWithBytes:_matrix_A length:totalBytesA options: MTLResourceCPUCacheModeDefaultCache];
MPSMatrixDescriptor *descriptionA = [[MPSMatrixDescriptor alloc] init];
[descriptionA setRows:3];
[descriptionA setColumns:1];
[descriptionA setRowBytes:totalBytesA/3];
[descriptionA setDataType:MPSDataTypeFloat32];
MPSMatrix *A = [[MPSMatrix alloc] initWithBuffer:_bufferA descriptor:descriptionA];
printf("\n A row: %lu ", (unsigned long)A.rows);
printf("\n A columns: %lu ", (unsigned long)A.columns);
printf("\n A rowBytes: %lu", (unsigned long)A.rowBytes);
printf("\n A totalBytes: %lu \n\n", (unsigned long)totalBytesA);
///</A>
///<B>
int totalBytesB = sizeof(_matrix_B);
_bufferB = [_device newBufferWithBytes:_matrix_B length:totalBytesB options: MTLResourceCPUCacheModeDefaultCache];
MPSMatrixDescriptor *descriptionB = [[MPSMatrixDescriptor alloc] init];
[descriptionB setRows:1];
[descriptionB setColumns:3];
[descriptionB setRowBytes:totalBytesB/1];
[descriptionB setDataType: MPSDataTypeFloat32];
MPSMatrix *B = [[MPSMatrix alloc] initWithBuffer:_bufferB descriptor:descriptionB];
printf("\n B row: %lu ", (unsigned long)B.rows);
printf("\n B columns: %lu ", (unsigned long)B.columns);
printf("\n B rowBytes: %lu", (unsigned long)B.rowBytes);
printf("\n B totalBytes: %lu \n\n", (unsigned long)totalBytesB);
///</B>
///<C>
int totalBytesC = matrix_A_length* matrix_B_length*sizeof(float);
_bufferC = [_device newBufferWithLength:totalBytesC options:MTLResourceCPUCacheModeDefaultCache];
MPSMatrixDescriptor *descriptionC = [[MPSMatrixDescriptor alloc] init];
[descriptionC setRows:A.rows];
[descriptionC setColumns:B.columns];
[descriptionC setRowBytes:totalBytesC/A.rows];
[descriptionC setDataType: MPSDataTypeFloat32];
MPSMatrix *C = [[MPSMatrix alloc] initWithBuffer:_bufferC descriptor:descriptionC];
printf("\n C row: %lu ", (unsigned long)C.rows);
printf("\n C columns: %lu ", (unsigned long)C.columns);
printf("\n C rowBytes: %lu", (unsigned long)C.rowBytes);
printf("\n C totalBytes: %lu \n\n", (unsigned long)totalBytesC);
///</C>
_mmKernel = [[MPSMatrixMultiplication alloc]
initWithDevice:_device
transposeLeft:false
transposeRight:false
resultRows:3
resultColumns:1
interiorColumns:1
alpha:1.0
beta:0.0];
[_mmKernel encodeToCommandBuffer: _commandBuffer leftMatrix:B rightMatrix:A resultMatrix:C];
[_commandBuffer commit];
[_commandBuffer waitUntilCompleted];
}
return self;
}
#end
I would be grateful if just point out the problem I have in my code.

That's a bizarre error message, and is worth a bug report on its own.
The problem is that you're manually filling out your matrix descriptors, but you're not initializing all of the necessary fields. In particular, your matrix descriptors each have their matrices and matrixBytes properties set to 0, which is an invalid configuration.
Although these properties doesn't seem to be checked at matrix-creation time, they do seem to get validated at encoding time, leading to this failure. In my opinion, there should be stricter validation in place at the time the matrix is created, but because MPSMatrix is such a lightweight wrapper around a buffer, this probably wasn't deemed necessary.
In any event, the best way to avoid this is to use the matrix descriptor factory method to fill out the remaining fields for you. It's less code, and it's more stylish. For example:
MPSMatrixDescriptor * descriptionA =
[MPSMatrixDescriptor matrixDescriptorWithRows:3
columns:1
rowBytes:totalBytesA/3
dataType:MPSDataTypeFloat32];
Also, you seem to have an error in your kernel creation and encoding calls: resultColumns should be 3, not 1; leftMatrix should be A; and rightMatrix should be B, to produce a 3x3 result matrix.

Related

How to use assign instead of memcpy

I learned memcpy here
The demo shows memcpy is much like assign
How to use assign instead of memcpy
- (uint64_t)parseHeader:(NSData *)data {
uint64_t headerLength = 0;
memcpy(&headerLength, data.bytes, sizeof(uint64_t));
// set a breakpoint
return headerLength;
}
I suppose that headerLength should equal to (uint64_t)(data.bytes)
And I p at that breakpoint
here is the result:
(lldb) p headerLength
(uint64_t) $0 = 318
(lldb) p data.bytes
(const void *) $1 = 0x00006000000050e0
(lldb) p (uint64_t)(data.bytes)
(uint64_t) $2 = 105553116287200
So how to understand memcpy in this context?
I want to use more OOP to get rid of memcpy
More Code:
I encounter this when learning GCDAsyncSocket
The above is the receiving data part.
The sending data part:
typedef NS_ENUM(NSInteger, PacketType){
PacketTypeUnknown = -1,
PacketTypeDidAddDisc,
PacketTypeStartNewGame
};
typedef NS_ENUM(NSInteger, PacketAction){
PacketActionUnknown = -1
};
#interface PacketH: NSObject<NSSecureCoding>
#property (strong, nonatomic) id data;
#property (assign, nonatomic) PacketType type;
#property (assign, nonatomic) PacketAction action;
#end
- (void)sendPacket:(PacketH *)packet {
// Encode Packet Data
NSError * error;
NSData * encoded = [NSKeyedArchiver archivedDataWithRootObject:packet requiringSecureCoding:NO error: &error];
// Initialize Buffer
NSMutableData *buffer = [[NSMutableData alloc] init];
// buffer = header + packet
// Fill Buffer
uint64_t headerLength = encoded.length;
[buffer appendBytes:&headerLength length:sizeof(uint64_t)];
[buffer appendBytes: encoded.bytes length: headerLength];
// Write Buffer
[self.socket writeData:buffer withTimeout:-1.0 tag:0];
}
Thanks #user3386109,
getBytes:length: method of NSData , is much like memcpy
- (uint64_t)parseHeader:(NSData *)data {
NSInteger headerLength = 0;
[data getBytes: &headerLength length:sizeof(uint64_t)];
return headerLength;
}

concatenate Strings from a NSArray?

#import <Foundation/Foundation.h>
#include <stdlib.h>
#include "GenerarPassword.h"
#implementation GenerarPassword
-(NSDictionary*) generarDiccionario
{
NSDictionary* m_Dict =[NSDictionary dictionaryWithObjectsAndKeys:
#"Dx", #"1",
#"Om", #"2",
#"Al", #"3",
#"Na", #"4",
#"Je", #"5",
#"Ko", #"6",
#"Ke", #"7",
#"Fi", #"8",
#"Re", #"9",
#"Me", #"10",
#"Mu", #"11",
#"Ra", #"12",
#"Lu", #"13",
#"Lo", #"14",
#"Ka", #"15"
,nil];
return m_Dict;
}
-(void) printPassword:(int) password
{
NSLog(#"%d",password);
}
/*Se genera la clave numérica*/
-(int) generarClave
{
srand(time(0));
int r = rand() %(9999-1000+1) +1000;
return r;
}
//Esta función Genera el valor Aleatorio
-(NSString*) GenerarValor:(NSString*) key
{
NSString *valor = [[self generarDiccionario] valueForKey: key];
return valor;
}
-(NSArray*) generarlistaletras:(int) numero
{
NSArray* lista = [[NSArray alloc] initWithObjects: [NSNumber numberWithInt:0], [NSNumber numberWithInt:0], [NSNumber numberWithInt:0],nil];
return lista;
}
-(void) imprimirArreglo:(NSArray*) arreglo
{
int i = 0;
NSString *str1 =#" ";
for (i=0;i<2;i++)
{
[str1 stringByAppendingString:[arreglo objectAtIndex:0]];
}
NSLog(#"%#",str1);
}
// lista = [0,0,0]
//lista[random.randrange(0,3)] = [GenerarValor(numero)]
//for i in range(len(lista)):
// if lista[i] == 0:
// lista[i] = [GenerarValor(random.randrange(11,20))]
// return lista
#end
int main(int argc, char const *argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
GenerarPassword *Generar1 = [[GenerarPassword alloc]init];
int clave = [Generar1 generarClave];
[Generar1 printPassword:clave];
NSDictionary* dict = [Generar1 generarDiccionario];
NSLog(#"%#",[[Generar1 generarDiccionario] valueForKey:#"1"]);
[Generar1 imprimirArreglo:[Generar1 generarlistaletras:123]];
[pool drain];
return 0;
}
My idea is to print the array (the function imprimirArreglo), but when run the program I get this exception: Uncaught exception NSInvalidArgumentException, reason: Can not determine type information for -[NSIntNumber (null)]; what i wanted was to print the array with format 0 0 0.
OK, I edited your code into something more sensible.
#import <Foundation/Foundation.h>
#include "GenerarPassword.h"
#interface GenerarPassword ()
#property (nonatomic, strong) NSArray *generarArray;
#end
#implementation GenerarPassword
- (NSArray *)generarArray
{
if (!_generarArray) {
_generarArray = #[#"Dx", #"Om", #"Al", #"Na", #"Je", #"Ko", #"Ke", #"Fi", #"Re", #"Me", #"Mu", #"Ra", #"Lu", #"Lo", #"Ka"];
}
return _generarArray;
}
- (void)printPassword:(NSInteger)password
{
NSLog(#"%#", #(password));
}
/*Se genera la clave numérica*/
- (NSInteger)generarClave
{
return arc4random_uniform(9000) + 1000;
}
//Esta función Genera el valor Aleatorio
- (NSString *)generarValor:(NSInteger)key
{
return self.generarArray[key];
}
- (NSArray *)generarlistaletras:(NSInteger)numero
{
return #[#0, #0, #0];
}
- (void)imprimirArreglo:(NSArray *)arreglo
{
NSString *string1 = [NSString stringWithFormat:#"%#", [arreglo componentsJoinedByString:#" "]];
NSLog(#"%#", string1);
}
#end
int main(int argc, char const *argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
GenerarPassword *generarPassword = [[GenerarPassword alloc] init];
NSInteger clave = [generarPassword generarClave];
[generarPassword printPassword:clave];
NSLog(#"%#", generarPassword.generarArray[0]);
[generarPassword imprimirArreglo:[generarPassword generarlistaletras:123]];
[pool drain];
return 0;
}
There are a few things I noticed.
First, if you're going to store value next to keys that are just counting numbers... then use an array not a dictionary.
Second, you're never actually calling the method generarValor so a lot of the code doesn't seem to be doing anything.
Third, you should really be using a property to store the array/dictionary in. You are repeatedly creating new dictionaries in your code.
The line NSDictionary* dict = [Generar1 generarDiccionario]; is actually just wasting processor cycles because you're not actually using dict at all.
Fourth, variable names and methods should start with a lowercase letter, class name start with an uppercase letter.
Fifth, there is a lot of useless code here. I think I've fixed the problem, in your imprimirArreglo method though.
Sixth, format your code nicely when you put it onto StackOverflow. Pretend you're doing and exam for college. No one here is paid so the chance they'll struggle through code that's all over the place is very small.
Also, take a look through the code. I've made some improvements and changes to it to make it Objective-C code and not C++ code.

Building a USB Tree in Objective C

I am trying to build a tree to store USB device information. I thought that I would use NSMutableArray and NSMutableDictionary to contain this information. My problem is that I've never studied software engineering - I'm learning as I go - and I haven't the faintest idea about tree theory.
I'm basing my tree on the USB Location ID, which is eight nibbles long. As I understand it, each nibble represents a layer of the tree (if you see what I mean). I've written a little bit of test code to see if I can build my tree properly - and, sadly, it seems that I can't!
#import <Foundation/Foundation.h>
#define MAXCHILDREN 0xf
NSDictionary* AddItemToTree(NSDictionary* nodeEntry, unsigned int value, int depth)
{
// Convert the value into a set of nibbles
char *bytes = (char *)&value;
char byte = bytes[depth];
NSMutableDictionary* thisEntry = [[[NSMutableDictionary alloc] initWithDictionary:nodeEntry] autorelease];
if (byte == 0)
{
[thisEntry setObject:[NSString stringWithFormat:#"%08x",value] forKey:#"Value"];
[thisEntry setObject:[NSString stringWithFormat:#"%08x",byte] forKey:#"Byte"];
[thisEntry setObject:[NSNumber numberWithInt:depth] forKey:#"Depth"];
return thisEntry;
}
if(![[thisEntry allKeys]containsObject:#"ChildEntries"])
{
NSMutableArray* childArray = [[NSMutableArray alloc]init];
NSMutableDictionary* newNode = [[NSMutableDictionary alloc] init];
[childArray addObject:AddItemToTree(newNode,value,++depth)];
[thisEntry setObject:[NSNumber numberWithInt:depth] forKey:#"Depth"];
[thisEntry setObject:[NSString stringWithFormat:#"%08x",value] forKey:#"Value"];
[thisEntry setObject:[NSString stringWithFormat:#"%08x",byte] forKey:#"Byte"];
[thisEntry setObject:childArray forKey:#"ChildEntries"];
[newNode release];
[childArray release];
}
else
{
[[thisEntry objectForKey:#"ChildEntries"]addObject:AddItemToTree(thisEntry,value, ++depth)];
}
return thisEntry;
}
int main(int argc, char *argv[]) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSMutableDictionary* treenode=[[NSMutableDictionary alloc]init];
char bytearray[4] = {0x0F, 0x0F, 0x02, 0x00};
unsigned int *value = (unsigned int*)bytearray;
char bytearray2[4] = {0x0F, 0x02, 0x00, 0x00};
unsigned int *value2 = (unsigned int*)bytearray2;
char bytearray3[4] = {0x0F, 0x02, 0x00, 0x00};
unsigned int *value3 = (unsigned int*)bytearray3;
[treenode setObject:[NSNumber numberWithInt:0] forKey:#"Depth"];
[treenode setObject:[NSString stringWithFormat:#"%08x",*value] forKey:#"Value"];
[treenode setObject:AddItemToTree(treenode,*value, 0) forKey:#"ChildEntries"];
// [[treenode objectForKey:#"ChildEntries"]addObject:AddItemToTree(treenode,*value2, 0)];
[treenode writeToFile:#"/Users/headbanger/Desktop/test.plist" atomically:YES];
[pool release];
}
Adding one USB location ID works perfectly. Adding a second (by uncommenting-out the line in main) causes SIGABRT. I'm sure that it's perfectly simple, and I've committed a typical newbie error. However, it's not obvious to me and any help that you can provide would be more than welcome.
My tree will need to look something like this:
F-
|--F-
| |--2
|
|--2
This tree should be true even if an attempt is made to add the third byte array.If you can answer the question without being USB specific then that would be most helpful, because I'd really like to understand about trees and what I've done wrong. That said, if there's a quick and easy way to get a tree built for me in Objective-C then I'd love to hear it.So please, experts, can someone tell me what I'm doing wrong? Thank you for your time.
One problem is that you set dictionary as the type of the ChildEntries:
[treenode setObject:AddItemToTree(treenode,*value, 0) forKey:#"ChildEntries"];
but elsewhere you attempt to use it as a NSMutableArray (mind the addObject: method):
[[thisEntry objectForKey:#"ChildEntries"]addObject:AddItemToTree(thisEntry,value, ++depth)];
To fix it, in your main you could do
[treenode setObject:[[NSMutableArray alloc] initWithObjects:AddItemToTree(treenode,*value, 0), nil]forKey:#"Children"];
but even when your recursion progresses towards the 0x00 byte if (byte==0), I think, from mentally inspecting it, that it's going to add duplicated children and produce a reeeally deep tree.
There is something wrong with your environment if you didn't get a message warning you of the wrong method addObject with the SIGABORT.
Btw, it's hard to read. Lines like these
[treenode setObject:[[NSMutableArray alloc] initWithObjects:AddItemToTree(treenode,*value, 0), nil]forKey:#"Children"];
are easier to scan and less prone to mistakes if you write:
NSString * const kChildren = #"Children";
// ...
NSMutableArray *children = [[NSMutableArray alloc] initWithObjects:AddItemToTree(treenode,*value, 0), nil];
[treenode setObject:children forKey:kChildren];
The style is not very objective-c-ish, you could use NSUInteger and NSData instead unsigned int and char arrays.
You should first write a generic tree, then use it for your purposes. This is my tree example. It's ugly but it's mine. As you see, it's common sense. You could set conditions like, two childs per node, and left child < root < right child, and then you would get a binary search tree which has better properties to find stuff. But that will take you a lot more code I guess.
#import <Foundation/Foundation.h>
typedef NS_ENUM(unsigned char, MyTreeVisitingOrder) {
MyTreeOrderDepthFirst,
MyTreeOrderValueFirst
};
#define Tree NSObject<MyTree>
#protocol MyTree
#property (nonatomic,strong) NSObject<NSCopying>* key;
#property (nonatomic,strong) NSObject *value;
#property (nonatomic,strong) NSMutableDictionary *children;
-(void) insertChild:(Tree*)node;
-(void) each:(void(^)(NSObject*))block order:(MyTreeVisitingOrder)order;
#end
#interface TreeImpl : NSObject <MyTree>
-(id) init __attribute__((unavailable("disabled")));
#end
#implementation TreeImpl
#synthesize key = _key;
#synthesize value = _value;
#synthesize children = _children;
-(id) initWithKey:(NSObject<NSCopying>*)key value:(NSObject*)value {
self = [super init];
if (self){
_key = key;
_value = value;
_children = [NSMutableDictionary new];
}
return self;
}
-(void) insertChild:(Tree*)node {
[_children setObject:node forKey:node.key];
}
-(void) each:(void(^)(NSObject*))block order:(MyTreeVisitingOrder)order {
switch (order) {
case MyTreeOrderDepthFirst:{
if (_children) {
for (id key in _children){
[[_children objectForKey:key] each:block order:order];
}
}
block(_value);
break;
}
case MyTreeOrderValueFirst:{
block(_value);
if (_children) {
for (id key in _children){
[[_children objectForKey:key] each:block order:order];
}
}
break;
}
}
}
#end
int main(int argc, char *argv[]) {
#autoreleasepool {
TreeImpl *a = [[TreeImpl alloc] initWithKey:#"A" value:#"A"];
TreeImpl *b = [[TreeImpl alloc] initWithKey:#"B" value:#"B"];
TreeImpl *c = [[TreeImpl alloc] initWithKey:#"C" value:#"C"];
[a insertChild:b];
[a insertChild:c];
[a each:^(NSObject* value) {
NSLog(#"> %#",value);
} order:MyTreeOrderValueFirst];
return EXIT_SUCCESS;
}
}

Is it possible to use a wildcard in KVC?

I'm trying to use wildcard in KVC like this.
Is it possible?
Or Is there other ways to use a wildcard to indicate a member variable?
#interface MyClass : NSObject
#property(nonatomic, retain) NSNumber *test1;
#property(nonatomic, retain) NSNumber *test2;
#end
#implementation MyClass{
NSNumber * test1;
NSNumber * test2;
}
#synthesize test1;
#synthesize test2;
#end
using wildcard
MyClass *testClass = [[[MyClass alloc] init] autorelease];
testClass.test1 = #50;
NSLog(#"test value : %#", [testClass valueForKey:#"*1"]);
For detail codes.
A real reason i wanted is to indicate a member variable of instance by value of integer or nsnumber type.
If possible, it is easier to set values and read values of any instance.
For example of property part copy.
MyClass *testClass = [[[MyClass alloc] init] autorelease];
testClass.year_1 = #2012;
testClass.quarter_2 = #3;
testClass.month_3 = #8;
testClass.day_4 = #20;
testClass.week_5 = #4;
// copy propertys to other instance.
// Normal way
MyClass *testClassCopy = [[[MyClass alloc] init] autorelease];
testClassCopy.year_1 = testClass.year_1;
testClassCopy.quarter_2 = testClass.quarter_2;
testClassCopy.month_3 = testClass.month_3;
testClassCopy.day_4 = testClass.day_4;
// copy propertys by using wildcard
for (int j = 0; j < 4; j++) {
NSString *indicate = [NSString stringWithFormat:#"*%#", [NSNumber numberWithInteger:j + 1]];
NSNumber *sourceProperty = [testClass valueForKey:indicate];
[testClassCopy setValue:sourceProperty forKey:indicate];
}
I'll raise your wildcards by adding Regex, and by using categories:
To read about how regex works with this, please read the NSRegularExpression Class Reference.
Features:
Uses regex, for matching of a wide variety of keys
Uses a category that works on any instance
Caches key lists per class
Full KVC support (not just properties, but accessor methods & iVars too!)
Integrates flawlessly with current KVC methods (only uses the regex if the key wasn't found, improving performance)
Subclassing doesn't mess it up, like #JamesWebster's solution
Doesn't needlessly pollute the list of keys with NSObject's methods
Returns a NSDictionary of matched keys & values
Cons:
Uses regex, which is slower and more complex to understand
Slow initial lookup for a class (must iterate through all methods & iVars)
Automatically overwrites the -valueForUndefinedKey: method, so it's possible that this could break some existing code (move it to it's own method to fix).
Currently doesn't support setting of values (by design, that's a whole other bag of cats).
Can have duplicate keyPaths in the result (not the biggest of issues, but stems from the fact that KVC matching is complex, and I have to implement all of the rules)
Uses NSRegularExpression, which is only available in iOS 4 and later (not the largest of issues).
Version History:
1.0: Initial Release
So, here is the code:
NSObject+KVCRegex.h:
//
// NSObject+KVCRegex.h
// TestProj
//
// Created by Richard Ross on 8/20/12.
// Copyright (c) 2012 Ultimate Computer Services, Inc. All rights reserved.
//
#import <Foundation/Foundation.h>
#interface NSObject (KVCRegex)
// custom implemenation
-(id) valueForUndefinedKey:(NSString *)key;
#end
NSObject+KVCRegex.m:
//
// NSObject+KVCRegex.m
// TestProj
//
// Created by Richard Ross on 8/20/12.
// Copyright (c) 2012 Ultimate Computer Services, Inc. All rights reserved.
//
#import "NSObject+KVCRegex.h"
#import <objc/runtime.h>
#implementation NSObject (KVCRegex)
static NSSet *keyPathsForClass(Class cls)
{
NSMutableSet *keys = [NSMutableSet set];
do
{
if (cls == [NSObject class])
{
// nothing good can come from trying to use KVC on NSObject methods
break;
}
unsigned count = 0;
Method *methods = class_copyMethodList(cls, &count);
for (int i = 0; i < count; i++) {
// make sure that the method returns a value
const char *methodName = sel_getName(method_getName(methods[i]));
char returnType[64];
method_getReturnType(methods[i], returnType, 64);
if (strcmp(returnType, "v") == 0)
continue;
// make sure that the method takes no args (except for self & _cmd)
if (method_getNumberOfArguments(methods[i]) == 2)
{
// add a duplicate entry for ones matching 'is'
if (strstr(methodName, "is") == methodName)
{
char *newStr = strdup(methodName + 2);
newStr[0] = tolower(newStr[0]);
[keys addObject:[NSString stringWithUTF8String:newStr]];
free(newStr);
}
[keys addObject:[NSString stringWithUTF8String:methodName]];
}
}
free(methods);
// now copy iVars
count = 0;
Ivar *ivars = class_copyIvarList(cls, &count);
for (int i = 0; i < count; i++)
{
const char *ivarName = ivar_getName(ivars[i]);
if (strstr(ivarName, "_") == ivarName)
[keys addObject:[NSString stringWithUTF8String:ivarName + 1]]; // iVar name starting with _<key>
[keys addObject:[NSString stringWithUTF8String:ivarName]];
}
free(ivars);
} while ((cls = [cls superclass]));
return [NSSet setWithSet:keys];
}
// returns a dictionary based on 'key' as a regex
-(id) valueForUndefinedKey:(NSString *)key
{
// lookup for later use
static NSMutableDictionary *keyClassPairs;
if (!keyClassPairs)
keyClassPairs = [NSMutableDictionary dictionary];
if (!keyClassPairs[[self class]])
{
keyClassPairs[(id<NSCopying>)[self class]] = keyPathsForClass([self class]);
}
NSSet *keyPaths = keyClassPairs[[self class]];
// assume 'key' is a regex
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:key options:0 error:nil];
NSMutableArray *matches = [NSMutableArray array];
for (NSString *keyPath in keyPaths)
{
NSRange matchRange = [regex rangeOfFirstMatchInString:keyPath options:0 range:(NSRange) { 0, keyPath.length }];
if (matchRange.length == keyPath.length)
{
// we have a match
[matches addObject:keyPath];
}
}
if (matches.count)
return [self dictionaryWithValuesForKeys:matches];
else
[NSException raise:NSUndefinedKeyException format:#"Could not find a key that matches the regex in %#", key];
return nil;
}
#end
Example:
#interface MyObject : NSObject
{
#public
int normalIvar;
id _underscoreIvar;
}
#property id someProp;
#property BOOL isProperty;
#property int nativeProp;
-(void) notAKey;
-(id) aKey;
#end
#implementation MyObject
#synthesize someProp, isProperty, nativeProp;
-(void) notAKey
{
NSLog(#"Not a key!");
}
-(id) aKey
{
return #"Value";
}
#end
int main()
{
#autoreleasepool {
MyObject *obj = [MyObject new];
obj.someProp = #"a property";
obj.nativeProp = 15;
obj.isProperty = YES;
obj->normalIvar = 172;
obj->_underscoreIvar = #"Ivar";
NSString *regex = #"[a|s].*"; // match a key starting with 'a' or 's', then matching anything else after
NSLog(#"%#", [obj valueForKey:regex]); // prints "{ aKey = 'Value', someProp = 'a property' }"
regex = #"_.*"; // match a key starting with '_', and then match anything else after
NSLog(#"%#", [obj valueForKey:regex]); // prints "{ _underscoreIvar = 'Ivar' }"
regex = #".*"; // match any key declared for this object
NSLog(#"%#", [obj valueForKey:regex]); // prints "{ "_underscoreIvar" = Ivar; aKey = Value; isProperty = 1; nativeProp = 15; normalIvar = 172; property = 1; someProp = "a property"; underscoreIvar = Ivar; }"
regex = #"(?i)[A-J].*"; // match (case insensitive) a key starting with A - J
NSLog(#"%#", [obj valueForKey:regex]); // prints "{ aKey = value; isProperty = 1; }"
}
}
Though I couldn't find a way to support wildcards using the syntax you were attempting. I found this roundabout method using the Objective-C runtime!
First we get all of the properties of the class you'd like to use
#import <objc/runtime.h>
unsigned int outCount;
objc_property_t *properties = class_copyPropertyList([MyClass class], &outCount);
NSMutableArray *array = [NSMutableArray arrayWithCapacity:outCount];
for (int i = 0; i < outCount; i++)
{
objc_property_t property = properties[i];
const char *propName = property_getName(property);
if(propName)
{
NSString *propertyName = [NSString stringWithUTF8String:propName];
[array addObject:propertyName];
}
}
free(properties);
Then filter out the ones you actually want
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF ENDSWITH '1'"];
[array filterUsingPredicate:predicate];
Then actually use them
for (NSString *key in array)
NSLog(#"%#", [testClass valueForKey:key]);

Write a complex array of custom structs to file Objective C

I need to save and load the contents of an array of structs, but I know that Objective C is very particular about which data types you can read/write with.
Here is my struct:
struct SCourse
{
NSMutableArray* holes; // holds integers (pars)
NSString* name;
int size;
BOOL inUse;
};
#interface CoursesManager : NSObject
{
struct SCourse courses[5];
}
What are the data types I'll need to use? Do they each have different methods needed in order to read/write? I'm just looking for a non-complex way to get all the data I need to and from a file. I could do this quite easily in a language I'm more familiar with (C++), but some of the particulars of Objective-c are still lost on me.
EDIT: Solution (thanks for the help, everyone)
-(void)applicationWillResignActive:(UIApplication *)application {
// save the courses
NSMutableArray* totalWriteArray = [[NSMutableArray alloc] initWithCapacity:MAX_COURSES];
for (int i = 0; i < MAX_COURSES; ++i)
{
struct SCourse saveCourse = [coursesManager GetCourseAtIndex:i];
NSNumber* nInUse = [NSNumber numberWithBool:saveCourse.inUse];
NSNumber* nSize = [NSNumber numberWithInt:saveCourse.size];
NSMutableArray* writeArray = [[NSMutableArray alloc] initWithCapacity:4];
[writeArray addObject:nInUse];
[writeArray addObject:nSize];
[writeArray addObject:saveCourse.name];
[writeArray addObject:saveCourse.holes];
[totalWriteArray addObject:writeArray];
}
[totalWriteArray writeToFile:[self saveFilePath] atomically:YES];
}
And for the loading back in...
-(void)loadFile {
NSString *myPath = [self saveFilePath];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:myPath];
if (fileExists) {
NSMutableArray* totalReadArray = [[NSMutableArray alloc] initWithContentsOfFile:[self saveFilePath]];
for (int i = 0; i < MAX_COURSES; ++i)
{
struct SCourse loadCourse = [coursesManager GetCourseAtIndex:i];
NSMutableArray* loadArray = [totalReadArray objectAtIndex:i];
NSNumber* nInUse = [loadArray objectAtIndex:0];
loadCourse.inUse = [nInUse boolValue];
NSNumber* nSize = [loadArray objectAtIndex:1];
loadCourse.size = [nSize integerValue];
NSString* inName = [loadArray objectAtIndex:2];
loadCourse.name = inName;
NSMutableArray* inHoles = [loadArray objectAtIndex:3];
loadCourse.holes = inHoles;
[coursesManager ReplaceCourseAtIndex:i With:loadCourse];
}
}
}
First thing first. You shouldn't use plain old C structures. The ARC memory management will not appreciate.
If you are familiar with C++, you should maybe use a C++ class instead, which will please the compiler and runtime. Depends on what you want to do.
Array. Use either NSArray or std::vector but please, no plain C arrays. Not sure how ARC will handle this but I suppose it will not appreciate much. Objective-C and C++ both provides all the tools you need to handle collections of whatever.
Serialization. You have several possibilities, one of them is NSCoder.
Last word, with the so called modern syntax, converting things into ObjC objects is quite easy.
BOOL b = YES;
int i = 10;
double d = 3.14;
char* s = "Pouf pouf";
You get the ObjC equivalents with the boxin' thingy:
NSNumber* bo = #( b );
NSNumber* io = #( i );
NSNumber* do = #( d );
NSString* so = #( s );
NSArray* ao = #[ #( i ), do ];
NSDictionary* = #{ #"num" : io, #"str" : #( s ) };
To write something in a file, in one gracious step:
[#{ #"bool" : bo, #"array" : #[ #"string", #10, #( 10 + 20 ) ] }
writeToFile: #"path.plist" atomically: YES];
But the question remains, what are you trying to accomplish?
One easy approach is to store these arrays in an NSMutableDictionary object and use the method:
[mutableDict writeToFile:#"path/to/file" atomically:YES];
To store the data and:
NSMutableDictionary *anotherDict = [NSMutableDictionary dictionaryWithContentsOfFile:#"path/to/file"];
To read the contents back in.
Here's what I'd suggest:
Make a custom class with the properties you want (.h file):
#import <Foundation/Foundation.h>
#interface CustomHolder : NSObject {
NSString *last;
NSString *first;
NSString *middle;
}
#property (strong, nonatomic) NSString *last;
#property (strong, nonatomic) NSString *first;
#property (strong, nonatomic) NSString *middle;
#end
And then set the .m file up so that you can encode/decode the object
#import "CustomHolder.h"
#implementation CustomHolder
#synthesize last, first, middle;
- (void)encodeWithCoder:(NSCoder *)encoder
{
[encoder encodeObject:first forKey:#"first"];
[encoder encodeObject:last forKey:#"last"];
[encoder encodeObject:middle forKey:#"middle"];
}
- (id)initWithCoder:(NSCoder *)decoder
{
if (self = [super init])
{
self.first = [decoder decodeObjectForKey:#"first"];
self.last = [decoder decodeObjectForKey:#"last"];
self.middle = [decoder decodeObjectForKey:#"middle"];
}
return self;
}
#end
Then you can just
[NSKeyedArchiver archiveRootObject:obj toFile:[self saveFilePath]] to save and
[NSKeyedUnarchiver unarchiveObjectWithFile:[self saveFilePath]] to load
That's probably the most similar to using C-structs (especially because ARC doesn't let you use structs).