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