一.通知

对于通知,大家想必都不陌生,它是一个单例,允许当事件发生时通知一些对象,让我们在低程度耦合的情况下,来达到通信的目的。

通知的优势:
1.不需要编写太多代码,实现比较简单
2.对于一个发出的通知,可以多个对象作出反应,即是说通知是一对多的形式

通知的缺点:
1.在编译期不会检查通知是否能够被观察者正确处理
2.在释放注册的对象时,需要在通知中心取消注册
3.在调试应用时,难以跟踪程序
4.发出通知后,不能够从观察者那里获取任何反馈信息

通知的基本实现:

- (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(@"发送通知完成 - %@",[NSThread currentThread]);

}

- (void)test {

NSLog(@"接收到通知 - %@",[NSThread currentThread]);

sleep(3);

}

打印结果:

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] 发送通知完成 - <NSThread: 0x600000079c80>{number = 1, name = main}

注意打印结果:在test方法执行完毕之后,才会打印发送完成的log。

如果在子线程发送通知:

- (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(@"发送通知完成 - %@",[NSThread currentThread]);

});

}

- (void)test {

NSLog(@"接收到通知 - %@",[NSThread currentThread]);

sleep(3);

}

打印结果:

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] 发送通知完成 - <NSThread: 0x608000267980>{number = 3, name = (null)}

得出结论:接收通知的线程和发送通知的线程是一样的,如果在实际开发过程中,我们是在子线程中发送通知的,在接收到通知之后,需要刷新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(@"发送通知完成 - %@",[NSThread currentThread]);

});

}

打印结果:

2017-06-13 18:21:38.367 通知的基本使用[29365:3382047] 接收到通知 - <NSThread: 0x600000063d80>{number = 1, name = main}

2017-06-13 18:21:41.368 通知的基本使用[29365:3382100] 发送通知完成 - <NSThread: 0x600000071bc0>{number = 3, name = (null)}

得出结论:使用NSOperationQueue可以让接收通知的线程和发送通知的线程不一样,让接收通知的线程在主线程,就可以刷新UI等操作了。

二.Xcode何时会报unrecognized selector 的错误

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {

WWPerson *person = [[WWPerson alloc] init];

[person test];

}

当向person发送test这个消息时,runtime库会根据对象的isa指针找到该对象实际所属的类,然后在该类的方法列表以及父类的方法列表里面找相应的方法运行,如果在最顶层的父类中依然找不到相应的方法实现时,程序在运行时就会报unrecognized selector sent to的错误并且崩溃,但是在此之前,objc的运行时给出了避免程序崩溃的三次机会。

1.Method resolution
objc运行时会调用+resolveInstanceMethod:或者+resolveClassMethod:,让我们有机会提供一个函数实现而不导致程序崩溃,如果在这里面添加了函数,系统就会重新启动一次消息发送的过程,否则就会移到下一步的消息转发。

