月度归档: 2021 年 3 月

如何学习Android系统源代码?

Android系统的源代码非常庞大和复杂,我们不能贸然进入,否则很容易在里面迷入方向,进而失去研究它的信心。我们应该在分析它的源代码之前学习好一些理论知识,下面就介绍一些与Android系统相关的资料。

我们知道,Android系统是基于Linux内核来开发的,在分析它在运行时库层的源代码时,我们会经常碰到诸如管道(pipe)、套接字(socket)和虚拟文件系统(VFS)等知识。此外,Android系统还在Linux内核中增加了一些专用的驱动程序,例如用于日志系统的Logger驱动程序、用于进程间通信的Binder驱动程序和用于辅助内存管理的匿名共享内存Ashmem驱动程序。在分析这些Android专用驱动程序的时候,也会碰到Linux内核中与进程、内存管理相关的数据结构。因此,我们有必要掌握一些Linux内核的基础知识,下面就介绍四本典经的Linux内核书籍。

  1. Linux Kernel Development.这本书的作者是Robert Love,目前*新的版本是第3版。这本书对Linux内核的设计和实现提供了一个总览视图,从概念上对Linux内核的各个子系统的设计目标和实现思路进行了清晰的描述,非常适合初学者阅读。如果从软件工程的角度来看,这本书就相当于是Linux内核的概要设计文档。
  2. Understanding the Linux Kernel.这本书的作者是Daniel P. Bovet和Marco Cesati,目前*新的版本是第3版。这本书对Linux内核的实现提供了更多的细节,详细地描述了内核开发中用到的重要数据结构、算法以及编程技巧,非常适合中高级读者阅读。如果从软件工程的角度来看,这本书就相当于是Linux内核的详细设计文档。
  3. Linux Device Drivers.这本书的作者是Jonathan Corbet, Alessandro Rubini和Greg Kroah-Hartman,目前*新的版本是第3版。这本书更加注重实际操作,它详细地讲解了Linux内核驱动程序的实现原理和实现方法,读者可以跟着它来实际地编写出自己的Linux驱动程序。阅读了这本书之后,对我们后续去分析Android的专用驱动程序是有非常大的帮助的。
  4. Linux内核源代码情景分析这本书的作者是毛德操和胡希明,是中国人自己编写的一本经典的Linux内核书籍。这本书*大的特点是从使用情景出发,对Linux内核的源代码作了详细的分析,帮助读者把枯燥无味的源代码给理顺了。

掌握了Linux内核的基础知识之后,还不宜马上就去分析Android系统的源代码,因为这样做是漫无目的的,我们应该带着问题或者目标去分析Android系统的源代码。要把问题或者目标挖掘出来,*好的方法就莫过于是在Android平台上编写自己的应用程序了。通过编写应用程序,我们可以知道Android平台都提供了哪些功能,进而我们就会想去了解这些功能是怎么实现的,这样就可以达到带着问题或者目标去分析Android系统的源代码了。这里介绍两个Android应用程序开发教程的书籍:

  1. Professional Android 2 Application Development.
  2. Google Android SDK开发范例大全.

这两本书都使用了大量的例子来说明如何使用Android SDK来开发Android应用程序。读者可以根据实际情况来练习一下,主要掌握Android应用程序四大组件(Activity、Service、Broadcast Receiver和Content Provider)的用法,因为Android系统的整个架构和实现就是为了向开发者提供这四大组件来实现各种各样的应用程序的。在学习的过程中,如果遇到其它问题,还可以参考官方文档,其网址为:http://developer.android.com/index.html

环境搭建

开发Android应用程序可以在两种环境下进行,一是在Android SDK环境下进行,一般是集成在Eclipse里面进行开发,二是在Android源代码工程环境下进行,在这种环境进行开发的好处是可以使用一些在SDK中不公开的接口。但是如果我们要修改Android系统的源代码,或者为Android系统增加新的功能接口,那么就只能在Android源代码工程环境下进行了。由于我们的目的是对Android系统源代码进行分析,因此,我们在开发Android应用程序时,也在Android源代码环境下进行。这样,我们就需要搭建一套Android源代码工程环境了。

目前,Android源代码工程环境只能在Linux平台上使用,而Linux系统的发行版本比较多,这里我们推荐Ubuntu系统。Ubuntu系统是免费的,而且非常易于使用,安装和更新应用程序也非常方便,它的官方下载地址为:http://www.ubuntu.com/

安装好Ubuntu系统之后,我们就可以在上面下载、编译和安装Android源代码了,具体方法和步骤可以参考下面这篇文章:在Ubuntu上下载、编译和安装Android*新源代码

Android系统的源代码工程默认是不包含Linux内核源代码的,如果我们需要修改Android系统的内核或者在里面增加新的模块,那么就要把Android内核源代码一起下载、编译和安装了,具体方法和步骤可以参考下面这篇文章:在Ubuntu上下载、编译和安装Android*新内核源代码(Linux Kernel)

Android源代码工程环境搭建好了之后,我们就可以在里面开发新的应用程序或者修改系统代码了。增加了新的应用程序或者修改了系统的代码之后,不需要重新编译整个源代码工程,只要单独编译有改动的模块就可以了,具体方法可以参考下面这篇文章:如何单独编译Android源代码中的模块

对于已经开发好的应用程序或者系统功能,如果想把当作Demo展示给客户来体验时,我们既可以在真机上面运行,也可以在模拟器(Android源代码工程环境或者Android SDK环境都集成了模拟器)上面运行。当我们手头上没有真机,而且我们又不想把整个Android源代码工程环境或者Android SDK环境带去展示我们的Demo时,就可以考虑把模拟器这两个环境中独立出来了,具体方法可以参考下面这篇文章:制作可独立分发的Android模拟器

系统架构

Android系统是按层次、分模块来设计的。在我们着手对Android系统的源代码进行分析前,需要对Android系统的架构有一个总体的认识,这样我们就能够快速地知道哪些代码位于哪个层次上的哪个模块中,节省搜索代码的时间,把更多的精力投入在源代码的分析上去。

%title插图%num

整个系统划分内核空间和用户空间两部分。内核空间包含了进程管理、内存管理以及设备驱动程序模块等,其中Android专用驱动Binder、Logger和Ashmem就是在内核空间实现的。用户空间包含了硬件抽象层(HAL)、外部库和运行时库层(External Libraries & Android Runtime)、应用程序框架层(Application Framework)和应用程序层(Applications)四个层次。我们应该如何去掌握这个层次结构呢?*好的方法就是从学习Android的硬件抽象层作为切入点了。

可能读者会觉得比较奇怪,为什么要把Android系统的硬件抽象层作为学习Android系统架构的切入点呢?这个层次因为涉及到硬件,看起来这是一个比较复杂和深奥的知识点。其实不然,Android系统的硬件抽象层在实现和使用上,层次都是非常清晰的,它从上到下涵盖了Android系统的用户空间和内核空间。内核空间主要就是涉及到硬件驱动程序,而用户空间就涉及到了Android系统应用程序层、应用程序框架层和系统运行时库层的相关知识。因此,学习Android系统的硬件抽象层,可以使读者快速地认识整个Android系统,从而对Android系统得到一个感性的认识,为后面深入分析Android系统的源代码打下良好的基础。

Android硬件抽象层的学习可以参考下面的一系列文章:

  • Android硬件抽象层(HAL)概要介绍和学习计划
  • 在Ubuntu上为Android系统编写Linux内核驱动程序
  • 在Ubuntu上为Android系统内置C可执行程序测试Linux内核驱动程序
  • 在Ubuntu上为Android增加硬件抽象层(HAL)模块访问Linux内核驱动程序
  • 在Ubuntu为Android硬件抽象层(HAL)模块编写JNI方法提供Java访问硬件服务接口
  • 在Ubuntu上为Android系统的Application Frameworks层增加硬件访问服务
  • 在Ubuntu上为Android系统内置Java应用程序测试Application Frameworks层的硬件服务

学会了编写基本的Android应用程序并且对Android系统的整体架构有一个大概的了解之后,我们就可以去分析Android系统的源代码了。

在分析Android源代码的过程中,我们经常进入到应用程序框架层去分析它的源代码,而在应用程序框架层中,有一部分代码是使用C++来实现的,这时候就会经常碰到智能指针,因此,我们把Android系统中的智能指针也作为一个基础知识点来学习。相信使用过C++语言来做开发的读者对智能指针不会感到陌生。用C++来写代码*容易出错的地方就是指针了,一旦使用不当,轻则造成内存泄漏,重则造成系统崩溃,因此,系统为我们提供了智能指针,避免出现上述问题。

在Android系统中,提供了三种类型的智能指针,分别是轻量级指针、强指针和弱指针,它们都是基于对象引用计数技术来实现的。轻量级指针的计数技术比较简单,只要对象的引用计数值为0,它就会被释放。强指针和弱指针的计数技术相对比较复杂,一个对象可以同时被强指针和弱指针引用,但是这个对象的生命周期一般只受强指针的控制,即当这个对象的强引用计数为0的时候,这个对象就被释放了,即使这时候这个对象的弱引用计数不为0。引进强指针和弱指针这种复杂的引用计数技术是为了解决垃圾收集(Garbage Collection)问题而提出的。考虑这样的一个场景,系统中有两个对象A和B,在对象A的内部引用了对象B,而在对象B的内部也引用了对象A。当两个对象A和B都不再使用时,垃圾收集系统会发现无法回收这两个对象的所占据的内存的,因为系统一次只能收集一个对象,而无论系统决定要收回对象A还是要收回对象B时,都会发现这个对象被其它的对象所引用,因而就都回收不了,这样就造成了内存泄漏。如果采用强指针和弱指针技术,这个问题就迎刃而解了,即A和B都用弱指针来引用对方。Android智能指针的学习,可以参考下面这篇文章: Android系统的智能指针(轻量级指针、强指针和弱指针)的实现原理分析

掌握了本文所介绍的这些基础知识后,我们就可以正式开始分析Android系统的源代码了。

——————————————————————————————————————————————————————————————————————

以下是百度知道的回答:

我干了3年Android sdk开发,觉得到了瓶劲没法更进一步,于是花了一年多点时间,大概摸到点门径。根据前辈的经验,Android底层完全
入门需要两年。

先说下我的入门过程:
第零步,下载源码,我下的4.2的,框架层源码10G,内核2G多,ctags给框架层建的标签文件都有600M,当时让我有点震撼,
用的vim+ctags+cscope来阅读,还算不错,架构挺清晰的。

*步,我找到了一本好书《Android的设计与实现 *卷》它讲了Android框架层的启动,初始化,服务框架初始化,Binder,消息循环,
PackageManagerService,ActivityManagerService。据作者说后面会出讲UI子系统的第二卷,拭目以待。
其实这本书看了几十页我就发现需要第二步的知识,否则看不下去,于是跳去第二步。

第二步,学习Linux系统编程,在看《Android的设计与实现》的时候我发现,框架层的Native部分,全是Linux编程。为了掌握这部分知识,
我花了4个月学习了《Linux系统编程手册》(TLPI)这本1000多页的书,我以前是搞WIndows文件系统这块的,所以C语言还比较熟,TLPI的
习题很有意思,量也比较大,坚持下来还是收获很多。

第三步,花了4个月学习了一些Linux内核的知识,看了LKD,PLKA看了一半多。越学越没底,觉得不懂得越来越多,不过这个也正常,只有靠
慢慢磨,估计以后要不断的磨这块。

