iOS - 常見問題的整理(一)

一.通知

對于通知,大家想必都不陌生饭豹,它是一個單例鸵赖,允許當(dāng)事件發(fā)生時通知一些對象务漩,讓我們在低程度耦合的情況下,來達(dá)到通信的目的它褪。

通知的優(yōu)勢:
1.不需要編寫太多代碼饵骨,實現(xiàn)比較簡單
2.對于一個發(fā)出的通知,可以多個對象作出反應(yīng)茫打,即是說通知是一對多的形式

通知的缺點:
1.在編譯期不會檢查通知是否能夠被觀察者正確處理
2.在釋放注冊的對象時居触,需要在通知中心取消注冊
3.在調(diào)試應(yīng)用時,難以跟蹤程序
4.發(fā)出通知后老赤,不能夠從觀察者那里獲取任何反饋信息

通知的基本實現(xiàn):

- (void)viewDidLoad {
    [super viewDidLoad];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(test) name:@"test" object:nil];
    NSLog(@"注冊通知 - %@",[NSThread currentThread]);
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    [[NSNotificationCenter defaultCenter] postNotificationName:@"test" object:nil];
    NSLog(@"發(fā)送通知完成 - %@",[NSThread currentThread]);

}

- (void)test {
    NSLog(@"接收到通知 - %@",[NSThread currentThread]);
    sleep(3);
}

打印結(jié)果:

2017-06-13 16:53:01.040 通知的基本使用[24531:3283934] 注冊通知 - <NSThread: 0x600000079c80>{number = 1, name = main}
2017-06-13 16:53:10.334 通知的基本使用[24531:3283934] 接收到通知 - <NSThread: 0x600000079c80>{number = 1, name = main}
2017-06-13 16:53:13.335 通知的基本使用[24531:3283934] 發(fā)送通知完成 - <NSThread: 0x600000079c80>{number = 1, name = main}

注意打印結(jié)果:在test方法執(zhí)行完畢之后轮洋,才會打印發(fā)送完成的log。

如果在子線程發(fā)送通知:

- (void)viewDidLoad {
    [super viewDidLoad];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(test) name:@"test" object:nil];
    NSLog(@"注冊通知 - %@",[NSThread currentThread]);
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
        
        NSNotification *notification = [NSNotification notificationWithName:@"test"
                                                                     object:nil];
        // NSPostASAP是接收不到通知的 要使用NSPostNow
        [[NSNotificationQueue defaultQueue] enqueueNotification:notification postingStyle:NSPostNow];
        NSLog(@"發(fā)送通知完成 - %@",[NSThread currentThread]);
        });
}

- (void)test {
    NSLog(@"接收到通知 - %@",[NSThread currentThread]);
    sleep(3);
}

打印結(jié)果:

2017-06-13 17:05:01.133 通知的基本使用[25191:3296062] 注冊通知 - <NSThread: 0x608000076440>{number = 1, name = main}
2017-06-13 17:05:02.423 通知的基本使用[25191:3296125] 接收到通知 - <NSThread: 0x608000267980>{number = 3, name = (null)}
2017-06-13 17:05:05.523 通知的基本使用[25191:3296125] 發(fā)送通知完成 - <NSThread: 0x608000267980>{number = 3, name = (null)}

得出結(jié)論:接收通知的線程和發(fā)送通知的線程是一樣的抬旺,如果在實際開發(fā)過程中弊予,我們是在子線程中發(fā)送通知的,在接收到通知之后开财,需要刷新UI等操作汉柒,一定要回到主線程。

- (void)viewDidLoad {
    [super viewDidLoad];
     _observe = [[NSNotificationCenter defaultCenter] addObserverForName:@"test" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
       NSLog(@"接收到通知 - %@",[NSThread currentThread]);
        sleep(3);   
    }];
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
        [[NSNotificationCenter defaultCenter] postNotificationName:@"test" object:nil];
        NSLog(@"發(fā)送通知完成 - %@",[NSThread currentThread]);
        });
}

