How to copy custom objects in Objective-C? - ios

I have two arrays. First contains custom objects. Now I want to copy all object of first array in another array. For that I am using below code.
Arays.
arr_post=[[NSMutableArray alloc]init];
copy_arr_user_post=[[NSMutableArray alloc]init];
am adding the objects into them like this.
for(i=0;i<[arr_main count];i++)
{
Post *obj=[[Post alloc]init];
obj.name=#"abc";
obj.category=#"social";
[arr_post addObject:obj];
}
Now I am copying to another array like this
[arr_post addObject:user_post];
Post *objectCopy = [user_post copy]; //create a copy of our object
[copy_arr_user_post addObject: objectCopy]; //insert copy into other array
In Post.h
#interface Post : NSObject<NSCopying>
In Post.m
- (id)copyWithZone:(NSZone *)zone
{
// Copying code here.
Post *another =[[[self class] allocWithZone:zone] init];
another.id=self.id;
another.category=self.category;
return another;
}
But it does not copy objects I get null value. Why?

One method that I find faster than NSCopyng
-Create an NSObject category with this two method
#import <objc/runtime.h>
-(id)deepCopy
{
NSArray *tmpArray = #[self];
NSData *buffer = [NSKeyedArchiver archivedDataWithRootObject:tmpArray];
return [NSKeyedUnarchiver unarchiveObjectWithData:buffer][0];
}
- (NSMutableArray *)allProperties
{
NSMutableArray *props = [NSMutableArray array];
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList([self class], &outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
//Excluding all readOnly properties
unsigned int numOfAttributes;
objc_property_attribute_t *propertyAttributes = property_copyAttributeList(property, &numOfAttributes);
BOOL foundReadonly = NO;
for ( unsigned int ai = 0; ai < numOfAttributes; ai++ )
{
switch (propertyAttributes[ai].name[0]) {
case 'T': // type
break;
case 'R': // readonly
foundReadonly = YES;
break;
case 'C': // copy
break;
case '&': // retain
break;
case 'N': // nonatomic
break;
case 'G': // custom getter
break;
case 'S': // custom setter
break;
case 'D': // dynamic
break;
default:
break;
}
}
free(propertyAttributes);
if (!foundReadonly)
{
NSString *propertyName = [[NSString alloc] initWithCString:property_getName(property) encoding:NSASCIIStringEncoding];
[props addObject:propertyName];
}
}
free(properties);
return props;
}
-Make your object conforms to NSCoding
#pragma mark - NSCoding
- (instancetype)initWithCoder:(NSCoder *)decoder {
self = [super init];
if (self)
{
NSArray *keys = [self allProperties];
for (NSString *key in keys)
{
[self setValue:[decoder decodeObjectForKey:key] forKey:key] ;
}
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
NSArray *keys = [self allProperties];
for (NSString *key in keys)
{
[aCoder encodeObject:[self valueForKey:key] forKey:key];
}
}
-Import the category
Now you are able to copy any kinds of object
MYObject *copy = [originalObject deepCopy];
NSArray *arrayWithCopiedObjects = [originalArray deepCopy];
etc....

Try
NSMutableArray * arr_post=[[NSMutableArray alloc]init];
NSMutableArray * copy_arr_user_post=[[NSMutableArray alloc]init];
for(int i=0;i<3;i++)
{
Post *obj=[[Post alloc]init];
obj.name=#"abc";
obj.category=#"social";
[arr_post addObject:obj];
}
[arr_post enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[copy_arr_user_post addObject:[obj copy]];
}];
Post * temp = [arr_post objectAtIndex:0];
temp.name = #"123";
NSLog(#"%#",arr_post);
NSLog(#"%#",copy_arr_user_post);
And log
2015-10-23 13:25:31.994 OCTest[1784:130931] (
"123 social",
"abc social",
"abc social"
)
2015-10-23 13:25:31.995 OCTest[1784:130931] (
"abc social",
"abc social",
"abc social"
)
I add description for debugging
-(NSString *)description{
return [NSString stringWithFormat:#"%# %#",self.name,self.category];
}

Related

Trying to sort a NSString using NSMutableArray

I have a NSString which holds info like this:
%hook APMIntro
- (bool)isIntroductoryOffer {
return NO;
}
%end
%hook APMIntro
- (void)setIntroductoryOffer:(bool)arg1 {
arg1 = NO;
%orig;
}
%end
%hook ChallengeProgressHolder
- (void)setHasItem:(bool)arg1 {
arg1 = NO;
%orig;
}
%end
%hook ChallengeProgressHolder
- (bool)hasItem {
return NO;
}
%end
The end result I'm trying to achieve is this
%hook APMIntro
- (bool)isIntroductoryOffer {
return NO;
}
- (void)setIntroductoryOffer:(bool)arg1 {
arg1 = NO;
%orig;
}
%end
%hook ChallengeProgressHolder
- (void)setHasItem:(bool)arg1 {
arg1 = NO;
%orig;
}
- (bool)hasItem {
return NO;
}
%end
So far, I've tried using NSMutableArray to separate the different lines to try and organize them - but the result is an infinite loop. I've tried this code so far
-(NSString *)cleanUp:(NSString *)cleanUp{
NSMutableArray *content = [[cleanUp componentsSeparatedByString:#"%hook "] mutableCopy];
// NSMutableArray *content = [[NSMutableArray alloc] init];
NSMutableArray *cleaned = [[NSMutableArray alloc] init];
NSMutableArray *hooks = [[NSMutableArray alloc] init];
// NSArray *messy = [[cleanUp componentsSeparatedByString:#"\n"] mutableCopy];
cleanUp = #"";
NSString *line, *line1;
//NSString *fixMe = #"";
for (unsigned h = 1; h < content.count; h++){
line = [content objectAtIndex:h-1];
if ([line hasPrefix:#"#import"]){
[cleaned addObject:[NSString stringWithFormat:#"%#\n", line]];
continue;
} else {
cleanUp = [NSString stringWithFormat:#"%#%%hook %#", cleanUp, line];
//[content addObject:[NSString stringWithFormat:#"%hook %#", line]];
}
}
//NSLog(#"\n\n\n%#\n\n\n", cleanUp);
BOOL lookingForEnd = NO;
BOOL hookFound = NO;
hooks = [[cleanUp componentsSeparatedByString:#"\n"] mutableCopy];
// NSString *hook;
//NSLog(#"\n\n\n%#\n\n\n", hooks);
for (unsigned i = 1; i < hooks.count; i++){
line = [hooks objectAtIndex:i-1];
//NSLog(#"%i: %#\n\n", i, line);
if (lookingForEnd) {
[cleaned addObject:[NSString stringWithFormat:#"%#\n", line]];
if ([line isEqualToString:#"%end"]){
lookingForEnd = NO;
//i = 1;
continue;
} else if ([line1 isEqualToString:line]){
lookingForEnd = YES;
}
}
if ([line hasPrefix:#"%hook"]){
line1 = line;
for (unsigned j = 1; j < hooks.count; j++){
printf("\n\n\nHOOK\n\n\n");
if ([[hooks objectAtIndex:j-1] isEqual:line1]){
printf("\n\n\nFOUND\n\n\n");
[hooks addObject:line];
[cleaned addObject:[NSString stringWithFormat:#"%#\n", line]];
lookingForEnd = YES;
hookFound = YES;
//break;
}
}
}
}
NSLog(#"%#\n", cleaned);
return cleaned;
}
After setting this up and reading further into how to sort arrays - I found that I'm going about this all wrong - I'm just not sure how to go about doing it right.
Thanks in advance - any help is greatly appreciated
You possibly could do this all with a complex RegEx -- but, here's a bit of a "step by step" approach.
Create a NSObject class with two string properties - blockName and blockBody.
We'll use this regular expression:
(^%hook )(.*?)(?=%end$)
to split the string into "pieces" of text contained between "%hook " and "%end".
Then, we'll loop through the matches creating an array of "block" objects, each with the name and body.
Next, we sort the array by blockName.
Finally, we loop through the sorted array, formatting an output string with the "bodies" grouped into the named blocks.
So, if we have:
MyBlock.h
#interface MyBlock : NSObject
#property (strong, nonatomic) NSString *blockName;
#property (strong, nonatomic) NSString *blockBody;
#end
MyBlock.m
#import "MyBlock.h"
#implementation MyBlock
#end
and we start with your sample NSString:
%hook APMIntro
- (bool)isIntroductoryOffer {
return NO;
}
%end
%hook APMIntro
- (void)setIntroductoryOffer:(bool)arg1 {
arg1 = NO;
%orig;
}
%end
%hook ChallengeProgressHolder
- (void)setHasItem:(bool)arg1 {
arg1 = NO;
%orig;
}
%end
%hook ChallengeProgressHolder
- (bool)hasItem {
return NO;
}
%end
We can do this:
NSString *input = [self sampleString];
NSMutableArray <MyBlock *> *blocks = [NSMutableArray new];
NSString *pattern = #"(^%hook )(.*?)(?=%end$)";
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:NSRegularExpressionDotMatchesLineSeparators | NSRegularExpressionAnchorsMatchLines error:NULL];
NSArray *myArray = [regex matchesInString:input options:0 range:NSMakeRange(0, [input length])] ;
for (NSTextCheckingResult *match in myArray) {
// get the range starting after "%hook "
NSRange matchRange = [match rangeAtIndex:2];
// get the "block" string
NSString *s = [input substringWithRange:matchRange];
// split it by newLines
NSArray <NSString *> *a = [s componentsSeparatedByString:#"\n"];
// we want the "body" to skip the first line
NSRange r;
r.location = 1;
r.length = [a count] - 1;
// get the lines excluding the first line
NSArray <NSString *> *a2 = [a subarrayWithRange:r];
// new MyBlock object
MyBlock *b = [MyBlock new];
b.blockName = [a firstObject];
b.blockBody = [a2 componentsJoinedByString:#"\n"];
[blocks addObject:b];
}
// sort the array of blocks by blockName
NSArray <MyBlock *> *sortedArray;
sortedArray = [blocks sortedArrayUsingComparator:^NSComparisonResult(MyBlock *a, MyBlock *b) {
return [a.blockName compare:b.blockName];
}];
NSMutableString *output = [NSMutableString new];
NSString *curName = #"";
// loop through the array of blocks
for (int i = 0; i < [sortedArray count]; i++) {
MyBlock *b = sortedArray[i];
// if we're at a "new" blockName
if (![curName isEqualToString:b.blockName]) {
curName = b.blockName;
// add %end if output is not ""
if ([output length] != 0) {
[output appendString:#"%end\n"];
}
// add a new line of %hook blockName
[output appendFormat:#"\n%%hook %#\n", curName];
}
// add the block body
[output appendFormat:#"%#", b.blockBody];
}
// "close" the last block
[output appendString:#"%end\n"];
// log the resulting string
NSLog(#"%#", output);
and the output from that sample is:
%hook APMIntro
- (bool)isIntroductoryOffer {
return NO;
}
- (void)setIntroductoryOffer:(bool)arg1 {
arg1 = NO;
%orig;
}
%end
%hook ChallengeProgressHolder
- (void)setHasItem:(bool)arg1 {
arg1 = NO;
%orig;
}
- (bool)hasItem {
return NO;
}
%end
Play with the output formatting (line breaks, spacing, etc) as desired :)

NSDictionary getting sorted alphabetically [duplicate]

This question already has answers here:
NSDictionary with ordered keys
(9 answers)
Closed 6 years ago.
I am passing NSDictionary to my function as a parameter. I want it's key and values to be in order as I inserted.
for eg. expected output is:
mutable dict:{
zKey1 = Value1;
fKey2 = Value2;
aKey3 = Value3;
}
I have tried following ways to create and set value for keys.
NSMutableDictionary *mutableDict = [[NSMutableDictionary alloc]init];
[mutableDict setObject:#"Value1" forKey:#"zKey1"];
[mutableDict setObject:#"Value2" forKey:#"fKey2"];
[mutableDict setObject:#"Value3" forKey:#"aKey3"];
NSMutableDictionary *dic2=[[NSMutableDictionary alloc]initWithObjectsAndKeys:#"1004",#"userId",#"cucumber",#"domain",#"168d5c02f ",#"token",#"1004",#"userId1",#"cucumber",#"domain1",#"168d5c02f ",#"token1", nil];
NSDictionary * dict = [NSDictionary
dictionaryWithObjects:#[#"Ravi",#"33",#"India",#"India"]
forKeys:#[#"name",#"age",#"location",#"country"]];
NSArray *sortedKeys = [[dict allKeys] sortedArrayUsingSelector: #selector(compare:)];
NSMutableArray *sortedValues = [NSMutableArray array];
for (NSString *key in sortedKeys) {
[sortedValues addObject: [dict objectForKey: key]];
}
NSString *obj1=#"1004";
NSString *obj2=#"cucumber";
NSString *obj3=#"168d5c02f";
NSString *key1=#" userId";
NSString *key2=#"domain";
NSString *key3=#"token";
NSLog(#"dict %#",dict);
NSDictionary *dict8 =[NSDictionary
dictionaryWithObjects:#[obj1,obj2,obj3]
forKeys:#[key1,key2,key3]];
But nothing has worked I am always getting output as
mutable dict:{
aKey3 = Value3;
fKey2 = Value2;
zKey1 = Value1;
}
dict8 {
domain = cucumber;
token = 168d5c02f;
userId = 1004;
}
dict {
age = 33;
country = India;
location = India;
name = Ravi;
}
dic= {
domain = cucumber;
domain1 = cucumber;
token = "168d5c02f ";
token1 = "168d5c02f ";
userId = 1004;
userId1 = 1004;
}
It is always sorting values according to alphabetical order of keys. Many people say that NSDictionary is an unsorted container. But it does gets sorted. Need help desperately. Thank you in advance.
NSDictionary is not ordered by default. It will always be without any order. To create an ordered dictionary, you will need to override the existing form of the Data structure. You can read This tutorial to achieve your end.
To summarize the tutorial (Because everyone hates link-only answers and links can die any time):
NSDictionary stores its keys in a hash table, which is unordered by design. Since this lack of order is fundamental to the hash table storeage, you have to perform subclassing of NSMutableDictionary (and hence reimplementation of the storage).
In your .h file
//
// OrderedDictionary.h
// OrderedDictionary
//
// Created by Matt Gallagher on 19/12/08.
// Copyright 2008 Matt Gallagher. All rights reserved.
//
// Permission is given to use this source code file without charge in any
// project, commercial or otherwise, entirely at your risk, with the condition
// that any redistribution (in part or whole) of source code must retain
// this copyright and permission notice. Attribution in compiled projects is
// appreciated but not required.
//
#import <Cocoa/Cocoa.h>
#interface OrderedDictionary : NSMutableDictionary
{
NSMutableDictionary *dictionary;
NSMutableArray *array;
}
- (void)insertObject:(id)anObject forKey:(id)aKey atIndex:(NSUInteger)anIndex;
- (id)keyAtIndex:(NSUInteger)anIndex;
- (NSEnumerator *)reverseKeyEnumerator;
#end
In your .m file:
//
// OrderedDictionary.m
// OrderedDictionary
//
// Created by Matt Gallagher on 19/12/08.
// Copyright 2008 Matt Gallagher. All rights reserved.
//
// Permission is given to use this source code file without charge in any
// project, commercial or otherwise, entirely at your risk, with the condition
// that any redistribution (in part or whole) of source code must retain
// this copyright and permission notice. Attribution in compiled projects is
// appreciated but not required.
//
#import "OrderedDictionary.h"
NSString *DescriptionForObject(NSObject *object, id locale, NSUInteger indent)
{
NSString *objectString;
if ([object isKindOfClass:[NSString class]])
{
objectString = (NSString *)[[object retain] autorelease];
}
else if ([object respondsToSelector:#selector(descriptionWithLocale:indent:)])
{
objectString = [(NSDictionary *)object descriptionWithLocale:locale indent:indent];
}
else if ([object respondsToSelector:#selector(descriptionWithLocale:)])
{
objectString = [(NSSet *)object descriptionWithLocale:locale];
}
else
{
objectString = [object description];
}
return objectString;
}
#implementation OrderedDictionary
- (id)init
{
return [self initWithCapacity:0];
}
- (id)initWithCapacity:(NSUInteger)capacity
{
self = [super init];
if (self != nil)
{
dictionary = [[NSMutableDictionary alloc] initWithCapacity:capacity];
array = [[NSMutableArray alloc] initWithCapacity:capacity];
}
return self;
}
- (void)dealloc
{
//This method is pre-ARC. Manual Release commands don't work now.
//[dictionary release];
//[array release];
//[super dealloc];
}
- (id)copy
{
return [self mutableCopy];
}
- (void)setObject:(id)anObject forKey:(id)aKey
{
if (![dictionary objectForKey:aKey])
{
[array addObject:aKey];
}
[dictionary setObject:anObject forKey:aKey];
}
- (void)removeObjectForKey:(id)aKey
{
[dictionary removeObjectForKey:aKey];
[array removeObject:aKey];
}
- (NSUInteger)count
{
return [dictionary count];
}
- (id)objectForKey:(id)aKey
{
return [dictionary objectForKey:aKey];
}
- (NSEnumerator *)keyEnumerator
{
return [array objectEnumerator];
}
- (NSEnumerator *)reverseKeyEnumerator
{
return [array reverseObjectEnumerator];
}
- (void)insertObject:(id)anObject forKey:(id)aKey atIndex:(NSUInteger)anIndex
{
if (![dictionary objectForKey:aKey])
{
[self removeObjectForKey:aKey];
}
[array insertObject:aKey atIndex:anIndex];
[dictionary setObject:anObject forKey:aKey];
}
- (id)keyAtIndex:(NSUInteger)anIndex
{
return [array objectAtIndex:anIndex];
}
- (NSString *)descriptionWithLocale:(id)locale indent:(NSUInteger)level
{
NSMutableString *indentString = [NSMutableString string];
NSUInteger i, count = level;
for (i = 0; i < count; i++)
{
[indentString appendFormat:#" "];
}
NSMutableString *description = [NSMutableString string];
[description appendFormat:#"%#{\n", indentString];
for (NSObject *key in self)
{
[description appendFormat:#"%# %# = %#;\n",
indentString,
DescriptionForObject(key, locale, level),
DescriptionForObject([self objectForKey:key], locale, level)];
}
[description appendFormat:#"%#}\n", indentString];
return description;
}
#end
You can Download Matt Gallagher's orderedDictionary here.

Get comma separated string for a property from a array of custom object

I have a Array of custom objects with object having following properties optionID,OptionText. I want to get comma separated string for the optionID property. What would be the best approach to do this in iOS SDK.
for example NSString CommaSeperted = #"1,3,5" etc.
Category to NSArray:
#implementation NSArray(CustomAdditions)
- (NSString *)commaSeparatedStringWithSelector:(SEL)aSelector
{
NSMutableArray *objects = [NSMutableArray array];
for (id obj in self)
{
if ([obj respondsToSelector:aSelector]) {
IMP method = [obj methodForSelector:aSelector];
id (*func)(id, SEL) = (void *)method;
id customObj = func(obj, aSelector);
if (customObj && [customObj isKindOfClass:[NSString class]]) {
[objects addObject:customObj];
}
}
}
return [objects componentsJoinedByString:#","];
}
#end
Example:
#implementation NSDictionary(Test)
- (NSString*)optionID
{
return [self objectForKey:#"optionID"];
}
- (NSString*)OptionText
{
return [self objectForKey:#"OptionText"];
}
#end
NSArray *customObjects = #[#{#"optionID": #"id1", #"OptionText": #"text1" }, #{#"optionID" : #"id2", #"OptionText": #"text2"}];//List of Your custom objects
NSString *commaSeparatedOptionIDs = [customObjects commaSeparatedStringWithSelector:NSSelectorFromString(#"optionID")];
NSString *commaSeparatedOptionTexts = [customObjects commaSeparatedStringWithSelector:NSSelectorFromString(#"OptionText")];
Try this
NSString *commaSeparatedStringOfID = #"";
for (CustomClass *object in yourArray){
commaSeparatedStringOfID = [commaSeparatedStringOfID stringByAppendingString:[NSString stringWithFormat:#"%#,"]];
}
// removing last comma
commaSeparatedStringOfID = [commaSeparatedStringOfID substringToIndex:[commaSeparatedStringOfID length]-1];
commaSeparatedStringOfID will be your required string.

class_copyIvarList leak

- (NSMutableArray *)getArrayValue:(NSArray *)array{
NSMutableArray *valueArray = [NSMutableArray array]; //value数组
for (NSObject *object in array) {
unsigned int numberofIvars = 0;
Ivar* ivars = class_copyIvarList([object class], &numberofIvars);
NSMutableArray *objectArray = [NSMutableArray array];
for(const Ivar* p = ivars; p< ivars+numberofIvars;p++){
Ivar const ivar = *p ;
NSString* key = [NSString stringWithUTF8String:ivar_getName(ivar)];
NSString *value = [object valueForKey:key];
[objectArray addObject:value];
}
[valueArray addObject:objectArray];
}
return valueArray;
}
I'm having a memory leak when using this method.
What chould I do ?
free(ivars);
return valueArray;
}
According to the documentation: "You must free the array with free()"
(https://developer.apple.com/library/mac/documentation/cocoa/reference/objcruntimeref/Reference/reference.html#//apple_ref/c/func/class_copyIvarList)

Mapping NSDictionary to NSObject subclass

I'm working on cline-server app. I'm getting JSON objects as response from the server,
then I'm converting JSON to NSDictionary. Now I need to map NSDictionary to custom data object.
So I have created BasicDataObject class that has:
#pragma mark - Inits
- (id)initWithDictionary:(NSDictionary *)dictionary {
self = [super init];
if (self) {
[self setValuesForKeysWithDictionary:dictionary];
}
return self;
}
#pragma mark - Service
- (id)valueForUndefinedKey:(NSString *)key {
NSArray *allKeys = [self allKeys];
id returnObject = nil;
BOOL keyFound = NO;
for (NSString *propertyName in allKeys) {
if ([propertyName isEqualToString:key]) {
id object = [self performSelector:NSSelectorFromString(key)];
returnObject = object ? object : [NSNull null];
keyFound = YES;
break;
}
}
if (!keyFound) {
#throw [NSException exceptionWithName:NSUndefinedKeyException reason:[NSString stringWithFormat:#"key '%#' not found", key] userInfo:nil];
}
return returnObject;
}
- (void)setValue:(id)value forUndefinedKey:(NSString *)key {
NSString *capitalizedString = [key stringByReplacingCharactersInRange:NSMakeRange(0,1)
withString:[[key substringToIndex:1] capitalizedString]];
NSString *setterString = [NSString stringWithFormat:#"set%#:", capitalizedString];
[self performSelector:NSSelectorFromString(setterString) withObject:value];
}
- (void)setNilValueForKey:(NSString *)key {
object_setInstanceVariable(self, key.UTF8String, 0);
}
- (NSArray *)allKeys {
unsigned int propertyCount = 0;
objc_property_t *properties = class_copyPropertyList(self.class, &propertyCount);
NSMutableArray *propertyNames = [NSMutableArray array];
for (unsigned int i = 0; i < propertyCount; ++i) {
objc_property_t property = properties[i];
const char *name = property_getName(property);
[propertyNames addObject:[NSString stringWithUTF8String:name]];
}
free(properties);
return propertyNames;
}
Each data object is a subclass of this class, so it could be initialised from NSDictionary.
If some data object subclass needs some custom initialisation, I'm overriding it's:
- (id)initWithDictionary:(NSDictionary *)dictionary
Is it correct/good approach, or do I need to add something more?
This is an OK way to do this. Ive found that rarely are the json keys the most appropriate names for my object's properties, so most people's code that I've seen manually sets each property on their object so they can 1) name it whatever they want 2) convert to primitive values from the json dictionary, which will always contain objects. (Eg NSNumber -> float)

Resources