All single dot components of the path must be removed.
For example, "abi/./bune" should be normalized to "abi/bune".
All double dot components of the path must be removed, along with their parent directory. For example, "abi/ba/../bune" should be normalized to "abi/bune".
without using regular expressions. Any idea how to achieve?
This can be achieved by calling
NSString *standardizedPath = [path stringByStandardizingPath];
For example:
"/usr/bin/./grep" -> "/usr/bin/grep"
"/usr/include/objc/.." -> "/usr/include"
Worth noticing is that this also removes any initial components of /private and tries to expand any initial tilde expressions using stringByExpandingTildeInPath.
#Daniel's answer is the correct one. But since there was a discussion in the comments to the question, I decided to provide a code example for # Rey Gonzales's idea of tokenizing a string and using a stack.
WARNING: this code is here for purely educational purposes (because someone asked for it in the comments to the question). In real life stick to the #Daniel's solution.
The code might look like this:
-(NSString *)normalizePath:(NSString *)path {
NSArray *pathComponents = [path componentsSeparatedByString:#"/"];
NSMutableArray *stack = [[NSMutableArray alloc] initWithCapacity:pathComponents.count];
for (NSString *pathComponent in pathComponents) {
if ([pathComponent isEqualToString:#".."]) {
[stack removeLastObject];
}
else {
if (![pathComponent isEqualToString:#"."]) {
[stack addObject:pathComponent];
}
}
}
return [stack componentsJoinedByString:#"/"];
}
Related
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 am doing a tuturial on Lynda.com for objective-c, and ran accross this example code. This is a part of the ViewController.m file. The idea behind the exercise was to create a picker object with custom elements in it.
The following code works just fine and gives me a picker with "happy" and "sad" as the options:
#implementation ViewController
-(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{
return 1;
}
-(NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{
return [[self moods] count];
}
-(NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component{
return self.moods[row];
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.moods = #[#"happy",#"sad"];
}
However, I prefer square brackets to dot syntax and, as you can see I experimented in a few different places with it. Thereturn [[self moods] count was written as return [self.moods count] in the tutorial, but I wanted to use square brackets to verify that it still worked and I understood what was going on, so I changed it and it worked just fine. HOWEVER, I have been trying to do the same thing with the self.moods = #[#"happy",#"sad"]; because I don't like how it looks. I tried:
[[self moods] initWithObjects: #"happy",#"sad", nil];
But I just got a blank picker and a warning "expression result unused". I tried putting _moods = before that expression, and still got a blank picker. What is wrong here?
The reason that [[self moods] initWithObjects: #"happy",#"sad", nil]; is not doing what you expect is due to a misunderstanding in what is happening with regards to dot syntax and how it relates to message sending using square brackets.
Dot syntax is the "syntactic sugar" and recommended way of accessing properties of classes, such as the mood property from your question. Dot syntax is simply a shorthand for accessors (setters / getters) in Objective-C. A quick example might help clear this up.
When dot syntax finds itself on the right hand side of an assignment operator OR as the receiver of a message, the getter method is invoked.
// These two are equivalent
NSArray *allMoods = self.moods
NSArray *allMoods = [self moods]
// These two are equivalent
NSUInteger count = [self.moods count];
NSUInteger count = [[self moods] count];
When dot syntax finds itself on the left hand side of an assignment operator, the setter method is invoked.
// These two are equivalent
self.moods = #[#"happy", #"sad"];
[self setMoods:#[#"happy", #"sad"];
Using dot syntax is not only a nice shorthand, it makes your intentions clearer and newcomers to your code immediately aware that moods is a property of your class.
Also, the reason that [[self moods] initWithObjects: #"happy",#"sad", nil]; is not valid is because -initWithObjects: is an initializer of NSArray that should be called immediately following +alloc. In the piece of code above, [self moods] is returning an NSArray that already exists or lazily instantiating one. For completeness, -initWithObjects should be used as follows:
NSArray *myArray = [[NSArray alloc] initWithObjects:#"happy", #"sad", nil];
I assume you declared #property (strong, nonatomic) NSArray *moods; in the interface since self.moods works.
Setter and getter methods setMoods and getMoods are created automatically.
Here's how the dot syntax boils down to
// These are equivalent
self.moods = #[#"happy",#"sad"];
[self setMoods:#[#"happy",#"sad"]]; // Literal declaration
[self setMoods:[[NSArray alloc] initWithObjects:#"happy",#"sad",nil]]; // Full declaration
This works because you were using the "literal" way of declaring an NSArray* which includes both "allocation" and "initialization".
- (instancetype)initWithObjects: is an instance method which should be called on an instance variable already allocated with alloc. You tried to initialize a variable which has never been allocated in memory.
An slightly cleaner alternative would be:
[self setMoods:[NSArray arrayWithObjects:#"happy",#"sad",nil]];
arrayWithObjects: include both allocation and initialization.
the [self moods] way of referencing it can only be used on the right hand side of an expression, it's calling the getter for the property. self.moods = ... is actually syntactic sugar for [self setMoods:...]
so try [self setMoods:#[#"happy",#"sad"]]
You'll want to read up on the #property declaration and how it "synthesizes" getter and setter methods. What you want to do is "set" the moods property:
[self setMoods: #[#"happy",#"sad"]];
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.
I'm a beginner in iPhone development and I need some help.
I created an NSDictionary to hold some quotes followed by keys (which is the person who quoted them).
Now, I'v created a method to pull a random quote, and to use this method 'arc4random_uniform' I need to use integer...and them I do 'return [self.insperationalQuotes objectForKey:"here i want a random number"];
Usually it probably would be easier to use an array but I want to use the key to attach it to a specific photo of the person who quoted it...or you still recommend to do it with array and say object at #1 will einstein for instance...
This is my code:
#import "NOQuotes.h"
#implementation NOQuotes
- (NSDictionary *) insparationalQuotes {
if (_insparationalQuotes == nil) {
_insparationalQuotes = [[NSDictionary alloc] initWithObjectsAndKeys:
#"Try not to become a man of success but a man of value.",
#"ALBERT EINSTEIN",
#"What lies behind us and what lies before us are tiny matters compared to what lies within us.",
#"HENRY STANLEY HASKINS",
#"It is never too late to be what you might have been.",
#"GEORGE ELIOT",
#"All our dreams can come true–if we have the courage to pursue them.",
#"WALT DISNEY",
#"The best way to predict the future is to invent it.",
#"ALAN KAY",
#"You miss 100% of the shots you don’t take.",
#"WAYNE GRETZKY",
#"If opportunity doesn’t knock, build a door.",
#"MILTON BERLE",
#"Failure is the condiment that gives success its flavor.",
#"TRUMAN CAPOTE", nil];
}
return _insparationalQuotes;
}
- (NSString *) getRandomQuote {
int randomNum = arc4random_uniform(self.insparationalQuotes.count);
return [self.insparationalQuotes objectForKey:randomNum]; //error
}
Thanks!
For what you're trying to do, I recommend creating a Quote class, which will store a quote, an "author", and the image associated with that person. I'd then fill an NSArray or NSMutableArray with instances of these Quote objects so you can randomly select an index.
#interface Quote : NSObject
#property NSString *quote;
#property NSString *author;
#property UIImage *picture;
+(instancetype)quoteWithQuote:(NSString*)quote
author:(NSString*)author
picture:(UIImage*)picture;
-(instancetype)initWithQuote:(NSString*)quote
author:(NSString*)author
picture:(UIImage*)picture;
#end
Then just fill in that class. Arguably, you may even want the #propertys to be readonly, making the Quote class immutable.
Alternatively, you can keep your NSDictionary as is, and pair it with an NSArray made up of all the author names (which you're using as keys). Then select a random index from the array of names, and use that name as the key into the dictionary for the quote. The problem here is it's a little redundant, and you can't have multiple quotes with the same author.
Use allValues to pull an array of values, and index into that array using a random number, like this:
- (NSString *) getRandomQuote {
int randomNum = arc4random_uniform(self.insparationalQuotes.count);
return [[self.insparationalQuotes allValues] objectAtIndex:randomNum];
}
I agree that what you really want is an array of custom objects, but I would like to point out that it is possible to select a random entry from an NSDictionary object:
- (id)randomObjectInDictionary:(NSDictionary *)dict {
NSArray *keyArray = [dict allKeys];
int rand = arc4random_uniform([keyArray count]);
return dict[keyArray[rand]];
}
I'm writing an iPhone app, and I'm surprised that there seem to be no NSQueue or NSStack classes in Apple's Foundation Framework. I see that it would be quite easy to roll my own, starting with an NSMutableArray, so I'll do that unless I've missed something. Have I missed something?
Here's my Stack class, in case it's useful to those who come after me. As you can see, the pop method involves enough code that you'd want to factor it out.
Stack.h:
#import <Foundation/Foundation.h>
#interface Stack : NSObject {
NSMutableArray *contents;
}
- (void)push:(id)object;
- (id)pop;
#end
Stack.m
#import "Stack.h"
#implementation Stack
// superclass overrides
- (id)init {
if (self = [super init]) {
contents = [[NSMutableArray alloc] init];
}
return self;
}
- (void)dealloc {
[contents release];
[super dealloc];
}
// Stack methods
- (void)push:(id)object {
[contents addObject:object];
}
- (id)pop {
id returnObject = [[contents lastObject] retain];
if (returnObject) {
[contents removeLastObject];
}
return [returnObject autorelease];
}
#end
as far as I know there is no generic class avaialbe. Try using the NSMutableArray, add via addObject and get first/last via objectAtIndex and removeObjectAtIndex.
Another easy way would be to extend NSMutableArray's capabilities by making use of Objective C's categories. You can do that by adding two files to your project:
NSMutableArray+Stack.h
#interface NSMutableArray (StackExtension)
- (void)push:(id)object;
- (id)pop;
#end
NSMutableArray+Stack.m
#import "NSMutableArray+Stack.h"
#implementation NSMutableArray (StackExtension)
- (void)push:(id)object {
[self addObject:object];
}
- (id)pop {
id lastObject = [self lastObject];
[self removeLastObject];
return lastObject;
}
#end
Now you can use a regular NSMutableArray in every other file of your project like a stack and call push or pop on that object. Don't forget to #import NSMutableArray+Stack.h in those files. Here is some sample code how you can use your new NSMutableArray as a stack:
NSMutableArray *myStack = [[NSMutableArray alloc] init]; // stack size = 0
NSString *aString = #"hello world";
[myStack push:myString]; // stack size = 1
NSString *anotherString = #"hello universe";
[myStack push:anotherString]; // stack size = 2
NSString *topMostStackObject;
topMostStackObject = [myStack pop]; // stack size = 1
NSLog("%#",topMostStackObject);
topMostStackObject = [myStack pop]; // stack size = 0
NSLog("%#",topMostStackObject);
The log output will be:
hello universe
hello world
I'm a bit late to this party, but are you aware of CHDataStructures?
http://cocoaheads.byu.edu/code/CHDataStructures
I have put a working iOS Objective C queue object on GitHub. The code was taken from various posts and by no means is owned by me.
https://github.com/esromneb/ios-queue-object/
If you see any problems please fork, and make a pull request!
Yes, an NSMutableArray doubles as a stack or queue. (It would be slightly inefficient as a queue.)
You could also use C++'s stack and queue adapter, but it makes memory management a bit messy if you want to store Objective-C objects with it.
ObjectiveSugar is a very popular CocoaPod that provides, among a bunch of other great stuff, push and pop API calls on NSMutableArray. Sure, it's not in the iOS SDK, but I'm sharing it here because I was looking for the same thing, and this was the solution I went with (and it certainly didn't hurt that we were already using this CocoaPod in our codebase).
No. You missed nothing. That's all. Objective-C is higher level language look like C. Low level control is not required.
Cocoa classes are designed for easier use than efficiency. If you want to deal with performance, you have an option of raw C (or C++) implementation. Otherwise, just use easy way. Of course, early-optimization is evil.
If you want a kind of encapsulation, just make a new class which contains NSMutableArray within it. Hide inner NSMutableArray and just expose what you want. But you'll realize this is unnecessary.