打印結(jié)果:

2017-06-13 18:21:38.367 通知的基本使用[29365:3382047] 接收到通知 - <NSThread: 0x600000063d80>{number = 1, name = main}
2017-06-13 18:21:41.368 通知的基本使用[29365:3382100] 發(fā)送通知完成 - <NSThread: 0x600000071bc0>{number = 3, name = (null)}

得出結(jié)論:使用NSOperationQueue可以讓接收通知的線程和發(fā)送通知的線程不一樣责鳍,讓接收通知的線程在主線程碾褂,就可以刷新UI等操作了。

二.Xcode何時會報unrecognized selector 的錯誤

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    WWPerson *person = [[WWPerson alloc] init];
    [person test];
}

當(dāng)向person發(fā)送test這個消息時历葛,runtime庫會根據(jù)對象的isa指針找到該對象實際所屬的類正塌,然后在該類的方法列表以及父類的方法列表里面找相應(yīng)的方法運行,如果在最頂層的父類中依然找不到相應(yīng)的方法實現(xiàn)時恤溶,程序在運行時就會報unrecognized selector sent to的錯誤并且崩潰乓诽,但是在此之前,objc的運行時給出了避免程序崩潰的三次機會宏娄。

  1. Method resolution
    objc運行時會調(diào)用+resolveInstanceMethod:或者+resolveClassMethod:问裕,讓我們有機會提供一個函數(shù)實現(xiàn)而不導(dǎo)致程序崩潰,如果在這里面添加了函數(shù)孵坚,系統(tǒng)就會重新啟動一次消息發(fā)送的過程粮宛,否則就會移到下一步的消息轉(zhuǎn)發(fā)。
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == NSSelectorFromString(@"test")) {
        /**
          class: 給哪個類添加方法
          SEL: 添加哪個方法
          IMP: 方法實現(xiàn) => 函數(shù) => 函數(shù)入口 => 函數(shù)名
          type: 方法類型:void用v來表示卖宠,id參數(shù)用@來表示巍杈,SEL用:來表示
         */
        class_addMethod(self, sel, (IMP)test, "v@:@");
        return YES;
    }else {
        return [super resolveClassMethod:sel];
    }
}

void test(id self, SEL _cmd, NSNumber *meter) {
    NSLog(@"測試 - WWPerson");
}

2.Fast forwarding
如果目標(biāo)對象實現(xiàn)了-forwardingTargetForSelector:的方法,runtime就會調(diào)用這個方法扛伍,給我們一個機會把這個消息轉(zhuǎn)發(fā)給其他的對象筷畦,只要這個方法返回值不是nilself,整個消息發(fā)送的過程就會被重啟,這時發(fā)送的對象會變成我們返回的這個對象鳖宾,否則就會移到下一步吼砂。

- (id)forwardingTargetForSelector:(SEL)aSelector {
    WWTarget *target = [[WWTarget alloc] init];
    if ([target respondsToSelector:aSelector]) {
        return target; // 就會去調(diào)用WWTarget里面的test方法
    }else {
        return [super forwardingTargetForSelector: aSelector];
    }
}

3.Normal Fowarding
如果上面兩種方法都沒有被實現(xiàn)的話,就會來到第三步鼎文,這是runtime給我們最后一次避免崩潰的機會渔肩,首先它會-methodSignatureForSelector:來獲得函數(shù)的參數(shù)和返回值類型,如果返回值為nil拇惋,則runtime會發(fā)出-doesNotRecognizeSelector: 的消息周偎,程序崩潰。如果返回了一個函數(shù)簽名撑帖,runtime會創(chuàng)建一個NSInvocation對象并發(fā)送-forwardInvocation:的消息給目標(biāo)對象蓉坎。

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:"v@:"];
    return signature;
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {
    SEL selector = [anInvocation selector];// anInvocation里面保存的是selector/target/參數(shù)
    WWTarget *target = [[WWTarget alloc] init];
    if ([target respondsToSelector:selector]) {
        [anInvocation invokeWithTarget:target];
    }    
}

