I'm not really sure exactly how to describe what I want to do - the best I can do is provide some code as an example:
- (void) doStuffInLoopForDataArray:(NSArray *)arr forObjectsOfClass:(NSString *)class
{
for ([class class] *obj in arr)
{
// Do stuff
}
}
So I might call this like
NSArray *arr = [NSArray arrayWithObjects:#"foo",#"bar", nil];
[self doStuffInLoopForDataArray:arr forObjectsOfClass:#"NSString"];
and I would expect the code to be executed as if I had wrote
- (void) doStuffInLoopForDataArrayOfStrings:(NSArray *)arr
{
for (NSString *obj in arr)
{
// Do KVC stuff
}
}
Is there a way to get this kind of behavior?
I don't see much point in passing the class to the method. Run your loop as:
for (id obj in arr) {
and check the methods you want to call exist. Passing the class is only really useful if you want to check that the objects in the array are actually of that class, but you couldn't then do much with that information.
Another approach would be to create a single superclass that all the classes I'd like to use this method for inherit from. I can then loop using that superclass.
So if I want to be able to loop for MyObject1 and MyObject2, I could create a BigSuperClass, where MyObject1 and MyObject2 are both subclasses of BigSuperClass.
- (void) doStuffInLoopForDataArray:(NSArray *)arr
{
for (BigSuperClass *obj in arr)
{
// Do stuff
}
}
This loop should work for arrays of MyObject1 objects, arrays of MyObject2 objects, or arrays of BigSuperClass objects.
The more I've been thinking about this, the more I'm leaning towards this being the best approach. Since I can setup my BigSuperClass with all the #propertys and methods I'd be interested in as part of my // Do Stuff, which means I won't have to check respondsToSelector as with the other answers. This way just doesn't feel quite as fragile.
I came up with an idea while I was typing up this question, figured I might as well finish it. I just need to change how I'm doing my loop, and I don't really need to send in the class.
- (void) doStuffInLoopForDataArray:(NSArray *)arr
{
for (int i=0; i < [arr count]; i++)
{
// Do stuff
}
}
I should note that part of my // Do stuff is checking to make sure if ([[arr objectAtIndex:i] respondsToSelector:...]) before I actually try to do anything with it - and from what I understand that should prevent any nasty crashes.
Related
So I'm plan to create a safe init function for NSDictionary like someone else did, but as I'm a SDK developer:
I want add a switch for it, the user can decide if he want open it or not;
I don't want to use Category to implement it.
So I create a totally new class named "ALDictionarySafeFunction.h", it has two functions, the first one is the switch function, like this:
+(void)enableSafeFunction{
[ALSwizzlingHelper swizzleSelector:#selector(initWithObjects:forKeys:count:)
ofClass:NSClassFromString(#"__NSPlaceholderDictionary")
withSwizzledSelector:#selector(safeInitWithObjects:forKeys:count:)
ofClass:[ALDictionarySafeFunction class]];
}
The ALSwizzlingHelper can help me to swizzle two functions.
The second is the safe init function, like this:
-(instancetype)safeInitWithObjects:(const id _Nonnull __unsafe_unretained *)objects forKeys:(const id _Nonnull __unsafe_unretained *)keys count:(NSUInteger)cnt {
BOOL containNilObject = NO;
for (NSUInteger i = 0; i < cnt; i++) {
if (objects[i] == nil) {
containNilObject = YES;
NSLog(#"There is a nil object(key: %#)", keys[i]);
}
}
if (containNilObject) {
//Do something to make sure that it won't cause a crash even it has some nil value
}
//There is the problem, next line
[self safeInitWithObjects:objects forKeys:keys count:cnt];
}
For the normal situation(Write the swizzled method in the Category), I need to do like I wrote to invoke the original method.
But the problem is I cannot do it here, because that the "self" object is the instance of “__NSPlaceholderDictionary”, and the "__NSPlaceholderDictionary" class doesn't have the instance method "safeInitWithObjects:forKeys:count:".
So what should I do?
Is there a way to make it?
Any advice will be appreciated.
I want to write a helper class to get values from my info.plist and have them be cast to their correct type. So, if I try to access a property that's actually a number as a date, I should get back nil or an error.
I'm having trouble coming up with a nice way to check the type. I've tried to read [val class]. In the example below, it comes back as __NSTaggedDate for date values, which seems like an implementation detail I don't want to rely on.
- (NSDate *)dateConfig:(NSString *)name
{
_configs = [[NSBundle mainBundle] objectForInfoDictionaryKey:#"myConfigDictionary"];
id val = [_configs objectForKey:name];
// TODO how do I tell?
if([val class] != ???)
{
return nil;
}
return val;
}
I want to do this reliably for all other plist types as well. What's an elegant way to get this done?
You are looking for the message isKindOfClass:
if([val isKindOfClass:[NSNumber class]])
{
return (NSNumber *)val;
}
else
{
return nil;
}
Be aware that there is also isMemberOfClass: but you rarely would want this. Many foundation objects are really Core Foundation based (i.e. NSString is NSCFString most of the time).
I feel like there is a more regulation way to do what I am doing in, either by some iOS specific thing, or pattern I'm aware of. I'm trying to create an NSMutableArray variable, that essentially acts as temporary storage for a logger class. Each time the array is accessed, I want to either lazily instantiate it, or set it to nil. The way I am thinking of doing it seems a little hacky and I'm looking for some input?
- (NSMutableArray)myArray {
if (!_myArray) {
_myArray = [[NSMutableArray alloc] init];
} else {
_myArray = nil;
}
return _myArray;
}
The effect I'm hoping to achieve is using a logger that is logging details about network requests - http codes, URLs, repsonse times, etc. I want the logger to amalgamate all this output in this storage array. Later on, when I'm hitting an API, I want to take the contents of this array, and send it up to the API, and I also want the array to reset (so the array is essentially a log of network request data since the last time the app hits the API, versus a log of what has happened since the app launched.
I realise that I could do this manually by niling the array when I access it, but I'm trying to do this in a more of a plug and play way, where it you don't need to worry if someone forgets to nil the array etc
The effect that you are trying to achieve is perfectly legitimate, but you shouldn't try to achieve it with a getter alone: the very fact that a simple getter could reset something back to nil would be counter-intuitive to your readers.
Instead, you should make two methods - one to prepare the array, and another one to harvest it, and replace with a fresh nil:
- (NSMutableArray*)myArray {
if (!_myArray) {
_myArray = [[NSMutableArray alloc] init];
}
return _myArray;
}
- (NSMutableArray*)resetArray{
NSMutableArray *res = _myArray;
_myArray = nil;
return res;
}
Now the sequence of operations becomes intuitively clear: you get myArray as many times as you wish, add as many items as you need, and then when you are done call resetArray. This would get you a complete array with all the data, and reset the object to be ready for the next call:
for (int col = 0 ; col != 10 ; col++) {
[log.myArray addObject:[self getDataForIndex:col]];
}
NSMutableArray* logArray = [log resetArray];
What you're doing doesn't make any sense to me.
Creating it empty if it doesn't exist makes sense.
Setting it to nil if it does exist does not make sense.
The usual pattern for lazy loading is to use a property with a getter:
#property (nonatomic, retain) NSMutableArray * myArray;
and then the implementation:
//Custom getter
-(NSMutableArray*) myArray;
{
if (!_myArray)
_myArray = [[NSMutableArray alloc] init];
return _myArray;
}
Then you ALWAYS refer to the array using the getter. If it hasn't yet been created, the getter creates and returns the empty array. If it does exist, it returns the existing one.
Is it better to initialize an NSArray in viewDidLoad when the program first starts up, or define the NSArray only if a condition is met?
Basically, I initialized an NSArray in an IF condition of one of my methods. This method may be called multiple times, and want to know if it's better on memory if the NSArray is created and destroyed in the method, or if it's better to define it once in viewDidLoad and reference it in the method?
If I'm not clear, please let me know.
Thanks
Create any data collection only when you need it, this way you make sure that the program/screen is initially launched quickly. This is called as lazy loading, this approach should be followed whenever possible.
If you are using NSMutableArray and managing this in run time, you can do something like this for lazy loading, and clearing it from memory when not needed. Add helper methods to add and remove objects from an array, array is created automatically when needed and removed from memory when it is empty.
- (void)addObject:(NSObject *)value
{
if (value == nil) return;
if (_collection == nil) {
_collection = [[NSMutableArray alloc] init];
}
[_collection addObject:value];
}
- (void)removeObject:(NSObject *)value
{
if (value == nil) return;
[_collection removeObject:value];
if ([_collection count] == 0) {
[_collection release], _collection = nil;
}
}
I have an noob problem and I would like you yo point me in the right direction. Basicly I have a custom class which implements the copying protocol. However when I save the class during execution the custom class i released and I get a bad access. I can see in instruments that the retain count is -2. I save the custom class with the following method:
-(void)storeDataInFile:(NSString*)dataFileName DataArray:(NSArray*)dataToStore
{
//Get the path
NSString *path = [self pathToDocumentsForDataFile:dataFileName];
//Archive the file
[NSKeyedArchiver archiveRootObject:dataToStore toFile:path];
}
Is I use the method sor saving a array with strings it works flawless. What should I look deeper into regarding my custom class?
Regards
I soved this issue, however I only provided the solution in a comment which apprantly has been deleted. So I just wanted to post the answer which indicates it was a noob mistake.
From an eralier test implementation I had the following method in the class
- (id)copyWithZone:(NSZone *)zone
{
return self;
}
//retain is counted up
- (id)retain {
return self;
}
- (unsigned)retainCount {
return UINT_MAX;
}
These methods ruined my retain count :)