第四步,回头看Android源码,这次一口气看完了《Android的设计与实现 *卷》,终于对框架层有了谱。同时真的数次把我看晕,前面看
Linux内核源码都没这么晕,不断在Java层和Native层之间跳有点磨脑浆。其中我又觉得Java的基础没有打太牢,回去补了一个月的
《Core Java》第八版。但是这书没有涉及UI子系统,于是又看了《Android内核剖析》

第五步,《Android内核剖析》(这本书实际上是讲框架层的,作者也是个搞嵌入式的,所以他在写框架层的时候文笔不太好,很罗嗦,不过
还是有很多看点,到他后来写做ROM,玩开发板时估计是说到了他的本行,一下子遛起来了看得出还是挺有水平的,这本书知识有点旧毕竟讲
的是2.3很多代码已经过时,但是作者很多点子很有参考价值)这本书讲UI子系统和按键/触摸消息处理系统还是很有分量的,尤其13章View
绘制那里,结合源码研究很有收获。而后面他讲编译框架和ROM相关的东西都是挺宝贵的资料。

第六步,为了再补一下其他诸如电源管理模块等子系统的知识看了,《深入理解android》系列,个人认为这个系列看起来有点不太舒服,不
过作为补充印证还是比较有价值。

第七步,《Android系统源代码情景分析》,罗升阳的源码分析大作,比《Android的设计与实现》分析得更细致,但缺点是涉及到模块比较
少,选用的源码也比《Android的设计与实现》更旧一点。看完书后需要去研究作者的博客,东西挺多的,一定让你满意。

第八步,买块开发板自己玩。这步我还没走到,原因是我觉得我还差点准备知识。可能要再几个月,到时准备入块6410或者树莓派。

*后,由于我11年以前都是搞Windows这块的,所以对Linux知识不是很了解,不得已看了这么些书,如果是一直做Linux的人,很多步骤估计
可以省掉了。直接上源码才是正道。

我本身做着移动GIS开发的工作,学框架层全是因为兴趣,但招聘平台Android框架层开发人员还是蛮有竞争力的有不少定制ROM,智能电视的
工作都处于人才难求状态,毕竟有一定的门槛,现在各种ios培训,让奔着钱干开发的人纷纷涌入,而ios只能干sdk开发的缺点就暴露出来了,
一堆新手老手,菜鸟大牛全挤在SDK开发这块,我觉得不太妙。 反观Android这边,虽然入门菜鸟没有搞ios来钱,但是可持续性很好,从
sdk-》框架》驱动》内核这样干下去。干着干着发现自己渐渐变成了Linux开发者/嵌入式开发者的人也不少,新人,老手,菜鸟大牛各居其
位,层次性很好。

使用SSH公钥登录服务器

使用Xshell 或 secure CRT 先生成密钥对,将密钥对放到服务器 .ssh/authorized_keys 文件中,如果没有次文件,需要创建一个。

Xshell :

设置服务器IP:

%title插图%num

设置用户验证方式:

%title插图%num

将生成或者导入的公钥以文本的显示添加到服务器.ssh/authorized_keys文件中,如果文件不存在需要新建文件:

*后即可登录成功。

secure CRT:

%title插图%num

如果已经有公钥,选择导出:

%title插图%num

%title插图%num

这样便获得了公钥,放入服务器.ssh/authorized_keys文件中,如果文件不存在需要新建文件。

如果没有公钥,则点击创建身份文件进行创建:

%title插图%num

之后根据提示进行创建即可:

%title插图%num

如果服务器已经将公钥导入,但还没有生效,则需要重启SSH进程:

$ service ssh restart
如何从一台服务器登录到另一台服务器:

//以下是两种登录方法

ssh user_name@ip

ssh ip -l user_name
比如从我的虚拟机登录服务器

ssh ip -l usr_name
接下来输入服务器usr_name 账户的密码即可登录

使用SSH 公钥登录:

将虚拟机的公钥放到服务器的.ssh/authorized_keys文件中,如果文件不存在需要新建文件

下一步会要求输入私钥密码,输入正确即可登录服务器;如果连续三次输入错误,将自动使用用户密码进行登录

ssh ip -l user_name
sun@machine:~$ ssh usr_name@ip
Enter passphrase for key ‘/home/sun/.ssh/id_rsa’:

IOS开发中的那些琐碎的小问题

整理了一个多月开发中的一些小问题,当然,本身遇到问题肯定还有更多,

比如类别的一些使用的技巧,协议格式的一些特殊字符,

同步于异步的网络处理的选择,数据的存储选择,类的设计。

因为其实大部分ios互联网应用的技术含量不会是很深奥,基本到后来发现就是对UI控件的一些需熟能生巧的应用,网络编程以及逻辑的处理,

更多的还有是一些经验和积累。

这边是几个早期的问题,也就是一个开发开始一个*拜中一些问题我还做记载,后面一个月进度匆忙,也就懒于记录

1.UILabel中显示多行时,自动换行。

[cpp] view plain copy

  1. label.numberOfLines =0;
  2. label.lineBreakMode = UILineBreakModeWordWrap;
  1. label.numberOfLines =0;
  2. label.lineBreakMode = UILineBreakModeWordWrap;

前提是该label设置的frame是足够大的。不支持滚动。

理论上不支持这么做,因为TextView控件相对来说更好

2.nil和release区别

nil和release的作用:
nil就是把一个对象的指针置为空,只是切断了指针与内存中对象的联系;
而release才是真正通知内存释放这个对象。

所以nil并没有释放内存,只有release才回真正释放内存。
再分析一下二者使用的先后顺序:
如果没有release就直接nil,那么虽然不会出错,却等于自己制造内存泄漏了,因为nil之后release就已经不起作用了。

相反,如果在使用接口对象时只仅仅release没有设置self.myOutlet =
nil,那么程序可能也不会报错,但却会十分不稳定、不健壮,很容易发生崩溃现象。

因为一个接口对象在release之后,给它所分配等内存就已经被释放了,如果释放之后系统再用到这个对象,那么程序就会crash。

如果释放之后把它的指针置为空,则即便后面的程序用到该对象,也不会崩溃。

3.获取当前时间和格式化

[cpp] view plain copy

  1. NSString *postTime;
  2.     NSDateFormatter *formatter = [[NSDateFormatteralloc] init];
  3.     [formatter setDateFormat:@“YYYYMMddhhmmss”];
  4.     postTime = [formatter stringFromDate:[NSDatedate]];<STRONG>
  5. </STRONG>
  1. NSString *postTime;
  2. NSDateFormatter *formatter = [[NSDateFormatteralloc] init];
  3. [formatter setDateFormat:@“YYYYMMddhhmmss”];
  4. postTime = [formatter stringFromDate:[NSDatedate]];

4.巧妙的运用宏,高效的预处理

5.关于版本控制工具Versions:Versions上FileMerger找不到路径的报错问题。

应用会提醒建议重装,说没有安装这个工具,但是在Xcode下我们又是能发现该应用已经装了的。
*后网上查了下说是Xcode版本升级问题,导致应用路径该了,当时versions默认对filemerge路径没改。
剛剛把 Xcode 升級到 4.3.2 發現 Versions 的文件對比功能無法使用了。因為 Xcode 自從 4.3 之後整體打包成一個App放到了 /Applications 下面,過去的 /Developer 整個被建議刪除掉了。
Versions 按照原來的路徑沒辦法找到 FileMerge.app 所以搞罷工。處理起來很簡單,去命令行執行
sudo /usr/bin/xcode-select -switch /Applications/Xcode.app/Contents/Developer

6.项目编译时一种常见格式报错

Undefined symbols for architecture i386:
“_OBJC_CLASS_$_ASIHTTPRequest”, referenced from:
objc-class-ref in ASIInputStream.o
_OBJC_CLASS_$_ASIFormDataRequest in ASIFormDataRequest.o
objc-class-ref in ASIFormDataRequest.o
objc-class-ref in ASINetworkQueue.o
objc-class-ref in ASIDownloadCache.o
objc-class-ref in NLNetHelper.o
“_OBJC_METACLASS_$_ASIHTTPRequest”, referenced from:
_OBJC_METACLASS_$_ASIFormDataRequest in ASIFormDataRequest.o
“_NetworkRequestErrorDomain”, referenced from:
-[ASIFormDataRequest addFile:withFileName:andContentType:forKey:] in
这是我们在引入第三方类库文件时,build后错误。比如引入_ASIHTTPRequest和SBJson时。
如果出现以上错误了,比较简单的方法就是。点击项目,–》build Phase s–》compile sources中,点击add后,我们能看到项目下的源文件,此时,建议把你能看到后缀为.m文件全add了~~。
后来发现同事引入时没没什么问题,经提醒发现一个一直被我忽略的问题,就是拖动文件进来时,一般我们是选了非引入而是copy一份,这个没问题,但是下面海域个选矿,addtarget。这个记得勾上,因为关系到项目的一些配置,我估计是就是这个原因。

7.ios客户端于java服务端交互关于null的的问题

在ios网络操作时解析json时,有时会有这样额一种情况: “myThing”:null;
而myThing根据情况分成:1,在有返回的时候是一个字典;2若无内容,返回null。
然后我们在客户端处理是需要判断是佛为null才能来做相应的解析(如果null依旧去解析这个自定则会报错),但是仅仅的一句 if(dic)并不试用。
必须[Dic  isEqual: [NSNull null]]

这个其实是指针空于内容空的一种区别。一般来说我们在判断指针空是用nil,而内容空则是NSNull

8.判断字窜A中是否包含字窜B

暂时没找到java中的包含函数
oc提供了一个rangeOfString。然后看length长度。

9.关于ViewController之间交互,数据的传递

在Android我们知道两个Activity之间可以使用bundle来传递,iOS中其实提供了很多解决方法.

但是后来发现*简单的一种就是,我们在第二个ViewController中申明一个这样的属性,

在第*个ViewController中初始化第二个vc后,设置进去要传递的数据就可以了~

当然,用一个全局或是单例类存放在某些情况下会很合适。

10.NSNotificationCenter的妙用和小技巧

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(refeshLogin) name:@”login” object:nil];

消息的注册和监听,以及发送通知调用post发送然后监听处理。

我觉得善用这个类很美妙。

记录 GPS 轨迹,什么地图软件比较好?

在国外,所以一众国产的各个地图 app 排除;

不是所有手机都刷了谷歌服务包,所以要求不依赖谷歌服务的,这样谷歌地图大概用不了了吧?

*好能把我的 GPS 轨迹保存、导出、再导入的,因为经常刷机,又不喜欢账户系统(免去被记录隐私信息)

有啥 app 可以推

48 条回复  •  2021-03-30 19:21:12 +08:00

1 opengps   1 天前 via Android 这基础需求,懂安卓开发的开发起来很容易,但是费电问题始终绕不过,如果允许亮屏,用网页就可以实现,我有个类似的 demo 用于用来研究过这个方向,楼主可以体验下

2 HankAviator   1 天前 顺手一搜不就有了 https://play.google.com/store/apps/details?id=eu.basicairdata.graziano.gpslogger&hl=en_CA&gl=US

3 handuo   1 天前 via Android 我自己用 leaflet api 结合 openstreet map 搞过一个 demo

4 PMR   1 天前   1 github.com/OpenTracksApp/OpenTracks

5 jiangwenwenmodes   1 天前 via Android google fit 软件可以试一下