如果上面的三步都沒有實現(xiàn)的話,就會調(diào)用-doesNotRecognizeSelector:胡嘿,程序崩潰蛉艾。

三.深拷貝和淺拷貝

深拷貝:內(nèi)容拷貝,拷貝出來的對象和之前的對象的地址不一樣灶平。
淺拷貝:指針拷貝伺通,拷貝出來的對象和之前的對象的地址一樣。
直接上簡單示例比較好:

1.對可變對象進(jìn)行 copy操作

- (void)viewDidLoad {
    [super viewDidLoad];
    NSMutableString *mStr = [NSMutableString stringWithString:@"mStr"];
    NSString *copyStr = [mStr copy];
    [mStr appendString:@"123"];
    // mStr:0x60800007f440 - copyStr:0xa0000007274536d4
    NSLog(@"mStr:%p - copyStr:%p",mStr, copyStr);
}
 結(jié)論:1.對可變對象 進(jìn)行 copy 操作是內(nèi)容拷貝(深拷貝)
      2. copy 出來的copyStr是NSString類型的逢享,如果對copyStr調(diào)用
NSMutableString的方法appendString是會崩潰的。

2.對可變對象進(jìn)行mutableCopy操作

- (void)viewDidLoad {
    [super viewDidLoad];
    NSMutableString *mStr = [NSMutableString stringWithString:@"mStr"];
    NSMutableString *mutableCopyStr =  [mStr mutableCopy];
    // str:0x608000260140 - mutableCopyStr:0x608000260440
    NSLog(@"str:%p - mutableCopyStr:%p",mStr, mutableCopyStr); 
}
 結(jié)論:1.對可變對象 進(jìn)行 mutableCopy 操作是內(nèi)容拷貝(深拷貝)
      2. mutableCopy 出來的mutableCopyStr是 NSMutableString 類型

3.對不可變對象進(jìn)行copy操作

- (void)viewDidLoad {
    [super viewDidLoad];
    NSString *Str = [NSString stringWithFormat:@"Str"];
    NSString *copyStr = [Str copy];
    // str:0x10147e128 - copyStr:0x10147e128
    NSLog(@"str:%p - copyStr:%p",Str, copyStr);
}
結(jié)論:對不可變對象 進(jìn)行 copy 操作是指針拷貝(淺拷貝)

4.對不可變對象進(jìn)行mutableCopy操作

- (void)viewDidLoad {
    [super viewDidLoad];
    NSString *mStr = [NSString stringWithFormat:@"mStr"];
    NSMutableString *mutableCopyStr = [mStr mutableCopy];
   // str:0xa0000007274536d4 - mutableCopyStr:0x60800026a680
    NSLog(@"str:%p - mutableCopyStr:%p",mStr, mutableCopyStr);
}
 結(jié)論:1.對不可變對象 進(jìn)行 mutableCopy操作 是內(nèi)容拷貝(深拷貝)
      2.對mStr進(jìn)行mutableCopy操作的mutableCopyStr是NSMutableString類型的

綜合以上所述:只有對 不可變對象進(jìn)行copy操作是指針拷貝(淺拷貝)吴藻,其他的都是內(nèi)容拷貝(深拷貝)

四.調(diào)起鍵盤時瞒爬,如何將鍵盤的“換行”變成“發(fā)送/完成”等

設(shè)置returnKeyType屬性即可,

    UIReturnKeyDefault,
    UIReturnKeyGo,// 前往
    UIReturnKeyGoogle,// google
    UIReturnKeyJoin,// 加入
    UIReturnKeyNext,// 下一步
    UIReturnKeyRoute,// 路線
    UIReturnKeySearch,// 搜索
    UIReturnKeySend, // 發(fā)送
    UIReturnKeyYahoo,// 搜索
    UIReturnKeyDone,// 完成
    UIReturnKeyEmergencyCall,// 緊急電話
    UIReturnKeyContinue NS_ENUM_AVAILABLE_IOS(9_0),// 繼續(xù)

