iOS 知識(shí)點(diǎn)個(gè)人總結(jié)(不定期持續(xù)性更新)

1.單個(gè)控制器監(jiān)聽進(jìn)入后臺(tái)

在該控制器的-viewDidLoad方法中,添加代碼監(jiān)聽notification,也可以使用監(jiān)聽APP返回方法

[[NSNotificationCenter defaultCenter] addObserver:self

selector:@selector(someMethod:)

name:UIApplicationDidBecomeActiveNotification object:nil];

監(jiān)聽方法的具體實(shí)現(xiàn)

-(void)someMethod:(NSNotification *)noti

{

? ? ? ? //從該控制器進(jìn)入后臺(tái)時(shí)需要執(zhí)行的代碼

}

恢復(fù)到前臺(tái)

- (void)applicationDidBecomeActive:(UIApplication *)application{

????NSLog(@"---applicationDidBecomeActive----");

}

2.十六進(jìn)制字符串轉(zhuǎn)換成字節(jié)數(shù)組.

目標(biāo)字符串 NSString *str = @"0200107580FD7590FD750FD7590FD75A0FD";

調(diào)用方法:NSData *temp = [string hexToBytes]; ?轉(zhuǎn)換后

?byte數(shù)組?Byte byteArr[] = { 0x02, 0x00, 0x10, ... , ?0xFD };


3、對(duì)于項(xiàng)目中的第三庫(kù)一定要進(jìn)行再次封裝,包括網(wǎng)絡(luò)、刷新、提示、模型轉(zhuǎn)換等所有能封裝的部分,一直用MJExtension來(lái)做字典轉(zhuǎn)模型,突然想用YYModel了,項(xiàng)目中替換很麻煩,刷新也一樣,一直用第三方框架,突然想自己寫,改起來(lái)麻煩的不要不要的;

4、熟悉一下測(cè)試的幾種方案,例如交叉測(cè)試(一個(gè)功能正在運(yùn)行,另一個(gè)功能運(yùn)行對(duì)它的影響,例如 掃碼時(shí)打開燈光,突然退到后臺(tái),再回來(lái)查看,可能燈光已經(jīng)熄滅,按鈕還沒改變狀態(tài))等,這樣寫程序時(shí)才知道往哪些方面考慮

5、多寫點(diǎn)代碼塊,寫起代碼來(lái)會(huì)很快,我把自定義Cell都封裝了代碼塊,用xib布局cell就沒有純手寫這么快了,改起來(lái)還麻煩,這就是代碼塊的好處;

6、主控制器因?yàn)榇a比較多,結(jié)構(gòu)一定要清晰,才方便尋找,插入的類按 服務(wù)工具類+MVC 劃分,屬性按修飾符劃分,下面代碼按功能劃分,如下

#import "ViewController.h"

// 工具和服務(wù)類

#import "Header.h"

#import "Tool1.h"

#import "Tool2.h"


// Model

#import "Model1.h"

#import "Model2.h"

#import "Model3.h"


// View

#import "View1.h"

#import "View2.h"

#import "View3.h"


// Controller

#import "ViewController1.h"

#import "ViewController2.h"

#import "ViewController3.h"

@interface ViewController ()

// 按修飾符劃分

@property (nonatomic, assign) NSInteger num1;

@property (nonatomic, assign) NSInteger num2;


@property (nonatomic, strong, nonnull) NSMutableArray<NSString *> *object3;@property (nonatomic, strong, nonnull) NSMutableArray<NSNumber *> *object4;


@property (nonatomic, copy, nullable) NSString *object5;

@property (nonatomic, copy, nullable) NSString *object6;



@property (nonatomic, weak) UILabel *object1;

@property (nonatomic, weak) UILabel *object2;

@end


@implementation ViewController

- (void)viewDidLoad {? ?

????[super viewDidLoad]; ? ?

}


#pragma mark - 初始化View

- (void)initView{

?}

#pragma mark - 系統(tǒng)方法,界面顯示到銷毀

#pragma mark - 代理

#pragma mark - 按鈕點(diǎn)擊,通知,定時(shí)器

#pragma mark - 輔助方法#pragma mark - 懶加載


@end


7、定時(shí)器不是馬上開始的,多久觸發(fā)一次事件,多久才開始,記得在退出頁(yè)面的時(shí)候釋放定時(shí)器,否則控制器不會(huì)釋放;

8、如果錯(cuò)誤提示中出現(xiàn)了duplicate這樣的字眼,很可能就是引入了.m文件

9、UIView的tag不能為0;

10、字典轉(zhuǎn)JSON字符串;

NSData *data = [NSJSONSerialization dataWithJSONObject:params options:NSJSONWritingPrettyPrinted error:nil];

NSString *paramStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];

11、有時(shí)在動(dòng)畫過(guò)程中,需要避免用戶重復(fù)操作,否則很容易崩潰。

建議動(dòng)畫過(guò)程中設(shè)置[[UIApplication sharedApplication] beginIgnoringInteractionEvents];

允許用戶操作[[UIApplication sharedApplication] endIgnoringInteractionEvents];

