2020iOS开发笔记

1.ViewController不释放的原因:

  • viewController中有Timer.
  • block中有self. 要改为弱引用的self,如:__weak typeof(self) weakself = self;

2.问题:重新载入tableview数据,后tableview里的cell都消失了

原因是在cell中刷新tableview的时候最好在在主线程调用,如:

//在cell中setModel中的代码
dispatch_async(dispatch_get_main_queue(), ^{  //必须加这个在主线程调用,要不界面会消失
            //通知聊天界面刷新列表
            [[NSNotificationCenter defaultCenter] postNotificationName:kMessageListChanged object:nil];
            //[weakself.delegate toReloadMessageList];
        });

3.防止应用崩溃的简单方法:

这个简单的方法是使用try catch语句,原先会崩溃的代码放在try中就不会崩溃了。

    @try {
        NSMutableArray *array = @[@"a", @"b"];  //没有使用mutableCopy,返回的是不可变数组
        //由于array其实是不可变数组,这里会抛出异常,如果没有放在try中会导致程序崩溃。
        array[1] = @"abc";
    } @catch (NSException *exception) {
        NSLog(@"myexception: %@  inFunction:%s", exception, __func__);
    } @finally {
        //不论是否有catch到异常,都会执行finally中的代码 这也挺用,比如可以替代goto。避免因为returen错过了一定要执行的代码
    }

那么没有崩溃我们怎么发现被try隐藏的问题呢?可以在catch中打出日志方便查找问题,或者在断点中添加All Exceptions,当有异常抛出时就会进入断点。


截屏2020-07-31 下午1.50.37.png

4. iOS中的过滤器NSPredicate:

ios中也有过滤器可以用来过滤数据模型对象的数组或集合。这使我们不需要自己用for循环和if来过滤tableview的数据。让过滤数据和在数据库中用sql语句一样方便。
谓词(NSPredicate)是Objective-C中针对数据集合的一种逻辑筛选条件。用法如下:

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"age > 10"];
NSArray *newArray = [objectArray filteredArrayUsingPredicate:predicate];

条件表达式的左边是一个对象的属性键值(键路径keypass)。中间是逻辑运算符,它们与SQL语句中的语法基本相对应,除了最基本的逻辑运算符:>、==、<=、&&等之外,还有逻辑词IN、CONTAINS、like等。


逻辑运算符的用法

5.如何在UIViewController中得知应用回到了前台:

在AppDelegate有应用生命周期的回调方法,可在UIViewController要怎么得知呢?可以使用添加通知监听的方法,如:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willEnterForeground) name:UIApplicationWillEnterForegroundNotification object:nil];

6. id类型修饰符的应用。

id的说明:使用id类型将对象的类型推迟到运行时才确定,由赋给它的对象类型决定对象指针的类型。另外,类型确定推迟到运行时之后,可以通过NSObject的isKindOfClass方法动态判断对象最后的类型(动态类型识别)。也就是说,id修饰的对象为动态类型对象,其他在编译器指明类型的为静态类型对象。
id的用法:在不知道dict中的类型是NSString还是NSNumber类型时,可以用id类型的对象来接收,此对象也可以调用NSString对象的方法(貌似可调用任意类型的方法)。为了防止意外情况,可以判断类型再调用方法,也可以把代码放到try catch里。

7. 用AFNetworking网络请求,如何post JSON数据。

        AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
        manager.requestSerializer.timeoutInterval = TimeOutSecond; //超时时间
        //以下两句话是让body里传的是json格式
        manager.requestSerializer = [AFJSONRequestSerializer serializer];
        [manager.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
        NSString *strToken = [LoginDataModel sharedLoginDataModel].token;
        [manager.requestSerializer setValue:strToken forHTTPHeaderField:@"token"];
        [manager POST:strUrl parameters:bean progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        }];

除了设置Content-Type,还要设置为json的序列化。这样传过去的直接是json数据,而不需要有参数的key。

8. 录音中断的现象和解决方法。

现象是并感觉不到中断,录音结束的回调函数并不会提前结束,只是对比保存的录音文件的时长和录音过程的时长会发现,实际并没有录完整。原因是[AVAudioSession sharedInstance]的setCategory方法会中断录音。也就是说录音时不能播放声音。比如录音时收到消息就不能播放提示音,而应该跳过。

录制语音前需要对录音设备属性Category设置如下:
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryRecord error:nil];
或[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];