五.viewDidLayoutSubviews和layoutSubviews的調(diào)用順序

viewDidLayoutSubviewslayoutSubviews前面調(diào)用
layoutSubviewsdrawRect :前面調(diào)用

2017-06-14 10:31:35.215 layoutSubviews等的調(diào)用順序[7357:98975] -[ViewController viewDidLoad]
2017-06-14 10:31:35.215 layoutSubviews等的調(diào)用順序[7357:98975] -[WWView initWithFrame:]
2017-06-14 10:31:35.220 layoutSubviews等的調(diào)用順序[7357:98975] -[ViewController viewWillLayoutSubviews]
2017-06-14 10:31:35.220 layoutSubviews等的調(diào)用順序[7357:98975] -[ViewController viewDidLayoutSubviews]
2017-06-14 10:31:35.220 layoutSubviews等的調(diào)用順序[7357:98975] -[WWView layoutSubviews]
2017-06-14 10:31:35.221 layoutSubviews等的調(diào)用順序[7357:98975] -[WWView drawRect:]

六.如何給分類動態(tài)添加屬性

#import "WWView+Tools.h"
#import <objc/runtime.h>

static char strKey;

@implementation WWView (Tools)

- (void)setDynamicStr:(NSString *)dynamicStr {
    /**
     id object: 需要給哪個對象的屬性賦值
     const void *key:屬性對應(yīng)的key值
     id value:設(shè)置屬性的值為value
     objc_AssociationPolicy policy:關(guān)聯(lián)策略 枚舉值 一般選擇NONATOMIC
     */
    objc_setAssociatedObject(self, &strKey, dynamicStr, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

- (NSString *)dynamicStr {
    return objc_getAssociatedObject(self, &strKey);
}

七.如何把一個view生成一張圖片沟堡,并且保存到本地

因為涉及到訪問相冊侧但,所以先在plist文件里面添加NSPhotoLibraryUsageDescription允許應(yīng)用程序訪問你的相冊

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // self.testView:要生成為圖片的view  
     UIGraphicsBeginImageContextWithOptions(self.testView.bounds.size, 0, [[UIScreen mainScreen] scale]);
        [self.testView.layer renderInContext:UIGraphicsGetCurrentContext()];
        UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        UIImageWriteToSavedPhotosAlbum(viewImage, self, @selector(imageSavedToPhotosAlbum:didFinishSavingWithError:contextInfo:), nil);
    });
}

- (void)imageSavedToPhotosAlbum:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo
{
    if (!error) {
        NSLog(@"成功");
    }else {
        NSLog(@"失敗 - %@",error);
    }
}

八.如果服務(wù)器返回給我們的數(shù)據(jù)是包含標(biāo)簽的,我們應(yīng)該如何加載

// html_content:含有html標(biāo)簽的富文本
1.用UILabel去加載
NSMutableAttributedString *attributeStr = [[NSMutableAttributedString alloc] initWithData:[html_content dataUsingEncoding:NSUnicodeStringEncoding] options:@{                                                                                                                                                                                     NSDocumentTypeDocumentAttribute:NSHTMLTextDocumentType                                                                                                                                                                                     }documentAttributes:nil error:nil];
self.contentLabel.attributedText = attributeStr;
PS:如果要改變文本的字體大小顏色等航罗,一定要在這后面改

2.直接使用UIWebView去加載
 //設(shè)置字體大小為15禀横,顏色rgb(124,181,236),邊距為15粥血,并且圖片的寬度自動充滿屏幕柏锄,高度自適應(yīng)
 NSString *html_content = @"要加載的html內(nèi)容";
 NSString *htmls = [NSString stringWithFormat:@"<html> \n"
                           "<head> \n"
                           "<style type=\"text/css\"> \n"
                           "body {margin:15;font-size:15;color:%@}\n"
                           "</style> \n"
                           "</head> \n"
                           "<body>"
                           "<script type='text/javascript'>"
                           "window.onload = function(){\n"
                           "var $img = document.getElementsByTagName('img');\n"
                           "for(var p in  $img){\n"
                           " $img[p].style.width = '100%%';\n"
                           "$img[p].style.height ='auto'\n"
                           "}\n"
                           "}"
                           "</script>%@"
                           "</body>"
                           "</html>",@"rgb(124,181,236)", html_content];
 [self.contentWebView loadHTMLString:htmls baseURL:nil];