6 zi   1 天前 via Android 户外 APP 就可以,我常用的两步路,里面选择 Google 地图,轨迹也可以导出

7 crab   1 天前 谷歌地图,离线都可以。

8 freed   1 天前 nike+ running

9 also24   1 天前 之前都是用行者: https://www.imxingzhe.com/ 现在 Strava 多一点: https://www.strava.com/ 行者可以导出 gpx,Strava 可以导出原始的 fit 文件。 我是使用码表记录的原始信息,不太确定直接用 APP 记录的是否可以。

10 18912017465   1 天前 via Android 奥维地图,*牛逼没有之一

11 Feing   1 天前 via iPhone 足迹

12 loveyu   1 天前 via Android   1 https://f-droid.org/packages/com.mendhak.gpslogger/

13 godpeo   1 天前 via iPhone @Feing 奥, 一直想要这样记录的, 现在才发现哈

14 nutting   1 天前 via Android 我的车载导航是安卓,我就安装过一个记录 gps 点位的,忘了叫啥了。

15 yehoshua   1 天前 via Android osmand+ 这个的离线地图很强大

16 zouzou0208   1 天前 via iPhone https://github.com/yihong0618/running_page

17 Tinyang   1 天前 奥维?

18 nightv2   1 天前 via Android oruxmap

19 liyang5945   1 天前 via Android 用软件不如用硬件,GPS 手持机,自行车 GPS 码表,这些东西都比手机续航强而且信号好

20 cpstar   1 天前 在国内这种软件不好找(火星坐标),但是在国外,一把一把的。。。

21 OnewayStreet   1 天前 via iPhone 世界迷雾?

22 soulzz   1 天前 现在国内有老人小孩防丢的定位器 和挂坠一样 有些能够要到协议,个人使用的话要想精度高点密集基本一天一充电? 好处是不依赖手机,因为基本都需要插物联网卡,用的流量一个月*多 100M 就够了 我*份工作在某汽车金融风控领域排行第二的公司做过这些设备的解析,以及全套技术栈都了解>就差个前端了

23 ZaneCheney   1 天前   2 这题我会 =。= IOS 配合 捷径,获取经纬度高程 txt,存储到 iCloud,五分钟后台不亮屏幕运行一次。 导出导入无碍,耗电我看了下,*近 24 小时耗电占比百分之四,*近 10 天耗电占比百分之三 。

24 ZaneCheney   1 天前 贴一个 demo

25 ZaneCheney   1 天前 IPhone 位置自动轨迹 时间:2021-01-09T16:30:01+08:00 位置:中国山东省泰安市 肥城市 G22 青兰高速 经度:116.8563443897652 纬度:36.13549669992123 海拔:189.186018348672 备注: [14.4] [38] []

26 aphorism   1 天前 via Android Under armour 的系列软件: Mapmyride Mapmyrun Mapmywalk

27 LiYanHong   1 天前 六只脚

28 homelajiang   1 天前 traccar 可以自己搭建服务器也可以用官方的 demo

29 lzl2000   1 天前 via iPhone   1 OsmAnd

30 littlewing   1 天前 via iPhone gaia gps

31 littleboyzt   1 天前 奥维没了谷歌地图,弃用了

32 varrily   1 天前 有单独的 GPS 记录仪。 参考: https://dorole.com/1878/

33 lixuda   1 天前 六只脚

34 Q2F5emxo   1 天前 @OnewayStreet 我也想到这个。。。

35 shulinbao   1 天前 via Android 如果要更方便的记录,就用专门的 gps 记录仪。 如果就是自己记录玩玩,没听说过谷歌有个臭名远扬的服务叫 timeline (时间轴)么?只要你手机有谷歌服务,它就会自动记录你的行进轨迹,除非你手动关掉它。至于如何查看 timeline,请访问谷歌地图 app 或网站。

36 shulinbao   1 天前 via Android @shulinbao 好吧,再次看了你的问题描述,说“在国外”,可是“又不是所有手机

37 shulinbao   1 天前 都刷了谷歌服务包”……那就这种吧 https://f-droid.org/packages/com.mendhak.gpslogger/ 但是,还是更推荐专门的记录仪,因为用手机真的很费电

38 sillydaddy   1 天前 前一阵正好研究过这个,来说一嘴。 手机 GPS 记录真的费电! 3000mAh 的电池,3~5 秒记录一次的话,大概能用 6~8 小时吧,这还是不开其他应用,不开屏幕,只在后台记录的情况下。GPS 应用一般都会阻止 Android 系统进入睡眠,因为一旦睡眠,应用就与 GPS 设备“失联”了,这就导致系统一直在唤醒状态,浪费无谓的电量。所以,感觉还是用专门的 GPS 记录仪会比较好一些(不过我也没试过)。

39 tooyotu   23 小时 29 分钟前 足迹很好用

40 bleeontheway   22 小时 58 分钟前 买个儿童手表戴上呀 [狗]

41 dingdangnao   22 小时 39 分钟前 世界迷雾?

42 shakoon   21 小时 57 分钟前 若干年前我一直用谷歌我的足迹,后来停服后用行者,目前用的是 2.9.2,后续的 3.x 感觉又耗电界面又丑还容易被杀后台,非常不喜欢。虽然我一直在用行者,但是这个 app 重点是面向骑行用户的,各种很多功能对我来说没用且影响界面简洁。所以*近新发现了一个国人做的东西也不错,国内版叫徒步路线,国外叫我的路线,在试用中。之前还用过行者出的一个小硬件记录仪,但是启动后要好几分钟才定位成功,更不幸的是在某次旅游时给弄丢了。 我从 2011 年开始用安卓手机起就开始用手机记录轨迹了,刚才看了一下,已经记了 16w 公里了。

43 Jasmine2016   21 小时 44 分钟前 Gyroscope

44 c7in7   21 小时 38 分钟前 足迹

45 expkzb   21 小时 35 分钟前 iOS 上以前用过 “地图加加”, *对符合你的要求。但安卓就不知道了

46 laydown   20 小时 42 分钟前 @ZaneCheney ”五分钟后台不亮屏幕运行一次“,请问这个怎么弄啊,不知道哪里可以看到设置的效果。。

