1、網(wǎng)絡(luò)請(qǐng)求封裝采用類方法還是單例模式?
2、extern字符串常量,宏定義字符串常量怎么選?
const常量有數(shù)據(jù)類型,而宏常量沒(méi)有數(shù)據(jù)類型。編譯器可以對(duì)前者進(jìn)行類型安全檢查,而對(duì)后者只進(jìn)行字符替換,沒(méi)有類型安全檢查,并且在字符替換時(shí)可能會(huì)產(chǎn)生意料不到的錯(cuò)誤(邊際效應(yīng))。StackOverflow鏈接
// Constants.h
FOUNDATION_EXPORT NSString *const MyFirstConstant;
FOUNDATION_EXPORT NSString *const MySecondConstant;
//etc.
(you can use extern instead of FOUNDATION_EXPORT if your code will not be used in mixed C/C++ environments or on other platforms)
You can include this file in each file that uses the constants or in the pre-compiled header for the project.
You define these constants in a .m file like
// Constants.m
NSString *const MyFirstConstant = @"FirstConstant";
NSString *const MySecondConstant = @"SecondConstant";
Constants.m should be added to your application/framework's target so that it is linked in to the final product.
The advantage of using string constants instead of #define'd constants is that you can test for equality using pointer comparison (stringInstance == MyFirstConstant) which is much faster than string comparison ([stringInstance isEqualToString:MyFirstConstant]) (and easier to read, IMO).
3、文本添加超鏈接
NSAttributedString *linkStr = [[NSAttributedString alloc] initWithString:tagName attributes:@{NSLinkAttributeName: urlStr}];
[attributeString replaceCharactersInRange:range withAttributedString:linkStr];
[attributeString addAttribute:NSLinkAttributeName value:urlStr range:NSMakeRange(0, linkStr.length)];
4、YYModel使用
+ (NSDictionary *)modelCustomPropertyMapper {
return @{@"messageId":@"i",
@"content":@"c",
@"time":@"t"};
}
- (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic {
uint64_t timestamp = [dic unsignedLongLongValueForKey:@"t" default:0];
self.time = [NSDate dateWithTimeIntervalSince1970:timestamp / 1000.0];
return YES;
}
5、Copy與MutableCopy
對(duì)于非集合對(duì)象
copy:因?yàn)閏opy默認(rèn)返回的是不可變的,所以當(dāng)我們對(duì)一個(gè)不可變的字符串進(jìn)行copy的時(shí)候,我們只是拷貝了它的指針(淺拷貝)。當(dāng)我們對(duì)一個(gè)可變的字符串進(jìn)行拷貝的時(shí)候,因?yàn)轭愋娃D(zhuǎn)變了,我們需對(duì)其進(jìn)行深拷貝。
mutableCopy:默認(rèn)返回的是一個(gè)可變的對(duì)象,適用于可變的對(duì)象,例如NSMutableString,NSMutableArray,NSMutableDictionary、etc。 無(wú)論對(duì)于可變的字符串還是不可變的字符串進(jìn)行mutableCopy,系統(tǒng)都默認(rèn)進(jìn)行深拷貝,那么為什么對(duì)于相同類型的進(jìn)行mutableCopy返回的仍然是新的對(duì)象呢,因?yàn)樵谶@里系統(tǒng)要保證,舊的對(duì)象和新的對(duì)象都是可變的,且他們之前不會(huì)相互影響。
對(duì)于集合對(duì)象
對(duì)于不可變的集合對(duì)象,copy 是淺拷貝,mutableCopy 是單層深拷貝。
對(duì)于可變的集合對(duì)象,無(wú)論 copy 或者 mutableCopy 都是單層深拷貝。
6、通過(guò)圖片Data數(shù)據(jù)第一個(gè)字節(jié) 來(lái)獲取圖片擴(kuò)展名
- (NSString *)contentTypeForImageData:(NSData *)data {
uint8_t c;
[data getBytes:&c length:1];
switch (c)
{
case 0xFF:
return @"jpeg";
case 0x89:
return @"png";
case 0x47:
return @"gif";
case 0x49:
case 0x4D:
return @"tiff";
case 0x52:
if ([data length] < 12) {
return nil;
}
NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(0, 12)] encoding:NSASCIIStringEncoding];
if ([testString hasPrefix:@"RIFF"]
&& [testString hasSuffix:@"WEBP"])
{
return @"webp";
}
return nil;
}
return nil;
}
7、GET、POST請(qǐng)求
GET
在請(qǐng)求URL后面以?的形式跟上發(fā)給服務(wù)器的參數(shù),多個(gè)參數(shù)之間用&隔開(kāi),比如http://ww.test.com/login?username=123&pwd=234&type=JSON
注意:由于瀏覽器和服務(wù)器對(duì)URL長(zhǎng)度有限制,因此在URL后面附帶的參數(shù)是有限制的,通常不能超過(guò)1KB
POST
發(fā)給服務(wù)器的參數(shù)全部放在請(qǐng)求體中
理論上,POST傳遞的數(shù)據(jù)量沒(méi)有限制(具體還得看服務(wù)器的處理能力)
選擇GET和POST的建議
(1)如果要傳遞大量數(shù)據(jù),比如文件上傳,只能用POST請(qǐng)求
(2)GET的安全性比POST要差些,如果包含機(jī)密\敏感信息,建議用POST
(3)如果僅僅是索取數(shù)據(jù)(數(shù)據(jù)查詢),建議使用GET
(4)如果是增加、修改、刪除數(shù)據(jù),建議使用POST
8、圖片的縮放
原圖為200*100,放在100*100的imageView上
// 改變內(nèi)容的高寬比例,縮放內(nèi)容,UIView中完整顯示內(nèi)容,填滿UIView
case scaleToFill
// 保持內(nèi)容的高寬比,縮放內(nèi)容,完整顯示內(nèi)容,最大化填充UIview,沒(méi)填充上的區(qū)域透明
case scaleAspectFit
// 保持內(nèi)容高寬比,縮放內(nèi)容,超出視圖的部分內(nèi)容會(huì)被裁減,填充UIView
case scaleAspectFill
9、防止離屏渲染,為image添加圓角
// image分類
- (UIImage *)circleImage
{
// NO代表透明
UIGraphicsBeginImageContextWithOptions(self.size, NO, 1);
// 獲得上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 添加一個(gè)圓
CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);
// 方形變圓形
CGContextAddEllipseInRect(ctx, rect);
// 裁剪
CGContextClip(ctx);
// 將圖片畫上去
[self drawInRect:rect];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
10、鍵盤上方增加工作欄
UIToolbar *keyboardDoneButtonView = [[UIToolbar alloc] init];
[keyboardDoneButtonView sizeToFit];
UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithTitle:@"Done"
style:UIBarButtonItemStyleBordered target:self
action:@selector(doneClicked:)];
[keyboardDoneButtonView setItems:[NSArray arrayWithObjects:doneButton, nil]];
txtField.inputAccessoryView = keyboardDoneButtonView;
11、將一個(gè)View保存成PDF
- (void)createPDFfromUIView:(UIView)aView saveToDocumentsWithFileName:(NSString)aFilename
{
NSMutableData *pdfData = [NSMutableData data];
UIGraphicsBeginPDFContextToData(pdfData, aView.bounds, nil);
UIGraphicsBeginPDFPage();
CGContextRef pdfContext = UIGraphicsGetCurrentContext();
[aView.layer renderInContext:pdfContext];
UIGraphicsEndPDFContext();
NSArray* documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);
NSString* documentDirectory = [documentDirectories objectAtIndex:0];
NSString* documentDirectoryFilename = [documentDirectory stringByAppendingPathComponent:aFilename];
[pdfData writeToFile:documentDirectoryFilename atomically:YES];
NSLog(@"documentDirectoryFileName: %@",documentDirectoryFilename);
}
12、weak關(guān)鍵詞的使用
weak 的用處用一句話可歸納為:弱引用,在對(duì)象釋放后置為 nil,避免錯(cuò)誤的內(nèi)存訪問(wèn)。用更通俗的話來(lái)表述是:weak 可以在不增加對(duì)象的引用計(jì)數(shù)的同時(shí),又使得指針的訪問(wèn)是安全的。
13、 @IBOutlet 的 didSet
如果我們由于某種原因,確實(shí)需要在代碼中設(shè)置一些 view 的屬性,在連接 @IBOutlet 后,不少開(kāi)發(fā)者會(huì)選擇在 viewDidLoad 中進(jìn)行設(shè)置。其實(shí)個(gè)人認(rèn)為一個(gè)更合適的地方是在該 @IBoutlet 的 didSet 中進(jìn)行。@IBoutlet 所修飾的也是一個(gè)屬性,這個(gè)關(guān)鍵詞所做的僅只是將屬性暴露給 IB,所以它的各種屬性觀察方法 (willSet,didSet 等) 也會(huì)被正常調(diào)用。比如,下面我們實(shí)際項(xiàng)目中的一段代碼:
@IBOutlet var myTextField: UITextField! {
didSet {
// Workaround for https://openradar.appspot.com/28751703
myTextField.layer.borderWidth = 1.0
myTextField.layer.borderColor = UIColor.lineGreen.cgColor
}
}
14、Swift進(jìn)階
1、Swift風(fēng)格指南
對(duì)于命名,在使用時(shí)能清晰表意是最重要。因?yàn)?API 被使用的次數(shù)要遠(yuǎn)遠(yuǎn)多于被聲明的次數(shù),所以我們應(yīng)當(dāng)從使用者的角度來(lái)考慮它們的名字。盡快熟悉 Swift API 設(shè)計(jì)準(zhǔn)則,并且在你自己的代碼中堅(jiān)持使用這些準(zhǔn)則。
簡(jiǎn)潔經(jīng)常有助于代碼清晰,但是簡(jiǎn)潔本身不應(yīng)該獨(dú)自成為我們編碼的目標(biāo)。
務(wù)必為函數(shù)添加文檔注釋 — 特別是泛型函數(shù)。
類型使用大寫字母開(kāi)頭,函數(shù)、變量和枚舉成員使用小寫字母開(kāi)頭,兩者都使用駝峰式命名法。
使用類型推斷。省略掉顯而易見(jiàn)的類型會(huì)有助于提高可讀性。
如果存在歧義或者在進(jìn)行定義的時(shí)候不要使用類型推斷。(比如 func 就需要顯式地指定返回類型)
優(yōu)先選擇結(jié)構(gòu)體,只在確實(shí)需要使用到類特有的特性或者是引用語(yǔ)義時(shí)才使用類。
除非你的設(shè)計(jì)就是希望某個(gè)類被繼承使用,否則都應(yīng)該將它們標(biāo)記為 final。
除非一個(gè)閉包后面立即跟隨有左括號(hào),否則都應(yīng)該使用尾隨閉包 (trailing closure) 的語(yǔ)法。
使用 guard 來(lái)提早退出方法。
避免對(duì)可選值進(jìn)行強(qiáng)制解包和隱式強(qiáng)制解包。它們偶爾有用,但是經(jīng)常需要使用它們的話往往意味著有其他不妥的地方。
不要寫重復(fù)的代碼。如果你發(fā)現(xiàn)你寫了好幾次類似的代碼片段的話,試著將它們提取到一個(gè)函數(shù)里,并且考慮將這個(gè)函數(shù)轉(zhuǎn)化為協(xié)議擴(kuò)展的可能性。
試著去使用 map 和 reduce,但這不是強(qiáng)制的。當(dāng)合適的時(shí)候,使用 for 循環(huán)也無(wú)可厚非。高階函數(shù)的意義是讓代碼可讀性更高。但是如果使用 reduce 的場(chǎng)景難以理解的話,強(qiáng)行使用往往事與愿違,這種時(shí)候簡(jiǎn)單的 for 循環(huán)可能會(huì)更清晰。
試著去使用不可變值:除非你需要改變某個(gè)值,否則都應(yīng)該使用 let 來(lái)聲明變量。不過(guò)如果能讓代碼更加清晰高效的話,也可以選擇使用可變的版本。用函數(shù)將可變的部分封裝起來(lái),可以把它帶來(lái)的副作用進(jìn)行隔離。
Swift 的泛型可能會(huì)導(dǎo)致非常長(zhǎng)的函數(shù)簽名。壞消息是我們現(xiàn)在除了將函數(shù)聲明強(qiáng)制寫成幾行以外,對(duì)此并沒(méi)有什么好辦法。我們會(huì)在示例代碼中在這點(diǎn)上保持一貫性,這樣你能看到我們是如何處理這個(gè)問(wèn)題的。
除非你確實(shí)需要,否則不要使用 self.。在閉包表達(dá)式中,使用 self 是一個(gè)清晰的信號(hào),表明閉包將會(huì)捕獲 self。
盡可能地對(duì)現(xiàn)有的類型和協(xié)議進(jìn)行擴(kuò)展,而不是寫一些全局函數(shù)。這有助于提高可讀性,讓別人更容易發(fā)現(xiàn)你的代碼。
2、數(shù)組
想要迭代數(shù)組? for x in array
想要迭代除了第一個(gè)元素以外的數(shù)組其余部分? for x in array.dropFirst()
想要迭代除了最后 5 個(gè)元素以外的數(shù)組? for x in array.dropLast(5)
想要列舉數(shù)組中的元素和對(duì)應(yīng)的下標(biāo)? for (num, element) in collection.enumerated()
想要尋找一個(gè)指定元素的位置? if let idx = array.index { someMatchingLogic($0) }
想要對(duì)數(shù)組中的所有元素進(jìn)行變形? array.map { someTransformation($0) }
想要篩選出符合某個(gè)標(biāo)準(zhǔn)的元素? array.filter { someCriteria($0) }
函數(shù)的使用
map 和 flatMap — 如何對(duì)元素進(jìn)行變換
filter — 元素是否應(yīng)該被包含在結(jié)果中
reduce — 如何將元素合并到一個(gè)總和的值中
sequence — 序列中下一個(gè)元素應(yīng)該是什么?
forEach — 對(duì)于一個(gè)元素,應(yīng)該執(zhí)行怎樣的操作
sort,lexicographicCompare 和 partition — 兩個(gè)元素應(yīng)該以怎樣的順序進(jìn)行排列
index,first 和 contains — 元素是否符合某個(gè)條件
min 和 max — 兩個(gè)元素中的最小/最大值是哪個(gè)
elementsEqual 和 starts — 兩個(gè)元素是否相等
split — 這個(gè)元素是否是一個(gè)分割符
Range
只有半開(kāi)范圍能夠表達(dá)空區(qū)間的概念 (當(dāng)范圍的上下邊界相等時(shí),比如 5..<5)。
只有閉合范圍能夠包含它的元素類型所能表達(dá)的最大值 (比如 0...Int.max)。半開(kāi)范圍總是最少會(huì)有一個(gè)值比范圍所表達(dá)的只要大。
函數(shù):
函數(shù)可以像 Int 或者 String 那樣被賦值給變量,也可以作為另一個(gè)函數(shù)的輸入?yún)?shù),或者另一個(gè)函數(shù)的返回值來(lái)使用。
函數(shù)能夠捕獲存在于其局部作用域之外的變量。
有兩種方法可以創(chuàng)建函數(shù),一種是使用 func 關(guān)鍵字,另一種是 { }。在 Swift 中,后一種被稱為閉包表達(dá)式。