How to subclass NSMutableData - ios

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

Related

How can I use and access a struct value as a property in obj C? Getting exec bad access

I have the following struct
Helper.h
typedef struct fileInfo {
UInt8 *fileHeaderContent;
UInt32 fileHeaderLength;
}
typedef struct globalFileStruct {
UInt8 *data;
UInt32 dataLength;
fileInfo fp;
}
I need to use this as a part of my singleton as follows:
#interface CommonFile : NSObject
+ (instancetype)sharedInstance;
#property (nonatomic, assign) globalFileStruct *gFileInfo;
#end
#implementation CommonFile
+ (instancetype)sharedInstance
{
static CommonFile *sharedInfo = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInfo = [[self alloc] init];
});
return sharedInfo;
}
-(void)someMethod {
CommonFile *file = [CommonFile sharedInstance];
/* BAD ACCESS ERR */
if( file.gFileInfo->fp.fileHeaderContent == NULL) {
//do something
}
}
#end
Like I pointed out in the code, I am getting a bad access error, I presume because gFileInfo is NULL.
My question is, What is the best way to handle this scenario? How can I ensure that the pointer object points to a variable that is real and not NULL?
I originally tried having the code be :
#property (nonatomic, assign) globalFileStruct gFileInfo;
However, the problem with this was when I used it in a method as follows:
file.gFileInfo->fp.fileHeaderContent = [somedata bytes]
I got the error : "Expression not assignable"
file.gFileInfo->fp.fileHeaderContent = [somedata bytes]
I got the error : "Expression not assignable"
Construct the whole struct as a local variable, replace the field and assign it to property. Structs are quite heavyweight to be passed as arguments by value though.

Xcode 6: Why this code doesn't compile now?