47 ZaneCheney   18 小时 8 分钟前 @laydown 捷径,自动化,对自动运行的时间进行设置。然后取消运行提醒,就后台自动运行了。 上面发的 demo 就是运行的效果。 ![微信图片_20210330191646.jpg]( https://i.loli.net/2021/03/30/cb7EPfGX4ZtplIV.jpg) ![微信图片_20210330191655.jpg]( https://i.loli.net/2021/03/30/njJ2rhvb7UtKDSc.jpg)

48 Rasphino   18 小时 5 分钟前 世界迷雾

iOS块语法详解(block编程)

——译自Apple Reference Library《Blocks Programming Topic》

简介

块对象是C语言的句法和运行时特性。它类似于标准C函数,但可以将代码、变量绑定到堆(heap)、栈(stack)。一个块还维护了一系列的状态,这些状态或数据影响着执行的结果。

可以把块组成函数表达式,用于传递给API,或者使用在多线程里。*有用的是回调,因为块在回调时能把代码和数据一起传送。

在OSX 10.6的Xcode中,可以使用块,它随GCC和 Clang 一起集成。在OSX 10.6及iOS 4.0以后支持块语法。 块运行时是开源的,它能被集成到 LLVM’s compiler-rt subproject repository 中。标准C工作组的 N1370: Apple’s Extensions to C 中 ( 其中也包括垃圾回收 ) 对块进行了定义。O-C和C++都来自于C,块在3种语言(包括O-C++)都能工作。

这篇文档中,你会学习到什么是块对象,以及怎样在C,C++和O-C中使用它,使代码的性能和可维护性更高。

开始

声明块

^ 操作符声明一个块变量的开始(跟C一样用; 来表示表达式结束),如代码所示:

int multiplier = 7;

int (^myBlock)(int) = ^(int num) {

return num * multiplier;

};

解释 :

%title插图%num

 

注意,块可以使用同一作用域内定义的变量。

 

一旦声明了块,你可以象使用函数一样调用它:

int multiplier = 7;

int (^myBlock)(int) = ^(int num) {

return num * multiplier;

};

printf(“%d”, myBlock(3));

直接使用块

很多情况下,你不必声明块变量,而简单地写一个行内块并把它当作一个参数,如下面的代码所示。

gsort_b类似标准的 gsort_r 函数,但它*后一个参数是一个块。

char *myCharacters[3] = { “TomJohn”, “George”, “Charles Condomine” };

qsort_b(myCharacters, 3, sizeof(char *), ^(const void *l, const void *r) {

 

char *left = *(char **)l;

 

char *right = *(char **)r;

 

return strncmp(left, right, 1);

 

});

// myCharacters is now { “Charles Condomine”, “George”, TomJohn” }

Cocoa 和块

在Cocoa框架中,有几种把块作为参数的方法。典型的是在集合中进行一个操作,或者在操作完成后作为一个回调。下列代码显示如何在NSArray的sortedArrayUsingComparator方法中使用块。这个方法使用了一个块参数。为了演示,在这里把块定义为一个NSComparator本地变量。

 

NSArray *stringsArray = [NSArray arrayWithObjects:                                 @”string 1″,  @”String 21″,@”string 12″,

@”String 11″, @”String 02″, nil];

static NSStringCompareOptions comparisonOptions = NSCaseInsensitiveSearch | NSNumericSearch |

NSWidthInsensitiveSearch | NSForcedOrderingSearch;

NSLocale *currentLocale = [NSLocale currentLocale];

NSComparator finderSortBlock = ^(id string1, id string2) {

NSRange string1Range = NSMakeRange(0, [string1 length]);

return [string1 compare:string2 options:comparisonOptions range:string1Range locale:currentLocale];

};

NSArray *finderSortArray = [stringsArray sortedArrayUsingComparator:finderSortBlock];

NSLog(@”finderSortArray: %@”, finderSortArray);

 

/*Output:

finderSortArray: (

“string 1”,

“String 02”,

“String 11”,

“string 12”,

“String 21″

)*/

块变量

块的一个强大功能它可以改变在同一作用域内的变量。用__block修饰符来标识一个变量能够被块改变。使用下面的代码,你可以用一个块变量计算进行比较的字符串中有多少是相同的。为了演示,块是直接使用的,同时currentLocal变量对于块来说是只读的。

NSArray *stringsArray = [NSArray arrayWithObjects:

@”string 1″,  @”String 21″, // <-

@”string 12″,  @”String 11″,@”Strîng 21″, // <-

@”Striñg 21″, // <-

@”String 02″, nil];

NSLocale *currentLocale = [NSLocale currentLocale];

__block NSUInteger orderedSameCount = 0;

NSArray *diacriticInsensitiveSortArray = [stringsArray sortedArrayUsingComparator:^(id string1, id string2) {

NSRange string1Range = NSMakeRange(0, [string1 length]);

NSComparisonResult comparisonResult = [string1 compare:string2 options:NSDiacriticInsensitiveSearch range:string1Range locale:currentLocale];

 

 

 

if (comparisonResult == NSOrderedSame) {

orderedSameCount++;

}

return comparisonResult;

}];

NSLog(@”diacriticInsensitiveSortArray: %@”, diacriticInsensitiveSortArray);

NSLog(@”orderedSameCount: %d”, orderedSameCount);

/*Output:

diacriticInsensitiveSortArray: (

“String 02”,

“string 1”,

“String 11”,

“string 12”,

“String 21”,

“Str/U00eeng 21”,

“Stri/U00f1g 21”

)

orderedSameCount: 2

*/

相关概念

块提供了一种方法,允许你创建一种特殊的函数体,在C及C派生语言如O-C和C++中,可以把块视为表达式。其他语言中,为了不与C术语中的块混淆,块也被称作closure(国内译作闭包),这里它们都称做blocks。

块的功能

 

块是行内的代码集合:

▪        同函数一样,有类型化参数列表

▪        有返回结果或者要申明返回类型

▪        能获取同一作用域(定义块的相同作用域)内的状态

▪        可以修改同一作用域的状态(变量)

▪        与同一范围内的其他块同享变量

▪        在作用域释放后能继续共享和改变同一范围内的变量

甚至可以复制块并传递到其他后续执行的线程。编译器和运行时负责把所有块引用的变量保护在所有块的拷贝的生命周期内。对于C和C++,块是变量,但对于O-C ,块仍然是对象。

块的使用

块通常代表小段的、自包含的代码片段。

因此,它们封装为可以并行执行的工作单元额外有用,要么用于在集合中进行遍历,要么在其他操作完成使作为回调。

块代替传统回调函数的意义有两个:

1.             它们允许在方法实现的调用中就近地写入代码。而且块经常被作为框架中一些方法的参数。

2.             它们允许访问本地变量。在进行线程操作时,相比回调函数需要把所需的上下文信息植入数据结构中而言,块直接访问本地变量显然更加简单。

 

块的声明和创建

声明块变量

块变量引用了块。它的声明语法类似函数指针,除了需要使用^代替*。

void (^blockReturningVoidWithVoidArgument)(void);

int (^blockReturningIntWithIntAndCharArguments)(int, char);

void (^arrayOfTenBlocksReturningVoidWithIntArgument[10])(int);

块支持可变参数(…)。如果块没有参数,则必需使用void来代替整个参数列表。

块是类型安全的,通过设置编译选项,编译器会检查块的调用、参数和返回类型。可以把块变量转换为指针类型,但不能使用*对其解除引用——块的长度在编译时无法确定。

可以创建一个块类型,这样你就可以把块当作一个可以反复多次使用的符号:

typedef float (^MyBlockType)(float, float);

MyBlockType myFirstBlock = // … ;

MyBlockType mySecondBlock = // … ;

创建块

块以^开始,以;结束。下面显示了块的定义:

int (^oneFrom)(int);

oneFrom = ^(int anInt) {

return anInt – 1;

};

如果未显式地声明块的返回值类型,可能会自动从块代码中推断返回类型。如果参数列表为void,而且返回类型依靠推断,你可以省略参数列表的void。否则,当块中存在return语句时,它们应当是精确匹配的(可能需要必要的类型转换)。

全局块

可以把块定义为全局变量,在文件级别上使用。

#import <stdio.h>

int GlobalInt = 0;

int (^getGlobalInt)(void) = ^{ return GlobalInt; };

 

块和变量

本节描述块和变量之间的交互,包括内存管理。

变量类型

在块代码内部,变量会被处理为5种不同情况。

就像函数一样,可以引用3种标准的变量:

▪        全局变量,包括静态变量

▪        全局函数

▪        本地变量及参数(在块范围内)

此外块还支持两种变量:

1.    在函数级别,是__block变量。它们在块范围内是可变的,如果所引用的块被复制到堆后,它们也是被保护的。

2.    const imports.

在方法体内,块还可以引用O-C 实例变量,见 “ 对象和块变量 ”.

在块中使用变量有以下规则:

1.      可访问在同一范围内的全局变量包括静态变量。

2.      可以访问传递给块的参数(如同函数参数)。

3.      同一范围的栈(非static)变量视作const变量。它们的值类似块表达式。嵌套块时,从*近的作用域取值。

4.      在同一范围内声明的变量,如果有__block修饰符修饰,则值是可变的。在该范围内包括同一范围内的其他块对该变量的改变,都将影响该作用域。具体见“__block 存储类型”。

5.      在块的范围内(块体)声明的本地变量,类似于函数中的本地变量。块的每次调用都会导致重新拷贝这些变量。这些变量可作为const或参考(by-reference)变量。

下面演示本地非静态变量的使用:

int x = 123;

void (^printXAndY)(int) = ^(int y) {

printf(“%d %d/n”, x, y);

};

printXAndY(456); // prints: 123 456

注意,试图向x进行赋值将导致错误:

int x = 123;

void (^printXAndY)(int) = ^(int y) {

x = x + y; // error

printf(“%d %d/n”, x, y);

};

要想在块内改变x的值,需要使用__block修饰x。见“__block存储类型”。

__block 存储类型

你可以规定一个外部的变量是否可变——可读写——通过使用__block存储类型修饰符。__block存储类似但不同于register,auto和static存储类型。

__block变量在变量声明的作用域、所有同一作用域内的块,以及块拷贝之间同享存储。而且这个存储将在栈帧(stack frame)释放时得以保留,只要同一帧内申明的块的拷贝仍然存活(例如,被入栈以便再次使用)。在指定作用域内的多个块能同时使用共享变量。

作为一种优化,块存储使用栈存储,就如同块自身一样。如果使用Block_copy拷贝块(或者在O-C向块发送copy消息),变量被拷贝到堆里。而且,__block变量的地址随后就会改变。

__block变量有两个限制:不能是可变长度的数组,也不能是包含C99可变长度数组的结构体。

下面显示了__block变量的使用:

__block int x = 123; //  x lives in block storage

void (^printXAndY)(int) = ^(int y) {

x = x + y;

printf(“%d %d/n”, x, y);

};

printXAndY(456); // prints: 579 456

// x is now 579

下面显示了在块中使用多种类型的变量:

extern NSInteger CounterGlobal;

static NSInteger CounterStatic;

{

NSInteger localCounter = 42;

__block char localCharacter;

void (^aBlock)(void) = ^(void) {

++CounterGlobal;

++CounterStatic;

CounterGlobal = localCounter; // localCounter fixed at block creation

localCharacter = ‘a’; // sets localCharacter in enclosing scope

};

++localCounter; // unseen by the block

localCharacter = ‘b’;

aBlock(); // execute the block

// localCharacter now ‘a’

}

对象和块变量

块提供了对O-C和C++对象的支持 。

O-C对象

在引用计数的情况下,当你在块中引用一个O-C对象,对象会被retained。甚至只是简单引用这个对象的实例变量,也是一样的。

但对于__block标记的对象变量,就不一样了。

注意:在垃圾回收的情况下,如果同时用__weak和__block修饰变量,块可能不一定保证它是 可用 的。

如果在方法体中使用块,对象实例变量的内存管理规则 比较微妙:

▪        如果通过对象引用方式访问实例变量,self 被 retained;

▪        如果通过值引用方式访问实例变量,变量是retained;

下面代码演示了这2种情况:

dispatch_async(queue, ^{

// instanceVariable is used by reference, self is retained

doSomethingWithObject(instanceVariable);

});

id localVariable = instanceVariable;

dispatch_async(queue, ^{

// localVariable is used by value, localVariable is retained (not self)

doSomethingWithObject(localVariable);

});

C++ 对象

一般,可以在块中使用C++对象。在成员函数中对成员变量进行引用,俨然是对指针的引用,可以对其进行改变。如果块被拷贝,有两种结果:

如果有__block存储类型的类,该类是基于栈的C++对象,通常会使用复制构造函数;

如果使用了其他块中的基于栈的C++对象,它必需有一个const的复制构造函数。该C++对象使用该构造函数进行拷贝。

拷贝块时,其引用的其它块可能也被拷贝(从顶部开始)。如果有块变量,并且在这个块中引用了一个块,那个块也会被拷贝。

拷贝一个基于栈的块时,你得到的是新的块。拷贝一个基于堆的块时,只是简单的增加了retain数,然后把copy方法/函数的结果返回这个块。

 

 

使用块

块的调用

如果把块申明为变量,可以把它当成函数使用,例如:

int (^oneFrom)(int) = ^(int anInt) {

return anInt – 1;

};

printf(“1 from 10 is %d”, oneFrom(10));

// Prints “1 from 10 is 9”

float (^distanceTraveled) (float, float, float) =

^(float startingSpeed, float acceleration, float time) {

float distance = (startingSpeed * time) + (0.5 * acceleration * time * time);

return distance;

};

float howFar = distanceTraveled(0.0, 9.8, 1.0);

// howFar = 4.9

但时常会将块以参数形式传递给一个函数或方法,这样,就会使用行内(inline)块。

把块作为函数参数

在这种情况下,不需要块申明。简单地在需要把它作为参数的地方实现它就行。如下所示,gsort_b是一个类似标准gsort_r的函数,它的*后一个参数使用了块。

char *myCharacters[3] = { “TomJohn”, “George”, “Charles Condomine” };

qsort_b(myCharacters, 3, sizeof(char *), ^(const void *l, const void *r) {

char *left = *(char **)l;

char *right = *(char **)r;

return strncmp(left, right, 1);

});

// Block implementation ends at “}”

 

 

// myCharacters is now { “Charles Condomine”, “George”, TomJohn” }

注意,块包含在函数的参数列表中。

接下来的例子显示如何在dispath_apply函数中使用块。dispatch_apply的声明是:

void dispatch_apply(size_t iterations, dispatch_queue_t queue, void (^block)(size_t));

这个函数把块提交给dispatch队列以进行调用。它有3个参数:要操作的次数;块被提交到的队列;块——这个块有一个参数——遍历操作的当前次数。

可以用dispatch_apply简单地打印出遍历操作的索引:

#include <dispatch/dispatch.h>

size_t count = 10;

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_apply(count, queue, ^(size_t i) {

printf(“%u/n”, i);

});

把块作为参数使用

Cocoa提供了大量使用块的方法。把块作为参数使用与使用其他类型的参数并无不同。

以下代码判断数组中前5个元素中含有给定filter集合的索引。

NSArray *array = [NSArray arrayWithObjects: @”A”, @”B”, @”C”, @”A”, @”B”, @”Z”,@”G”, @”are”, @”Q”, nil];

NSSet *filterSet = [NSSet setWithObjects: @”A”, @”Z”, @”Q”, nil];

BOOL (^test)(id obj, NSUInteger idx, BOOL *stop);

test = ^ (id obj, NSUInteger idx, BOOL *stop) {

if (idx < 5) {

if ([filterSet containsObject: obj]) {

return YES;

}

}

return NO;

};

NSIndexSet *indexes = [array indexesOfObjectsPassingTest:test];

NSLog(@”indexes: %@”, indexes);

/*Output:

indexes: <NSIndexSet: 0x10236f0>[number of indexes: 2 (in 2 ranges), indexes: (0 3)]

*/

以下代码判断一个NSSet对象中是否包含指定的本地变量,如果是的话把另一个本地变量(found)设置为YES(并停止搜索)。注意found被声明为__block变量,块是在行内声明的:

__block BOOL found = NO;

NSSet *aSet = [NSSet setWithObjects: @”Alpha”, @”Beta”, @”Gamma”, @”X”, nil];

NSString *string = @”gamma”;

[aSet enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {

if ([obj localizedCaseInsensitiveCompare:string] ==NSOrderedSame) {

*stop = YES;

found = YES;

}

}];

 

 

 

// At this point, found == YES

 

块复制

一般,你不需要复制块。只有当你希望在这个块申明的范围外使用它时需要复制它。复制将导致块移动到堆中。

可以使用C函数释放和复制块。

Block_copy();

Block_release();

对于O-C,则可向块发送copy,retain和release(以及autorelease)消息。

为避免内存泄露,一个Block_copy()总是对应一个Block_release()。每个copy/retain总是有对应的release(或autorelease)——使用垃圾回收则例外。

避免的用法

一个块声明(即^{…})是一个本地栈式数据结构(stack-local data structure)的地址,这个地址就代表了块。本地栈式数据结构是{}围住的复合语句,因此应该避免如下用法:

void dontDoThis() {

void (^blockArray[3])(void);// array of 3 block   references

for (int i = 0; i < 3; ++i) {

blockArray[i] = ^{ printf(“hello, %d/n”, i); };

// WRONG: The block literal scope is the “for” loop

}

}

void dontDoThisEither() {

 

void (^block)(void);

int i = random():

if (i > 1000) {

block = ^{ printf(“got i at: %d/n”, i); };

// WRONG: The block literal scope is the “then” clause

}

// …

}

 

调试

可以在块内设置断点,并进行单步调试。在GDB会话中,使用invoke-block调用块,比如:

$ invoke-block myBlock 10 20

如果需要传递C字符串,必需用双引号把它引住。例如,向doSomethignWithString块传递一个字符串:

$ invoke-block doSomethingWithString “/”this string/””

ios自定义控件复选框和单选框的实现

我们先实现单个按钮,为了复用,不管单选还是复选按钮都是使用同一个类来实现,为了区别单选还是复选,我们用一个自定义枚举类型CheckButtonStyle属性style来区别,当其值设置为CheckButtonStyleDefault或CheckButtonStyleBox时,为复选按钮:

  当其值设为CheckButtonStyleRadio时,为单选按钮:

当按钮在选中/反选状态间切换时,文字左边的图片自动转换。

整个控件是由一个ImageView、一个Label、一个BOOL变量及其他变量组成,.h文件如下:

typedef enum {

    CheckButtonStyleDefault 0 ,

    CheckButtonStyleBox 1 ,

    CheckButtonStyleRadio 2

} CheckButtonStyle;

#import <Foundation/Foundation.h>

 

@interface CheckButton : UIControl {

//UIControl* control;

UILabel label ;

UIImageView icon ;

BOOL checked ;

id value , delegate ;

CheckButtonStyle style ;

NSString * checkname ,* uncheckname ; // 勾选/反选时的图片文件名

}

@property ( retain , nonatomic ) id value,delegate;

@property ( retain , nonatomic )UILabel* label;

@property ( retain , nonatomic )UIImageView* icon;

@property ( assign )CheckButtonStyle style;

-( CheckButtonStyle )style;

-( void )setStyle:( CheckButtonStyle )st;

-( BOOL )isChecked;

-( void )setChecked:( BOOL )b;

@end

具体实现如下:

#import “CheckButton.h”

 

 

@implementation CheckButton

@synthesize label,icon,value,delegate;

-( id )initWithFrame:( CGRect ) frame

{

if ( self =[ super initWithFrame : frame ]) {

icon =[[ UIImageView alloc ] initWithFrame :

  CGRectMake ( 10 0 frame . size . height frame . size . height )];

[ self setStyle : CheckButtonStyleDefault ]; // 默认风格为方框(多选)样式

//self.backgroundColor=[UIColor grayColor];

[ self addSubview : icon ];

label =[[ UILabel alloc ] initWithFrame : CGRectMake ( icon . frame . size . width + 24 0 ,

   frame . size . width  icon . frame . size . width  24 ,

   frame . size . height )];

label . backgroundColor =[ UIColor clearColor ];

label . font =[ UIFont fontWithName : @”Arial” size : 20 ];

label . textColor =[ UIColor

  colorWithRed : 0xf9 / 255.0

  green : 0xd8 / 255.0

  blue : 0x67 / 255.0

  alpha : 1 ];

label . textAlignment = UITextAlignmentLeft ;

[ self addSubview : label ];

[ self addTarget : self action : @selector ( clicked forControlEvents : UIControlEventTouchUpInside ];

}

return self ;

}

-( CheckButtonStyle )style{

return style ;

}

-( void )setStyle:( CheckButtonStyle )st{

style =st;

switch ( style ) {

case CheckButtonStyleDefault :

case CheckButtonStyleBox :

checkname = @”checked.png” ;

uncheckname = @”unchecked.png” ;

break ;

case CheckButtonStyleRadio :

checkname = @”radio.png” ;

uncheckname = @”unradio.png” ;

break ;

default :

break ;

}

[ self setChecked : checked ];

}

-( BOOL )isChecked{

return checked ;

}

-( void )setChecked:( BOOL )b{

if (b!= checked ){

checked =b;

}

if ( checked ) {

[ icon setImage :[ UIImage imageNamed : checkname ]];

} else {

[ icon setImage :[ UIImage imageNamed : uncheckname ]];

}

}

-( void )clicked{

[ self setChecked :! checked ];

if ( delegate != nil ) {

SEL sel= NSSelectorFromString ( @”checkButtonClicked” );

if ([ delegate respondsToSelector :sel]){

[ delegate performSelector :sel];

}

}

-( void )dealloc{

value = nil ; delegate = nil ;

[ label release ];

[ icon release ];

[ super dealloc ];

}

@end

使用CheckButton类很简单,构造、设置标签文本等属性,然后addSubview:

CheckButton * cb=[[ CheckButton a lloc initWithFrame : CGRectMake ( 20 60 260 32 )];

  1. label. text = @”checkbutton1″ ;
  2. value=[[ NSNumber alloc ] initWithInt : 18 ];
  3. style= CheckButtonStyleDefault ;

[ self . view addSubview :cb];

二、单选按钮组的实现

复选按钮无所谓“组”的概念,单选按钮则不同。在同一个组中,单选按钮只允许同时选择一个按钮,不能选多个,因此我们要实现一个单选按钮组的类:

#import <Foundation/Foundation.h>

#import “CheckButton.h”

 

@interface RadioGroup : NSObject {

NSMutableArray children ;

NSString text ;

id value ;

}

@property ( readonly )NSString* text;

@property ( readonly ) id value;

-( void )add:( CheckButton *)cb;

-( void )checkButtonClicked:( id )sender;

@end

#import “RadioGroup.h”

 

 

@implementation RadioGroup

@synthesize text,value;

-( id )init{

if ( self =[ super init ]){

children =[[ NSMutableArray alloc ] init ];

}

return self ;

}

-( void )add:( CheckButton *)cb{

  1. delegate= self ;

if (cb. checked ) {

text =cb. label . text ;

value =cb. value ;

}

[ children addObject :cb];

}

-( void )checkButtonClicked:( id )sender{

CheckButton * cb=( CheckButton *)sender;

if (!cb. checked ) {

// 实现单选

for ( CheckButton * each in children ){

if (each. checked ) {

[each setChecked : NO ];

}

}

[cb setChecked : YES ];

// 复制选择的项

text =cb. label . text ;

value =cb. value ;

}

NSLog ( @”text:%@,value:%d” , text ,[( NSNumber *) value intValue ]);

}

-( void )dealloc{

[ text release ];

value = nil ;

[ children release ];

[ super dealloc ];

}

@end

单选按钮组在ViewController中的使用:

-( id )initWithNibName:( NSString *)nibNameOrNil bundle:( NSBundle *)nibBundleOrNil{

if ( self =[ super initWithNibName :nibNameOrNil bundle :nibBundleOrNil]){

// 单选按钮组

rg =[[ RadioGroup alloc ] init ];

// 第 1 个单选按钮

CheckButton * cb=[[ CheckButton alloc ] initWithFrame : CGRectMake ( 20 60 260 32 )];

// 把单选按钮加入按钮组

[ rg add :cb];

  1. label. text = @”★” ;
  2. value=[[ NSNumber alloc ] initWithInt : 1 ];

// 把按钮设置为单选按钮样式

  1. style= CheckButtonStyleRadio ;

// 加入视图

[ self . view addSubview :cb];

[cb release ]; //add 后,会自动持有,可以释放

// 第 2 个单选按钮

cb=[[ CheckButton alloc ] initWithFrame : CGRectMake ( 20 100 260 32 )];

[ rg add :cb];

  1. label. text = @”★★” ;
  2. value=[[ NSNumber alloc ] initWithInt : 2 ];
  3. style= CheckButtonStyleRadio ;

[ self . view addSubview :cb];

[cb release ];

// 第 3 个单选按钮

cb=[[ CheckButton alloc ] initWithFrame : CGRectMake ( 20 140 260 32 )];

// 各种属性必须在 [rg addv] 之前设置,否则 text 和 value 不会被 populate

  1. checked= YES ;
  2. label. text = @”★★★” ;
  3. value=[[ NSNumber alloc ] initWithInt : 3 ];
  4. style= CheckButtonStyleRadio ;

[ self . view addSubview :cb];

[ rg add :cb]; // 属性设置完之后再 add

[cb release ];

// 第 4 个单选按钮

cb=[[ CheckButton alloc ] initWithFrame : CGRectMake ( 20 180 260 32 )];

[ rg add :cb];

  1. label. text = @”★★★★” ;
  2. value=[[ NSNumber alloc ] initWithInt : 4 ];
  3. style= CheckButtonStyleRadio ;

[ self . view addSubview :cb];

[cb release ];

// 第 5 个单选按钮

cb=[[ CheckButton alloc ] initWithFrame : CGRectMake ( 20 220 260 32 )];

[ rg add :cb];

  1. label. text = @”★★★★★” ;
  2. value=[[ NSNumber alloc ] initWithInt : 5 ];
  3. style= CheckButtonStyleRadio ;

[ self . view addSubview :cb];

[cb release ];

}

return self ;

}

运行效果:

%title插图%num

iOS开发经常用到的技术知识点

1.刷新单个tableviewcell

NSIndexPath * indexPat=[NSIndexPath indexPathForRow:indexPlay inSection:0];

NSArray * indexArray=[NSArray arrayWithObject:indexPat];

[self.tableView reloadRowsAtIndexPaths:indexArray withRowAnimation:UITableViewRowAnimationAutomatic];

2.   判断该方法是否执行??

BOOL isss=  [cell.queuePlayer respondsToSelector:@selector(play)];

instancesRespondToSelector是指类的实例们是否能响应某一个方法(类操作),respondsToSelector是指类是否能响应某一方法(对象)
3.代码块的使用
int (^oneFrom)(int) = ^(int anInt) {

return anInt -1;

};

NSLog(@”%d”,oneFrom(10));

4.  改变buuton的高亮

UIImageView * iv = [[UIImageView alloc] initWithFrame:CGRectMake(250, 5, 50, 34)];

iv.userInteractionEnabled = YES;

UIButton * navBtn = [UIButton buttonWithType:UIButtonTypeCustom];

navBtn.frame = CGRectMake(0, 0, 50, 34);

[navBtn setImage:[UIImage imageNamed:@”rong_Tian”] forState:UIControlStateNormal];

[navBtn setHighlighted:YES];

[navBtn addTarget:self action:@selector(btnPressed:) forControlEvents:UIControlEventTouchUpInside];

[navBtn setShowsTouchWhenHighlighted:YES];

[iv addSubview:navBtn];

[self.navigationController.navigationBar addSubview:iv];

5. 默认为cell*行

 

NSIndexPath *first=[NSIndexPath indexPathForRow:0 inSection:0];

[self.tableView selectRowAtIndexPath:first animated:YES scrollPosition:UITableViewScrollPositionBottom];

6.一个项目中  ARC和非ARC 的混合使用

 

点击项目–》TARGETS-》Build Phases  -》Compile  Sources   中选择要改的.m   双击   在标签中写:

1.如果是ARC项目,要加入非ARC的代码文件  fobjc-arc

2.如果是非ARC,要加入ARC的代码   -fno-objc-arc       Enter就OK

 

//    NSURL * url=[NSURL URLWithString:str];

//

//      NSURLRequest *requestt = [[NSURLRequest alloc]initWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10];

//     NSData *received = [NSURLConnection sendSynchronousRequest:requestt returningResponse:nil error:nil];

//    NSString *strr = [[NSString alloc]initWithData:received encoding:NSUTF8StringEncoding];

//    NSLog(@”这是  成功返回的信息     %@”,strr);

7.距离感应器

UIDeviceOrientation orientation3=  [[UIDevice currentDevice] orientation];

 

NSLog(@”获取当前状态   %d”,orientation3);

[[UIDevice currentDevice] setProximityMonitoringEnabled:YES];

[[NSNotificationCenter defaultCenter] addObserver:self

selector:@selector(sensorStateChange:)

name:@”UIDeviceProximityStateDidChangeNotification”

 

object:nil];

-(void)sensorStateChange:(NSNotificationCenter *)notification;

{

if ([[UIDevice currentDevice] proximityState] == YES) {

 

NSLog(@”Device is close to user”);

//在此写接近时,要做的操作逻辑代码

}else{

NSLog(@”Device is not close to user”);

}

}

8.获得cookie

 

NSHTTPCookieStorage *cookieJar = [NSHTTPCookieStorage sharedHTTPCookieStorage];

for (NSHTTPCookie *cookie in [cookieJar cookies]) {

NSLog(@”cookie=====     %@”, cookie);

}

9.从相册中只获得视频文件

 

if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary] == YES)

{

UIImagePickerController *videoLibraryController = [[[UIImagePickerController alloc] init] autorelease];

videoLibraryController.delegate = self;

videoLibraryController.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;

videoLibraryController.mediaTypes = [NSArray arrayWithObject:(NSString *)kUTTypeMovie];

[videoLibraryController setAllowsEditing:YES];

[self.navigationController presentViewController:videoLibraryController animated:YES completion:^{

 

}];

}

else

{

[self AlertlogError:@”暂时你还没有视频”];

}

10. 读取全局的Delegate:
KiloNetAppDelegate *appdelegate = (KiloNetAppDelegate *)[[UIApplication sharedApplication] delegate];

11.键盘透明

textField.keyboardAppearance = UIKeyboardAppearanceAlert;
12.URL错误:
Error Domain=ASIHTTPRequestErrorDomain Code=5 “Unable to create request (bad url?)” UserInfo=0x69ba0f0 {NSLocalizedDescription=Unable to create request (bad url?)}

解决办法:

NSString*url =@”http://oerp.xixingsoft.com:8083/oadata/MobileConfig.nsf/GetStandList?openagent&ViewNumber=新闻中心”;

url=[url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

 

NSStringEncodingenc = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);//gbk code–>utf8 code

NSData*data = [url dataUsingEncoding:NSUTF8StringEncoding];//[request responseData];

NSString*utf8str = [[[NSStringalloc] initWithData:data encoding:enc] autorelease];

13.请求中加cookie、 heard

当你需要添加更多的请求信息时,如,添加个请求Header:
[request addRequestHeader:@”name” value:@”Jory lee”];

14 Plist文件的保存 修改    除非在decument是可读可写的(在工程中  可读不可写)

//获取路径对象

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);

//获取完整路径

NSString *documentsDirectory = [paths objectAtIndex:0];

NSString *plistPath = [documentsDirectory stringByAppendingPathComponent:@”test.plist”];

NSLog(@”plist  地质   %@”,plistPath);

NSMutableDictionary *dictplist = [[NSMutableDictionary alloc ] init];

[dictplist setObject:self.strWeb_id forKey:@”web_id”];

[dictplist writeToFile:plistPath atomically:YES];

15.获取文件夹的大小

-(long long) fileSizeAtPath:(NSString*) filePath{

NSFileManager* manager = [NSFileManager defaultManager];

if ([manager fileExistsAtPath:filePath]){

return [[manager attributesOfItemAtPath:filePath error:nil] fileSize];

}

return 0;

}

16.改变tablevlewcell点击的颜色

 

cell.selectedBackgroundView = [[[UIView alloc] initWithFrame:cell.frame] autorelease];

cell.selectedBackgroundView.backgroundColor = [UIColor colorWithRed:54/255.0f green:110/255.0f blue:100/255.0f alpha:1.0f];

cell.textLabel.highlightedTextColor = [UIColor xxxcolor];  [cell.textLabel setTextColor:color

 

点击后,过段时间cell自动取消选中
[self performSelector:@selector(deselect) withObject:nil afterDelay:0.5f];

– (void)deselect

{

[self.tableVieww deselectRowAtIndexPath:[self.tableVieww indexPathForSelectedRow] animated:YES];

}

17.改变UITableViewStyleGrouped背景颜色

 

self.tableVieww.backgroundColor =[UIColor colorWithPatternImage:[UIImage imageNamed:@”更多背景图.png”]];

self.tableVieww.backgroundView =nil;

18.视图反转

//水平
queuePlayer.transform = CGAffineTransformScale(queuePlayer.transform, 1.0, -1.0);
//垂直    queuePlayer.transform = CGAffineTransformScale(queuePlayer.transform, -1.0, 1.0);
19.改变icon的阴影圆圈,取消图标上的高光

1.进入plist文件    2.在Supported interface orientations 添加  Icon already includes gloss effects  设置为YES              也就是用自己的icon,不用系统的了

20.动态UIlable后添加图片

self.userNameLabel=[[[UILabel alloc]initWithFrame:CGRectMake(60, 7, 220, 20)]autorelease];

self.userNameLabel.textColor= [UIColor blackColor];

self.userNameLabel.text=self.strNamee;

self.userNameLabel.backgroundColor=[UIColor clearColor];

self.userNameLabel.numberOfLines=0;

UIFont *font = [UIFont fontWithName:@”Helvetica-Bold” size:17.0f];

[self.userNameLabel setFont:font];

[self.contentView addSubview:self.userNameLabel];

CGSize size = [self.strNamee sizeWithFont:font constrainedToSize:CGSizeMake(277, 20.0f)];

NSLog(@”kuang %f  “,size.width);

CGRect rect=self.userNameLabel.frame;

rect.size=size;

NSLog(@”321    %f    %f”,rect.size.width,rect.size.height);

[self.userNameLabel setFrame:rect];

//判断男女

UIImageView * imaSex=[[UIImageView alloc]initWithFrame:CGRectMake(self.userNameLabel.frame.size.width+65, 10, 12, 13)];

21.向自定义的cell中传值,*好用 方法在tableview中来调用 如果有参数  直接来传
22.精确时间差
//时间差

– (NSString *)intervalSinceNow: (NSString *) theDate

{

NSDateFormatter *date=[[NSDateFormatter alloc] init];

[date setDateFormat:@”yyyy-MM-dd HH:mm:ss”];

NSDate *d=[date dateFromString:theDate];

NSTimeInterval late=[d timeIntervalSince1970]*1;

NSDate* dat = [NSDate dateWithTimeIntervalSinceNow:0];

NSTimeInterval now=[dat timeIntervalSince1970]*1;

NSString *timeString=@””;

NSTimeInterval cha=now-late;

if (cha/3600<1) {

timeString = [NSString stringWithFormat:@”%f”, cha/60];

timeString = [timeString substringToIndex:timeString.length-7];

timeString=[NSString stringWithFormat:@”%@分钟前”, timeString];

 

}

if (cha/3600>1&&cha/86400<1) {

timeString = [NSString stringWithFormat:@”%f”, cha/3600];

timeString = [timeString substringToIndex:timeString.length-7];

timeString=[NSString stringWithFormat:@”%@小时前”, timeString];

}

if (cha/86400>1)

{

timeString = [NSString stringWithFormat:@”%f”, cha/86400];

timeString = [timeString substringToIndex:timeString.length-7];

timeString=[NSString stringWithFormat:@”%@天前”, timeString];

 

}

[date release];

return timeString;

}

22.按钮在cell上单击第几行

在cell.contentView上:

//获得row

NSInteger row = [[self.tableView indexPathForCell:(UITableViewCell *)[[sender superview] superview]] row];

//获得section

NSInteger row = [[self.tableView indexPathForCell:(UITableViewCell *)[[sender superview] superview]] section];

//获得indexPath

NSIndexPath *indexPath = [self.tableView indexPathForCell:(UITableViewCell *)[[sender superview] superview]];

直接添加到cell上:
//获得row
NSInteger row = [[self.tableView indexPathForCell:(UITableViewCell *)[sender superview]] row];
//获得section
NSInteger section = [[self.tableView indexPathForCell:(UITableViewCell *)[sender superview]] section];
//获得indexPath
NSIndexPath *indexPath = [self.tableView indexPathForCell:(UITableViewCell *)[sender superview]];
23:判断Home键在哪个方法要执行对应的方法
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(rongTianOrientation:) name:@”UIDeviceOrientationDidChangeNotification” object:nil];

 

– (void) deviceOrientationDidChangeAction:(NSNotification *)note

{

NSInteger currentOrientation = [[note object] orientation];

switch (currentOrientation)  {

case0: {   //未知方向

break;

}

case1: {   //home键向下

break;

}

case2: {   //home键向上

break;

}

case3: {  //home键向左

break;

}

case4: {  //home键向右

break;

}

default:

break;

}

}

24.模拟器不能运行的错误

dyld: Library not loaded: @rpath/SenTestingKit.framework/Versi*****/A/SenTestingKit
Referenced from: /Users/⋯⋯/Application Support/iPhone Simulator/5.0/Applicati*****/F179924C-0EB7-4CCA-88D6-3BA1F68F122D/ILUTU.app/ILUTU
Reason: image not found

 

把SentestingKit。 frameWork 有原来的required改为Optional  就ok

25.还原状态栏

显示原来的状态栏

(1)

[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:YES];

[[UIApplication sharedApplication].keyWindow setFrame:CGRectMake(0, 20, 320, [UIScreen mainScreen].applicationFrame.size.height)];

 

//重新设定标题栏显示的位置

[self.navigationController.navigationBar setFrame:CGRectMake(0, 0, 320, 44)];

(2)

在别的页面[[UIApplication sharedApplication].keyWindow setFrame:CGRectMake(0, 0, 320, [UIScreenmainScreen].applicationFrame.size.height)];

26.获得相册视频的总时间
– (int)getVideopTime:(NSURL * )videourl

{
NSDictionary *opts = [NSDictionary dictionaryWithObject:[NSNumbernumberWithBool:NO]

forKey:AVURLAssetPreferPreciseDurationAndTimingKey];

AVURLAsset *urlAsset = [AVURLAsset URLAssetWithURL:videourl options:opts]; //初始化视频媒体文件

int minute = 0, second = 0;

second = urlAsset.duration.value / urlAsset.duration.timescale; // 获取视频总时长,单位秒

NSLog(@”movie duration : %d”, second);

if (second >= 60) {
int index = second / 60;

minute = index;

second = second – index*60;

}

return second;

}

27.视频播放器 循环播放 大小……

(1) MPMoviePlayerController

MPMoviePlayerController *player;

NSURL *url =[NSURL URLWithString:fileName];

player = [[MPMoviePlayerController alloc] init];

player.view.frame = CGRectMake(10, 30, 300    , 225);

player.contentURL = url;

player.repeatMode = MPMovieRepeatModeOne;

player.controlStyle = MPMovieControlStyleEmbedded;

 

player.scalingMode = MPMovieScalingModeAspectFill;   //充满屏幕

[self.view addSubview:player.view];

[player play];

(2).avplayer

 

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerItemDidReachEnd:)                                                       name:AVPlayerItemDidPlayToEndTimeNotification

