ObjC get property name - objective-c

I've looking for a way to get a property name as StringValue from inside a method.
Lets say:
My class has X Subviews from the Type UILabel.
#property (strong, nonatomic) UILabel *firstLabel;
#property (strong, nonatomic) UILabel *secondLabel;
[...]
and so on.
Inside the method foo, the views are iterated as followed:
-(void) foo
{
for (UIView *view in self.subviews) {
if( [view isKindOfClass:[UILabel class]] ) {
/*
codeblock that gets the property name.
*/
}
}
}
The Result should be something like that:
THE propertyName(NSString) OF view(UILabel) IS "firstLabel"
I've tried class_getInstanceVariable, object_getIvar and property_getName without Success.
For example, the code for:
[...]
property_getName((void*)&view)
[...]
Returns:
<UILabel: 0x6b768c0; frame = (65 375; 219 21); text = 'Something'; clipsToBounds = YES; opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; layer = <CALayer: 0x6b76930>>
But i'm looking for this kind of result: "firstLabel" , "secondLabel" and so on.
Solved
As in the Reply of graver described the solution is:
class_copyIvarList which returns the name of the Ivars.
Ivar* ivars = class_copyIvarList(clazz, &count);
NSMutableArray* ivarArray = [NSMutableArray arrayWithCapacity:count];
for (int i = 0; i < count ; i++)
{
const char* ivarName = ivar_getName(ivars[i]);
[ivarArray addObject:[NSString stringWithCString:ivarName encoding:NSUTF8StringEncoding]];
}
free(ivars);
See posts:
https://stackoverflow.com/a/2302808/1228534
and
Objective C Introspection/Reflection

untested code from Getting an array of properties for an object in Objective-C
id currentClass = [self class];
NSString *propertyName;
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList(currentClass, &outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
propertyName = [NSString stringWithCString:property_getName(property)];
NSLog#("The propertyName is %#",propertyName);
}

