I have a class which must initialize a group of similar objects the same way.
NSNumber *a = nil;
NSNumber *b = nil;
NSNumber *c = nil;
a, b and c are member variables of an existing object. In my code I use a more complex NSObject subclass, but this example is easier to understand using NSNumbers.
All 3 objects must be initialized in a similar way. Therefore I want to build an array of pointers which I can use within a for-loop like this:
NSPointerArray *objs = [NSPointerArray weakObjectsPointerArray];
[objs addPointer:&a];
[objs addPointer:&b];
[objs addPointer:&c];
Running the above code I get the following error:
error: address doesn't contain a section that points to a section in a object file
How can I build an array which I can explore with a loop like this?
for (id *o in objs) {
*o = #2;
}
just wrap the pointers in 'NSValue' objects and you're good
id v1 = [NSValue valueWithPointer:p1];
id v2 = [NSValue valueWithPointer:p2];
id v3 = [NSValue valueWithPointer:p3];
id array = #[v1,v2,v3];
for(NSValue *value in array) {
void *pointer = value.pointerValue;
NSNumber *n = (__bridge NSNumber*)pointer;
}
demo for what YOU want to do it appears: (NSNumbers are immutable)
#import <Foundation/Foundation.h>
int main(int argc, char *argv[]) {
#autoreleasepool {
NSNumber *a;
NSNumber *b;
NSArray *os = #[ [NSValue valueWithPointer:&a],
[NSValue valueWithPointer:&b] ];
for (NSValue *v in os) {
void *pointer = [v pointerValue];
NSNumber **n_ptr = (__bridge NSNumber**)pointer;
*n_ptr = #2.0;
}
NSLog(#"%# %#", a,b);
}
}
Related
I'm learning Objective-C runtime, and try to use method_exchangeImplementations to exchange addObject: method and removeObject: method of NSMutableArray.
My code like this:
int main(int argc, const char * argv[]) {
#autoreleasepool {
Method removeMethod = class_getInstanceMethod(NSMutableArray.class, #selector(removeObject:));
Method addMethod = class_getInstanceMethod(NSMutableArray.class, #selector(addObject:));
method_exchangeImplementations(addMethod, removeMethod);
NSMutableArray *array = [[NSMutableArray alloc] init];
NSObject *obj = [[NSObject alloc] init];
[array removeObject:obj];
NSLog(#"%lu", (unsigned long)array.count); // expect print 1, actual print 1
[array addObject:obj];
NSLog(#"%lu", (unsigned long)array.count); // expect print 0, actual print 2
}
return 0;
}
I expect exchange add/remove function, but seems like only removeObject: has been exchange to addObject: , addObject: still is addObject to array, now I have two addObject method of NSMutableArray
I'm not sure the reason. I try to exchange other method like uppercaseString/lowercaseString of NSString, that work correct.
OK, I solved problem. Thanks #Willeke for the tip.
The real class of my array is not NSMutableArray, so use NSMutableArray.class in class_getInstanceMethod can't get correct method. Use [array class] is the answer.
int main(int argc, const char * argv[]) {
#autoreleasepool {
NSMutableArray *array = [[NSMutableArray alloc] init];
Method removeMethod = class_getInstanceMethod([array class], #selector(removeObject:));
Method addMethod = class_getInstanceMethod([array class], #selector(addObject:));
method_exchangeImplementations(addMethod, removeMethod);
NSObject *obj = [[NSObject alloc] init];
[array removeObject:obj];
NSLog(#"%lu", (unsigned long)array.count); // expect print 1, actual print 1
[array addObject:obj];
NSLog(#"%lu", (unsigned long)array.count); // expect print 0, actual print 0
}
return 0;
}
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
#autoreleasepool {
NSMutableArray *groceries;
NSString *a = (#"loaf of bread");
NSString *b = (#"stick of butter");
NSString *c = (#"big ass cookie");
[groceries addObject:a];
[groceries addObject:b];
[groceries addObject:c];
for (NSString *d in groceries){
NSLog(#"%#", d);
}
}
return 0;
}
Why is this not working? What is wrong? Thanks.
I cannot seem to figure it out at this moment, the for loop defiantly seems to be the hangup.
While you didn't initialize NSMutableArray, it's is nil. Adding object to non-initialized mutable array always give nil.
Firstly you need to initialize groceries:
NSMutableArray *groceries = [NSMutableArray new];
or more liked by me way:
NSMutableArray *groceries = #[].mutableCopy;
In your case, for example, you can declare so:
NSMutableArray *groceries = #[#"loaf of bread", #"stick of butter", #"big ass cookie"].mutableCopy;
I am generating a random equation say like 2*3+4..... and using DDMathparser to evaluate it. Here I have a class method which is supposed to return a random equation(stored inside a mutable array) only if it evaluates to a integer.
however it keeps returning Null and i can't figure out why. Please help me out.!
#import "Equation.h"
#import "DDMathParser.h"
#implementation Equation
-(NSMutableArray*)randEquation{
NSMutableArray* usableEquation=[[NSMutableArray alloc]init];
while(1){
NSArray *nums = #[#"1", #"2", #"3", #"4", #"5",#"6",#"7",#"8",#"9"];
unsigned index1=arc4random()%9;
NSString* num = [NSString stringWithFormat:#"%#", [nums objectAtIndex:index1]];
NSArray *symbols = #[#"+", #"-", #"*", #"/"];
unsigned index=arc4random()%4;
NSString* symb = [NSString stringWithFormat:#"%#", [symbols objectAtIndex:index]];
NSMutableArray *arrayOfSymbolsAndNumbers = [[NSMutableArray alloc] init];
for( int i=0;i<=10;i++){
if (i%2==0) {
[arrayOfSymbolsAndNumbers addObject:num];
}
else{
[arrayOfSymbolsAndNumbers addObject:symb];
}
}
NSMutableString *stringOfSymbolsAndNumbers=[[NSMutableString alloc]init];
for (NSObject * obj in arrayOfSymbolsAndNumbers)
{
[stringOfSymbolsAndNumbers appendString:[obj description]];
}
usableEquation=arrayOfSymbolsAndNumbers;
NSNumber *result=[stringOfSymbolsAndNumbers numberByEvaluatingString];
float resultFloat = [result floatValue];
float checker=resultFloat;
if (floor(checker)==checker) {
break;
}
else{
continue;
}
}
return usableEquation;
}
#end
NSLog(#"The content of array is%#",[equation randEquation]);
Based on your code, for this log to output The content of array is(null) means that equation is nil. Your randEquation (while not efficient) looks ok, the problem is that you haven't created the equation instance when you run the log statement.
I have made a category in my x-code project like below:
+ (NSDictionary *)anagramMap {
static NSDictionary *anagramMap;
if (anagramMap != nil)
return anagramMap;
// this file is present on Mac OS and other unix variants
NSString *allWords = [NSString stringWithContentsOfFile:#"/usr/share/dict/words"
encoding:NSUTF8StringEncoding
error:NULL];
NSMutableDictionary *map = [NSMutableDictionary dictionary];
#autoreleasepool {
[allWords enumerateLinesUsingBlock:^(NSString *word, BOOL *stop) {
NSString *key = [word anagramKey];
if (key == nil)
return;
NSMutableArray *keyWords = [map objectForKey:key];
if (keyWords == nil) {
keyWords = [NSMutableArray array];
[map setObject:keyWords forKey:key];
}
[keyWords addObject:word];
}];
}
anagramMap = map;
return anagramMap;}
- (NSString *)anagramKey {
NSString *lowercaseWord = [self lowercaseString];
// make sure to take the length *after* lowercase. it might change!
NSUInteger length = [lowercaseWord length];
unichar sortedWord[length];
[lowercaseWord getCharacters:sortedWord range:(NSRange){0, length}];
qsort_b(sortedWord, length, sizeof(unichar), ^int(const void *a, const void *b) {
unichar c1 = *(const unichar *)a;
unichar c2 = *(const unichar *)b;
if (c1 > c2)
return -1;
if (c1 < c2)
return 1;
return 0;
});
return [NSString stringWithCharacters:sortedWord length:length];}
Basically, this code loops through the Mac OSx dictionary and turns it into an NSDictionary where the key is the alphabetically sorted word, and the object is an array of all the anagrams of that word.
What I was wondering, is how can I call this method, such as in the viewDidLoad part of an implementation file which would assign an NSDictionary (or mutable) this created dictionary of the sorted key and object array? Basically in pseudo code i do something like:
NSMutatableArray *englishDictionary = [[NSMutableArray alloc] init];
englishDictionary = [NSMutableArray anagramMapScrabble];
//Should mean now englishDictionary has turned into the NSDictionary where the key = sorted word and object is an array of English anagrams of that sorted word
I think I'd have to put some extra code in the methods but I'm not sure. Any suggestions would be much appreciated!
A category is a modification of an existing class. It works exactly the same as if the methods of the category were declared in that class - because they are. So it works like every other method. If you make a category on, say, NSObject declared like this:
+ (NSDictionary *)anagramMap;
Then that is a class method of NSObject and you call it by saying:
NSDictionary* d = [NSObject anagramMap];
If you make a category on NSObject declared like this:
- (NSDictionary *)anagramMap;
Then that is an method of NSObject and you call it by saying:
NSObject* o = [NSObject new];
NSDictionary* d = [o anagramMap];
I'm trying to get a list of all the properties of an unknown class and the class of every property. By the moment I get a list of all the properties of an object(I do it recursively to get all of the superclasses). I inspired in this post
+ (NSArray *)classPropsFor:(Class)klass
{
NSLog(#"Properties for class:%#", klass);
if (klass == NULL || klass == [NSObject class]) {
return nil;
}
NSMutableArray *results = [[NSMutableArray alloc] init];
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList(klass, &outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
const char *propName = property_getName(property);
if(propName) {
NSString *propertyName = [NSString stringWithUTF8String:propName];
[results addObject:propertyName];
}
NSArray* dict = [self classPropsFor:[klass superclass]];
[results addObjectsFromArray:dict];
}
free(properties);
return [NSArray arrayWithArray:results];
}
So now I want the class of every property and I do:
NSArray* properties = [PropertyUtil classPropsFor:[self class]];
for (NSString* property in properties) {
id value= [self valueForKey:property];
NSLog(#"Value class for key: %# is %#", property, [value class]);
}
The problem is it works for NSStrings or but not for custom classes, for that it returns me null. I want to recursively create a dictionary that represents an object that can have other objects inside and as I thinks I need to know the class of every property, is that possible?
Just made a tiny method for this.
// Simple as.
Class propertyClass = [customObject classOfPropertyNamed:propertyName];
Could be optimized in many ways, but I love it.
Implementation goes like:
-(Class)classOfPropertyNamed:(NSString*) propertyName
{
// Get Class of property to be populated.
Class propertyClass = nil;
objc_property_t property = class_getProperty([self class], [propertyName UTF8String]);
NSString *propertyAttributes = [NSString stringWithCString:property_getAttributes(property) encoding:NSUTF8StringEncoding];
NSArray *splitPropertyAttributes = [propertyAttributes componentsSeparatedByString:#","];
if (splitPropertyAttributes.count > 0)
{
// xcdoc://ios//library/prerelease/ios/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html
NSString *encodeType = splitPropertyAttributes[0];
NSArray *splitEncodeType = [encodeType componentsSeparatedByString:#"\""];
NSString *className = splitEncodeType[1];
propertyClass = NSClassFromString(className);
}
return propertyClass;
}
It is part of eppz!kit, within a developing object representer called NSObject+EPPZRepresentable.h. It actually does what you are to achieve originally.
// Works vica-versa.
NSDictionary *representation = [customObject dictionaryRepresentation];
CustomClass = [CustomClass representableWithDictionaryRepresentation:representation];
It encodes many types, iterate trough collections, represents CoreGraphics types, UIColors, also represent / reconstruct object references.
New version spits you back even C type names and named struct types as well:
NSLog(#"%#", [self typeOfPropertyNamed:#"index"]); // unsigned int
NSLog(#"%#", [self typeOfPropertyNamed:#"area"]); // CGRect
NSLog(#"%#", [self typeOfPropertyNamed:#"keyColor"]); // UIColor
Part of eppz!model, feel free to use method implementations at https://github.com/eppz/eppz.model/blob/master/eppz!model/NSObject%2BEPPZModel_inspecting.m#L111
You should probably store the class (as a string) for each property at the same time as you store the propertyName. Maybe as a dictionary with property name as the key and class name as the value, or vice versa.
To get the class name, you can do something like this (put this right after you declare propertyName):
NSString* propertyAttributes = [NSString stringWithUTF8String:property_getAttributes(property)];
NSArray* splitPropertyAttributes = [propertyAttributes componentsSeparatedByString:#"\""];
if ([splitPropertyAttributes count] >= 2)
{
NSLog(#"Class of property: %#", [splitPropertyAttributes objectAtIndex:1]);
}
The string handling code is because the attributes include a number of pieces of information - the exact details are specified here: https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html
UPDATED
This doesn't work for values that are nil. Instead you should use the runtime C API to obtain the class from the corresponding ivar or accessor method.
The following added to an NSObject category does the trick.
- (Class) classForKeyPath:(NSString*)keyPath {
Class class = 0;
unsigned int n = 0;
objc_property_t* properties = class_copyPropertyList(self.class, &n);
for (unsigned int i=0; i<n; i++) {
objc_property_t* property = properties + i;
NSString* name = [NSString stringWithCString:property_getName(*property) encoding:NSUTF8StringEncoding];
if (![keyPath isEqualToString:name]) continue;
const char* attributes = property_getAttributes(*property);
if (attributes[1] == '#') {
NSMutableString* className = [NSMutableString new];
for (int j=3; attributes[j] && attributes[j]!='"'; j++)
[className appendFormat:#"%c", attributes[j]];
class = NSClassFromString(className);
}
break;
}
free(properties);
return class;
}