This question already has answers here:
MD5 algorithm in Objective-C
(5 answers)
Closed 9 years ago.
I'd like to generate an MD5 hash for an NSObject:
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) NSString * type;
#property (nonatomic, retain) NSString * unit;
#property (nonatomic, retain) NSArray * fields;
What is the best way to do so? I've seen examples for hashing from a dictionary or an array, but not from an entire NSObject.
To generate a MD5 hash for an NSObject or a subclass of NSObject, you need to convert it into something that's easily hashable but still represents the state of the instance. A JSON string is one such option. The code looks like this:
Model.h
#import <Foundation/Foundation.h>
#interface Model : NSObject
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) NSString * type;
#property (nonatomic, retain) NSString * unit;
#property (nonatomic, retain) NSArray * fields;
- (NSString *)md5Hash;
#end
Model.m
#import <CommonCrypto/CommonDigest.h>
#import "Model.h"
#implementation Model
- (NSString *)md5Hash
{
// Serialize this Model instance as a JSON string
NSDictionary *map = #{ #"name": self.name, #"type": self.type,
#"unit": self.unit, #"fields": self.fields };
NSError *error = NULL;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:map
options:NSJSONWritingPrettyPrinted
error:&error];
if (error != nil) {
NSLog(#"Serialization Error: %#", error);
return nil;
}
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
// Now create the MD5 hashs
const char *ptr = [jsonString UTF8String];
unsigned char md5Buffer[CC_MD5_DIGEST_LENGTH];
CC_MD5(ptr, strlen(ptr), md5Buffer);
NSMutableString *output = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
for(int i = 0; i < CC_MD5_DIGEST_LENGTH; i++)
[output appendFormat:#"%02x",md5Buffer[i]];
return output;
}
#end
Then you can easily retrieve the MD5 hash just by calling the md5Hash method
Model *obj = [Model new];
obj.name = #"...";
obj.type = #"...";
obj.unit = #"...";
obj.fields = #[ ... ];
NSString *hashValue = [obj md5Hash];
You can convert the object into a dictionary if you already have code for creating the hash:
NSDictionary *dict = [myObject dictionaryWithValuesForKeys:#[#"name", #"type", #"unit", #"fields"]];
Or you could implement <NSCoding> on your class, archive it and hash the resulting data.
Related
I have a list of Emoji entities and each of the has property codes and I want to check string ("]:-)") if it contains any of them and then replace smile with an image.
for (Emoji *emoji in self.emojis) {
for (NSString *code in emoji.codes) {
NSString *pattern = [NSRegularExpression escapedPatternForString:code];
NSRegularExpression *regex = [[NSRegularExpression alloc] initWithPattern:pattern options:0 error:nil];
NSArray *matches = [regex matchesInString:[sourceString string] options:0 range:NSMakeRange(0, [sourceString length])];
[matches enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(NSTextCheckingResult * _Nonnull aResult, NSUInteger idx, BOOL * _Nonnull stop) {
NSTextAttachment *attachment = [[NSTextAttachment alloc] init];
[attachment setImage:[UIImage imageNamed:emoji.image]];
NSAttributedString *replacement = [NSAttributedString attributedStringWithAttachment:attachment];
[sourceString replaceCharactersInRange:[aResult range] withAttributedString:replacement];
}];
}
}
The problem is that smile with code ]:-) contains :-) and my method replacing it with next: bracket ] + [image] for :-), it is because :-) goes first in list.
How can I check for exact string?
I've tried:
]:-/), \\b]:-/)\\b, /^]:-/)$/
Maybe there is better solution to make this working.
If I understood correctly your current structure:
#interface Emoji : NSObject
#property (nonatomic, strong) NSString *image; //I'd expect a UIImage there and not an image name
#property (nonatomic, strong) NSArray *codes;
#end
A possible solution is instead to use a single couple of values: imageName/code
#interface Emoji : NSObject
#property (nonatomic, strong) NSString *image; //I'd expect a UIImage there and not an image name
#property (nonatomic, strong) NSString *code;
#end
self.emojis will have plenty of Emoji object that may have the same image name for different code, but an advantage of doing that is this little trick:
Sort self.emojis in a way that "smaller" emojis are at the end. So you'll replace first only the "lengthy" ones and the the smaller ones.
self.emojis = [arrayOfSingleEmojis sortedArrayUsingComparator:^NSComparisonResult(Emoji * _Nonnull emoji1, Emoji * _Nonnull emoji2) {
NSUInteger length1 = [[emoji1 code] length];
NSUInteger length2 = [[emoji2 code] length];
return [#(length2) compare:#(length1)]; //Or reverse length1 & length2, I never know, I always have to test, but I think it's the correct one
}];
So in your current case: ]:-) will be replace before :-) so you should have <imageFor:">:-)"] instead of ]<imageFor:":-)>
I am trying to subclass NSMutableData to add the ability to subdata without copying. Here is code
#interface myMutableData : NSMutableData
- (NSData *)subdataWithNoCopyingAtRange:(NSRange)range;
#end
#interface myMutableData()
#property (nonatomic, strong) NSData *parent;
#end
#implementation myMutableData
- (NSData *)subdataWithNoCopyingAtRange:(NSRange)range
{
unsigned char *dataPtr = (unsigned char *)[self bytes] + range.location;
myMutableData *data = [[myMutableData alloc] initWithBytesNoCopy:dataPtr length:range.length freeWhenDone:NO];
data.parent = self;
return data;
}
#end
But the problem is when I try to instantiate myMutableData, I got this error
"-initWithCapacity: only defined for abstract class. Define -[myMutableData initWithCapacity:]!'"
Why? So inheritance does not work? Thanks
NSData and NSMutableData are part of a class cluster. That means you need to do more work when subclassing to ensure that your subclass is fully valid.
In other words, don't subclass...
It's much easier for you to do what you want using a category, a wrapper or a helper / utility class. The best option is probably a wrapper which can return either the internal data directly or a specified range of the data.
This calls for a category. However, a category cannot by default have properties and instance variables. Hence you need to #import <objc/runtime.h> and use associated objects to get and set value of parent.
#interface NSMutableData(myMutableData)
- (NSData *)subdataWithNoCopyingAtRange:(NSRange)range;
#property (nonatomic, strong) NSData *parent;
#end
#implementation NSMutableData(myMutableData)
- (NSData *)subdataWithNoCopyingAtRange:(NSRange)range
{
unsigned char *dataPtr = (unsigned char *)[self bytes] + range.location;
NSMutableData *data = [[NSMutableData alloc] initWithBytesNoCopy:dataPtr length:range.length freeWhenDone:NO];
data.parent = self;
return data;
}
-(NSData*)parent
{
return objc_getAssociatedObject(self, #selector(parent));
}
-(void)setParent:(NSData *)parent
{
objc_setAssociatedObject(self, #selector(parent), parent, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
#end
This question already has answers here:
property type or class using reflection
(2 answers)
Closed 9 years ago.
I have an NSObject in objective-c at runtime and i want to know the class of a property in this object , i have the name of this property as NSString , how can I do that.
EDIT :
IntrospectionUtility class :
#implementation IntrospectionUtility
// this function returns an array of names of properties
+ (NSMutableArray*) getProperties:(Class)class
{
NSMutableArray *properties = [[NSMutableArray alloc] init];
unsigned int outCount, i;
objc_property_t *objc_properties = class_copyPropertyList(class, &outCount);
for(i = 0; i < outCount; i++) {
objc_property_t property = objc_properties[i];
const char *propName = property_getName(property);
if(propName) {
NSString *propertyName = [NSString stringWithCString:propName encoding:[NSString defaultCStringEncoding]];
[properties addObject:propertyName];
}
}
free(objc_properties);
return properties;
}
#end
class test :
#interface JustAnExample : NSObject
#property (nonatomic, strong) NSString *a;
#property (nonatomic, strong) NSString *b;
#property (nonatomic, strong) NSString *c;
#property (nonatomic, strong) NSString *d;
#end
#implementation JustAnExample
- (void) justAnExampleTest
{
NSMutableArray *attributes = [IntrospectionUtility getProperties:self.class];
for (NSString *attribute in attributes) {
//i want to know the type of each attributte
}
}
#end
i have the name of this property as NSString
You can use the function class_getProperty(Class cls, const char *name) to find the property for a given class. Then use property_getAttributes(objc_property_t property) to get the property's attributes, including the encoded type string. Read the Declared Properties section of the Objective-C Runtime Programming Guide for more info.
I want to write the method which should create for writing a plist file. I got the example code in the Web but can not understand what is wrong with it. First of all, when I try to call this method - I get a message in log:
2013-03-28 15:33:47.953 ECom[6680:c07] Property list invalid for format: 100 (property lists cannot contain NULL)
2013-03-28 15:33:47.954 ECom[6680:c07] An error has occures <ECOMDataController: 0x714e0d0>
Than why does this line return (null)?
data = [NSPropertyListSerialization dataWithPropertyList:plistData format:NSPropertyListXMLFormat_v1_0 options:nil error:&err];
and the last question - how to remove the warning message for the same line?
Incompatible pointer to integer conversion sending 'void *' to parameter of type 'NSPropertyListWriteOptions' (aka 'insigned int')
h-file
#import <Foundation/Foundation.h>
#interface ECOMDataController : NSObject
{
CFStringRef trees[3];
CFArrayRef treeArray;
CFDataRef xmlValues;
BOOL fileStatus;
CFURLRef fileURL;
SInt32 errNbr;
CFPropertyListRef plist;
CFStringRef errStr;
}
#property(nonatomic, retain) NSMutableDictionary * rootElement;
#property(nonatomic, retain) NSMutableDictionary * continentElement;
#property(nonatomic, strong) NSString * name;
#property(nonatomic, strong) NSString * country;
#property(nonatomic, strong) NSArray * elementList;
#property(nonatomic, strong) id plistData;
#property(nonatomic, strong) NSString * plistPath;
#property(nonatomic, strong) NSData * data;
#property(nonatomic, strong) id filePathObj;
-(void)CreateAppPlist;
#end
m-file
#import "ECOMDataController.h"
#implementation ECOMDataController
#synthesize rootElement, continentElement, country, name, elementList, plistData, data, plistPath;
- (void)CreateAppPlist {
// Get path of data.plist file to be created
plistPath = [[NSBundle mainBundle] pathForResource:#"data" ofType:#"plist"];
// Create the data structure
rootElement = [NSMutableDictionary dictionaryWithCapacity:3];
NSError *err;
name = #"North America";
country = #"United States";
continentElement = [NSMutableDictionary dictionaryWithObjects:[NSArray arrayWithObjects:name, country, nil] forKeys:[NSArray arrayWithObjects:#"Name", #"Country", nil]];
[rootElement setObject:continentElement forKey:#""];
//Create plist file and serialize XML
data = [NSPropertyListSerialization dataWithPropertyList:plistData format:NSPropertyListXMLFormat_v1_0 options:nil error:&err];
if(data)
{
[data writeToFile:plistPath atomically:YES];
} else {
NSLog(#"An error has occures %#", err);
}
NSLog(#"%# %# %#", plistPath, rootElement, data);
}
#end
It seems that you are serializing the wrong element, replace plistData by rootElement (and nil by 0) in
data = [NSPropertyListSerialization dataWithPropertyList:plistData format:NSPropertyListXMLFormat_v1_0 options:nil error:&err];
Problem
I have a set of placemark information (country, city, etc) and a Lat/Lon pair. I would like to use this to create an MKPlacemark object.
Discussion
It appears that this class can only be created by
- (id)initWithCoordinate:(CLLocationCoordinate2D)coordinate addressDictionary:(NSDictionary *)addressDictionary
whose docs state states
You can create placemark objects manually for entities for which you already have address information, such as contacts in the Address Book. Creating a placemark object explicitly avoids the need to query the reverse geocoder object for the same information.
Perfect! I have already reverse-geocoded and wish to avoid such a query. What can I add to the dictionary?
For a list of strings that you can use for the keys of this dictionary, see the “Address Property” constants in ABPerson Reference. All of the keys in should be at the top level of the dictionary.
Which shows relevant keys
const ABPropertyID kABPersonAddressProperty;
const CFStringRef kABPersonAddressStreetKey;
const CFStringRef kABPersonAddressCityKey;
const CFStringRef kABPersonAddressStateKey;
const CFStringRef kABPersonAddressZIPKey;
const CFStringRef kABPersonAddressCountryKey;
const CFStringRef kABPersonAddressCountryCodeKey;
This falls quite short of the base traits for an MKPlacemark:
Accessing the Location Data
location property
Accessing the Placemark Attributes
name property
addressDictionary property
ISOcountryCode property
country property
postalCode property
administrativeArea property
subAdministrativeArea property
locality property
subLocality property
thoroughfare property
subThoroughfare property
region property
Accessing Geographic Information
inlandWater property
ocean property
Accessing Landmark Information
areasOfInterest property
Fortunately, the actual header file for MKPlacemark's superclass says something about the address dictionary:
// address dictionary properties
#property (nonatomic, readonly) NSString *name; // eg. Apple Inc.
#property (nonatomic, readonly) NSString *thoroughfare; // street address, eg. 1 Infinite Loop
#property (nonatomic, readonly) NSString *subThoroughfare; // eg. 1
#property (nonatomic, readonly) NSString *locality; // city, eg. Cupertino
#property (nonatomic, readonly) NSString *subLocality; // neighborhood, common name, eg. Mission District
#property (nonatomic, readonly) NSString *administrativeArea; // state, eg. CA
#property (nonatomic, readonly) NSString *subAdministrativeArea; // county, eg. Santa Clara
#property (nonatomic, readonly) NSString *postalCode; // zip code, eg. 95014
#property (nonatomic, readonly) NSString *ISOcountryCode; // eg. US
#property (nonatomic, readonly) NSString *country; // eg. United States
#property (nonatomic, readonly) NSString *inlandWater; // eg. Lake Tahoe
#property (nonatomic, readonly) NSString *ocean; // eg. Pacific Ocean
#property (nonatomic, readonly) NSArray *areasOfInterest; // eg. Golden Gate Park
So, I create a dictionary and then pass it like so:
return [[[MKPlacemark alloc] initWithCoordinate:aLocation.coordinate addressDictionary:addressDictionary] autorelease];
Unfortunately, after all that, introspection shows that the information did not stick:
NSLog(#"placemark %# from %#", placemark, addressDictionary);
NSLog(#"has %#", placemark.thoroughfare);
Prints
2012-01-31 20:14:22.545 [15450:1403] placemark <+___,-___> +/- 0.00m from {
administrativeArea = __;
postalCode = _____;
subAdministrativeArea = ___;
subThoroughfare = __;
thoroughfare = "_____";
}
2012-01-31 20:14:22.545[15450:1403] has (null)
Conclusion
So, I'm about at the end here. Has anyone figured out how to create your own MKPlacemark? Thanks.
You can subclass MKPlacemark:
In MyPlacemark.h
#interface MyPlacemark : MKPlacemark
extern NSString * const kCustomPlacemarkAddressThoroughfareKey;
extern NSString * const kCustomPlacemarkAddressSubThoroughfareKey;
extern NSString * const kCustomPlacemarkAddressLocalityKey;
extern NSString * const kCustomPlacemarkAddressSubLocalityKey;
extern NSString * const kCustomPlacemarkAddressAdministrativeAreaKey;
extern NSString * const kCustomPlacemarkAddressSubAdministrativeAreaKey;
extern NSString * const kCustomPlacemarkAddressPostalCodeKey;
extern NSString * const kCustomPlacemarkAddressCountryKey;
extern NSString * const kCustomPlacemarkAddressCountryCodeKey;
#end
In MyPlacemark.m:
#import "MyPlacemark.h"
#implementation MyPlacemark
NSString * const kCustomPlacemarkAddressThoroughfareKey = #"thoroughfare";
NSString * const kCustomPlacemarkAddressSubThoroughfareKey = #"subThoroughfare";
NSString * const kCustomPlacemarkAddressLocalityKey = #"locality";
NSString * const kCustomPlacemarkAddressSubLocalityKey = #"subLocality";
NSString * const kCustomPlacemarkAddressAdministrativeAreaKey = #"administrativeArea";
NSString * const kCustomPlacemarkAddressSubAdministrativeAreaKey = #"subAdministrativeArea";
NSString * const kCustomPlacemarkAddressPostalCodeKey = #"postalCode";
NSString * const kCustomPlacemarkAddressCountryKey = #"country";
NSString * const kCustomPlacemarkAddressCountryCodeKey = #"countryCode";
- (NSString *)thoroughfare
{
return [self.addressDictionary objectForKey:kCustomPlacemarkAddressThoroughfareKey];
}
- (NSString *)subThoroughfare
{
return [self.addressDictionary objectForKey:kCustomPlacemarkAddressSubThoroughfareKey];
}
- (NSString *)locality
{
return [self.addressDictionary objectForKey:kCustomPlacemarkAddressLocalityKey];
}
- (NSString *)subLocality
{
return [self.addressDictionary objectForKey:kCustomPlacemarkAddressSubLocalityKey];
}
- (NSString *)administrativeArea
{
return [self.addressDictionary objectForKey:kCustomPlacemarkAddressAdministrativeAreaKey];
}
- (NSString *)subAdministrativeArea
{
return [self.addressDictionary objectForKey:kCustomPlacemarkAddressSubAdministrativeAreaKey];
}
- (NSString *)postalCode
{
return [self.addressDictionary objectForKey:kCustomPlacemarkAddressPostalCodeKey];
}
- (NSString *)country
{
return [self.addressDictionary objectForKey:kCustomPlacemarkAddressCountryKey];
}
- (NSString *)countryCode
{
return [self.addressDictionary objectForKey:kCustomPlacemarkAddressCountryCodeKey];
}
#end
It looks ugly, but it's the only way so far that I've found to work.