12、如果延時(shí)執(zhí)行事件會(huì)被多次觸發(fā),那是一件很危險(xiǎn)的事情,需要取消前面的延時(shí)執(zhí)行事件

[selfperformSelector:<#(nonnull SEL)#>

withObject:<#(nullable id)#>afterDelay:<#(NSTimeInterval)#>];// 延時(shí)執(zhí)行


[NSObject cancelPreviousPerformRequestsWithTarget:<#(nonnull id)#>

selector:<#(nonnull SEL)#>object:<#(nullable id)#>]// 取消延時(shí)執(zhí)行


13、測(cè)試某部分代碼的運(yùn)行時(shí)間

NSTimeInterval beginTime = CFAbsoluteTimeGetCurrent();

......// 執(zhí)行代碼

NSTimeInterval endTime = CFAbsoluteTimeGetCurrent();

time = endTime-beginTime;// 運(yùn)行時(shí)間


14、對(duì)某個(gè)控件截圖

UIGraphicsBeginImageContextWithOptions(view.bounds.size,NO,0);

[view.layer renderInContext:UIGraphicsGetCurrentContext()];

UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();


15、添加毛玻璃效果

UIBlurEffect *effect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight];

UIVisualEffectView *effectView = [[UIVisualEffectView alloc]initWithEffect:effect];

effectView.contentView = 控件;


16、添加長(zhǎng)按手勢(shì),在手勢(shì)開始時(shí)才執(zhí)行方法,避免方法被調(diào)用兩次

- (void)longPress:(UILongPressGestureRecognizer *)longPressGesture{

????if (UIGestureRecognizerStateBegan != longPressGesture.state) {

????????return;

????}

????... // 執(zhí)行方法

}


17、輸入框有值時(shí)才能點(diǎn)擊return key

textField.enablesReturnKeyAutomatically = YES;


18、isKindOfClass判斷對(duì)象是否是一個(gè)類的成員,或者是派生自該類的成員isMemberOfClass確定對(duì)象是否是當(dāng)前類的成員;

19、tableView設(shè)置cell的分割線從屏幕左側(cè)邊緣開始

?cell.separatorInset = UIEdgeInsetsMake(0, 0, 0, 0);


20、tableView當(dāng)內(nèi)容不夠時(shí),去掉底部的分割線

self.tableView.tableFooterView = [[UIView alloc]init];


21、UITableView設(shè)置為Plain的樣式時(shí),你又有多組時(shí),組頭就會(huì)默認(rèn)有懸浮效果,停留在上邊,如果不想組頭懸浮在上邊,可以將樣式設(shè)為Grouped,把足部設(shè)置很小,解決這問題;

UITableView *tableView = [[UITableView alloc] initWithFrame:CGRectMake(x,y,w,h) style:UITableViewStyleGrouped];

tableView.sectionFooterHeight = 0.0001;


22、設(shè)置UITableViewCell之間的間距,在自定義cell中重寫setFrame方法

-(void)setFrame:(CGRect)frame{

????frame.origin.y += 5;

????frame.size.height -= 5;

????[super setFrame:frame];

}


23、修改UISearchController上searchBar的風(fēng)格,遍歷子控件(很有用),找到合適的, 設(shè)置想要的子控件的顏色和分格;

UIImageView *barImageView = self.searchController.searchBar.subviews[0].subviews[0];

barImageView.layer.borderColor = [UIColor lightGrayColor];

barImageView.layer.borderWidth = 1;

UIView *textView = self.searchController.searchBar.subviews[0].subviews[1];

textView.backgroundColor = [UIColor whiteColor];


24、監(jiān)測(cè)WKWebView的加載進(jìn)度

[wkWebView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];


25、一個(gè)控件獲取在某個(gè)控件上的坐標(biāo)點(diǎn)的四種方式

// 獲取View在window上的坐標(biāo)點(diǎn)的四種寫法

// 使用 convertRect:toRect 方法

CGRect rect = [view convertRect:view.bounds toRect:window];

CGRect rect = [view.superView convertRect:view.frame toRect:window];


// 使用 convertRect:from 方法

CGRect rect = [window convertRect:view.boundsfrom:view];

CGRect rect = [window convertRect:view.framefrom:view.superView];


26、iOS11獲取最上面的window

// iOS 11 以前

UIView *windowView = [[UIApplication sharedApplication].windows lastObject];

// iOS 11 以后

UIView *windowView = [[UIApplication sharedApplication].windows firstObject];


27、某個(gè)控件有雙擊和單擊時(shí),設(shè)置雙擊失敗時(shí),才觸發(fā)單擊

[oneTap requireGestureRecognizerToFail:doubleTap];


28、設(shè)置圖片捏合縮放,雙擊放大

// 首先把imageView添加到scrollview中

- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView{

????return self.imageView;

????// 在代理返回當(dāng)前imageView現(xiàn)

}


- (void)scrollViewDidZoom:(UIScrollView *)scrollView{

????// 保證繞著中心點(diǎn)縮放

????????CGSize boundsSize = self.scrollView.bounds.size;

????????CGRect frameToCenter = self.imageView.frame;

????????if (frameToCenter.size.width < boundsSize.width) {

????????????frameToCenter.origin.x = floorf((boundsSize.width - frameToCenter.size.width) * 0.5f);

????????} else {

????????????frameToCenter.origin.x = 0;

????????}

????????if (frameToCenter.size.height < boundsSize.height) {

????????????????frameToCenter.origin.y = floorf((boundsSize.height - frameToCenter.size.height) * 0.5f);

????????}else {

????????????????frameToCenter.origin.y = 0;

????????}

????????if (!CGRectEqualToRect(self.imageView.frame, frameToCenter)) {

????????????????self.imageView.frame = frameToCenter;

????????}

}


-(void)doubleTap:(UITapGestureRecognizer *)tapBgRecognizer{

????CGFloat scale = 4; // 最大縮放比例

????if (self.self.imageView.frame.frame.size.width < kScreenWidth * scale) {

????????CGPoint point = [tapBgRecognizer locationInView:self.imageView.frame];

????????CGFloat xSize = kScreenWidth / scale;

????????CGFloat ySize = kScreenHeight / scale;

????????CGRect zoomRect = CGRectMake(point.x - xSize * 0.5f, point.y - ySize * 0.5f, xSize, ySize);

????????self.scrollView.maximumZoomScale = scale;

????????// 以點(diǎn)擊點(diǎn)為中心縮放到最大

????????[self.scrollView zoomToRect:zoomRect animated:YES];

????} else {

????????[UIView animateWithDuration:0.25 animations:^{

????????self.scrollView.zoomScale = 1.0;

????????self.scrollView.contentSize = self.scrollView.bounds.size;

? ? ????self.self.imageView.frame.frame = self.originFrame; }];

????}

}


29、尋找當(dāng)前控件的控制器

-(UIViewController*)ht_viewController{

????for(UIView *next = self; next; ?next = next.superview){

????????UIResponder *nextResponder = [next nextResponder];

????????if([nextResponder isKindOfClass:[UIViewController class]]{

????????????return (UIViewController *)nextResponder;

????????}

????}

????return nil;

}


30、UITextField的inputView屬性是指第一響應(yīng)的不是鍵盤,而是賦值給inputView的那個(gè)view, inputAccessoryView 是指往鍵盤上添加另一個(gè)view;


31、設(shè)置鍵盤在UIScrollView拖動(dòng)動(dòng)時(shí)消失

scrollview.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag


32、設(shè)置UITableViewCell之間的分割線顏色;

self.tableView.separatorColor = [UIColor redColor];


33、iOS9以后點(diǎn)擊狀態(tài)欄,UIScrollView可返回頂部;


34、顯示狀態(tài)欄的網(wǎng)絡(luò)請(qǐng)求菊花;

// 顯示菊花-----NO為關(guān)閉菊花

[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;


35、讓UILabel的字體適應(yīng)指定的寬度,當(dāng)寬度大時(shí),字體不變, 寬度小時(shí), 字體變小適應(yīng)寬度

label.adjustsFontSizeToFitWidth = YES;


36、監(jiān)聽拖動(dòng)進(jìn)度條時(shí)的狀態(tài)

監(jiān)聽拖動(dòng)進(jìn)度條時(shí)的狀態(tài)

[slider addTarget:self action:@selector(sliderChange:event:) forControlEvents:UIControlEventValueChanged];


- (void)sliderChange:(UISlider *)slider event:(UIEvent *)event{

????UITouch *touch = [[event allTouches] anyObject];

????// 根據(jù)狀態(tài)來(lái)做相應(yīng)的事情,避免拖動(dòng)時(shí)一直調(diào)用某些事件

????switch (touch.phase) {

????????case UITouchPhaseBegan: // 開始

????????case UITouchPhaseMoved: // 拖動(dòng)

????????case UITouchPhaseEnded: // 結(jié)束

????????default:

????????????break;

????????}

}


37、讓UITableView的某一行滾到底部;

[self.tableView scrollToRowAtIndexPath:indexPath? ? atScrollPosition:UITableViewScrollPositionBottom animated:YES];


38、以Modal的形式push出一個(gè)界面;

[UIView beginAnimations:nil context:NULL];

[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];

[UIView setAnimationDuration:0.75];

[self.navigationController pushViewController:vc animated:NO];

[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:self.navigationController.view cache:NO];

[UIView commitAnimations];


39、阻止設(shè)備自動(dòng)鎖屏[UIApplication sharedApplication].idleTimerDisabled = YES;在后臺(tái)不管用,退出當(dāng)前頁(yè)面時(shí),記得設(shè)為NO

40、為了避免循環(huán)引用,在block中我們一般都用弱引用,但是block中的弱引用對(duì)象可能會(huì)提前釋放,造成崩潰,我們需要在block中強(qiáng)引用一下這個(gè)弱對(duì)象;

__weak __typeof(self)weakSelf = self;

view.callback = ^(ViewStatus status) {

????// 強(qiáng)引用這個(gè)對(duì)象,避免執(zhí)行到一半 self釋放,造成崩潰;

????__strong __typeof(weakSelf)strongSelf = weakSelf;

????[strongSelf doSomething];

}


41、如果一個(gè)參數(shù)中需要包含多個(gè)枚舉值,用NS_OPTIONS,不要用NS_ENUM

// 位運(yùn)算保證任意組合的枚舉值進(jìn)行或運(yùn)算能得到唯一的值

typedefNS_OPTIONS(NSUInteger,TestName){

????????TestNameXiaoHua=1<<0,// 小花

????????TestNameXiaoBai=1<<1,// 小白

????????TestNameXiaoHei=1<<2// 小黑

};

[self eat:TestNameXiaoHua | TestNameXiaoBai];// 讓小花和小白有飯吃;


-(void)eat:(TestName)name{

????if( (name & TestNameXiaoHua) || (name & TestNameXiaoBai) ){

????????NSLog(@"有飯吃");

????}

}


42、快速生成一個(gè)帶值的可變字典

NSMutableDictionary *mutDic = @{@"key":@"value"}.mutableCopy;


43、忽略編譯器警告

#pragma clang diagnostic push

#pragma clang diagnostic ignored "-Wgnu"????// 設(shè)置要忽略的類型 這里是GNU警告

// 代碼

#pragma clang diagnostic pop


44、為了避免多線程訪問數(shù)據(jù)庫(kù),造成數(shù)據(jù)混亂,讓讀寫方法都在同一個(gè)隊(duì)列中進(jìn)行;

static const void * const IOKey = &IOKey;


// 開辟一個(gè)單例隊(duì)列

dispatch_queue_t NTESDispatchIOQueue(){

????static dispatch_queue_t queue;

????static dispatch_once_t onceToken;

????dispatch_once(&onceToken, ^{

????// dispatch_queue_create("", DISPATCH_QUEUE_SERIAL) queue = dispatch_queue_create("io.queue", 0);

????dispatch_queue_set_specific(queue, IOKey, (void *)IOKey, NULL); // 設(shè)置隊(duì)列的的標(biāo)記 });

????return queue;

}


typedef void(^dispatch_block)(void);

void io_sync_safe(dispatch_block block){

????// 如果是自己設(shè)置的隊(duì)列,執(zhí)行block

????if (dispatch_get_specific(IOKey)) {

????????block();

????} else// 如果不是自己設(shè)置的隊(duì)列,先創(chuàng)建隊(duì)列,再執(zhí)行block {

????????dispatch_sync(NTESDispatchIOQueue(), ^() {

????????block();

????});

}


45、監(jiān)聽UITextField值的改變,可以使用這個(gè)方法

[_textViewaddTarget:self.action:@selector(textFieldDidChangeValue:)forControlEvents:UIControlEventEditingChanged]

而不要使用這個(gè)代理,因?yàn)榭赡鼙O(jiān)聽不到

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { },


46、把C++算法庫(kù)封裝在OC文件里形成靜態(tài)庫(kù)時(shí),記得把OC的.m文件改成.mm文件


47、如果視圖里面存在唯一一個(gè)UIScrollView或其子類View時(shí),會(huì)主動(dòng)設(shè)置相應(yīng)的內(nèi)邊距,避免被導(dǎo)航欄遮住,如果我們的導(dǎo)航欄不透明,原點(diǎn)會(huì)從我們的導(dǎo)航欄下方算起,導(dǎo)致上方留白,解決這問題:

if (@available(iOS 11.0, *)) {

????????self.tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;????????//UIScrollView也適用

}else {

????????self.automaticallyAdjustsScrollViewInsets = NO;

?}


48、如果數(shù)組中的元素是唯一的,或者要查詢某個(gè)數(shù)據(jù)是否在該數(shù)組中,不要使用NSArray, 而是使用集合NSSet,集合采用的是哈希表,查詢速度更快;

49、動(dòng)畫里的萬(wàn)能神器CAShapeLayer,性能特別好,能實(shí)現(xiàn)很多神奇的效果,做什么動(dòng)畫前優(yōu)先考慮它,尤其做股票財(cái)經(jīng)、健康A(chǔ)PP類的,經(jīng)常需要畫圖,如果遇到性能問題,試試它;

50、swift中為了避免循環(huán)引用,我們使用weak或者unowned來(lái)解決,但它們是有區(qū)別的; unowned更像OC里的unsafe_unretained,當(dāng)引用對(duì)象釋放了以后,它仍然會(huì)保持對(duì)引用對(duì)象的一個(gè)無(wú)效引用,如果嘗試調(diào)用方法和成員屬性的話,程序就會(huì)崩潰;? weak則會(huì)在引用的內(nèi)容被釋放后,它會(huì)自動(dòng)地變成 nil;? 因此我們?cè)谑褂盟鼤r(shí),如果你引用對(duì)象不會(huì)釋放, 使用unowned,寫起來(lái)方便點(diǎn);? 如果你引用對(duì)象會(huì)被釋放,請(qǐng)使用weak,例如網(wǎng)絡(luò)請(qǐng)求;

51、在swift中用強(qiáng)制解包!一定要非常小心,盡量少用,一不小心程序就崩潰了;

52、在swift中數(shù)組和字典都屬于值類型,相當(dāng)于int類型,跟oc不一樣,當(dāng)你把數(shù)組賦值給另一個(gè)數(shù)組,修改數(shù)組的值不會(huì)影響另一個(gè)數(shù)組;

53、__bridge 用于OC指針與c語(yǔ)言中的 void *互相轉(zhuǎn)換,雖然?id 和void *能夠相互轉(zhuǎn)換。但轉(zhuǎn)換為void *,其安全性與賦值給__unsafe_unretained修飾符相近,甚至?xí)汀H绻芾頃r(shí)不注意賦值對(duì)象的所有者,就會(huì)因懸垂指針而導(dǎo)致程序崩潰。具體細(xì)節(jié),參考?__bridge 的那些事兒

54、void* 類型指針通用變體類型指針;可以不經(jīng)轉(zhuǎn)換,賦給其他指針,函數(shù)指針除外;malloc返回的就是void*類型。

NULL指針:是一個(gè)標(biāo)準(zhǔn)規(guī)定的宏定義;#define NULL ((void *) 0) ? ? ?用來(lái)表示空指針常量;

零指針:指針值為0,零值指針,沒有存儲(chǔ)任何內(nèi)存地址的指針;可以使任意一種指針類型,eg:void * ;int * ;double *;

空指針:指針賦值為0;0*7;3-3等之后,指針即變成空指針;即:空指針不指向任何實(shí)際的對(duì)象或者函數(shù);NULL指針和零指針都是空指針。

野指針指向垃圾內(nèi)存的指針;(1)指針變量沒有初始化(2)指針被delete或者free之后沒有置為空(3)指針操作超越了變量的范圍

懸垂指針:指向曾經(jīng)存放對(duì)象的內(nèi)存,但是該對(duì)象已經(jīng)不存在了;delete操作完成后的指針就是懸垂指針,此時(shí)需要將指針置為0變?yōu)榱阒抵羔槪?/p>


55、WKWebView 與 JS 交互,需要添加?[userController addScriptMessageHandler:self name:@"callFunction"]; ?callFunction 可自定義,與 JS 保持一致,才可交互。

于控制器中添加 evaluateJavaScript:方法

56.關(guān)于 -> 語(yǔ)法 與 .語(yǔ)法

self -> _topview 這個(gè)是把 self 當(dāng)成了個(gè)結(jié)構(gòu)體指針;

self.topview 是把self 當(dāng)成了一個(gè)對(duì)象,或者說(shuō)是結(jié)構(gòu)體變量;

另外,self-> 不會(huì)觸發(fā) set方法,self. 會(huì)觸發(fā)。

. 左側(cè)可以是結(jié)構(gòu)體變量,也可以是對(duì)象;->?左側(cè)肯定是當(dāng)成 結(jié)構(gòu)體指針。

像這種用法, k 就是個(gè)結(jié)構(gòu)體變量,p 是取了k 的地址,所以p 是個(gè) astrct 類型指針,他就可以用->

成員變量 + 讀寫方法 = 類屬性

簡(jiǎn)單直接的理解方式:.語(yǔ)法是去訪問類屬性,->是訪問成員變量。?. 語(yǔ)法用于尋址,在c 語(yǔ)言中,左側(cè)必須是個(gè)類型變量。而 - > 用于間接尋址。比如下圖

驗(yàn)證上面所說(shuō)

第一行 &k 和 &(k.a)的地址完全一樣,是因?yàn)樵谶@個(gè)結(jié)構(gòu)體中,于內(nèi)存存儲(chǔ)的時(shí)候,最開始的部分存放的就是a,然后緊接著就是b,當(dāng)拿到這個(gè)結(jié)構(gòu)題類型的變量K, 實(shí)際上就是拿到存儲(chǔ)的首地址。