+ (BOOL)resolveInstanceMethod:(SEL)sel {

if (sel == NSSelectorFromString(@"test")) {

/**

class: 给哪个类添加方法

SEL: 添加哪个方法

IMP: 方法实现 => 函数 => 函数入口 => 函数名

type: 方法类型:void用v来表示,id参数用@来表示,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
如果目标对象实现了-forwardingTargetForSelector:的方法,runtime就会调用这个方法,给我们一个机会把这个消息转发给其他的对象,只要这个方法返回值不是nilself,整个消息发送的过程就会被重启,这时发送的对象会变成我们返回的这个对象,否则就会移到下一步。

- (id)forwardingTargetForSelector:(SEL)aSelector {

WWTarget *target = [[WWTarget alloc] init];

if ([target respondsToSelector:aSelector]) {

return target; // 就会去调用WWTarget里面的test方法

}else {

return [super forwardingTargetForSelector: aSelector];

}

}

3.Normal Fowarding
如果上面两种方法都没有被实现的话,就会来到第三步,这是runtime给我们最后一次避免崩溃的机会,首先它会-methodSignatureForSelector:来获得函数的参数和返回值类型,如果返回值为nil,则runtime会发出-doesNotRecognizeSelector: 的消息,程序崩溃。如果返回了一个函数签名,runtime会创建一个NSInvocation对象并发送-forwardInvocation:的消息给目标对象。

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {

NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:"v@:"];

return signature;

}

- (void)forwardInvocation:(NSInvocation *)anInvocation {

SEL selector = [anInvocation selector];// anInvocation里面保存的是selector/target/参数

WWTarget *target = [[WWTarget alloc] init];

if ([target respondsToSelector:selector]) {

[anInvocation invokeWithTarget:target];

}

}

如果上面的三步都没有实现的话,就会调用-doesNotRecognizeSelector:,程序崩溃。

三.深拷贝和浅拷贝

深拷贝:内容拷贝,拷贝出来的对象和之前的对象的地址不一样。
浅拷贝:指针拷贝,拷贝出来的对象和之前的对象的地址一样。
直接上简单示例比较好:

1.对可变对象进行 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);

}

结论:1.对可变对象 进行 copy 操作是内容拷贝(深拷贝)

2. copy 出来的copyStr是NSString类型的,如果对copyStr调用

NSMutableString的方法appendString是会崩溃的。

2.对可变对象进行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);

}

结论:1.对可变对象 进行 mutableCopy 操作是内容拷贝(深拷贝)

2. mutableCopy 出来的mutableCopyStr是 NSMutableString 类型

3.对不可变对象进行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);

}

结论:对不可变对象 进行 copy 操作是指针拷贝(浅拷贝)

4.对不可变对象进行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);

}

结论:1.对不可变对象 进行 mutableCopy操作 是内容拷贝(深拷贝)

2.对mStr进行mutableCopy操作的mutableCopyStr是NSMutableString类型的

综合以上所述:只有对 不可变对象进行copy操作是指针拷贝(浅拷贝),其他的都是内容拷贝(深拷贝)

四.调起键盘时,如何将键盘的“换行”变成“发送/完成”等

设置returnKeyType属性即可,

UIReturnKeyDefault,

UIReturnKeyGo,// 前往

UIReturnKeyGoogle,// google

UIReturnKeyJoin,// 加入

UIReturnKeyNext,// 下一步

UIReturnKeyRoute,// 路线

UIReturnKeySearch,// 搜索

UIReturnKeySend, // 发送

UIReturnKeyYahoo,// 搜索

UIReturnKeyDone,// 完成

UIReturnKeyEmergencyCall,// 紧急电话

UIReturnKeyContinue NS_ENUM_AVAILABLE_IOS(9_0),// 继续

五.viewDidLayoutSubviews和layoutSubviews的调用顺序

viewDidLayoutSubviews在 layoutSubviews前面调用
layoutSubviewsdrawRect :前面调用

2017-06-14 10:31:35.215 layoutSubviews等的调用顺序[7357:98975] -[ViewController viewDidLoad]

2017-06-14 10:31:35.215 layoutSubviews等的调用顺序[7357:98975] -[WWView initWithFrame:]

2017-06-14 10:31:35.220 layoutSubviews等的调用顺序[7357:98975] -[ViewController viewWillLayoutSubviews]

2017-06-14 10:31:35.220 layoutSubviews等的调用顺序[7357:98975] -[ViewController viewDidLayoutSubviews]

2017-06-14 10:31:35.220 layoutSubviews等的调用顺序[7357:98975] -[WWView layoutSubviews]

2017-06-14 10:31:35.221 layoutSubviews等的调用顺序[7357:98975] -[WWView drawRect:]

六.如何给分类动态添加属性

 

#import "WWView+Tools.h"

#import <objc/runtime.h>

static char strKey;

@implementation WWView (Tools)

- (void)setDynamicStr:(NSString *)dynamicStr {

/**

id object: 需要给哪个对象的属性赋值

const void *key:属性对应的key值

id value:设置属性的值为value

objc_AssociationPolicy policy:关联策略 枚举值 一般选择NONATOMIC

*/

objc_setAssociatedObject(self, &strKey, dynamicStr, OBJC_ASSOCIATION_COPY_NONATOMIC);

}

- (NSString *)dynamicStr {

return objc_getAssociatedObject(self, &strKey);

}

七.如何把一个view生成一张图片,并且保存到本地

因为涉及到访问相册,所以先在plist文件里面添加NSPhotoLibraryUsageDescription允许应用程序访问你的相册

- (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);

}

}

八.如果服务器返回给我们的数据是包含标签的,我们应该如何加载

// html_content:含有html标签的富文本

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去加载

[self.contentWebView loadHTMLString:html_content baseURL:nil];

九.上传到应用商店太慢的话,怎么解决

可以考虑使用Xcode - Open Developer Tool - Application Loader来解决