九.上傳到應(yīng)用商店太慢的話,怎么解決

可以考慮使用Xcode - Open Developer Tool - Application Loader來解決

十.利用Application Loader打包提交到App Store可能會遇到錯誤:

The filename 未命名.ipa in the package contains an invalid character(s). The valid characters are:A-Z
,a-z,0-9,dash,period,underscore,but the name cannot start with a dash,period,or underscore.

解決辦法:在Archive之后的包不能再試中文名复亏,把XXX.ipa改成英文名就搞定了趾娃。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市缔御,隨后出現(xiàn)的幾起案子抬闷,更是在濱河造成了極大的恐慌,老刑警劉巖耕突,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件笤成,死亡現(xiàn)場離奇詭異评架,居然都是意外死亡,警方通過查閱死者的電腦和手機炕泳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進(jìn)店門纵诞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人喊崖,你說我怎么就攤上這事挣磨。” “怎么了荤懂?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵茁裙,是天一觀的道長。 經(jīng)常有香客問我节仿,道長晤锥,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任廊宪,我火速辦了婚禮矾瘾,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘箭启。我一直安慰自己壕翩,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布傅寡。 她就那樣靜靜地躺著放妈,像睡著了一般。 火紅的嫁衣襯著肌膚如雪荐操。 梳的紋絲不亂的頭發(fā)上芜抒,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天,我揣著相機與錄音托启,去河邊找鬼宅倒。 笑死,一個胖子當(dāng)著我的面吹牛屯耸,可吹牛的內(nèi)容都是我干的拐迁。 我是一名探鬼主播,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼肩民,長吁一口氣:“原來是場噩夢啊……” “哼唠亚!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起持痰,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤灶搜,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體割卖,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡前酿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了鹏溯。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片罢维。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖丙挽,靈堂內(nèi)的尸體忽然破棺而出肺孵,到底是詐尸還是另有隱情,我是刑警寧澤颜阐,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布平窘,位于F島的核電站,受9級特大地震影響凳怨,放射性物質(zhì)發(fā)生泄漏瑰艘。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一肤舞、第九天 我趴在偏房一處隱蔽的房頂上張望紫新。 院中可真熱鬧,春花似錦李剖、人聲如沸芒率。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽敲董。三九已至,卻和暖如春慰安,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背聪铺。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工化焕, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人铃剔。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓撒桨,卻偏偏與公主長得像,于是被迫代替她去往敵國和親键兜。 傳聞我的和親對象是個殘疾皇子凤类,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容

  • *面試心聲:其實這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個offer,總結(jié)起來就是把...
    Dove_iOS閱讀 27,125評論 29 470
  • 基礎(chǔ) 1. 為什么說Objective-C是一門動態(tài)的語言? 2. 講一下MVC和MVVM普气,MVP谜疤? 3. 為...
    波妞和醬豆子閱讀 3,307評論 0 46
  • 面試題參考1 : 面試題[http://www.cocoachina.com/ios/20150803/12872...
    江河_ios閱讀 1,710評論 0 4
  • 有人問我思念到極致是什么感覺。 我曾經(jīng)發(fā)了句晚安給她 一晚上等著手機信息。 就是那種可怕的朦朦朧朧的意識 夢里...
    H夜已殤閱讀 572評論 0 0
  • 01 2013年宣布不再制作長篇動畫的宮崎駿導(dǎo)演近日又表露出復(fù)出的意愿夷磕,11月13日播出的NHK紀(jì)錄節(jié)目《永不停歇...
    郁文堂閱讀 2,207評論 19 73