而第二行的 p ,其實(shí)是 指針p 自身的地址,而指針指向的是 k。


57.Implicit declaration of function XXX is invalid in C99

? ? ** 和CNCopySupportedInterfaces CNCopyCurrentNetworkInfo 等相關(guān)的錯(cuò)?需要?#import <SystemConfiguration/CaptiveNetwork.h>


58.關(guān)于?armv7、armv7s、arm64、i386、x86_64 的區(qū)別

arm64:iPhone6s | iphone6s plus|iPhone6| iPhone6 plus|iPhone5S | iPad Air| iPad mini2(iPad mini with Retina Display)

armv7s:iPhone5|iPhone5C|iPad4(iPad with Retina Display)

armv7:iPhone4|iPhone4S|iPad|iPad2|iPad3(The New iPad)|iPad mini|iPod Touch 3G|iPod Touch4

i386 是針對(duì)intel通用微處理器32位處理器

x86_64是針對(duì)x86架構(gòu)的64位處理器

模擬器32位處理器測(cè)試需要i386架構(gòu);

模擬器64位處理器測(cè)試需要x86_64架構(gòu);

真機(jī)32位處理器需要armv7,或者armv7s架構(gòu);

真機(jī)64位處理器需要arm64架構(gòu);


58.界面出來(lái)之前 xib 中的 frame 都不要去使用??梢栽趘iewDidAppear 方法里設(shè)置??♂?