iOS 一些常见问题的整理的更多相关文章

  1. iOS 实用博客整理(连载版)

    iOS 实用博客整理(连载版) 本博客为本人觉得不错的博客的暂存地,并不是本人所写. 1.iOS开发 如何适配iOS10? 2.UIWebView和WKWebView的比较和选择 3. 如何快速的开发 ...

  2. iOS 常用三方类库整理

    iOS 常用三方类库整理 1:基于响应式编程思想的oc 地址:https://github.com/ReactiveCocoa/ReactiveCocoa 2:hud提示框 地址:https://gi ...

  3. iOS学习笔记-精华整理

    iOS学习笔记总结整理 一.内存管理情况 1- autorelease,当用户的代码在持续运行时,自动释放池是不会被销毁的,这段时间内用户可以安全地使用自动释放的对象.当用户的代码运行告一段 落,开始 ...

  4. iOS学习笔记总结整理

    来源:http://mobile.51cto.com/iphone-386851_all.htm 学习IOS开发这对于一个初学者来说,是一件非常挠头的事情.其实学习IOS开发无外乎平时的积累与总结.下 ...

  5. iOS之UI组件整理

    作者:神兽gcc 授权本站转载. 最近把iOS里的UI组件重新整理了一遍,简单来看一下常用的组件以及它们的实现.其实现在这些组件都可以通过Storyboard很快的生成,只是要向这些组件能够变得生动起 ...

  6. iOS 开发学习资料整理(持续更新)

      “如果说我看得比别人远些,那是因为我站在巨人们的肩膀上.” ---牛顿   iOS及Mac开源项目和学习资料[超级全面] http://www.kancloud.cn/digest/ios-mac ...

  7. Cordoval在iOS中的运用整理

    一:关于Cordoval理论知识 1:PhoneGap是手机平台上流行的一款中间件.它构建在各种手机平台所提供的WebView(浏览器内核)组件的基础之上,使用javascript语言对应用开发者提供 ...

  8. iOS核心动画学习整理

    最近利用业余时间终于把iOS核心动画高级技巧(https://zsisme.gitbooks.io/ios-/content/chapter1/the-layer-tree.html)看完,对应其中一 ...

  9. iOS 相关职位要求整理版

    在拉勾上找了20家,BOSS直聘找了10家感兴趣的在招聘 iOS 程序员的公司,把职位要求整理了一下. 初创公司一般要求1年以上开发经验,成长型或者成熟型公司一般要求最低2年以上开发经验.这里针对的是 ...

随机推荐

  1. Code alignment 代码对齐改进(VS2017)

    In mathematics you always keep your equals lined up directly underneath the one above. It keeps it c ...

  2. CentOS6.8安装360 pika

    1.安装依赖包 yum install snappy-devel bz2 libzip-dev libsnappy-dev libprotobuf-dev libevent-dev protobuf- ...

  3. Python中List的append引用赋值问题处理

    Python中的对象之间赋值时是按引用传递的,如果需要拷贝对象,需要使用标准库中的copy模块. 1. copy.copy 浅拷贝 只拷贝父对象,不会拷贝对象的内部的子对象. 2. copy.deep ...

  4. Asp.Net Core 2.0 项目实战(2)NCMVC一个基于Net Core2.0搭建的角色权限管理开发框架

    Asp.Net Core 2.0 项目实战(1) NCMVC开源下载了 Asp.Net Core 2.0 项目实战(2)NCMVC一个基于Net Core2.0搭建的角色权限管理开发框架 Asp.Ne ...

  5. 完美的mysql备份脚本

    转自:https://www.cnblogs.com/leffss/p/7832047.html #!/bin/bash #全备方式,一般在从机上执行,适用于小中型mysql数据库 #删除15天以前备 ...

  6. BZOJ1192 [HNOI2006]鬼谷子的钱袋 数学推理

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ1192 题意概括 把一个数m拆成很多数字. 问至少拆成多少个数字,1~m中的所有数字才可以用这些数字 ...

  7. go语言学习-结构体

    结构体 go语言中的结构体,是一种复合类型,有一组属性构成,这些属性被称为字段.结构体也是值类型,可以使用new来创建. 定义: type name struct { field1 type1 fie ...

  8. Web大前端面试题-Day10

    1. px和em的区别? px和em都是长度单位; 区别是: px的值是固定的,指定是多少就是多少, 计算比较容易. em得值不是固定的,并且em会继承父级元素的字体大小. 浏览器的默认字体高都是16 ...

  9. Javascript日常编码中的一些常见问题

    一.尽量少用全局变量   这是一个疑问最少,同时流传最 广的一条.Javascript使用函数管理作用域,全局变量最大的问题在于同名变量冲突.这种隐患产生比较直接的两个原因就是Javascript语言 ...

  10. C#通用数据库操作类