Object-C 零碎知识点笔记

来源:互联网 发布:调音台软件 编辑:程序博客网 时间:2024/05/07 22:54

1、记录代码片段运行时间

double begin = mach_absolute_time();[self refreshSongImageInNewLogic];double end = mach_absolute_time();NSLog(@"time cost = %f",MachTimeToMilliSecond(end - begin)); //毫秒double MachTimeToMilliSecond(uint64_t time){    mach_timebase_info_data_t timebase;    mach_timebase_info(&timebase);    return (double)time * (double)timebase.numer / (double)timebase.denom / 1e6;}

以上是一种方法,今天看 YYCache 源码的时候发现另外一种记录代码片段运行时间的方法,也顺带记录一下吧:

NSTimeInterval begin, end, time;            printf("\n===========================\n");    printf("Memory cache set 200000 key-value pairs\n");        begin = CACurrentMediaTime();    @autoreleasepool {        for (int i = 0; i < count; i++) {            [nsDict setObject:values[i] forKey:keys[i]];        }    }    end = CACurrentMediaTime();    time = end - begin;    printf("NSDictionary:   %8.2f\n", time * 1000);

好奇点击 CACurrentMediaTime 进去看了一下,结果,哈哈.......它的其实也是调用 mach_absolute_time() 来计时的,同根同源啊!





2、关于视频播放,网上查找了一下资料主要有 MPMoviePlayerController (注意和 MPMoviePlayerViewController 的区别 )和 AVPlayer 这两个类来播放,如果对 UI 的自定义需求不大的话建议用 MPMoviePlayerController ; AVPlayer 是对 MPMoivePlayerController 的进一步封装,可提供更加个性化的定制,当然,使用起来就相对就没有 MPMoviePlayerController 那么方便了。具体的可以参考苹果的开发文档,两者的比较可以看这里:AVPlayer and MPMoivePlayerController difference

另外,在 github 上找到 2 个 star 比较多的开源库,也可参考一下:

ALMoviePlayerController  -  使用的是 MPMoviePlayerController

VKVideoPlayer                  -  使用的是 AVPlayer 

VideoPlayerKit                   -  封装了挺多功能的




3、手机一不小心升级 ios 9.1,然后 XCode还是7.0 ,导致真机 debug 不了了。想着算了那就升级一下 XCode 到 7.1 呗,结果 XCode 7.1 最低要求系统是 10.10.5, 尼玛,我又要升级系统。最后我一整天啥都没干成,就在那里升级升级......

      好不容易升级完了,结果XCode 7.2 beta ,ios 9.1 真机调试的时候报出 “could not find developer disk image” 的错误,泪崩~ 好吧,Google一下喽,最后的解决办法是:

把 XCode 7.1  /Applications/Xcode-beta.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/9.1  这个目录下(包括9.1这个文件夹)的所有内容 copy 到 XCode 7.2 beta 版本的

/Applications/Xcode-beta.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/  这个目录下,然后重启一下 XCode 就好了。




4、设置 UIButton 的 hidden 属性不起作用,很多情况下都是因为你在后台线程操作的原因。改为在主线程设置就好了。

dispatch_async(dispatch_get_main_queue(), ^{                weakSelf.toMVButton.hidden = YES;                weakSelf.toKTVButton.hidden = YES;            });

今天发现在 Block 中修改 button 的位置(frame)的时候看到 log 打印出来的位置明明对了,界面却显示不正确,setNeedsLayout 也没有用,后来改为在主线程设置就好了。



5、用宏定义检测block是否可用!

#define BLOCK_EXEC(block, ...) if (block) { block(__VA_ARGS__); };     // 宏定义之前的用法  /* if (completionBlock)   {       completionBlock(arg1, arg2);   }     */     // 宏定义之后的用法  BLOCK_EXEC(completionBlock, arg1, arg2);



6、对于逆向工程的目的,但是这是可以看的对象实例变量。它通常很容易用valueForKey这样获取。

还有一个情况下,它不能用valueForKey获取,虽然:当这个变量是void *类型。

1
2
3
4
@interface MPMoviePlayerController : NSObject {
    void *_internal;    // 4 = 0x4
    BOOL _readyForDisplay;  // 8 = 0x8
}

用底层方式来访问

1
id internal = *((const id*)(void*)((uintptr_t)moviePlayerController + sizeof(Class)));

不要使用这段代码,它的非常危险的。仅使用于逆向工程



7、获取沙盒路径的2种方式,第一种是比较常用的 

NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);

第二种是

char* home = getenv("HOME");strcat(home, "/Documents/recorder.pcm");

对于第二种,如果你需要在 C++ 文件里面读写文件又需要到沙盒路径的时候再适合不过了。




8、UILabel 设置透明字体





9、