My code was compiling and running great until I upgraded to Xcode 6.
Definition shows a Warning : Auto property synthesis will not synthesize property 'hash' because it is 'readwrite' but it will be synthesized 'readonly' via another property
#property (nonatomic, strong) NSString *hash; // (get/compute) hash code of the place (master hash of images)
Implementation shows error whenever I access to _hash: Use of undeclared identifier '_hash'
-(NSString *)hash {
if (_hash) return _hash;
// If place id, take it as the hash code
NSString *poiID = self.info[#"id"];
if (poiID) {
_hash = [NSString stringWithFormat:#"id-%lu",(unsigned long)[self.address hash]];
}
else if (CLLocationCoordinate2DIsValid(self.location.coordinate)) {
NSString *seed = [NSString stringWithFormat:#"%f,%f", self.location.coordinate.latitude, self.location.coordinate.longitude];
_hash = [NSString stringWithFormat:#"location-%lu",(unsigned long)[seed hash]];
}
else if (self.address) {
NSString *seed = self.address;
_hash = [NSString stringWithFormat:#"address-%lu",(unsigned long)[seed hash]];
}
else {
_hash = #"POI-unknownIDLocationOrAddress";
}
return _hash;
}
It doesn't compile because hash is already part of NSObject:
See:
https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Protocols/NSObject_Protocol/index.html#//apple_ref/occ/intfm/NSObject/hash
You need to add the following line to auto generate the setter method:
#synthesize hash = _hash;
If you don't want a setter method and only want a read-only property:
#property (nonatomic, strong, readonly) NSString *hash;

detect the class of a property with name in objective-c [duplicate]

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.

Generate MD5 hash from Objective-C object [duplicate]

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.

Usage of the "copy" property attribute to maintain an immutable NSString

I am very new to iOS development and programming in Objective-C. I have been doing the exercises on the app dev library.
This is the current exercise that I am trying to understand.
3. Test what happens if you set a mutable string as the person’s first name, then mutate that string before calling your modified sayHello method. Change the NSString property declarations by adding the copy attribute and test again.
I attempt to do this however, the NSString that I modify does in fact change despite the use of the copy property attribute.
Here are my declarations and implementations as well as my test code.
XYZPerson.h
#import <Foundation/Foundation.h>
#interface XYZPerson : NSObject
#property (copy) NSString *firstName;
#property NSString *lastName;
#property NSDate *dob;
- (void)sayHello;
- (void)saySomething:(NSString *)greeting;
+ (id)init;
+ (id)personWithFirstName:(NSString *)firstName lastName:(NSString *)lastName dob:(NSDate *)dateOfBirth;
#end
//XYZPerson.m
#import "XYZPerson.h"
#implementation XYZPerson
#synthesize firstName = _firstName;
#synthesize lastName = _lastName;
#synthesize dob = _dob;
- (void)sayHello {
[self saySomething:#"Hello World!"];
NSLog(#"This is %# %#", self.firstName, self.lastName);
}
- (void)saySomething:(NSString *)greeting {
NSLog(#"%#", greeting);
}
+ (id)init {
return [self personWithFirstName:#"Yorick" lastName:#"Robinson" dob:8/23/1990];
}
+ (id)personWithFirstName:(NSString *)firstName lastName:(NSString *)lastName dob:(NSDate *)dateOfBirth{
XYZPerson *person = [[self alloc] init];
person.firstName = firstName;
person.lastName = lastName;
person.dob = dateOfBirth;
return person;
}
#end
//Test code
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import "XYZPerson.h"
#import "XYZShoutingPerson.h"
int main(int argc, char *argv[])
{
#autoreleasepool {
XYZPerson *guy = [XYZPerson init];
[guy sayHello];
//I thought that this change would never be made, but it is everytime I run the code.
guy.firstName = #"Darryl";
[guy sayHello];
XYZShoutingPerson *girl = [XYZShoutingPerson init];
[girl sayHello];
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
Consider this shorter example (which runs in CodeRunner btw):
#import <Foundation/Foundation.h>
#interface Person : NSObject
#property (nonatomic,strong) NSString *name; // strong should be copy
#end
#implementation Person
#end
int main(int argc, char *argv[]) {
#autoreleasepool {
Person *p = [Person new];
NSMutableString *name = [[NSMutableString alloc] initWithString:#"Alice"];
p.name = name;
NSLog(#"%#",p.name); // prints Alice
[name appendString:#"xxx"];
NSLog(#"%#",p.name); // prints Alicexxx
}
}
I'm pointing the name to a mutable string, then appending some characters. As a result, the name has changed inside the object. However, if you replace strong with copy when declaring the property, a new immutable string will be created just for the Person object.
The moral of the story is, using copy prevents side effects when someone passes an object and then that object changes.
The message -[NSString copy] results in a copy when a mutable string is passed (NSMutableString) or retain when it is immutable (NSString). Therefore, always copy when declaring NSString properties:
#property (nonatomic,copy) NSString *string; // OK
#property (nonatomic,strong) NSString *string; // strong should be copy
I ran into this problem when I was doing the same book. I added copy and the exact same thing happened, it kept mutating when I appending something to the NSMutableString variable that I used for firstName. Then I read this section:
If you need to set a copy property’s instance variable directly, for example in an initializer method, don’t forget to set a copy of the original object:
-(id)initWithSomeOriginalString:(NSString *)aString {
self = [super init];
if (self) {
_instanceVariableForCopyProperty = [aString copy];
}
return self;
}
So, I went back into my XYZPerson.m and looked at my init code.
I changed:
- (id)initWithFirstName:(NSMutableString *)aFirstName lastName:(NSString *)aLastName
dateOfBirth:(NSDate *)aDate {
self = [super init];
if (self) {
_firstName = aFirstName;
_lastName = aLastName;
_dateOfBirth = aDate;
}
return self;
}
To:
- (id)initWithFirstName:(NSMutableString *)aFirstName lastName:(NSString *)aLastName
dateOfBirth:(NSDate *)aDate {
self = [super init];
if (self) {
_firstName = [aFirstName copy];
_lastName = aLastName;
_dateOfBirth = aDate;
}
return self;
}
And presto-chango: it worked the correct way! It made a copy of the NSMutableString that I had used that did not mutate when I appended something to the end of it before the method call.
I think you are misunderstanding of what copy does.
NSMutableString *string = [NSMutableString stringWithString:#"test"];
XYZPerson *guy = [XYZPerson init];
guy.firstName = string;
guy.lastName = string;
[string replaceCharactersInRange:NSMakeRange(1, 1) withString:#"x"];
[guy sayHello];
Output
This is test txst
In this example, firstName is copy do it doesn't change when string is changed, lastName is not copy so it value is changed when the mutable string string is changed.
What happened here is lastName and string are the same object, so when string is changed lastName is changed as a side effect. This is considered very bad and you never want this behavior. Using copy makes sure firstName and string are different objects and changes to string cannot effect firstName.

Resources