59.使用CocoaPods創(chuàng)建自己的私有庫(kù),首次使用?pod lib lint 可能遇到的問題

驗(yàn)證

這個(gè)問題是pod依賴的組件fourflusher與xcode版本不匹配造成的,可以使用如下命令:

1.sudo gem uninstall fourflusher?

2.sudo gem install fourflusher

必要情況下還需要更新pod

sudo gem update cocoapods

[!] TestLib did not pass validation, due to 3 warnings (but you can use `--allow-warnings` to ignore them).?

pod驗(yàn)證發(fā)現(xiàn)3個(gè)及以上的warning就會(huì)報(bào)這個(gè)錯(cuò),如果只是驗(yàn)證一下工程,能確保對(duì)外發(fā)布之前能修復(fù),可以使用 --allow-warnings

pod lib lint --allow-warnings

如果驗(yàn)證通過(guò),會(huì)看到 TestLib passed validation ,到這一步既完成驗(yàn)證。


60.WKWebView 背景透明

[self.webView setOpaque:NO];

[self.webView setBackgroundColor:[UIColor clearColor]];

[self.webView.scrollView setBackgroundColor:[UIColor clearColor]];


61.ARC 情況下堆、棧注意點(diǎn)

Block0在堆,Block1在棧

如圖第二個(gè)Block處不使用Copy發(fā)生崩潰,通過(guò)打印可以發(fā)現(xiàn)2個(gè)Block所處區(qū)域不同,前者?blk0 在堆區(qū),而后面的blk1在棧,容易被系統(tǒng)回收,在ARC 的情況下一般就直接拷貝到堆上去。