一个基于runtime的开源仓库"libextobjc",集合多种功能,例如可以给一个protocol中声明的方法增加默认实现,遵守此协议的类如果自己没有实现协议中声明的方法,就会调用到默认实现中。
   


10、事件的传递和响应的区别:
事件的传递是从上到下(父控件到子控件),事件的响应是从下到上(顺着响应者链条向上传递:子控件到父控件)

<1>、当一个事件发生后,事件会从父控件传给子控件,也就是说由UIApplication -> UIWindow -> UIView -> initial view,以上就是事件的传递,也就是寻找最合适的view的过程。

<2>、接下来是事件的响应。首先看initial view能否处理这个事件,如果不能则会将事件传递给其上级视图(inital view的superView);如果上级视图仍然无法处理则会继续往上传递;一直传递到视图控制器view controller,首先判断视图控制器的根视图view是否能处理此事件;如果不能则接着判断该视图控制器能否处理此事件,如果还是不能则继续向上传 递;(对于第二个图视图控制器本身还在另一个视图控制器中,则继续交给父视图控制器的根视图,如果根视图不能处理则交给父视图控制器处理);一直到 window,如果window还是不能处理此事件则继续交给application处理,如果最后application还是不能处理此事件则将其丢弃

<3>、在事件的响应中,如果某个控件实现了touches…方法,则这个事件将由该控件来接受,如果调用了[supertouches….];就会将事件顺着响应者链条往上传递,传递给上一个响应者;接着就会调用上一个响应者的touches….方法



11、主界面卡死的问题,除了考虑主线程阻塞的问题外还要看看是否是死锁导致的,项目中遇到的一个问题就是死锁的导致的界面卡死。切记。



12、预编译的宏一般写在 xxx.pch 文件中,如果不希望加载预编译.pch文件的话可以在项目 - Build Settings 里面找到.pch文件并把它删除就好了。



13、合并 arm64 和 x86 的 .a 文件可用 lipo 命令,如下:

分别选择iOS设备和模拟器进行编译,最后找到相关的.a进行合包,使用lipo -create 真机库.a的路径 模拟器库.a的的路径 output  路径/合成库的名字.a

查询 .a 包含的架构用 lipo -info xxx.a

13.1、Xcode 打开 bitcode 有 2 个地方要设置:
(1)
(2)

13.2、查看 .a 是否包含 bitcode 

参考 .a 是否包含bitCode

otool -arch armv7 -l xxx.a | grep __bitcode | wc -l

输出结果大于 0 的为包含,否则为不包含!(这个只是查看 armv7 的,查看所有的架构的话去掉 -arch armv7 就好了。)





14、利用 association 防止多次调进一个方法,总觉得为这种事情去加一个成员变量会让一段逻辑的代码过于分散,喜欢能 self-managed 的函数

参考链接:这里

15、因为是c/c++ 和 oc 混编,需要用到内核里面一个结构体成员的变量,所以就加了一个接口返回该结构体,胶水层也做了一个接口调用内核的这个接口,返回 void * 给上层调用,结果问题来了,不知道如何做 struct 类型和 void * 的转换,stackoverflow 上找了一下没找到答案;后来,想了一想,内核的接口不返回该结构体变量了,直接返回该结构体变量的地址,胶水层就能够直接转换为 void* 了。总结:还是思路问题,遇到死胡同,换一下思路和角度看问题,也许棘手的问题一下子就能迎刃而解。 



16、设置锁屏界面图片用到的主要两个类:MPMediaItemArtwork 和 MPNowPlayingInfoCenter 。



17、Objective-C 中的 Shallow Copies, Deep Copies, mutableCopyWithZone, copyWithZone 的详解。



18、断点之后打印 runtime 所有调用函数 

参考链接:这里(强烈推荐看一下)

19、私有化某个方法,不让其他人使用,例如在单例模式下,你要求别人使用 [MyClass shareInstance] 这个,而不希望别人自己另外创建一个实例去使用,这时候你就可以把 init 方法设置为不可用,如下
- (instancetype)init __attribute__((unavailable("Disabled. Use +sharedInstance instead")));




20、Debug 技巧:查看出错地址信息 LLDB 命令:

image lookup –address 0x0000000104c25550
拓展连接:LINUX 内核开发与调试

21、AudioQueue 音量淡入淡出设置
void AudioQueuePlayer::fade(float sec, float volume) {      if (audioQueue) {          mVolume = volume;          AudioQueueSetParameter(audioQueue, kAudioQueueParam_VolumeRampTime, sec);          AudioQueueSetParameter(audioQueue, kAudioQueueParam_Volume, volume);        }  }

关于 kAudioQueueParam_VolumeRampTime 这个参数的用法苹果的解释如下:
  For example, to fade from unity gain down to silence over the course of 1 second, set this parameter to 1 and then set the kAudioQueueParam_Volume parameter to 0.




1 0