Create NSMutableArray dynamically, and use selector with parameters on objects - ios

I'm creating an application, in which i used an NSMutableArray to stock some objects. To do this, no problem in first look :
ArrayOfViews = [[NSMutableArray alloc] init];
[ArrayOfViews addObject:...];
But my object are UIViews, that i create. For example, I have a file named "Level1". How could I add an object from "Level1", like :
Level1 * level1view;
decalered in the same UIViewcontroller from my NSMuttableArray ?
Like a sort of :
for (i = 0, i < max, i++)
{
[ArrayOfViews addObject:[objectWithName:[NSString stringWithFormat:#"level%iview", i]]];
}
I don't know how could I wrote it with good encoding.
Second, to use the objects with a selector and parameters, how could i do ?
Because i tried :
NSString * futureSelector = [NSString stringWithFormat:#"Level%iappearswithTime:", number];
SEL s = NSSelectorFromString(futureSelector);
NSInvocation * invoc = [[NSInvocation alloc] init];
[invoc setSelector:s];
[invoc setArgument:&t atIndex:1];
[invoc setTarget:[ArrayOfViews objectAtIndex:number]];
[invoc invoke];
To replace this sort of code :
[level1view Level1appearswithTime:t];
where variable t is an NSTimeInterval
Thanks for your help !

Since variable names are gone during compilation, you essentially can't do this (unless, of course, the variables are instance variables in which case their name is preserved, but then you still don't want to do it, I'm sure.)
As to the selector-and-string-problem: why not use NSStringFromSelector(), NSSelectorFromString() and - [NSObject performSelector:withObject:]?
SEL s = NSSelectorFromString([selectorsArray objectAtIndex:0]);
[someObject performSelector:s withObject:42];

Related

iOS Method Return Multiple Values NSDictionary

I have a method that I call that calculates the Sunrise, Noon and Sunset for any given day. I pass the method the day date as a Julian.
The method need to return the three numbers or strings: Sunrise, Noon and Sunset.
I am trying to call it as follows:
ClassSolarCalculations *LINK = [[ClassSolarCalculations alloc] init];
NSString dateSunrise= [[NSString alloc] initWithFormat:#"%f", [LINK CalculateSunrise: Julian]];
where the Method reads:
(NSDictionary *) CalculateSunrise: (double) Julian;
NSDictionary *returnTimes = [NSDictionary initWithObjectsAndKeys: SunriseText, #"Sunrise", NoonText, "#Noon", SunsetText, #"Sunset", nil];
return returnTimes;
I can this approach to work to return a single value but would like to return all three in one go rather than fudge the solution by calling variants of the routine three times…
Lots of things should be changed here:
method and variable names should start with lowercase letters and use camel case.
Rename your CalculateSunrise: method since it will return more values. Maybe calculateSunTimes:.
Since your method returns an NSDictionary you handling of the return needs to be different.
Try this:
ClassSolarCalculations *link = [[ClassSolarCalculations alloc] init];
NSDictionary *times = [link calculateSunTimes:julian];
NSString *sunrise = times[#"sunrise"];
NSString *noon = times[#"noon"];
NSString *sunset = times[#"sunset"];
Your method would be something like:
- (NSDictionary *)calculateSunTimes:(double)julian {
// calculate the three values:
return #{ #"sunrise" : sunriseText, #"sunset" : sunsetText, #"noon" : noonText };
}
Notice the use of modern Objective-C syntax.

EXC_BAD_ACCESS on performSelectorInBackground

I want to call this method in background,
-(void)downloadImage_3:(NSString* )Path AtIndex:(int)i
I am calling in this way but it crashes and shows EXC_BAD_ACCESS
[self performSelectorInBackground:#selector(downloadImage_3:AtIndex:) withObject:[NSArray arrayWithObjects:#"http://www.google.com",i, nil]];
How to call downloadImage_3: method in background ?
where i am doing mistake ?
try this
[self performSelectorInBackground:#selector(downloadImage_3:AtIndex:) withObject:[NSArray arrayWithObjects:#"http://www.google.com",i, nil] afterDelay:15.0];
Or try this
NSString* number = [NSString stringWithFormat:#"%d",i];
NSArray* arrayValues = [[NSArray alloc] initWithObjects:[[msg_array objectAtIndex:i] valueForKey:#"Merchant_SmallImage"],number, nil];
NSArray* arrayKeys = [[NSArray alloc] initWithObjects:#"Path",#"Index",nil];
NSDictionary* dic = [[NSDictionary alloc] initWithObjects:arrayValues forKeys:arrayKeys];
[self performSelectorInBackground:#selector(downloadImage_3:) withObject:dic];
Define downloadImage_3 function like this:
-(void)downloadImage_3:(NSDictionary *)dic
{
NSString *path = [dic valueForKey:#"Path"];
int i = [[dic valueForKey:#"Index"] intValue];
//Your code
}
You can not call parameterized function in #selector. Create NSDictionary and then pass that dictionary in withObject:
int atIndex = 2; // whatever index you want to pass
NSArray *arr = [NSArray arrayWithObjects:obj1,obj2,nil];
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:atIndex],#"AtIndex",arr,#"ImagesUrl", nil];
[self performSelectorInBackground:#selector(downloadImage_3:) withObject:dictionary];
Define downloadImage_3 function like this:
-(void)downloadImage_3:(NSDictionary *)dic {
// use dic and write your code
}
You can't use performSelectorInBackground:withObject: for selectors that take multiple arguments. The suggestions other answers give sort of work, but they all assume you can manipulate the method you are calling, which is not always possible (or even a good idea for clarity).
I recommend using NSInvocation instead, as it allows multiple arguments, or alternatively using GCD to dispatch a block to a background queue (or any queue other than the main for that matter).
Here's an example usage of NSInvocation
NSMethodSignature *sig = [[self class] instanceMethodSignatureForSelector:#selector(downloadImage_3:AtIndex:)];
NSInvocation *inv = [NSInvocation invocationWithMethodSignature:sig];
[inv setTarget:self];
[inv setSelector:#selector(downloadImage_3:AtIndex:)];
[inv setArgument:(__bridge void *)#"http://www.google.com" atIndex:2];
NSUInteger i = 1;
[inv setArgument:&i atIndex:3];
[inv performSelectorInBackground:#selector(invoke) withObject:nil];
It's worth double checking, I wrote the code in the browser so I might've missed something the compiler will pick up.
As additional note, you should really re-think your method naming conventions, a much more standard way to name methods would have the method be named -(void)downloadImage3:(NSString* )path atIndex:(int)i. Notice how atIndex begins with lowercase and how there is no underscore (which just looks weird in the middle of a method name). Also it might be worth noting that using NSUInteger is preferred for the index, as NSArray in general works with NSUIntegers (both should work, but there are cases in which int might not suffice).

is there way to replace the Core Data implementation of add and remove methods for relationships in a generic way?

PLEASE NOTE: This is not the same question as this question, as I already know that this stems from an Apple bug. Mind you, overriding the specific methods (say, -addFriendsObject: if I had a friends relationship) is not an option, since I need to do this in a category so it works for any managed object and regardless of modifying the model and rebuildiong the autogenerated classes from it.
The need for this stems from the fact that apparently the minute one makes relationships ordered and to-many (NSMutableOrderedSets) Core Data dynamic methods go to hell:
methods -add<Relationship>Object, -add<Relationship>, -remove<Relationship>Object and -remove<Relationship> will all crash with an exception alike 'NSInvalidArgumentException', reason: '*** -[NSSet intersectsSet:]: set argument is not an NSSet'
methods along the lines of insertObject:in<Relationship>AtIndex: will instead crash because no implementation for them was actually provided by CoreData.
As far as 2, I wrote a category that overrides -methodSignatureForSelector: and -forwardInvocation to instead do something like mutableOrderedSetValueForKey for the relationship name followed by the actual adding or removing.
Now for 1, the problem is that CoreData is actually providing implementations for those methods (though they are not the right implementations for ordered sets). So I need a way of intercepting those selectors too, so I can implement the behavior over mutableOrderedSetValueForKey.
Any ideas how to pull it off?
I don't know that this is the best approach (it's certainly not the prettiest thing ever), but it gets the job done:
#import "NSManagedObject+OrderedSets.h"
#import <objc/runtime.h>
#implementation NSManagedObject (OrderedSets)
+ (void)swizzleMethod:(SEL)originalSelector with:(SEL)replacementSelector
{
const char *methodTypeEncoding = method_getTypeEncoding(class_getInstanceMethod([self class], originalSelector));
class_replaceMethod(self,
originalSelector,
class_getMethodImplementation(self, replacementSelector),
methodTypeEncoding);
}
+ (void)initialize
{
NSEntityDescription *selfEntity = // get a hold of your NSManagedObjectContext and from its entities grab model.entitiesByName[NSStringFromClass(self)] ...
for (NSString *rKey in [selfEntity relationshipsByName]) {
NSRelationshipDescription *r = selfEntity.relationshipsByName[rKey];
if (r.isOrdered) {
NSString *rKeyFirstCaps = [[rKey substringToIndex:1] capitalizedString];
NSString *capitalizedKey = [rKey stringByReplacingCharactersInRange:NSMakeRange(0,1) withString:rKeyFirstCaps];
NSString *addSelectorString = [NSString stringWithFormat:#"add%#Object:", capitalizedKey];
NSString *removeSelectorString = [NSString stringWithFormat:#"remove%#Object:", capitalizedKey];
[self swizzleMethod:NSSelectorFromString(addSelectorString) with:#selector(addOrRemoveObjectInOrderedSet:)];
[self swizzleMethod:NSSelectorFromString(removeSelectorString) with:#selector(addOrRemoveObjectInOrderedSet:)];
}
}
}
- (void)addOrRemoveObjectInOrderedSet:(NSManagedObject*)object
{
NSString *selectorString = NSStringFromSelector(_cmd);
NSString *prefix = [selectorString hasPrefix:#"add"] ? #"add" : #"remove";
selectorString = [selectorString stringByReplacingCharactersInRange:[selectorString rangeOfString:prefix] withString:#""];
selectorString = [selectorString stringByReplacingCharactersInRange:[selectorString rangeOfString:#"Object" options:NSBackwardsSearch] withString:#""];
selectorString = [selectorString substringToIndex:selectorString.length - 1];
NSString *selectorFirstLowerCase = [[selectorString substringToIndex:1] lowercaseString];
NSString *camelCasedKey = [selectorString stringByReplacingCharactersInRange:NSMakeRange(0,1) withString:selectorFirstLowerCase];
if ([prefix isEqualToString:#"add"]) {
[[self mutableOrderedSetValueForKey:camelCasedKey] addObject:object];
}
else {
[[self mutableOrderedSetValueForKey:camelCasedKey] removeObject:object];
}
}
#end

Objective C, what [NSString initWithFormat] exactly does?

I just would like to know the difference between the line 1 and 2 bellow:
_subtitle = #"Test"; //Line 1
_subtitle = [NSString stringWithFormat: #"Test"]; //Line 2
If I asked that question, it is because I got a problem by using MKAnnotation. In the method bellow, I try to update the subtitle delegate property of MKAnnotation (which is nonatomic, copy and readonly). But it's look like that I got a zombie when using the line 2 and nothing when using the line 1. So my question is why?
- (void) initCoordinateWithAddress:(NSString*)address;
{
self.address = address;
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder geocodeAddressString: address completionHandler:^(NSArray *placemarks,NSError *error)
{
CLPlacemark *place = [placemarks objectAtIndex:0];
_coordinate = place.location.coordinate;
_title = self.address;
_subtitle = #"Test"; //Line 1: don't crash
_subtitle = [NSString stringWithFormat: #"Test"]; //Line 2: crash
//_subtitle = [[NSString stringWithFormat: #"[%.2f,%.2f]", self.coordinate.latitude, self.coordinate.longitude] copy];
_isInit = YES;
[self.customDelegate didCalculateCoordinate: place.location.coordinate forAnnotation: self];
}];
}
I actually already fixed my problem by using the method copy, but I still not understand what is the difference between the line 1 and 2, if anyone can help me to understand what the difference is, I will appreciate.
Edit:
1- I am not using ARC
2- _subtitle comes form #synthesize subtitle = _subtitle; And subtitle is a part of the MKAnnotation protocol, with the property nonatomic, readonly and copy
Regards,
Cyril
If you are not using ARC, the answer is straightforward and is what Anoop Vaida wrote. However, I think some further explanation is needed.
This line
_subtitle = #"Test";
Creates a reference to a string literal. If you take a peak at its retain count in the current implementation of foundation, you'll find it is a very large number (NSIntegerMax I think). If the code for -release and -retain comes across this value for the retain count, they do not decrement or increment it. Thus string literals have infinite life times.
This line:
_subtitle = [NSString stringWithFormat: #"Test"];
creates a string you do not own. Unless you take steps to claim ownership, it could disappear at any time, most likely when the autorelease pool is drained. Your options are create a string you do own
_subtitle = [[NSString alloc] initWithFormat: #"Test"];
or to retain it.
_subtitle = [NSString stringWithFormat: #"Test"];
[_subtitle retain]; // Can be combined with the previous line if you like.
or to copy it
_subtitle = [[NSString stringWithFormat: #"Test"] copy];
Note that, in all cases, you need to release the previous value of _subtitle before you overwrite it, or you will get a leak e.g.
[_subtitle release];
_subtitle = [[NSString alloc] initWithFormat: #"Test"];
This is why it is better to have a property. Just because the MKAnnotation subtitle property is read only, does not mean you can't override it with your own read/write property. e.g.
#interface MyAnnotation : NSObject <MKAnnotation>
// other stuff
#property (readwrite, copy, nonatomic) NSString* subtitle;
#end
If you then synthesize it, you'll get all the correct memory management code and you can just do
[self setSubtitle: [NSString stringWithFormat: #"test"]];
or, if you must use dot notation
self.subtitle = [NSString stringWithFormat: #"test"];
I just would like to know the difference between the line 1 and 2
bellow:
_subtitle = #"Test"; //Line 1
_subtitle = [NSString stringWithFormat: #"Test"]; //Line 2
If you ask just the above these both are same.
While checking your code, the difference is quite visible.
You are creating a new autoreleased subtitle which is getting released once the block is over.
I don't think your solution is to understand how the string initializes work, but more on how blocks deal with variables.
When I think about it I think you may want to try an access _subtitle by it's property and not it's ivar.
self.subtitle
This should increment the retain count and keep everything functioning fine.
If you look up the documentation for initWithFormat: it links you to Formatting String Objects with many examples.
This basically allows (s)printf-style format strings, like so:
NSString *string1 = [[NSString alloc] initWithFormat:#"A string: %#, a float: %1.2f",
#"string", 31415.9265];
// string1 is "A string: string, a float: 31415.93"
The key is to specify arguments by adding a , after the string argument.
The '[NSString stringWithFormat:]' allows you to add a variable into the string, for example:
int myVar = 3;
NSString *myString = [NSString stringWithFormat:#"This number is %i", myVar];
The resulting string would be (if you were to NSLog it, for example): "This number is 3"
However you cannot do it like this:
NSString *myString = #"This number is %i", myVar;
Which would present you with an error.

Why aren't these NSString instance variables allocated?

first post here. I was reading through an Objective-C tutorial earlier, and I saw that they had made a couple of NSString instance variables like this:
#implementation MyViewController {
NSString *stringOne;
NSString *stringTwo;
NSString *stringThree;
NSString *stringFour;
NSString *stringFive;
}
And then simply used them in ViewDidLoad like this:
- (void)viewDidLoad
{
[super viewDidLoad];
stringOne = #"Hello.";
stringTwo = #"Goodbye.";
stringThree = #"Can't think of anything else to say.";
stringFour = #"Help...";
stringFive = #"Pheww, done.";
}
How have they done this without instantiating the string? Why does this work? Surely you'd have to do something like stringOne = [NSString stringFromString:#"Hello."]; to properly alloc and init the object before you could simply do stringOne= #"Hello.";.
Sorry if this a dumb question, but I find these little things throw me.
Thanks,
Mike
From the Apple String Programming Guide:
Creating Strings
The simplest way to create a string object in source code is to use the Objective-C #"..." construct:
NSString *temp = #"Contrafibularity";
Note that, when creating a string constant in this fashion, you should use UTF-8 characters. Such an object is created at compile time and exists throughout your program’s execution. The compiler makes such object constants unique on a per-module basis, and they’re never deallocated. You can also send messages directly to a string constant as you do any other string:
BOOL same = [#"comparison" isEqualToString:myString];
String constants like #"Hello" are already allocated and initialized for you by the compiler.
Just remember this basic thing:-
NSString *string = ...
This is a pointer to an object, "not an object"!
Therefore, the statement: NSString *string = #"Hello"; assigns the address of #"Hello" object to the pointer string.
#"Hello" is interpreted as a constant string by the compiler and the compiler itself allocates the memory for it.
Similarly, the statement
NSObject *myObject = somethingElse;
assigns the address of somethingElse to pointer myObject, and that somethingElse should already be allocated and initialised.
Therefore, the statement: NSObject *myObject = [[NSObject alloc] init]; allocates and initializes a NSObject object at a particular memory location and assigns its address to myObject.
Hence, myObject contains address of an object in memory, for ex: 0x4324234.
Just see that we are not writing "Hello" but #"Hello", this # symbol before the string literal tells the compiler that this is an object and it returns the address.
I hope this would answer your question and clear your doubts. :)
actually this can be said "syntactic sugar". there are some other type of NS object that can be creatable without allocation or formatting.
e.g:
NSNumber *intNumber1 = #42;
NSNumber *intNumber2 = [NSNumber numberWithInt:42];
NSNumber *doubleNumber1 = #3.1415926;
NSNumber *doubleNumber2 = [NSNumber numberWithDouble:3.1415926];
NSNumber *charNumber1 = #'A';
NSNumber *charNumber2 = [NSNumber numberWithChar:'A'];
NSNumber *boolNumber1 = #YES;
NSNumber *boolNumber2 = [NSNumber numberWithBool:YES];
NSNumber *unsignedIntNumber1 = #256u;
NSNumber *unsignedIntNumber2 = [NSNumber numberWithUnsignedInt:256u];
NSNumber *floatNumber1 = #2.718f;
NSNumber *floatNumber2 = [NSNumber numberWithFloat:2.718f];
// an array with string and number literals
NSArray *array1 = #[#"foo", #42, #"bar", #3.14];
// and the old way
NSArray *array2 = [NSArray arrayWithObjects:#"foo",
[NSNumber numberWithInt:42],
#"bar",
[NSNumber numberWithDouble:3.14],
nil];
// a dictionary literal
NSDictionary *dictionary1 = #{ #1: #"red", #2: #"green", #3: #"blue" };
// old style
NSDictionary *dictionary2 = [NSDictionary dictionaryWithObjectsAndKeys:#"red", #1,
#"green", #2,
#"blue", #3,
nil];
for more information, see "Something wonderful: new Objective-C literal syntax".

Resources