驗(yàn)證


62.App 或 Mac 程序內(nèi)使用WebView播放網(wǎng)絡(luò)視頻,禁止自動(dòng)全屏:

? ? 目前在做一個(gè)Mac程序的監(jiān)控設(shè)備播放,由JavaScript控制播放,發(fā)現(xiàn)程序在加載頁(yè)面后會(huì)自動(dòng)彈出播放器并進(jìn)行全屏播放,碰巧視頻是采用如?.m3u8 格式,結(jié)果在退出全屏后,除非重新加載頁(yè)面,否則視頻無(wú)法播放;

WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];? ?

configuration.allowsInlineMediaPlayback = true;? ?

self.webView =[[WKWebView alloc] initWithFrame:CGRectMake(0,20,W,H-20)configuration: configuration];

如果使用的是UIWebView:

self.webView.allowsInlineMediaPlayback = YES;

如果是在Xib中創(chuàng)建,需要勾選Inline Playback選項(xiàng):

63.手機(jī)連接打印機(jī)

let printVC = UIPrintInteractionController.init()? ? ? ?

let printInfo = UIPrintInfo.printInfo()? ? ? ?

printInfo.outputType = .general? ? ? ?

printInfo.duplex = .longEdge? ? ? ?

printVC.showsPaperSelectionForLoadedPapers = true? ? ? ?

