Change index of specific char of NSString - ios

I have a string, for example "Soccer". Now I want to move every "e" by lets say 2 indexes(right word?), so my string looks like this = "erSocc". This has to work with whitespace and negative/- indexes.
I came a cross with this, not perfect working, solution:
NSString* text = #"Soccer";
NSString* sign = #"c";
int index = 1;
NSMutableArray* arrayText = [[NSMutableArray alloc]init];
NSMutableArray* arraySignNewPosition = [[NSMutableArray alloc]init];
NSMutableArray* arrayOldSignPosition = [[NSMutableArray alloc]init];
for(int i=0;i<(text.length);i++)
{
[arrayText addObject:[text substringWithRange:NSMakeRange(i, 1)]];
if ([[arrayText objectAtIndex:i]isEqualToString:sign])
{
[arrayOldSignPosition addObject:[NSNumber numberWithInt:i]];
if ((i+index)>(text.length-1))
{
int indexDifference = (i+index)-(text.length);
[arraySignNewPosition addObject:[NSNumber numberWithInt:indexDifference]];
}
else
{
[arraySignNewPosition addObject:[NSNumber numberWithInt:(i+index)]];
}
}
}
for (NSNumber* number in arraySignNewPosition)
{
[arrayText insertObject:sign atIndex:number.integerValue];
if(number.integerValue-index>0)
{
[arrayText removeObjectAtIndex:(number.integerValue-index)];
}
else
{
[arrayText removeObjectAtIndex:((arrayText.count-1)+(number.integerValue-index))];
}
}
I know the code is not working perfectly, but I would like to know if this is the right way or if there are some Cocoa functions I could use to accomplish my goal. Thanks for your time.