Easy way to get a specific property name without running a loop
Lets say custom object is like below
#interface StoreLocation : NSObject
#property (nonatomic, strong) NSString *city;
#property (nonatomic, strong) NSNumber *lat;
#property (nonatomic, strong) NSNumber *lon;
#property (nonatomic, strong) NSString *street;
#property (nonatomic, strong) NSString *state;
#property (nonatomic, strong) NSString *code;
#end
#interface AppleStore : NSObject
#property (nonatomic, strong) StoreLocation *storeLocation;
#end
Below Objective macros will get the desired result
#define propertyKeyPath(property) (#""#property)
#define propertyKeyPathLastComponent(property) [[(#""#property)componentsSeparatedByString:#"."] lastObject]
Use the code below to get property name
NSLog(#"%#", propertyKeyPath(appleStore.storeLocation)); //appleStore.storeLocation
NSLog(#"%#", propertyKeyPath(appleStore.storeLocation.street)); //appleStore.storeLocation.street
NSLog(#"%#", propertyKeyPathLastComponent(appleStore.storeLocation)); //storeLocation
NSLog(#"%#", propertyKeyPathLastComponent(appleStore.storeLocation.street)); //street
Source : http://www.g8production.com/post/78429904103/get-property-name-as-string-without-using-the-runtime

Related

How to access view's subviews without crash while hooking Swift? (Jailbreak)

I have an odd issue where when I change the background of a view, I can no longer see the UILabels that are subviews of that view. I cannot dump the headers for the Calculator app, as it is in Swift, and I don't know what the name of the UILabels are, and FLEXing is no help with that. I want to bring the subviews in front of the background view (there are two subviews), but both of my current methods crash the target app. I was trying to see if I could get it working with just one at the very front though.
My code:
NSMutableArray *labels = [NSMutableArray arrayWithArray:((DisplayView*)self).allSubviews];
if ([labels count] > 0) {
UILabel *mainLabel = labels[0];
[self bringSubviewToFront:mainLabel];
}
((DisplayView*)self).allSubviews = labels;
And I have also tried:
for (UILabel *label in ((DisplayView*)self).allSubviews) {
if ([label isKindOfClass:[UILabel class]]) {
[self bringSubviewToFront:label];
}
I need an alternative method of doing this, a way to bring all of the subviews forward, or someone to tell me what I am doing wrong.
Edit, here's my full code:
#interface DisplayView
#property (nonatomic, copy, readwrite) UIColor *backgroundColor;
#property (nonatomic, assign, readwrite, getter=isHidden) BOOL hidden;
#property (nonatomic, assign, readwrite, getter=isOpaque) BOOL opaque;
#property (nonatomic, assign, readwrite) CGFloat alpha;
#property (nonatomic, copy, readwrite) NSArray *allSubviews;
#property (nonatomic, assign, readwrite) CALayer *layer;
#end
%hook DisplayView
-(void)layoutSubviews {
((DisplayView*)self).opaque = NO;
((DisplayView*)self).backgroundColor = [UIColor blackColor];
((DisplayView*)self).hidden = NO;
((DisplayView*)self).alpha = 1.0;
NSMutableArray *labels = [NSMutableArray arrayWithArray:((DisplayView*)self).layer.sublayers];
if ([labels count] > 0) {
UILabel *mainLabel = labels[0];
[self bringSubviewToFront:mainLabel];
}
}
%end
%ctor {
%init(DisplayView=objc_getClass("Calculator.DisplayView"));
}
Seems like a few possible issues. First, there is no allSubviews on UIView, it probably should be subviews.
NSMutableArray *labels = [NSMutableArray arrayWithArray:((DisplayView*)self).subviews];
if ([labels count] > 0) {
UILabel *mainLabel = labels[0];
(Just want point out that you don't seem to change the array, so why copy it? You can directly access self.subviews)
Second, in the CALayer version, you're casting CALayer to UIView:
NSMutableArray *labels = [NSMutableArray arrayWithArray:((DisplayView*)self).layer.sublayers];
CALayer *mainLabel = labels.firstObject;
if (mainLabel) {
[labels removeObjectAtIndex:0];
[labels addObject:mainLabel];
self.layer.sublayers = labels;
}
Maybe some of this helps

search through data in multiple arrays

im doing a searchbar for my UITableview.
So far its only filtering one array in - (void) searchthroughdata, namely the self.Title.
But I want it to filter through two arrays - the self.Title and self.Description.
My .h file:
#property (nonatomic, strong) NSArray * Images;
#property (nonatomic, strong) NSArray * Title;
#property (nonatomic, strong) NSArray * Description;
#property (nonatomic, strong) NSMutableArray *results;
#property (nonatomic, strong) IBOutlet UISearchBar *SearchBar;
My .m file:
-(void)searchThroughData {
self.results = nil;
NSPredicate *resultsPredicate = [NSPredicate predicateWithFormat:#"SELF contains [search] %#", self.SearchBar.text];
self.results = [[self.Title filteredArrayUsingPredicate:resultsPredicate] mutableCopy];
}
-(void) searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
[self searchThroughData];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (tableView == self.tableView) {
return _Title.count;
} else {
[self searchThroughData];
return self.results.count;
}
// Return the number of rows in the section.
//return _Title.count;
}
How do I make it filter through the NSArray * Description as well?
Your best option would be to not have multiple arrays. Instead, create a custom object with your
#property (nonatomic, strong) NSArray * Images;
#property (nonatomic, strong) NSArray * Title;
#property (nonatomic, strong) NSArray * Description;
(or a dictionary) and have a single array containing those objects (I am assuming something about your data model here...).
Now, when you filter you can just check each item in the predicate with an OR.

Objective C serialize a collection to JSON/XML

I have a classes looking like this:
#interface AISlideItem: NSObject <NSCoding>
{
NSString* PlaceHolderName;
NSInteger PlaceHolderID;
}
#property (nonatomic, strong) NSString* PlaceHolderName;
#property (nonatomic) NSInteger PlaceHolderID;
#end
#interface AITitleSlideItem : AISlideItem
{
NSString* Title;
}
#property (nonatomic, strong) NSString* Title;
#end
#interface AIParagraphSlideItem : AISlideItem
{
NSMutableArray* Paragraphs;
}
#property (nonatomic, strong) NSMutableArray* Paragraphs;
#end
#interface AITextSlideItem : AISlideItem
{
NSString* Text;
}
#property (nonatomic, strong) NSString* Text;
#end
#interface AIPictureSlideItem : AISlideItem
{
NSMutableData* Picture;
}
#property (nonatomic, strong) NSMutableData* Picture;
#end
#interface AISlideContent : NSObject
{
NSString* LayoutName;
NSMutableArray* Items;
}
#property (nonatomic,strong) NSString* LayoutName;
#property (nonatomic,strong) NSMutableArray* Items;
#end
#interface ContentParaghraph : NSObject
{
NSInteger Level;
NSString* Text;
}
#property (nonatomic) NSInteger Level;
#property (nonatomic, strong) NSString* Text;
#end
I create a complex collection from these classes like this:
NSMutableArray* l = [[NSMutableArray alloc]init ];
AITitleSlideItem* tis1 = [[AITitleSlideItem alloc]init];
[tis1 setPlaceHolderName:#"I don't think we're using this..."];
[tis1 setPlaceHolderID:1];
[tis1 setTitle:#"This is a title"];
AITextSlideItem* tes1 = [[AITextSlideItem alloc]init];
[tes1 setPlaceHolderName:#"I don't think we're using this..."];
[tes1 setPlaceHolderID:2];
[tes1 setText:#"This is the description"];
AISlideContent* slide1 = [[AISlideContent alloc]init];
[slide1 setLayoutName:#"Title content"];
[[slide1 Items]addObject:tis1];
[[slide1 Items]addObject:tes1];
[l addObject:slide1];
[l addObject:slide1];
What i want to to serialize the NSMutableArray l into any portable format like json or xml.
Could you please post some code doing so?
Sincerely
Zoli
After further investigation i found nothing more suitable for my needs.
I decided to learn a bit more about JSON and create a function that creates the JSON structure manually. Even if it took 2-3 hours, it did worth all the effort.

Adding objects to a NSMutableArray property

this is my data strucure:
group [1...n] {
id,
name,
elements : [1...n]
}
I define a class for element with all properties and a class for group as:
#interface Group : NSObject {
NSInteger groupID;
NSString *groupName;
NSMutableArray *elements;
}
#property (assign, readwrite) NSInteger groupID;
#property (assign, readwrite) NSString *groupName;
#property (assign, readwrite) NSMutableArray *elements;
and single element with:
#interface Element : NSObject {
NSInteger elementID;
NSString *elementName;
}
#property (assign, readwrite) NSInteger elementID;
#property (assign, readwrite) NSString *elementName;
Both classes have properties and synthesize.
When application start I inserted data on data structure with this:
arrGroup = [NSMutableArray array];
[arrGroup retain];
Element *element1 = [[Element alloc] init];
element1.elemenID = 1;
element1.elemenName = #"Andrea";
Element *element = [[Element alloc] init];
element2.elementID = 2;
element2.elementName = #"Andrea2";
Group *group = [[Group alloc] init];
group.groupID = 1;
group.groupName = #"Grup 1";
[group.elements addObject:element1];
[group.elements addObject:element2];
[contact1 release];
[contact2 release];
[arrGroup addObject:group];
The problem is this the [group.elements addObjct:element1]. Nothing has been written on elements NSMutableArray.
Could you help me to find the error? There is a better method to retrieve structure data (groups of elemens)?
thanks for help!
Andrea
#synthesize only generates the getter and the setter for your property, you have to take care of initialization yourself if needed.
To initialize the mutable array do e.g. this in your initializer:
- (id)init { // or however it is named
if ((self = [super init])) {
elements = [[NSMutableArray alloc] init];
// ... more?
}
return self;
}
- (void)dealloc {
[elements release]; // don't forget to clean up
// ... more?
[super dealloc];
}

Observing a Change to ANY Class Property in Objective-C

Put simply, is there a way to receive a general notification when any property in an Objective-C class is changed? I know I can use KVO to monitor particular property changes, but I have the need to call a particular method whenever any setProperty: message is sent to my class. I want to be able to receive a generic notification without any concern about which property in particular was modified.
If it helps to clarify why I want to do this, I am making use of some fast table scrolling code found here: http://blog.atebits.com/2008/12/fast-scrolling-in-tweetie-with-uitableview/
Part of the process of accomplishing this is that whenever a property in a table view cell is modified, [ self setNeedsDisplay ] needs to be called. I'd rather not have to override the setter methods for every property in my class just to make this call.
As Chuck notes, you can create a dependent key, or of course you can directly observe all the properties (which is less work than overloading the setters).
Using the Objective-C runtime, if you exclusively use properties, you can automate this process using class_copyPropertyList(). But I'd probably only do this if this problem comes up a bit for you. If you only have one instance of this problem, it's probably easier and safer and more maintainable just to directly observe the list of properties unless you feel like working in the ObjC runtime.
Here's an example built off of Chuck and Rob's suggestions:
DrakeObject.h
#interface DrakeObject : NSObject
#property (nonatomic, strong) NSNumber *age;
#property (nonatomic, strong) NSNumber *money;
#property (nonatomic, strong) NSString *startPosition;
#property (nonatomic, strong) NSString *currentPosition;
#property (nonatomic, strong, readonly) id propertiesChanged;
#end
DrakeObject.m
#implementation DrakeObject
- (instancetype)init {
self = [super init];
if (self) {
self.age = #25;
self.money = #25000000;
self.startPosition = #"bottom";
self.currentPosition = #"here";
}
return self;
}
- (id)propertiesChanged {
return nil;
}
+(NSSet *)keyPathsForValuesAffectingPropertiesChanged {
return [NSSet setWithObjects:#"age", #"money", #"startPosition", #"currentPosition", nil];
}
observing propertiesChanged will let us know anytime a property has changed.
[self.drakeObject addObserver:self
forKeyPath:#"propertiesChanged"
options:NSKeyValueObservingOptionNew
context:nil];
Not exactly. You can create a dependent key that depends on every property you wish to expose and then observe that. That's about as close as you'll get, I think.
Here an example of code. I have a general object and dother object. Dother object has to save his state on change each property.
#import <Foundation/Foundation.h>
#interface GeneralObject : NSObject
+ (instancetype)instanceWithDictionary:(NSDictionary *)aDictionary;
- (instancetype)initWithDictionary:(NSDictionary *)aDictionary;
- (NSDictionary *)dictionaryValue;
- (NSArray *)allPropertyNames;
#end
implementation
#import "GeneralObject.h"
#import <objc/runtime.h>
#implementation GeneralObject
#pragma mark - Public
+ (instancetype)instanceWithDictionary:(NSDictionary *)aDictionary {
return [[self alloc] initWithDictionary:aDictionary];
}
- (instancetype)initWithDictionary:(NSDictionary *)aDictionary {
aDictionary = [aDictionary clean];
for (NSString* propName in [self allPropertyNames]) {
[self setValue:aDictionary[propName] forKey:propName];
}
return self;
}
- (NSDictionary *)dictionaryValue {
NSMutableDictionary *result = [NSMutableDictionary dictionary];
NSArray *propertyNames = [self allPropertyNames];
id object;
for (NSString *key in propertyNames) {
object = [self valueForKey:key];
if (object) {
[result setObject:object forKey:key];
}
}
return result;
}
- (NSArray *)allPropertyNames {
unsigned count;
objc_property_t *properties = class_copyPropertyList([self class], &count);
NSMutableArray *array = [NSMutableArray array];
unsigned i;
for (i = 0; i < count; i++) {
objc_property_t property = properties[i];
NSString *name = [NSString stringWithUTF8String:property_getName(property)];
[array addObject:name];
}
free(properties);
return array;
}
#end
and after all we have dother class, which should save his state on each change of any property
#import "GeneralObject.h"
extern NSString *const kUserDefaultsUserKey;
#interface DotherObject : GeneralObject
#property (strong, nonatomic) NSString *firstName;
#property (strong, nonatomic) NSString *lastName;
#property (strong, nonatomic) NSString *email;
#end
and implementation
#import "DotherObject.h"
NSString *const kUserDefaultsUserKey = #"CurrentUserKey";
#implementation DotherObject
- (instancetype)initWithDictionary:(NSDictionary *)dictionary {
if (self = [super initWithDictionary:dictionary]) {
for (NSString *key in [self allPropertyNames]) {
[self addObserver:self forKeyPath:key options:NSKeyValueObservingOptionNew context:nil];
}
}
return self;
}
- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSKeyValueChangeKey, id> *)change context:(nullable void *)context {
NSDictionary *dict = [self dictionaryValue];
[[NSUserDefaults standardUserDefaults] setObject:dict forKey:kUserDefaultsUserKey];
[[NSUserDefaults standardUserDefaults] synchronize];
}
- (NSString *)description {
return [NSString stringWithFormat:#"%#; dict:\n%#", [super description], [self dictionaryValue]];
}
#end
Happy coding!