printVC.printInfo = printInfo? ? ? ?

printVC.printFormatter = webView.viewPrintFormatter()? ? ? ?

printVC.present(animated: true, completionHandler: nil)

64.關(guān)于可變數(shù)組添加對(duì)象

? ? 假設(shè)有臨時(shí)變量可變數(shù)組A,可變數(shù)組B,可變數(shù)組B中有若干元素,當(dāng)可變數(shù)組A執(zhí)行 AddObject 添加數(shù)組B后,僅是添加可變數(shù)組B的指針,即數(shù)組B的首地址,當(dāng)操作數(shù)組B后,如移除數(shù)組元素,數(shù)組A中的數(shù)據(jù)也會(huì)隨之改變。

? ? 同理。數(shù)組A在添加字符串C后,改變字符串C的值,數(shù)組A中的元素也會(huì)改變。

65.dealloc 方法被調(diào)用后,不代表對(duì)象的內(nèi)存立刻被回收。詳情可以了解一下runloop內(nèi)部autoRelase的釋放機(jī)制.


66.ARC情況下,關(guān)于控制器dealloc未執(zhí)行,導(dǎo)致沒有移除通知的預(yù)防措施

????假設(shè) A、B、C三個(gè)類,都繼承自 D 類,即D類為基類,層級(jí)關(guān)系為A push B,B push C,由此可以知道,程序會(huì)當(dāng)C出棧時(shí),會(huì)執(zhí)行dealloc方法,然后D類執(zhí)行dealloc,接著B執(zhí)行dealloc,D又執(zhí)行dealloc,最后是A執(zhí)行dealloc,D再執(zhí)行dealloc。故而我們可以根據(jù)這一特性來(lái)設(shè)計(jì)。

? ? 首先定義一個(gè)全局的NSCache 來(lái)存儲(chǔ) 某個(gè)類class,其內(nèi)部監(jiān)聽所有KVO的Key,即?class 作為NSCache 的key, 所監(jiān)聽 key的數(shù)組作為Value,也就是一個(gè)類有多個(gè)通知的情況,然后在基類的delloc中,檢查是否存在這樣的key-value,如存在,for循環(huán)Remove。

