一.統(tǒng)計應(yīng)用中代碼的總行數(shù)
每一個程序員都想在開發(fā)完成之后知道總共寫了多少行的代碼,這里有一個比較簡單的方法烹植。
1斑鸦、打開終端
2、cd 進(jìn)入項(xiàng)目根目錄草雕,這里的根目錄可以是你這個項(xiàng)目的根目錄巷屿,也可以是你想統(tǒng)計那個文件夾的根目錄。這里以yykit為例子進(jìn)行測試墩虹。
3嘱巾、輸入命令 find . "(" -name "*.m" -or -name "*.mm" -or -name "*.cpp" -or -name "*.h" -or -name "*.rss" ")" -print | xargs wc -l ?這是可以統(tǒng)計每個文件的行數(shù)和總行數(shù)
find . -name "*.m" -or -name "*.h" -or -name "*.xib" -or -name "*.c" |xargs grep -v "^$"|wc -l ?這是可以直接統(tǒng)計總共的行數(shù),不顯示每個文件的行數(shù)
4诫钓、回車
說明:這里統(tǒng)計的行數(shù)包括系統(tǒng)頭文件里面的代碼旬昭,系統(tǒng)框架里面的代碼,所以比你自己寫的代碼要多一點(diǎn)菌湃。如果想統(tǒng)計自己寫了多少问拘,只需要cd到自己代碼的文件夾下,輸入find . "(" -name "*.m" -or -name "*.mm" -or -name "*.cpp" -or -name "*.h" -or -name "*.rss" ")" -print | xargs wc -l,回車即可骤坐。
二绪杏、在release版本禁止輸出NSLog內(nèi)容
前提:在XCode做開發(fā)調(diào)試時往往需要打印一些調(diào)試信息做debug用,大家知道當(dāng)打印信息的地方多了之后在模擬器上跑可能不會有什么問題或油,因?yàn)槟M器用的是電腦的硬件但是當(dāng)應(yīng)用跑在設(shè)備上時這些輸出語句會在很大程度上影響應(yīng)用的性能寞忿,針對這種問題可以寫一些宏來控制這些調(diào)試信息的輸出。
在release版本禁止輸出NSLog內(nèi)容
因?yàn)镹SLog的輸出還是比較消耗系統(tǒng)資源的顶岸,而且輸出的數(shù)據(jù)也可能會暴露出App里的保密數(shù)據(jù)腔彰,所以發(fā)布正式版時需要把這些輸出全部屏蔽掉。
我們可以在發(fā)布版本前先把所有NSLog語句注釋掉辖佣,等以后要調(diào)試時霹抛,再取消這些注釋,這實(shí)在是一件無趣而耗時的事卷谈!還好杯拐,還有更優(yōu)雅的解決方法,就是在項(xiàng)目的prefix.pch文件里加入下面一段代碼世蔗,加入后端逼,NSLog就只在Debug下有輸出,Release下不輸出了污淋。
如何實(shí)現(xiàn):
在-Prefix.pch(pch全稱是“precompiled header”顶滩,也就是預(yù)編譯頭文件,該文件里存放的工程中一些不常被修改的代碼寸爆,比如常用的框架頭文件礁鲁,這樣做的目的提高編譯器編譯速度。我們知道當(dāng)我們修改一個工程中某個文件代碼時候赁豆,編譯器并不是重新編譯所有所有文件仅醇,而是編譯改動過文件的,假如pch中某個文件修改了魔种,那么pch整個文件里包含的的其他文件也會重新編譯一次镇防,這樣就會消耗大量時間肩狂,所以它里面添加的文件最好是是很少變動或不變動的頭文件或者是預(yù)編譯的代碼片段骤素;)文件中添加
1#ifdef DEBUG
2#define NSLog(...) NSLog(__VA_ARGS__)
3#define debugMethod() NSLog(@"%s",__func__)
4#else
5#define NSLog(...)
6#define debugMethod()
7#endif
上段代碼的意思就是用宏指令做一個判斷膘茎,如果DEBUG為真售淡,則編譯#ifdef到#endif宏定義俘枫,否則編譯器就不編譯译断;這個DEBUG在哪設(shè)置呢谦炒,在"Target > Build Settings >
Preprocessor Macros > Debug"里有一個"DEBUG=1"挫剑。設(shè)置為Debug模式下去扣,Product-->Scheme-->SchemeEdit Scheme
設(shè)置Release,發(fā)布app版本的時候就不會打印了,提高了性能
三愉棱、修改項(xiàng)目APP名字后唆铐,在真機(jī)運(yùn)行報錯:
錯誤提醒:The provisioning profile specified in yourbuild settings (“haotian”) has an AppID of “com.basecom.vipose” which does notmatch your bundle identifier “com.baseus.iTemperature”.? Xcode can resolvethis issue by downloading a new provisioning profile from the Member Center.
原因:是我在xxx-info.plist中Bundle display name的值(用來改變APP在左邊顯示的名字),修改后再在真機(jī)運(yùn)行就報這個錯誤!奔滑,描述文件不對艾岂,設(shè)置好相應(yīng)的描述文件就行。
解決辦法:參考地址:http://stackoverflow.com/questions/1760518/codesign-error-provisioning-profile-cannot-be-found-after-deleting-expired-prof
這里所說的就是要通過修改你的項(xiàng)目的.xcodeproj文件來解決上述的錯誤朋其。
1.找到項(xiàng)目中的**.xcodeproj文件王浴,點(diǎn)擊右鍵,showpackage contents(打開包內(nèi)容)梅猿。
2.打開后找到project.pbxproj文件氓辣,用文本編輯器打開。其實(shí)就是右鍵袱蚓,點(diǎn)擊open就好了钞啸。
3.打開這個文件后,按command+F喇潘,在這個文件中查找“PROVISIONING_PROFILE",找到和這個“
四体斩、UUID和UDID 區(qū)別
UUID(Universally Unique IDentifier)是基于iOS設(shè)備上面某個單個的應(yīng)用程序,只要用戶沒有完全刪除應(yīng)用程序颖低,則這個UUID在用戶使用該應(yīng)用程序的時候一直保持不變絮吵。如果用戶刪除了這個應(yīng)用程序,然后再重新安裝枫甲,那么這個UUID已經(jīng)發(fā)生了改變源武。通過調(diào)用[[UIDevice currentDevice]identifierForVendor];方法可以獲取UUID。UUID不好的地方就是用戶刪除了你開發(fā)的程序以后想幻,基本上你就不可能獲取之前的數(shù)據(jù)了粱栖。
UDID(Unique Device Identifier)是一串由40位16進(jìn)制數(shù)組成的字符串,用以標(biāo)識唯一的設(shè)備脏毯,現(xiàn)在想通過代碼獲取是不可能的了闹究,如果你想看看你設(shè)備的UDID,可以通過iTunes來查看食店。蘋果從iOS5開始就移除了通過代碼訪問UDID的權(quán)限渣淤,所以碼農(nóng)啊,想知道用戶設(shè)備的UDID吉嫩,是不行的嘍价认。
五、Xcode6+環(huán)境下自娩,對iPhone5或iPhone5s模擬器用踩,在iOS7或iOS7.1下運(yùn)行,屏幕上下有黑邊
問題描述:
Xcode6環(huán)境下,對iPhone5或iPhone5s模擬器脐彩,在iOS7或iOS7.1下運(yùn)行碎乃,屏幕上下有黑邊。在iOS8下沒問題惠奸。
問題分析:
其實(shí)可以發(fā)現(xiàn)梅誓,不只是上下留白的問題,在這種狀態(tài)下LaunchScreen其實(shí)根本沒有加載佛南。
原因可想而知了梗掰。沒有相對應(yīng)的啟動圖。
Xcode6在Xcode5的基礎(chǔ)上共虑,做出的一些調(diào)整愧怜,其實(shí)并不是向下兼容的。Xcode5在啟動頁通過images asset進(jìn)行管理妈拌,而到了Xcode6拥坛,蘋果引入了一種新的啟動頁機(jī)制:LaunchScreen.xib,而這種機(jī)制恰恰是不向下兼容的尘分。Xcode6已經(jīng)自動為iPhone6以上的模擬器強(qiáng)制使用iOS8+系統(tǒng)猜惋,iPhone 6 (7.1)這種模擬器是不存在的(至少我沒有找到),因此對于iPhone 6以上的模擬器培愁,不存在這個問題著摔,對于iPhone 4s模擬器,在iOS7.1下也僅僅是沒有加載LaunchScreen.xib定续,這種蘋果最為古老的屏幕尺寸當(dāng)然不存在留白的狀況谍咆。但對于iPhone5/5s +
iOS7/7.1,由于iOS7無法兼容LaunchScreen.xib這種機(jī)制私股,而又找不到對應(yīng)的default-568h.png文件摹察,問題自然就出現(xiàn)了。
問題解決:
在項(xiàng)目配置頁倡鲸,General下面有一欄App Icons and Launch Images供嚎,其中有一項(xiàng)Launch Images Source,如果你遇到了問題峭状,那么旁邊顯示的可能是一個按鈕克滴,Use Asset
Catalog,點(diǎn)一下然后確定就可以了优床,會在項(xiàng)目中的Images.xcassets中生成LaunchImage劝赔,就像Xcode5中那樣。此時再運(yùn)行程序胆敞,已經(jīng)不會再有上下的黑邊問題了望忆。但為了更好的用戶體驗(yàn)罩阵,做張圖片放進(jìn)去吧。當(dāng)然启摄,在iOS8中依然會加載LaunchScreen.xib,看來至少二者是可以共存的幽钢。
六歉备、iOS開發(fā)6-Xcode使用第三方字體
Xcode自帶中文字體:PingFang
HK(香港)PingFang TC(繁體)PingFang SC(簡體)。但是我們有時候還是需要其他的中文字體匪燕,這時候就要將字體庫加入到工程中蕾羊。
目前網(wǎng)上有很多字體資源,這里推薦一個:http://font.knowsky.com/
1帽驯、將字體加入到工程中
檢查一下字體是否被加入到了project中龟再,可去Build?Phases下的Copy?Bundle?Resources中找一下。如果沒有尼变,點(diǎn)擊+來添加利凑。
2、設(shè)置info.plist文件
在info.plist文件中添加"Fonts provided by application"選項(xiàng).選擇+號嫌术,將字體文件名添加上哀澈。
3、使用字體
1.NSMutableArray*familyNames?=?[UIFontfamilyNames].mutableCopy;
2.[familyNamessortUsingSelector:@selector(compare:)];
3.for(NSString*familyName?in?familyNames?){
4.printf("Family:?%s?\n",?[familyNameUTF8String]?);
5.NSArray*fontNames?=?[UIFontfontNamesForFamilyName:familyName];
6.for(NSString*fontName?in?fontNames?){
7.printf("\tFont:?%s?\n",?[fontNameUTF8String]?);
8.}
9.}//打印出所有的字體
10./*
11.
12.Family:?FZCaiYun-M09//方正彩云
13.Font:?FZCYK--GBK1-0
14.Family:?FZKai-Z03//方正楷體
15.Font:?FZKTK--GBK1-0
16.Family:?Bradley?Gratis
17.Font:?BradleyGratis
18.
19.*/
20.
21.self.label1.text=@"hello";
22.self.label2.text=@"你好";
23.self.label3.text=@"你好";
24.//使用字體
25.self.label1.font=[UIFontfontWithName:@"BradleyGratis"size:20];
26.self.label2.font=[UIFontfontWithName:@"FZCaiYun-M09"size:20];
27.self.label3.font=[UIFontfontWithName:@"FZKTK--GBK1-0"size:20];
4度气、效果
七割按、用instancetype代替id作返回類型有什么好處?
蘋果在iOS 8中全面使用instancetype代替id
對于簡易構(gòu)造函數(shù)(convenience constructor)磷籍,應(yīng)該總是用instancetype适荣。編譯器不會自動將id轉(zhuǎn)化為instancetype。id是通用對象院领,但如果你用instancetype弛矛,編譯器就知道方法返回什么類型的對象。initializer的情況更復(fù)雜栅盲,當(dāng)你輸入
- (id)initWithBar:(NSInteger)bar
編譯器會假設(shè)你輸入了
- (instancetype)initWithBar:(NSInteger)bar
對于ARC而言汪诉,這是必須的。Clang Language Extensions的相關(guān)結(jié)果類型(Related result types)也講到了這一點(diǎn)谈秫。也許別人會據(jù)此告訴你不必使用instancetype扒寄,但我建議你用它。下面解釋我為什么如此建議拟烫。
使用instancetype有三點(diǎn)好處:
1该编、明確性。代碼只做你讓它做的事硕淑,而不是其他课竣。
2嘉赎、程式化。你會養(yǎng)成好習(xí)慣于樟,這些習(xí)慣在某些時候會很有用公条,而且肯定有用武之地。
3迂曲、一致性靶橱。讓代碼可讀性更好。
明確性
用instancetype代替id作為返回值的確沒有技術(shù)上的好處路捧。但這是因?yàn)榫幾g器自動將id轉(zhuǎn)化成了instancetype关霸。你以為init返回的值類型是id,其實(shí)編譯器返回了instancetype杰扫。
這兩行代碼對于編譯器來說是一樣的:
- (id)initWithBar:(NSInteger)bar; - (instancetype)initWithBar:(NSInteger)bar;
但在你眼里队寇,這兩行代碼卻不同。你不該學(xué)著忽視它章姓。
模式化
在使用init等方法時的確沒有區(qū)別佳遣,但在定義簡易構(gòu)造函數(shù)時就有區(qū)別了。
這兩行代碼并不等價:
+ (id)fooWithBar:(NSInteger)bar; + (instancetype)fooWithBar:(NSInteger)bar;
如果用instancetype作為函數(shù)的返回類型啤覆,就不會出錯苍日。
一致性:
最后,想象把所有東西放到一起時的情景:你想要一個init方法和一個簡易構(gòu)造函數(shù)窗声。
如果你用id來作為init函數(shù)的返回類型相恃,最終代碼如下:
- (id)initWithBar:(NSInteger)bar; + (instancetype)fooWithBar:(NSInteger)bar;
但如果你用instancetype,代碼如下:
- (instancetype)initWithBar:(NSInteger)bar; + (instancetype)fooWithBar:(NSInteger)bar;
代碼更加一致笨觅,可讀性更強(qiáng)拦耐。它們返回相同的東西,這一點(diǎn)一目了然见剩。
結(jié)論
除非你有意為舊編譯器寫代碼杀糯,不然你在合適的時候都應(yīng)該用instancetype。
在寫一條返回id的消息前苍苞,問自己:這個類返回實(shí)例嗎固翰?如果返回,用instancetype羹呵。
肯定有需要返回id的時候骂际,但你用instancetype的頻率應(yīng)該會更高。
八 NSbundle
bundle是一個目錄,其中包含了程序會使用到的資源.這些資源包含了如圖像,聲音,編譯好的代碼,nib文件(用戶也會把bundle稱為plug-in).對應(yīng)bundle,cocoa提供了類NSBundle.
我們的程序是一個bundle.在Finder中,一個應(yīng)用程序看上去和其他文件沒有什么區(qū)別.但是實(shí)際上它是一個包含了nib文件,編譯代碼,以及其他資源的目錄.我們把這個目錄叫做程序的main bundle
bundle中的有些資源可以本地化.例如,對于foo.nib,我們可以有兩個版本:一個針對英語用戶,一個針對法語用戶.在bundle中就會有兩個子目錄:English.lproj和French.lproj,我們把各自版本的foo.nib文件放到其中.當(dāng)程序需要加載foo.nib文件時,bundle會自動根據(jù)所設(shè)置的語言來加載.我們會在16章再詳細(xì)討論本地化
通過使用下面的方法得到程序的main bundle
NSBundle *myBundle= [NSBundle mainBundle];
一般我們通過這種方法來得到bundle.如果你需要其他目錄的資源,可以指定路徑來取得bundle
NSBundle*goodBundle;
goodBundle =[NSBundle bundleWithPath:@"~/.myApp/Good.bundle"];
一旦我們有了NSBundle對象,那么就可以訪問其中的資源了
// Extension is optional
NSString *path =[goodBundle pathForImageResource:@"Mom"];
NSImage *momPhoto= [[NSImage alloc] initWithContentsOfFile:path];
bundle中可以包含一個庫.如果我們從庫得到一個class, bundle會連接庫,并查找該類:
Class newClass =[goodBundle classNamed:@"Rover"];
id newInstance =[[newClass alloc] init];
如果不知到class名,也可以通過查找主要類來取得
Class aClass =[goodBundle principalClass];
id anInstance =[[aClass alloc] init];
可以看到, NSBundle有很多的用途.在這當(dāng)中, NSBundle負(fù)責(zé)(在后臺)加載nib文件.我們也可以不通過NSWindowController來加載nib文件,直接使用NSBundle:
BOOL successful =[NSBundle loadNibNamed:@"About" owner:someObject];
注意噢,我們指定了一個對象someObject作為nib的File's Owner
使用initWithContentsOfFile時冈欢,文件路徑的寫法使用initWithContentsOfFile方法可以通過讀取一個文件的內(nèi)容來初始化對象歉铝。但文件的路徑應(yīng)該怎么確定呢?可以使用NSBundle的對象來獲取凑耻。例如當(dāng)前程序所在目錄下有個文件re.xml太示,我們要將該文件的內(nèi)容做為NSData的數(shù)據(jù)源來初始化一個NSData對象柠贤,可以用下面的方法來實(shí)現(xiàn):?
NSString *filePath= [[NSBundle mainBundle] pathForResouse:@"re" ofType:@"xml"];NSData *data = [[NSData alloc] initWithContentsOfFile:filePath];
讀取plist中的內(nèi)容:
NSString *dataPath= [[NSBundle mainBundle] pathForResource:@"Data"ofType:@"plist"]; self.data = [NSArrayarrayWithContentsOfFile:dataPath];
刪除本地文件
NSString * thePath=[selfgetUserDocumentDirectoryPath];
NSMutableString *fullPath=[[[NSMutableString alloc]init]autorelease];
[fullPathappendString:thePath];
NSString *idString=[idArray objectAtIndex:indexPath.row];
NSString *coverName=[NSString stringWithFormat:@"/%@.jpg",idString];
[fullPathappendString:coverName];
NSFileManager*defaultManager;
defaultManager =[NSFileManager defaultManager];
-(BOOL)removeItemAtPath:(NSString *)path error:(NSError **)error BOOLboolValue=[defaultManager removeItemAtPath: fullPath error: nil];
if (boolValue) {
NSLog(@"removecover image ok");
}
-(NSString*)getUserDocumentDirectoryPath {
NSArray* array =NSSearchPathForDirectoriesInDomains( NSDocumentDirectory, NSUserDomainMask,YES);
if([array count])
return [arrayobjectAtIndex: 0];
else return@"";
} ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
九、數(shù)組和鏈表的區(qū)別
二者都屬于一種數(shù)據(jù)結(jié)構(gòu)
從邏輯結(jié)構(gòu)來看
1.數(shù)組必須事先定義固定的長度(元素個數(shù))类缤,不能適應(yīng)數(shù)據(jù)動態(tài)地增減的情況臼勉。當(dāng)數(shù)據(jù)增加時,可能超出原先定義的元素個數(shù)呀非;當(dāng)數(shù)據(jù)減少時坚俗,造成內(nèi)存浪費(fèi);數(shù)組可以根據(jù)下標(biāo)直接存取岸裙。
2.鏈表動態(tài)地進(jìn)行存儲分配,可以適應(yīng)數(shù)據(jù)動態(tài)地增減的情況速缆,且可以方便地插入降允、刪除數(shù)據(jù)項(xiàng)。(數(shù)組中插入艺糜、刪除數(shù)據(jù)項(xiàng)時剧董,需要移動其它數(shù)據(jù)項(xiàng),非常繁瑣)鏈表必須根據(jù)next指針找到下一個元素
從內(nèi)存存儲來看
1. (靜態(tài))數(shù)組從棧中分配空間,對于程序員方便快速,但是自由度小
2.鏈表從堆中分配空間,自由度大但是申請管理比較麻煩
從上面的比較可以看出破停,如果需要快速訪問數(shù)據(jù)翅楼,很少或不插入和刪除元素,就應(yīng)該用數(shù)組真慢;相反毅臊,如果需要經(jīng)常插入和刪除元素就需要用鏈表數(shù)據(jù)結(jié)構(gòu)了。
十黑界、objectForKey:和valueForKey區(qū)別
從NSDictionary取值的時候有兩個方法管嬉,objectForKey:和valueForKey:,這兩個方法具體有什么不同呢朗鸠?
先從NSDictionary文檔中來看這兩個方法的定義:
objectForKey: returns the value associated
with aKey, or nil if no value is associated with aKey.返回指定key的value蚯撩,若沒有這個key返回nil.
valueForKey: returns the value associated
with a given key.同樣是返回指定key的value。
直觀上看這兩個方法好像沒有什么區(qū)別烛占,但文檔里valueForKey:有額外一點(diǎn):
If key does not start with “@”, invokesobjectForKey:. If key does start with “@”, strips the “@” and invokes [supervalueForKey:] with the rest of the key. via Discussion
一般來說key可以是任意字符串組合胎挎,如果key不是以@符號開頭,這時候valueForKey:等同于objectForKey:忆家,如果是以@開頭犹菇,去掉key里的@然后用剩下部分作為key執(zhí)行[super valueForKey:]。
比如:
NSDictionary*dict = [NSDictionary dictionaryWithObject:@"theValue"
forKey:@"theKey"];
NSString*value1 = [dict objectForKey:@"theKey"];
NSString*value2 = [dict valueForKey:@"theKey"];
這時候value1和value2是一樣的結(jié)果弦赖。如果是這樣一個dict:
NSDictionary*dict = [NSDictionary dictionaryWithObject:@"theValue"
forKey:@"@theKey"];//注意這個key是以@開頭
NSString*value1 = [dict objectForKey:@"@theKey"];
NSString*value2 = [dict valueForKey:@"@theKey"];
value1可以正確取值项栏,但是value2取值會直接crash掉,報錯信息:
Terminating app due to uncaught exception‘NSUnknownKeyException’, reason: ‘[<__NSCFDictionary 0x892fd80>valueForUndefinedKey:]: this class is not key value coding-compliant for thekey theKey.’
這是因?yàn)関alueForKey:是KVC(NSKeyValueCoding)的方法蹬竖,在KVC里可以通過property同名字符串來獲取對應(yīng)的值沼沈。比如:
@interface Person : NSObject
@property (nonatomic, retain) NSString*name;
@end
Person *person= [[Person alloc] init];
person.name = @"fannheyward";
NSLog(@"name:%@",[person name]);
NSLog(@"name:%@",[person valueForKey:@"name"]);
valueForKey:取值是找和指定key同名的property
accessor流酬,沒有的時候執(zhí)行valueForUndefinedKey:,而valueForUndefinedKey:的默認(rèn)實(shí)現(xiàn)是拋出NSUndefinedKeyException異常列另。
回過頭來看剛才crash的例子芽腾,[dict valueForKey:@"@theKey"];會把key里的@去掉,也就變成了[dict valueForKey:@"theKey"];页衙,而dict不存在theKey這樣的property摊滔,轉(zhuǎn)而執(zhí)行[dict
valueForUndefinedKey:@"theKey"];,拋出NSUndefinedKeyException異常后crash掉店乐。
objectForKey:和valueForKey:在多數(shù)情況下都是一樣的結(jié)果返回艰躺,但是如果key是以@開頭,valueForKey:就成了一個大坑眨八,建議在NSDictionary下只用objectForKey:來取值腺兴。