object:plaitem];

}

}];

#pragma mark – Notification Callbacks

– (void)playerItemDidReachEnd:(NSNotification *)notification {
NSLog(@”是跳转*侦吗?  “);

[self.queuePlayer seekToTime:kCMTimeZero];

[self.queuePlayer play];

}

28.sina微博错误返回值格式

http://open.weibo.com/wiki/Error_code

29.ios 获得文件夹的大小

 

//计算文件夹下文件的总大小

-(long)fileSizeForDir:(NSString*)path

{

NSFileManager *fileManager = [[NSFileManager alloc] init];

long size = 0;

NSArray* array = [fileManager contentsOfDirectoryAtPath:path error:nil];

for(int i = 0; i<[array count]; i++)

{

NSString *fullPath = [path stringByAppendingPathComponent:[array objectAtIndex:i]];

 

BOOL isDir;

if ( !([fileManager fileExistsAtPath:fullPath isDirectory:&isDir] && isDir) )

{

NSDictionary *fileAttributeDic=[fileManager attributesOfItemAtPath:fullPath error:nil];

size+= fileAttributeDic.fileSize;

}        else        {

[self fileSizeForDir:fullPath];

}

}

[fileManager release];    return size;    }

30.    谓词过滤

//搜索用谓词过滤数组