? ? 解釋:假設(shè)因?yàn)镃類中的block強(qiáng)弱引用問題,或NSTime未釋放等原因?qū)е耫eallo未執(zhí)行,通知未移除。而當(dāng)B類出棧時(shí)若正常執(zhí)行dealloc,程序會(huì)自動(dòng)調(diào)用基類的dealloc,其方法內(nèi)檢查到NSCache中有通知未移除,就移除通知。以達(dá)到防止內(nèi)存泄漏的目的。

67. arrarWithArray 等效于 alloc init 的方式創(chuàng)建數(shù)組

深拷貝
輸出結(jié)果

68. 關(guān)于copy與mutableCopy

69. 不可變字符串相當(dāng)于常量,初始化后,只有當(dāng)程序結(jié)束運(yùn)行才會(huì)被釋放,并不會(huì)因?yàn)槌隽撕瘮?shù)作用域或者控制器被銷毀而釋放

驗(yàn)證:開始執(zhí)行for循環(huán),循環(huán)創(chuàng)建1億個(gè)String對(duì)象,CPU與內(nèi)存開始上漲

循環(huán)執(zhí)行中

循環(huán)結(jié)束,CPU使用率下降,內(nèi)存占有率不變

循環(huán)結(jié)束


70.?WWDC 2020 ,蘋果宣布當(dāng)用戶退款成功時(shí),無(wú)論哪種內(nèi)購(gòu)類型,開發(fā)者都能收到退款通知!

當(dāng)Apple受理了用戶(玩家)的退款申請(qǐng)后,即允許退款后,蘋果服務(wù)器會(huì)發(fā)送通知,(商家)通過(guò)處理退款信息以響應(yīng)退款通知,達(dá)到因用戶(退款)而采取的相應(yīng)行動(dòng),如扣除相關(guān)訂閱服務(wù)或游戲道具等。

流程

詳細(xì)說(shuō)明:https://developer.apple.com/documentation/storekit/in-app_purchase/handling_refund_notifications

在蘋果后臺(tái)可以配置一個(gè)退款通知的回調(diào)地址(一個(gè)App配置一條鏈接):

退款回調(diào)鏈接

配置的回調(diào)鏈接必須滿足條件:

滿足應(yīng)用傳輸安全要求(使用https)

URL 最長(zhǎng) 255 字符

注意:這里的?https?是指蘋果的 App Transport Security (ATS),其中有協(xié)議的要求,比如使用 Transport Layer Security (TLS) protocol 1.2 版本,具體見蘋果文檔:Preventing Insecure Network Connections | Apple Developer Documentation。


蘋果把回調(diào)的通知分為2種類型:

退款通知類型

取消通知類型

其中新增加的退款通知類型是針對(duì):

消耗型

非消耗型

非續(xù)期訂閱

取消通知類型是針對(duì):

自動(dòng)續(xù)期訂閱


退款通知流程

流程


退款通知的內(nèi)容:

蘋果返回的通知內(nèi)容為 JSON 對(duì)象數(shù)據(jù),所有的退款訂單的通知是在?unified_receipt?里的?latest_receipt_info?數(shù)組中:

退款數(shù)據(jù)格式
數(shù)據(jù)說(shuō)明

在?unified_receipt?里的?latest_receipt_info?是一個(gè)數(shù)組,其中包含的最近的100次應(yīng)用內(nèi)購(gòu)買交易:

數(shù)據(jù)中每個(gè)退款訂單的主要字段:

詳細(xì)的返回字段見官方文檔:

responseBody | Apple Developer Documentation

unified_receipt | Apple Developer Documentation

responseBody.Latest_receipt_info | Apple Developer Documentation


退款通知的響應(yīng)

您的服務(wù)器應(yīng)發(fā)送HTTP狀態(tài)代碼,以指示服務(wù)器到服務(wù)器的通知接收是否成功:

如果回調(diào)接收成功,則發(fā)送 HTTP?200。您的服務(wù)器不需要返回?cái)?shù)據(jù)。

如果回調(diào)接收不成功,請(qǐng)發(fā)送 HTTP?50x?或?40x?讓 App Store 重試該通知。App Store在一段時(shí)間內(nèi)嘗試重試該通知,但在連續(xù)失敗嘗試后最終停止(3次)。


注意事項(xiàng):

當(dāng)您使用包含退款交易的收據(jù)?transaction_data?向蘋果服務(wù)器校驗(yàn)?verifyReceipt?時(shí),JSON響應(yīng)中不存在退款交易,自動(dòng)續(xù)訂訂閱除外。

收到?REFUND?通知時(shí),您有責(zé)任為每筆退款交易存儲(chǔ),監(jiān)控并采取適當(dāng)?shù)拇胧?。(因?yàn)樘O果只通知一次,暫時(shí)無(wú)法在蘋果后臺(tái)查詢退款的訂單。也不能由開發(fā)者主動(dòng)去蘋果服務(wù)器查詢。)