由于程序逻辑可能出现问题,在录音过程中重新设置Category属性,比如设置如下:
[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
这个设置会取消设备录音功能,从而导致录音中断

9. 全局变量的用法。

在.m文件中定义,不用加extern。不要在.h中定义要不编译时会报重复定义的错误。
其他地方用到时在变量的声明前加extern。

10. MJExterntion用法之在模型中包含数组模型。

需要重写类函数mj_objectClassInArray,说明对应的某个Array属性里是什么类对象。

//改变字典的key对应的属性名
+(NSDictionary *)mj_replacedKeyFromPropertyName{
    return @{@"fId":@"id"};
}

//说明数组中包含的模型是什么类,OaHandleModel是类,handleLogList是模型中的某个属性名。
+ (NSDictionary *)mj_objectClassInArray{
    return @{@"handleLogList":@"OaHandleModel"};
}

11.不需要授权就能获取的有趣设备信息。

UIDevice *device = [UIDevice currentDevice];
 device.batteryMonitoringEnabled = YES;  //需要打开才能获取到设备电量
device.name; //设备名称,一般默认会是“谁的iPhone”
device.batteryLevel; //设备电量
device.identifierForVendor; //可以用做设备ID虽然卸载重装后会变化

12.新增SceneDelegate之后,对ios13.0以下版本的适配。(swift)

ios13.0之后,原来在AppDelegate中的window属性,转移到了SceneDelegate。要适配ios13.0以下的版本,又不想使用Storyboard,可以在AppDelegate自己添加window属性并初始化它。代码如下

//  在AppDelegate.swift中
    var window: UIWindow?
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
//        let systemVersion = UIDevice.current.systemVersion
//        let version = systemVersion.components(separatedBy: ".")
//        if (Int(version[0])! < 13){}
        
        if #available(iOS 13, *) {
        }else {
            let vc = MyTabController()
            //let nav = UINavigationController(rootViewController: vc);
            
            //创建window 如果是用默认的storyboard,下面的代码可以不写
            window = UIWindow.init()
            window?.frame = UIScreen.main.bounds
            window?.makeKeyAndVisible()
            window?.rootViewController = vc //UIStoryboard.init(name: "Main", bundle: nil).instantiateInitialViewController()
            
            //UIApplication.shared.keyWindow?.rootViewController = vc
            //let rootVC = UIApplication.shared.delegate as! AppDelegate
            //rootVC.window?.rootViewController = vc
        }
                
        return true
    }

13. 出现[__NSSingleEntryDictionaryI length]: unrecognized selector sent to instance的情况。

   //当nameList不是NSString数组而是NSDictionary数组时会出现错误:'-[__NSSingleEntryDictionaryI length]: unrecognized selector sent to instance 0x283ce9580'
    for (NSString *strName in nameList) {
      GradeButton *btn = [self createButton];
       [btn setTitle:strName forState:UIControlStateNormal];
}
//可能是因为setTitle时有用到获取字符串长度的方法。

14.将代码同步到主线程执行的三种方法如下:

// 1.NSThread
[self performSelectorOnMainThread:@selector(updateUI) withObject:nil waitUntilDone:NO];
- (void)updateUI {
    // UI更新代码
    self.alert.text = @"Hello!";
}

// 2.NSOperationQueue
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
    // UI更新代码
    self.alert.text = @"Hello!";
    }];

// 3.GCD
dispatch_async(dispatch_get_main_queue(), ^{
   // UI更新代码
   self.alert.text = @"Hello!";
});

15.runLoop在实际开发中涉及到的地方。

日常开发中,与 runLoop 接触得最近可能就是通过 NSTimer 了。一个 Timer 一次只能加入到一个 RunLoop 中。我们日常使用的时候,通常就是加入到当前的 runLoop 的 default mode 中,而 ScrollView 在用户滑动时,主线程 RunLoop 会转到 UITrackingRunLoopMode 。而这个时候, Timer 就不会运行。
有如下两种解决方案:
第一种: 设置RunLoop Mode,例如NSTimer,我们指定它运行于 NSRunLoopCommonModes ,这是一个Mode的集合。注册到这个 Mode 下后,无论当前 runLoop 运行哪个 mode ,事件都能得到执行。
第二种: 另一种解决Timer的方法是,我们在另外一个线程执行和处理 Timer 事件,然后在主线程更新UI。

16.导致崩溃的异常编码

异常编码,就在异常信息里。一些被系统杀掉的情况,可以通过异常编码来分析。维基百科里列出了 44 种异常编码,但常见的就是如下三种:
0x8badf00d,表示 App 在一定时间内无响应而被 watchdog 杀掉的情况。
0xdeadfa11,表示 App 被用户强制退出。
0xc00010ff,表示 App 因为运行造成设备温度太高而被杀掉。

17.Objective-C与C和C++混编的注意事项

  1. oc中用到c++部分时,要记得把扩展名改为.mm,即使不直接在文件中写C++,只是引入C++的头文件也要改成.mm,否则C++文件类的代码会报错,类似没有C++的编译环境。
    .m 和.mm 的区别是告诉编译器在编译时要加的一些参数。.mm也可以命名成.m,手动加编译参数。

  2. 如果在c ++文件中使用c函数。您应该使用extern "c"{}。在.h文件中

#ifdef __cplusplus
extern "C" {
#endif

swrve_currency_given(parameter1, parameter2, parameter3);// a c function

#ifdef __cplusplus
}
#endif  

extern“ C”旨在由C ++编译器识别,并通知编译器所注明的功能已(或将要)以C样式进行编译。
如果要链接到已编译为C代码的库。使用

extern "C" {
  #include "c_only_header.h"
}

18.实现可以取消的延时定时器

使用GCD的dispatch_after无法取消,可以使用[self performSelector: withObject: afterDelay: ]
需要取消时调用[NSObject cancelPreviousPerformRequestsWithTarget:self] 来取消

19.图片转换函数

// return image as PNG. May return nil if image has no CGImageRef or invalid bitmap format
NSData *data = UIImagePNGRepresentation(image);
// return image as JPEG. May return nil if image has no CGImageRef or invalid bitmap format. compression is 0(most)..1(least)
UIImageJPEGRepresentation(UIImage, 0.8);
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345