NSArray * arrMy=@[@”张2荣三a”,@”李四b”,@”王五a”,@”李流j”,@”荣天321″,@”iOS基地”,@”iOS7″];

NSString  * strg=@”荣”;

NSPredicate * fiecate=[NSPredicate predicateWithFormat:@”SELF CONTAINS %@”,strg];

NSArray * arr3=[arrMy filteredArrayUsingPredicate:fiecate];

NSLog(@”这是我过滤的数组对吗?%@”,arr3);

31.多线程的多种创建

 

//    NSThread * th=[[NSThread alloc]initWithTarget:self selector:@selector(thAction) object:nil];

//    [th start];

 

//[NSThread detachNewThreadSelector:@selector(thAction) toTarget:self withObject:nil];

 

// [self performSelectorInBackground:@selector(thAction) withObject:self];

 

//    NSOperationQueue * operationQueue=[[NSOperationQueue alloc]init];

//    [operationQueue addOperationWithBlock:^{

//        for(int i=0; i<20;i++){

//            NSLog(@”This isThread: %d”,i);

//        }

//    }];

 

//    NSOperationQueue * operationQueue=[[NSOperationQueue alloc]init];

//    //设置线程池中的并发数

//    operationQueue.maxConcurrentOperationCount=1;

//

//    NSInvocationOperation * invocation1=[[NSInvocationOperation alloc]initWithTarget:self selector:@selector(threadOne) object:nil];