自動(dòng)續(xù)訂訂閱通知

這個(gè)取消通知之前就一直有,所以這里不重復(fù)了,需要的自行搜索。

自動(dòng)續(xù)訂訂閱的相關(guān)文檔:

Handling Subscriptions Billing | Apple Developer Documentation

In-App Purchases and Using Server-to-Server Notifications - WWDC 2019 - Videos - Apple Developer

Subscription Offers Best Practices - WWDC 2019 - Videos - Apple Developer

最后有2點(diǎn),1.退款通知目前并不能在沙盒中測(cè)試;2.蘋果后臺(tái)也無(wú)法看到退款的訂單詳情;


71.?命令行修改App版本號(hào)

? ? cd 進(jìn)入工程所在目錄后

修改 Version 版本號(hào) - 如改成?3.0.0

/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString 3.0.0" Info.plist

修改 Build 版本號(hào) - 如改成?3.0.202103051700

/usr/libexec/PlistBuddy -c "Set :CFBundleVersion 3.0.202103051700" Info.plist


72. 真機(jī)調(diào)試下查看沙盒文件

連接真機(jī)設(shè)備,打開Xcode ,選擇Window -->Device and Simulator?,快捷鍵 Command + Shift + 2;

Device and?Simulator

找到對(duì)應(yīng)的APP,選擇設(shè)置圖標(biāo),點(diǎn)擊Download?Container 下載到桌面

Download?Container

完成后就會(huì)在桌面上看見如下文件,右擊 ——> 選擇“顯示包內(nèi)容”

沙盒文件
沙盒目錄

如果要想替換真機(jī)中的沙盒文件也是如上操作,在已下載好的文件中修改完成后。打開Xcode,Command + Shift + 2。選擇Replace Container,選擇修改后的沙盒文件,點(diǎn)擊Open。

Replace Container


73. 獲取不到內(nèi)購(gòu)套餐產(chǎn)品信息的情況

1.檢查App的Bundle ID是否與蘋果商店中配置的信息統(tǒng)一;

2.檢查產(chǎn)品ID是否填寫錯(cuò)誤;

3.檢查是否同意App付費(fèi)協(xié)議,及填寫銀行卡等稅務(wù)信息;

????????當(dāng)在蘋果商店配置好內(nèi)購(gòu)套餐后,即便不提交審核(二進(jìn)制文件缺失),或者審核失敗,App在沙盒環(huán)境下,仍然能獲取、購(gòu)買App內(nèi)購(gòu)套餐;


74. 簡(jiǎn)單的線程死鎖

線程死鎖


75.并行與串行的區(qū)別

并行與串行的區(qū)別

在子線程中啟動(dòng)定時(shí)器,需要使用Runloop


76.OC的動(dòng)態(tài)消息機(jī)制

調(diào)用某個(gè)對(duì)象的私有方法

私有方法
NSInvocation調(diào)用方法


77.字符轉(zhuǎn)換

將普通話轉(zhuǎn)換為拉丁符號(hào),且?guī)б魳?biāo),包括希伯來(lái)語(yǔ)、阿拉伯語(yǔ)、泰語(yǔ)等多種語(yǔ)言。對(duì)處理除英語(yǔ)以外的語(yǔ)言和非拉丁文字時(shí)非常有用

示例
支持的類型


78.將時(shí)間戳轉(zhuǎn)換為NSDate 或 NSString (如今日0點(diǎn)時(shí)間戳1639929600 -> 2021-12-20 00:00:00)

將時(shí)間戳轉(zhuǎn)換為NSDate 或 NSString


79.在字典A中SetValue一個(gè)從字典B轉(zhuǎn)換成的字符串B‘,然后將字典A轉(zhuǎn)成字符串A’導(dǎo)致Web端解析失敗的問題

示例

? ? 因?yàn)橥值渲蠸et 字符串pkgStr,會(huì)自動(dòng)為Value添加引號(hào),導(dǎo)致最后將字典infoData轉(zhuǎn)成字符串時(shí),會(huì)出現(xiàn) "webInfo":"{xxxx 的情況,導(dǎo)致Web解析失敗。

? ? 解決方法,直接在 infoData 中Set另一個(gè)字典 pkgInfo,不需要轉(zhuǎn)成字符串后再寫入

解決方案
例子

這樣就不會(huì)出現(xiàn) Web 解析失敗的問題;


80.關(guān)于App后臺(tái)推送標(biāo)題、副標(biāo)題、正文及圖片音頻視頻等內(nèi)容正常應(yīng)由服務(wù)器確定,但App配置文件與代碼優(yōu)先級(jí)更高。優(yōu)先級(jí)為:App配置文件(TARGETS——NotificationService——Build Settings——Info.plist Value內(nèi)修改Display Name)、NotificationService.m內(nèi)UNMutableNotificationContent對(duì)象的title屬性、服務(wù)器推送內(nèi)容中的title字段,以上內(nèi)容都為空時(shí),系統(tǒng)默認(rèn)以App名作為推送標(biāo)題。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容