以前覺得這種標題有點偏向於理論,實際開發中怎麼會有這種詭異的需求,但是真正遇到了這種硬需求時覺得還是有那麼點價值的,理論付諸了實踐在此也就做了個整理。
以我私下開發中的一處程式碼為例,本意是希望有這麼一個方法:能夠傳入一個開始標記(NSString*)一個結束標記(NSString*)一段文字(NSString*) 然後內部在文字中掃描並返回標記包裹內容的範圍(NSRange這個範圍是忽視標記的)這個範圍可能會有多個所以返回的應該是一個裝著range的陣列。並且順便把原來字串中的開始和結束標記全過濾掉,把過濾後的字串也返回出來。
舉個例子就是:傳入開始標記“ 結束標記“>” 一段文字 “會議需要叫上和” 然後希望返回一個陣列 [{location:6,length:2},{location:9,length:2}] ,和返回處理後的字串“會議需要叫上彼得和羅賓”。
程式碼希望能夠寫成這樣,但是是不可能的。
1 |
- (NSArray *,NSMutableString *)scanBeginStr:(NSString *)beginstr endStr:(NSString *)endstr inText:(NSMutableString *)text |
好下面提供三種途徑完成此需求。
1.使用字典
這種方法是最low但是最容易理解的,就是如果你需要返回多個物件,直接將多個物件塞在一個字典裡面自己設定合理的key並返回字典,字典裡面可以放任意數量的“返回值”。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
- (NSDictionary *)scanBeginStr:(NSString *)beginstr endStr:(NSString *)endstr inText:(NSMutableString *)text{ NSRange range1,range2; NSUInteger location =0,length=0; range1.location = 0; NSMutableArray *rangeArray = [NSMutableArray array]; while (range1.location != NSNotFound) { range1 = [text rangeOfString:beginstr]; range2 = [text rangeOfString:endstr]; if (range1.location != NSNotFound) { location = range1.location; length = range2.location - range1.location - 1; if (length > 5000)break; [text replaceOccurrencesOfString:beginstr withString:@"" options:NSCaseInsensitiveSearch range:NSMakeRange(0, range1.location + range1.length)]; [text replaceOccurrencesOfString:endstr withString:@"" options:NSCaseInsensitiveSearch range:NSMakeRange(0, range2.location + range2.length - 1)]; } [rangeArray addObject:@{@"location":@(location),@"length":@(length)}]; } return @{@"rangeArray":rangeArray,@"text":text}; } |
這個方法在呼叫時也就是這樣了,非常樸實的程式碼。
1 2 3 |
NSDictionary* result = [self scanBegin2Str:@"" inText:mutableText]; NSArray *rangeArray = result[@"rangeArray"]; NSMutableString *text = [result[@"text"] mutableCopy]; |
如果覺得字典不舒服也完全可以用模型,自定義一個物件然後給這個物件的各個屬性賦值然後再把這個自定義物件返回回去,雖然程式碼看上去更科學一點但是需要寫一些額外的程式碼並且不能實現任意可配置(每一種屬性都必須要提前設定好),這個和上面算是一個相同的思路就不單獨再列一條說了。
2.使用指標的指標
這種方法是我實際使用的方法,就是把需要修改的text的指標的指標傳進去,然後在方法的內部對這個實參取一下值得到text的指標。然後通過這個指標修改外部的變數的值。程式碼實現如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
- (NSArray *)scanBeginStr:(NSString *)beginstr endStr:(NSString *)endstr inText:(NSMutableString * *)textPointer{ NSRange range1,range2; NSUInteger location =0,length=0; range1.location = 0; NSMutableString *text = *textPointer; NSMutableArray *rangeArray = [NSMutableArray array]; while (range1.location != NSNotFound) { range1 = [text rangeOfString:beginstr]; range2 = [text rangeOfString:endstr]; if (range1.location != NSNotFound) { location = range1.location; length = range2.location - range1.location - 1; if (length > 5000)break; [text replaceOccurrencesOfString:beginstr withString:@"" options:NSCaseInsensitiveSearch range:NSMakeRange(0, range1.location + range1.length)]; [text replaceOccurrencesOfString:endstr withString:@"" options:NSCaseInsensitiveSearch range:NSMakeRange(0, range2.location + range2.length - 1)]; } [rangeArray addObject:@{@"location":@(location),@"length":@(length)}]; } return rangeArray; } |
這個方法在呼叫時就這麼寫了,因為mutabletext的修改是無聲無息的。
1 2 |
NSArray *rangeArray = [self scanBegin3Str:@"" inText:&mutableText]; // 董鉑然部落格園 |
3.使用block回撥
這種方法實際上嚴格意義來說不能算返回值,但是能夠實現返回值的效果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
- (void)scanBeginStr:(NSString *)beginstr endStr:(NSString *)endstr inText:(NSMutableString *)text result:(void(^)(NSArray *rangeArray,NSMutableString *text))result{ NSRange range1,range2; NSUInteger location =0,length=0; range1.location = 0; NSMutableArray *rangeArray = [NSMutableArray array]; while (range1.location != NSNotFound) { range1 = [text rangeOfString:beginstr]; range2 = [text rangeOfString:endstr]; if (range1.location != NSNotFound) { location = range1.location; length = range2.location - range1.location - 1; if (length > 5000)break; [text replaceOccurrencesOfString:beginstr withString:@"" options:NSCaseInsensitiveSearch range:NSMakeRange(0, range1.location + range1.length)]; [text replaceOccurrencesOfString:endstr withString:@"" options:NSCaseInsensitiveSearch range:NSMakeRange(0, range2.location + range2.length - 1)]; } [rangeArray addObject:@{@"location":@(location),@"length":@(length)}]; } result(rangeArray,text); } |
這個block在使用時可能比較特殊就這麼寫了
1 2 3 |
[self scanBeginStr:@"" inText:mutabletext result:^(NSArray *rangeArray, NSMutableString *text) { NSLog(@"%@,%@",rangeArray,text); }]; |
如果把block的返回值寫成一個字典或是模型也可以,但是那就多此一舉了。 返回值不能嘗試結構體型別,結構體內不能用OC物件只能用基本資料型別。
其實感覺還有別的方法,比如設定N個成員變數在方法內部計算後重新set也完全可以,但是可能大家也知道成員變數多了比較噁心。最近比較火的函數語言程式設計一直在倡導“方法內不能產生副作用”“實現引用透明” ,如果這麼看那後兩種方法就不符合FP的規則了,但是用著也有自己的特色。