//    [invocation1 setQueuePriority:NSOperationQueuePriorityLow];

//

//    NSInvocationOperation * invocation2=[[NSInvocationOperation alloc]initWithTarget:self selector:@selector(threadTwo) object:nil];

//    [invocation2 setQueuePriority:NSOperationQueuePriorityHigh];

//    [operationQueue addOperation:invocation1];

 

//    [operationQueue addOperation:invocation2];

32.用多线程开启Nstimer提高精确度

//用多线程开启nstimer提高精确度

– (void)mutiThread

{

NSLog(@”Start NStimer”);

@autoreleasepool {

[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerAction:) userInfo:nil repeats:YES];

}

//获得当前的runloop,线程就停在这里

[[NSRunLoop currentRunLoop]run];

NSLog(@”after”);

}

– (void)timerAction:(NSTimer * )timer

{

i++;

NSLog(@”Print NSTimer”);

if (i==5) {

[timer invalidate];

}

}

33.判断此页面是push,还是模态过来的

if (self.presentingViewController) {

NSLog(@”这个是模态过来的!”);}

34。等比例放大缩小视图

UILabel * la=(UILabel * )[self.view viewWithTag:908];

[UIView animateWithDuration:.5 animations:^{

CGAffineTransform transform=la.transform;

transform=CGAffineTransformScale(la.transform, 1.5, 1.5);

la.transform=transform;

} completion:^(BOOL finished) {
CGAffineTransform transform=la.transform;

transform=CGAffineTransformScale(la.transform, 0.5, 0.5);

la.transform=transform;

}];

35.清掉编译文件

~/Library/Developer/Xcode/DerivedData

模拟器清空编译

~/Library/Application Support/iPhone Simulator/

36.改变状态栏的颜色状态

[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault];

[[UIApplication sharedApplication] setStatusBarHidden:NO];

37.给自己的项目中添加特殊的标示符号

http://patorjk.com/software/taag/#p=moreopts&h=0&v=1&f=优雅&t=V

38 清空某个文件夹

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);

NSString *outputURL = paths[0];

[NSFileManager.new removeItemAtPath:outputURL error:nil];

[NSFileManager.new createDirectoryAtPath:outputURL

withIntermediateDirectories:YES

attributes:nil

error:NULL];

39. 在document下创建文件

NSString *writePath=[NSString stringWithFormat:@”%@/%@.txt”,stre,@”aaa”];

NSData *data = [@”” dataUsingEncoding:NSUTF8StringEncoding];//新文件的初始数据,设为空

[[NSFileManager defaultManager] createFileAtPath:writePath contents:data attributes:nil];//创建文件的命令在这里

40.layoutSubviews在以下情况下会被调用:

1、init初始化不会触发layoutSubviews

2、addSubview会触发layoutSubviews
3、设置view的Frame会触发layoutSubviews,当然前提是frame的值设置前后发生了变化
4、滚动一个UIScrollView会触发layoutSubviews
5、旋转Screen会触发父UIView上的layoutSubviews事件
6、改变一个UIView大小的时候也会触发父UIView上的layoutSubviews事件

41.苹果审核加急通道

https://developer.apple.com/appstore/contact/?topic=expedite

51 .美化配置git log

$ git config –global alias.lg “log –color –graph –pretty=format:’%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset’ –abbrev-commit –”
$ git lg
52.查看远程 git
git remote show origin
53.显示远程git仓库
git remote show
54.OC冒泡排序
//   NSMutableArray 自带排序方法

NSMutableArray *lastArrary = [NSMutableArray arrayWithArray:sinceArray];

[lastArrary sortedArrayUsingSelector:@selector(compare:)];

– (NSComparisonResult)compare:(NSNumber *)otherNumber

{

// 设置升 降序
return NSOrderedDescending;

}

// 冒泡排序
NSMutableArray *sinM_Array = [NSMutableArray arrayWithArray:sinceArray];

for (int x = 0; x < sinceArray.count -1; x ++) {

for (int y = x + 1; y < sinceArray.count – 1 -x; y ++) {

int leftNum = [[sinM_Array objectAtIndex:x] intValue];

int rightNum = [[sinM_Array objectAtIndex:y]intValue];

if (leftNum > rightNum) {

[sinM_Array replaceObjectAtIndex:x withObject:[NSNumber numberWithInt:leftNum]];

[sinM_Array replaceObjectAtIndex:y withObject:[NSNumber numberWithInt:rightNum]];

}

}

}
55.前后摄像头的切换

– (AVCaptureDevice *)cameraWithPosition:(AVCaptureDevicePosition)position

{

NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];

for ( AVCaptureDevice *device in devices )

if ( device.position == position )

return device;

return nil;

}

– (void)backCarme:(UIButton *)button

{

NSArray *inputs = self.session.inputs;

for ( AVCaptureDeviceInput *input in inputs ) {

AVCaptureDevice *device = input.device;

if ( [device hasMediaType:AVMediaTypeVideo] ) {

AVCaptureDevicePosition position = device.position;

AVCaptureDevice *newCamera = nil;

AVCaptureDeviceInput *newInput = nil;

if (position == AVCaptureDevicePositionFront)

newCamera = [self cameraWithPosition:AVCaptureDevicePositionBack];

else

newCamera = [self cameraWithPosition:AVCaptureDevicePositionFront];

newInput = [AVCaptureDeviceInput deviceInputWithDevice:newCamera error:nil];

[self.session beginConfiguration];

[self.session removeInput:input];

[self.session addInput:newInput];

[self.session commitConfiguration];

break;

}

}

}
56.#pragma mark –获得视频的尺寸
-(CGSize)getImage:(NSURL *)url

{
NSDictionary *opts = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:NO]forKey:AVURLAssetPreferPreciseDurationAndTimingKey];

AVURLAsset *urlAsset = [AVURLAsset URLAssetWithURL:url options:opts];

CGSize size = [[[urlAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] naturalSize];

return size;

}

57.播放音频

// 导入框架 AudioToolbox.framework

NSString *path = [[NSBundle mainBundle] pathForResource:@”msgcome” ofType:@”wav”];

NSURL *url = [NSURL fileURLWithPath:path];

SystemSoundID soundId;

AudioServicesCreateSystemSoundID((CFURLRef)url, &soundId);

AudioServicesPlaySystemSound(soundId);

有用 kotlin 开发后端的吗?

目前只敢工具类用 kotlin,不知道 kotlin 和 spring 会不会有兼容上的坑,兼容 spring 各种注解么。 目前发现比较难受的是,数据类(DO, DTO)必须全部用可空,不然 fastjson 反序列化会有问题。 有生产环境用 kotlin 开发的老哥吗,目前有什么问题?

20 条回复  •  2021-03-31 13:00:48 +08:00     
1 rust   2 小时 13 分钟前 公司一个小项目用了 Ktor,开发确实爽,目前还没有发现什么问题.    
2 xuanbg   2 小时 10 分钟前 自从 get 了终*秘技 cv 大法,Java 语言啰嗦繁琐的弱点也就不存在了,反倒是隐隐成了优势。所以 kotlin 什么的也就没有吸引力了    
3 zoharSoul   2 小时 8 分钟前 有, json 序列化建议用 gson, 和 kt 配合的资料比较多    
4 yazinnnn   2 小时 7 分钟前 json 现在在用 moshi 用 kotlin 可以尝试一下其他东西,比如 quarkus vertx 啥的    
5 micean   2 小时 0 分钟前 兼容旧系统的话,带问号是必要的。 所有的 pojo 用 IDEA 自带的.java 转.kt 就行 kotlin 目前*主要的问题在于 debug 反编译依赖 source,异常打印 stack 的行号不那么准    
6 zerofancy   1 小时 55 分钟前
毕设项目都是 Kotlin,目前遇到过两个奇怪的问题。

1. 同时有 Java 和 Kotlin 的 RestController,Java 的找不到
2. 类型推断好像有点问题

有如下代码:

“`kt
val gridFsFile: GridFSFile? = gridFsTemplate.findOne(Query(Criteria.where(“_id”).`is`(id)))
“`

idea 提示我