You're really just getting substrings and moving them around, so you could do something like this:
- (NSString *)shiftRight:(NSUInteger)places
{
NSAssert(places > 0, #"places must be greater than 0");
NSAssert(places < [self length], #"places must be less than the length of the string");
places = [self length] - places;
NSString *start = [self substringFromIndex:places];
NSString *end = [self substringToIndex:places];
return [start stringByAppendingString:end];
}
Here's a complete code listing, with examples.

Related

IOS/Objective-C: NSArray randomization issue

I have this array with strings and I want to randomize it and display the output in a label. But with this code I'm having always the same strange output which is "2"...Any ideas what I'm doing wrong?
- (void)viewDidLoad {
[super viewDidLoad];
self.quoteInput.delegate=self;
self.quoteLabel=_quotationLabel;
self.correctLabel.alpha=0;
self.wrongLabel.alpha=0;
_quoteInput.delegate=self;
_levelOne = #[
[words Quotes:#"First Quote."],
[words Quotes:#"Second Quote."],
[words Quotes:#"And so on."]];
}
-(void)randomQuotes{
for (NSInteger x = 0; x < [_levelOne count]; x++) {
NSInteger randInt = arc4random() % ([_levelOne count] - x) + x;
[_levelOne objectAtIndex:randInt];
NSString *one = [NSString stringWithFormat:#"%d", randInt];
self.quoteLabel.text = one;
}
}
- (IBAction)generateQuote:(UIButton *)sender {
[self randomQuotes];
}
EDIT
The words class:
+ (instancetype)Quotes:(NSString *)quotes
{
return [[words alloc] initWithQuotes:quotes];
}
Unless you can explain what [word Quotes:] method does, it should be much simpler than what you are trying to do.
_levelOne = #[#"First Quote.", "Second Quote.", #"And so on."];
-(void) randomQuotes{
NSInteger randInt = arc4random_uniform( [_levelOne count] );
self.quoteLabel.text = [_levelOne objectAtIndex:randInt];
}

Does CHCSVParser have a column parsing limit?

In my application you can import data through a tab separated values file. I don't have any challenge until I parse "locations" that have multiple items attached to them. If you scroll to the very bottom of the second method you can see how I create a relationship between items and the locations that contain them inside Core Data. The problem occurs when I parse past column 31 in a location. It doesn't attach those items to the location. So my question is this; is there a limit to columns in the NSArray that is parsed by CHCSVParser? If not, what would cause this limiting to 31 columns?
I've posted the two methods that I encounter the bug with below.
+ (void) importDatabaseTSVURL:(NSURL*)url {
// First check if there is already a database. If so, stop import.
if ([XSELLocation locations].count > 0) return;
if ([XSELItem items].count > 0) return;
if ([XSELVendor vendors].count > 0) return;
NSError *error;
NSArray *array = [NSArray arrayWithContentsOfDelimitedURL:url options:CHCSVParserOptionsSanitizesFields delimiter:'\t' error:&error];
if ([[[array firstObject] firstObject] isEqualToString:#"XSELINVENTORYTSV"]) {
for (NSArray *row in array) {
[XSELSettings parseImportDataRow:row];
}
}
}
+ (void) parseImportDataRow:(NSArray*)array {
// Create logic to seperate data entered next
static NSString *operation = #"none";
if ([array.firstObject isEqualToString:#"ITEMLIST"]) {
operation = #"items";
return;
}
else if ([array.firstObject isEqualToString:#"LOCATIONLIST"]) {
operation = #"locations";
return;
}
else if ([array.firstObject isEqualToString:#"VENDORLIST"]) {
operation = #"vendors";
return;
}
else if ([array.firstObject isEqualToString:#"ENDLIST"]) { // Create database, relate objects, and clean up the data
operation = #"none";
return;
}
// Parse rows to the correct array.
if ([operation isEqualToString:#"vendors"]) {
NSLog(#"adding vendor");
XSELVendor *vendor = [XSELVendor addVendor];
vendor.vendorID = [NSNumber numberWithInteger:[[array objectAtIndex:0] integerValue]];
[XSELSettings nextVendorID];
vendor.name = [array objectAtIndex:1];
vendor.contactID = [array objectAtIndex:2];
}
else if ([operation isEqualToString:#"items"]) {
NSLog(#"adding item");
XSELItem *item = [XSELItem addItem];
item.itemID = [NSNumber numberWithDouble:[[array objectAtIndex:0] integerValue]];
[XSELSettings nextItemID];
item.name = [array objectAtIndex:1];
item.smallPackageName = [array objectAtIndex:2];
item.bigPackageName = [array objectAtIndex:3];
item.smallPerBig = [NSNumber numberWithDouble:[[array objectAtIndex:4] integerValue]];
item.buildTo = [NSNumber numberWithDouble:[[array objectAtIndex:5] integerValue]];
item.price = [NSNumber numberWithDouble:[[array objectAtIndex:6] integerValue]];
// Relate preferred vendor to item
for (XSELVendor *vendor in [XSELVendor vendors]) {
if ([vendor.vendorID.stringValue isEqualToString:[array objectAtIndex:7]]) {
item.preferredVendor = vendor;
break;
}
}
}
else if ([operation isEqualToString:#"locations"]) {
NSLog(#"adding location");
XSELLocation *location = [XSELLocation addLocation:[array objectAtIndex:1]];
location.locationID = [NSNumber numberWithInteger:[[array objectAtIndex:0] integerValue]];
[XSELSettings nextLocationID];
location.position = [NSNumber numberWithInteger:[[array objectAtIndex:2] integerValue]];
// Relate location with items
unsigned long itemsRelatedCount = array.count - 3;
NSLog(#"\n\nitemsRelated: %lu\n\n", itemsRelatedCount);
NSMutableOrderedSet *items = [NSMutableOrderedSet orderedSet];
for (int i = 0; i < itemsRelatedCount; i++) {
NSString *itemID = [array objectAtIndex:i];
for (XSELItem *item in [XSELItem items]) {
if ([item.itemID.stringValue isEqualToString:itemID]) {
[items addObject:item];
break;
}
}
}
location.items = items;
}
}

iOS: Tracking amount of allocated objects

We can use instruments for various kinds of analysis. But many programmers find this tool to be too complicated and too heavy to bring real value.
Is there a simple way to track all objects of a specific class, and for each to know who exactly was allocating them and to verify that they are being freed correctly?
The answer is yes! there is a way, and I'll demo it in my answer below
Tracking allocations easily:
How to use: you can put the 150 lines of code below into a file named AllocTracker.m and drag it into your project files.
Use the check box at the right pane of Xcode to enable/disable it in your compilation target.
What you'll get?
when enabled, this module will track all allocations and deallocations of UIImage objects and log them. (It can easily be modified for tracking other classes.)
In addition to logging every allocation and deallocation, it will periodically (currently every 15 seconds) dump all objects which are currently allocated, with some added info and the call stack which allocated them.
What is the added value?
This code was used in big projects to get rid of orphan objects which were left allocated without notice, allowing to significantly reduce the memory footprint of the app and fix memory leaks.
So here is the code for AllocTracker.m:
#define TRACK_ALLOCATIONS
#ifdef TRACK_ALLOCATIONS
#import <UIKit/UIKit.h>
#define TIMER_INTERVAL 15
#implementation UIApplication(utils)
+(NSString *)dateToTimestamp:(NSDate *)date
{
if (date == nil) {
date = [NSDate date];
}
static NSDateFormatter *dateFormatter = nil;
if (!dateFormatter) {
dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:#"en_US_POSIX"];
[dateFormatter setDateFormat:#"HH:mm:ss.S"];
}
NSString *ts = [dateFormatter stringFromDate:date];
return ts;
}
+(NSString*) getCaller:(int)stackDepth
{
#ifndef DEBUG
return #"NON DBG";
#else
NSArray *symbols = [NSThread callStackSymbols];
int lastIndex = (int)(symbols.count - 1);
if (lastIndex < 3) {
return #"NO DATA";
}
NSMutableString *result = [NSMutableString string];
int foundCount = 0;
for (int ix=3; ix <= lastIndex; ix++) {
NSString *line = symbols[ix];
NSRange rng1 = [line rangeOfString:#"["];
if (rng1.location == NSNotFound) {
continue;
}
NSRange rng2 = [line rangeOfString:#"]"];
NSString *caller = [line substringWithRange:NSMakeRange(rng1.location+1, rng2.location-rng1.location-1)];
if (foundCount > 0) { //not first
[result appendString:#"<--"];
}
[result appendString:caller];
if (++foundCount == stackDepth) {
break;
}
}
return (foundCount > 0) ? result : #"NO SYMBOL";
#endif
}
#end
#implementation UIImage(memoryTrack)
static NSMapTable *g_allocsMap;
static NSTimer *g_tmr;
static NSDate *g_lastDump = nil;
+(void)gotTimer:(NSTimer *)timer
{
[self dumpAllocs];
}
+(void)startTimer
{
static int count = 0;
g_tmr = [NSTimer scheduledTimerWithTimeInterval:15 target:self selector:#selector(gotTimer:) userInfo:#(count++) repeats:YES];
NSLog(#"starting timer %i", count);
}
+(void)cancelTimer
{
[g_tmr invalidate];
g_tmr = nil;
}
+(void)dumpAllocs
{
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), ^{
NSMutableString *str = [NSMutableString string];
[str appendString:#"\n#$# ========== Non-freed UIImages =========\n"];
NSMutableArray *sorted = [NSMutableArray array];
//make sure map is not changed while enumerating
static int s_ts_start = -1;
#synchronized (g_allocsMap) {
NSEnumerator *keysEnum = [g_allocsMap keyEnumerator];
UIImage *img;
while (img = [keysEnum nextObject]) {
NSString *value = [g_allocsMap objectForKey:img];
if (value) { //might be nulled because declared as weak
NSUInteger memUsed = CGImageGetHeight(img.CGImage) * CGImageGetBytesPerRow(img.CGImage);
NSString *objData = [NSString stringWithFormat:#"mem=%5ikb, size=%4ix%-4i", (int)(memUsed/1024), (int)img.size.width, (int)img.size.height];
NSString *line = [NSString stringWithFormat:#"%p - %# [%#]\n", img, objData, value];
if (s_ts_start<0) {
s_ts_start = (int)[line rangeOfString:#"["].location + 1;
}
if (line.length > (s_ts_start+10)) {
[sorted addObject:line];
}
}
}
}
if (sorted.count > 0) {
[sorted sortUsingComparator: ^NSComparisonResult(NSString *s1, NSString *s2)
{
//we expect '0x15a973700 - mem=3600kb, size=640x360 [16:14:27.5: UIIma...'
NSString *ts1 = [s1 substringWithRange:NSMakeRange(s_ts_start, 10)];
NSString *ts2 = [s2 substringWithRange:NSMakeRange(s_ts_start, 10)];
return [ts1 compare:ts2];
}];
int ix = 0;
for (NSString *line in sorted) {
[str appendFormat:#"#$# %3i) %#", ix++, line];
}
}
[str appendString:#"#$# ======================================================\n"];
NSLog(#"%#", str);
});
}
+(instancetype)alloc
{
NSString *caller = [UIApplication getCaller:4];
#synchronized (self) {
id obj = [super alloc];
NSLog(#"#$# UIImage alloc: [%p], caller=[%#]", obj, caller);
NSDate *now = [NSDate date];
NSString *value = [NSString stringWithFormat:#"%#: %#", [UIApplication dateToTimestamp:now], caller];
if (!g_allocsMap) {
g_allocsMap = [NSMapTable mapTableWithKeyOptions:NSMapTableWeakMemory valueOptions:NSMapTableStrongMemory];
}
[g_allocsMap setObject:value forKey:obj];
if (!g_lastDump) {
[self startTimer];
g_lastDump = now;
}
return obj;
}
}
-(void)dealloc
{
NSLog(#"#$# UIImage dealloc: [%#]", self);
}
#end
#endif //TRACK_ALLOCATIONS
How it works?
We create a category of UIImage and set our own version for alloc and dealloc. Every allocated object is saved into an NSMapTable object which works like a dictionary but allow storing object with weak pointers.
For convenience we were adding two methods under UIApplication which can be used by other modules if an appropriate header file is created. One method is for formatting the timestamp, and the other is for reading the call stack (only works in debug builds).
Tip for use:
if you use a real device and install idevicesyslog (brew install libimobiledevice), you can use the terminal to see all allocation debug, like this:
idevicesyslog | grep "#\$#"

Executes same code more than once. How to use Goto statement inside for loop

I have code which is used more than two times in different condition in same function. So I decided to use Goto statement. But that code will be executed inside for loop. So I don't understand how to call same code in same function. I don't want to create one more function. My code is...
- (void)setSelectedSearchCriteria:(NSString *)storedValue storedTag:(NSString *)storedTag D_Key:(NSString *)D_Key D_Tag_Value:(NSString *)D_Tag_Value arrayMain:(NSMutableArray *)arrayMain bgView:(UIView *)bgView
{
//Add data
NSMutableArray *sArray = [[storedValue componentsSeparatedByString:#","] mutableCopy];
NSMutableArray *sTagArray = [[storedTag componentsSeparatedByString:#","] mutableCopy];
[sArray removeObject:#""];
[sTagArray removeObject:#""];
int maxTag = 0;
if (sTagArray.count != 0)
{
maxTag = [[sTagArray valueForKeyPath:#"#max.intValue"] intValue];
for (int i = maxTag + 1; i <= [D_Tag_Value intValue]; i++)
goto add_value;
}
else
goto add_value;
add_value:
{
NSString *D_Value = [[arrayMain objectAtIndex:[D_Tag_Value intValue]] valueForKey:PARAMETER_KEY];
if (![sArray containsObject:D_Value])
{
[sArray addObject:D_Value];
[sTagArray addObject:D_Tag_Value];
}
//Add data
UIButton *btn = (UIButton *)[bgView viewWithTag:[D_Tag_Value intValue]];
[self setSelectedButtonStyle:btn];
}
storedValue = [[[sArray valueForKey:KEY_DESCRIPTION] componentsJoinedByString:#","] mutableCopy];
storedTag = [[[sTagArray valueForKey:KEY_DESCRIPTION] componentsJoinedByString:#","] mutableCopy];
[SEARCH_CRITERIAS setValue:storedValue forKey:D_Key];
[SEARCH_CRITERIAS_TAG setValue:storedTag forKey:D_Key];
}
Code inside add_value executed in for loop and also in else part. So I don't know how to manage this.
Define a block inside your function
void(^theBlock)(void) = ^(){
NSString *D_Value = [[arrayMain objectAtIndex:[D_Tag_Value intValue]] valueForKey:PARAMETER_KEY];
if (![sArray containsObject:D_Value])
{
[sArray addObject:D_Value];
[sTagArray addObject:D_Tag_Value];
}
//Add data
UIButton *btn = (UIButton *)[bgView viewWithTag:[D_Tag_Value intValue]];
[self setSelectedButtonStyle:btn];
};
I don't fully understand what do you do in your add_value. If it can change to a block receive some parameters and return some value that would be better
after that you simply call the block
theBlock();
The code doesn't actually depend on the loop counter, so it isn't too hard to refactor the code so that you can simply execute the loop the appropriate number of times.
- (void)setSelectedSearchCriteria:(NSString *)storedValue storedTag:(NSString *)storedTag D_Key:(NSString *)D_Key D_Tag_Value:(NSString *)D_Tag_Value arrayMain:(NSMutableArray *)arrayMain bgView:(UIView *)bgView
{
//Add data
NSMutableArray *sArray = [[storedValue componentsSeparatedByString:#","] mutableCopy];
NSMutableArray *sTagArray = [[storedTag componentsSeparatedByString:#","] mutableCopy];
[sArray removeObject:#""];
[sTagArray removeObject:#""];
int loopCount = 1;
if (sTagArray.count != 0) {
int maxTag = [[sTagArray valueForKeyPath:#"#max.intValue"] intValue];
loopCount = [D_Tag_Value intValue] - maxTag;
}
for (int i = 0; i < loopCount ; i++) {
NSString *D_Value = [[arrayMain objectAtIndex:[D_Tag_Value intValue]] valueForKey:PARAMETER_KEY];
if (![sArray containsObject:D_Value])
{
[sArray addObject:D_Value];
[sTagArray addObject:D_Tag_Value];
}
//Add data
UIButton *btn = (UIButton *)[bgView viewWithTag:[D_Tag_Value intValue]];
[self setSelectedButtonStyle:btn];
}
storedValue = [[[sArray valueForKey:KEY_DESCRIPTION] componentsJoinedByString:#","] mutableCopy];
storedTag = [[[sTagArray valueForKey:KEY_DESCRIPTION] componentsJoinedByString:#","] mutableCopy];
[SEARCH_CRITERIAS setValue:storedValue forKey:D_Key];
[SEARCH_CRITERIAS_TAG setValue:storedTag forKey:D_Key];
}

Printing from NSMutableArray

So i want to print the users in an NSMutableArray. But the strings keep coming out as nil.
here is what i have:
int users = 0;
- (IBAction)addNewUser:(id)sender {
NSString *string;
string = userNameTextField.text;
[usernameArray insertObject:string atIndex:users];
users++;
[self showUsers];
}
-(void)showUsers{
for (int i = 0; i < users; i++){
NSString *s = textView.text;
NSString *add;
add = [NSString stringWithFormat:#"%# ",[usernameArray objectAtIndex:i]];
NSString *display = [NSString stringWithFormat:#"%# \n %#", s, add];
textView.text = display;
}
}
i have also tried
-(void)showUsers{
for (int i = 1; i < users; i++){
NSString *s = textView.text;
NSString *add;
add = [usernameArray objectAtIndex:i];
NSString *display = [NSString stringWithFormat:#"%# \n %#", s, add];
textView.text = display;
}
}
First of all try using more comprehensive names for the objects. I'm rewriting your code.
Common Causes for the problem : Array not initialized, you are starting your for cycle with int i equal to 1, so you are missing the object at index 0 at your mutable array. Try the following code.
#interface InterfaceName : InterfaceInherits <IfDelegate> {
int usersCount;
NSMutableArray * usernameArray;
}
#implementation InterfaceName
/*There's no more confident way to initialize a variable than in the init method of the class. */
-(id)init{
usersCount = 0;
//You have to be sure that your array is not nil
usernameArray = [NSMutableArray alloc]init]];
return self;
}
- (IBAction)addNewUser:(id)sender {
NSString *username = [usernameTextField text];
[usernameArray insertObject:username atIndex:usersCount];
usersCount++;
//I'll omit the display as I'm not sure what you were doing with it.
}
-(void)showUsers{
for (int i = 0; i < usersCount; i++){
NSString *retrievedUser = [usernameArray objectAtIndex:i];
NSString *display = [NSString stringWithFormat:#"User Retrieved : %#",retrievedUser];
textView.text = display;
}
}
#end

Resources