“`
‘gridFsFile’ is always non-null type
“`

很明显 gridFsTemplate 是可空的,甚至在接口 GridFsOperations 中已经标注了  。
尝试加入判空逻辑:

“`
if (gridFsFile == null) {
// do something
}
“`

idea 提示:

“`
No method ‘equals(Any?): Boolean’ available
“`

猜想是不是 Kotlin 调用 java 都会有这样问题,结果未能在 demo 项目中复现。
目前用这样的方式判断,原理不明:

“`kt
if (gridFsFile === null) {
// do something
}
“`

7 anke1938   1 小时 55 分钟前 两年前小公司用的纯 kotlin 后端 开发的 web 后台 / 小程序后台 用起来还是很爽的 没啥特别的坑 或者没怎么遇到    
8 hantsy   1 小时 52 分钟前 你多少年没用 Spring 了?
Spring 5.0(2017 年) 开始, 除了 Reactive,Kotlin 就是*重要的特性之一,Spring 对 Kotlin 进行深度集成(远超过之差的 Groovy 语言支持)。
针对很多 Kotlin 特性很多优化,例如,不必声明 open, data class 可以用于 JPA Entity 等。

Kotlin DSL 声明 beans 定义,安全 (参考 Spring 中 BeanDefinitionDSL )等。

val beans = beans {
bean {
CommandLineRunner {
println(“start data initialization…”)
val posts = ref<PostRepository>()
posts.deleteAll()
.thenMany<Post>(
posts.saveAll(
arrayListOf(
Post(null, “my first post”, “content of my first post”),
Post(null, “my second post”, “content of my second post”)
)
)
)
.log()
.subscribe(
{ println(it) },
{ println(it) },
{ println(“data initialization done…”) }
)
}
}

https://github.com/hantsy/spring-kotlin-dsl-sample/blob/master/reactive/src/main/kotlin/com/example/demo/DemoApplication.kt

现在 Spring 还有一个 Spring Kofu 孵化项目(提供完全 Kotlin DSL )。

val app = reactiveWebApplication {
configurationProperties<SampleProperties>(prefix = “sample”)
enable(dataConfig)
enable(webConfig)

listener<ApplicationReadyEvent> {
println(“start data initialization…”)
ref<PostRepository>().init()
}
profile(“foo”) {
beans { bean<Bar>() }
}
}

https://github.com/hantsy/spring-kotlin-dsl-sample/blob/master/kofu-reactive-mongo/src/main/kotlin/com/example/demo/DemoApplication.kt

9 optional   1 小时 51 分钟前 via iPhone   1 你不觉得 fastJson 比较坑。。。    
10 wellsc   1 小时 50 分钟前 借楼问一个 kotlin 的 protobuf grpc 的库    
11 hantsy   1 小时 50 分钟前 @zerofancy Spring Data 一般中 Template, repository 都有写 Kotlin 扩展。估计那 GridFsTemplate 没有处理。    
12 hantsy   1 小时 48 分钟前 @optional 在 Spring 3 。x 就开始提供了内容协商机制,为什么要用 FastJSON 这种东西???实在不明白。    
13 ChangQin   1 小时 10 分钟前 via iPhone @rust ktor+1    
14 zoharSoul   1 小时 3 分钟前 @zerofancy gridFsTemplate.findOne 返回的是 nullable 的吗?    
15 loshine1992   1 小时 0 分钟前 用 moshi 或 kotlin-serialization    
16 xmumiffy   56 分钟前 via Android 建议换掉 fastjson 另外也可以混用 java 至少我这 data class 都是用 java 的,kotlin 的 data class 非常不好用    
17 Kasumi20   42 分钟前 没有发现 Kotlin 不支持 IoC 和 AOP,所以 Kotlin 和 Spring 百分百兼容。 至于什么反序列化的如果是先构造一个空对象,当然会有问题。    
18 boris93   34 分钟前 via iPhone 正在用 Kotlin+Spring Boot+Webflux json 序列化反序列化用的 jackson 目前没啥问题 话说干嘛要用 fastjson 呢?框架自己序列化反序列化用自带的 jackson 就行了啊,也就稍微配置一下的事 偶尔用 gson 也能搞定    
19 youngxhui   27 分钟前 via Android @wellsc grpc 官方有的    
20 wellsc   9 分钟前 via iPhone @youngxhui protobuf 我没找到好用的

关于学习Python的一点学习总结

关于学习Python的一点学习总结

27.创建和使用字典:字典由键及其相应的值组成,这种键值对称为项(item)
方法一:

>>> name={‘Hongkong’:’45’,’shanghai’:’67’,’guizhou’:’89’}
>>> name
{‘Hongkong’: ’45’, ‘shanghai’: ’67’, ‘guizhou’: ’89’}

方法二:

>>> item={(‘hongkong’,’45’),(‘guizhou’,’67’),(‘shanghai’,’89’)}
>>> name=dict(item)
>>> name
{‘hongkong’: ’45’, ‘shanghai’: ’89’, ‘guizhou’: ’67’}
>>> name[‘shanghai’]
’89’

方法三:

>>> name=dict(first=’one’,shanghai=’67’,guizhou=’89’)
>>> name
{‘first’: ‘one’, ‘shanghai’: ’67’, ‘guizhou’: ’89’}
>>> name[‘first’]
‘one’
>>> name[‘guizhou’]
’89’

关于字典的一些基本操作:
1. len(d)返回字典d包含的项(键值对)数。
2.d[k]返回与键k相关联的值。
3.d[k] = v将值v关联到键k。 
4.del d[k]删除键为k的项。
5.k in d检查字典d是否包含键为k的项。

>>> name=dict(first=’one’,shanghai=’67’,guizhou=’89’)
>>> len(name)

>>> name[‘shanghai’]=78
>>> name
{‘first’: ‘one’, ‘shanghai’: 78, ‘guizhou’: ’89’}
>>> del name[‘first’]
>>> name
{‘shanghai’: 78, ‘guizhou’: ’89’}
>>> ‘guizhou’ in name
True

注意:将一个字符赋值给列表是不可以的;而将一个字符赋值给字典是可以的。
例如:

` >>> item=[]
>>> item[2]=’guizhou’
Traceback (most recent call last):
File “<pyshell#43>”, line 1, in <module>
item[2]=’guizhou’
IndexError: list assignment index out of range
——————————————————-
>>> item={}
>>> item[2]=’guizhou’
>>> item[2]
‘guizhou’

遍历点击下拉树图标

遍历点击下拉树图标

发现有下级菜单的图标,都有一个特点,放到css里面了。

然后进行循环,点击一个展开后 需要重新定位这样不会有重复的。这里有个特殊的,是从0开始的,循环的时候要注意。

#循环点击左侧树状图 先遍历点下拉图标
ele_icon=driver.find_elements_by_css_selector(‘.aty-tree-node-hd .aty-tree-node-trigger’)
css=’.aty-tree-node-hd .aty-tree-node-trigger’
left_list=driver.find_elements_by_css_selector(css)
print(f”’我在外面{len(left_list)}”’)
for i in range(0,len(left_list)+1):
driver.find_elements_by_css_selector(css)[i].click()
left_list = driver.find_elements_by_css_selector(css)
print(f”’我在里面{len(left_list)}”’)
time.sleep(2)
print(i)
再附上一个  固定两个层级的代码–备用

import time
import self
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver.support import expected_conditions as EC
from selenium import webdriver

# 判断元素是否存在的方法
def isElementExist(ele_icon):
flag=True
# browser=self.driver
try:
driver.find_element_by_xpath(ele_icon)
return flag
except:
flag=False
return flag

if __name__ == ‘__main__’:

driver = webdriver.Chrome()
driver.maximize_window()
driver.implicitly_wait(30)
first_url = ‘http://172.18.65.1111111136:8081/#/query/1’
driver.get(first_url)
#点击到编辑查询界面
driver.find_element_by_xpath(‘//*[@id=”app”]/div[1]/div[2]/div[2]/div/div[3]/div[1]/div/div[2]/span’).click()
driver.find_element_by_xpath(‘//*[@id=”app”]/div[2]/div/div[2]/div[1]/div[1]/button/span’).click()

#循环点击左侧树状图
left_table=driver.find_elements_by_css_selector(‘#jsTreeContain>.aty-tree-node’)
index=len(left_table)
for i in range(1,index+1):
try:
#*个层级 这里有可能会有多个层级,一般是一个 //*[@id=”jsTreeContain”]/li[1]/h3/span[2]
ele=driver.find_element_by_xpath(‘//*[@id=”jsTreeContain”]/li[‘+str(i)+’]/h3/span[2]’)
ele_icon=driver.find_element_by_xpath(‘//*[@id=”jsTreeContain”]/li[‘+str(i)+’]/h3/span[1]’)
flag=isElementExist(ele_icon)
ele_icon.click()
print(‘已展开下级菜单’.center(50,’-‘))
time.sleep(1)
# 第二个层级 #jsTreeContain .aty-tree-node-content >.aty-tree-node
ele2 = driver.find_elements_by_css_selector(‘#jsTreeContain .aty-tree-node-content >.aty-tree-node’)
num = len(ele2)
print(f”'{i}组:{ele.text}里有{num}个表”’)
for m in range(1, num + 1):
ele_inner = driver.find_element_by_xpath(‘//*[@id=”jsTreeContain”]/li[‘ + str(i) + ‘]/ul/li[‘ + str(m) + ‘]’)
ele_inner.click()
print(f”’第{m}个表为:{ele_inner.text}”’)
time.sleep(0.5)

except NoSuchElementException:
driver.find_element_by_xpath(‘//*[@id=”jsTreeContain”]/li[‘+str(i)+’]/h3/span’).click()

友情链接: SITEMAP | 旋风加速器官网 | 旋风软件中心 | textarea | 黑洞加速器 | jiaohess | 老王加速器 | 烧饼哥加速器 | 小蓝鸟 | tiktok加速器 | 旋风加速度器 | 旋风加速 | quickq加速器 | 飞驰加速器 | 飞鸟加速器 | 狗急加速器 | hammer加速器 | trafficace | 原子加速器 | 葫芦加速器 | 麦旋风 | 油管加速器 | anycastly | INS加速器 | INS加速器免费版 | 免费vqn加速外网 | 旋风加速器 | 快橙加速器 | 啊哈加速器 | 迷雾通 | 优途加速器 | 海外播 | 坚果加速器 | 海外vqn加速 | 蘑菇加速器 | 毛豆加速器 | 接码平台 | 接码S | 西柚加速器 | 快柠檬加速器 | 黑洞加速 | falemon | 快橙加速器 | anycast加速器 | ibaidu | moneytreeblog | 坚果加速器 | 派币加速器 | 飞鸟加速器 | 毛豆APP | PIKPAK | 安卓vqn免费 | 一元机场加速器 | 一元机场 | 老王加速器 | 黑洞加速器 | 白石山 | 小牛加速器 | 黑洞加速 | 迷雾通官网 | 迷雾通 | 迷雾通加速器 | 十大免费加速神器 | 猎豹加速器 | 蚂蚁加速器 | 坚果加速器 | 黑洞加速 | 银河加速器 | 猎豹加速器 | 海鸥加速器 | 芒果加速器 | 小牛加速器 | 极光加速器 | 黑洞加速 | movabletype中文网 | 猎豹加速器官网 | 烧饼哥加速器官网 | 旋风加速器度器 | 哔咔漫画 | PicACG | 雷霆加速