iOS Hook在IDA中显示为sub_xxx的函数

基础
1. Mach-O文件组成部分

Header、Load commands、Raw segment date(常见的一些段__PAGEZERO空指针陷阱段、_TEXT程序代码段、__DATA程序数据段、__LINKEDIT:链接器使用段等);

2. Mach-O文件的加载 dyld

Mach-O文件被dyld进行加载的;dyld(the dynamic link editor)是 Apple 的动态链接器,系统 kernel 做好启动程序的初始准备后,交给 dyld 负责;

3. ASLR

也就是地址空间布局随机化,它会让 Mach-O 文件每次加载的时候是随机地址;

4. PIC

位置代码独立,位置和代码无关,假如我们要调用外部函数,首先会在映射表中增加一个间接指针,指向外部的函数,dyld会动态的去绑定,将指针指向外部的函数地址;

为什么不使用fishhook?
1. fishhook原理

dyld通过更新Mach-O二进制文件__DATA段的特定部分中的指针来绑定lazy 和 non-lazy 的符号。fishhook通过传递给rebind_symbols的每个符号名称,确定某一个符号(外部函数的符号)在 __DATA 段中的位置,保存原符号对应的函数指针,将原有符号的函数指针指向内部函数,实现重新绑定符号,从而实现了对C函数的Hook。其中*复杂的部分就是从二进制文件中寻找某个符号的位置,具体可查看fishhook部分;

2. 不使用的原因

(1)fishhook是通过绑定lazy 和 non-lazy 的符号,将指向系统方法(外部函数)的符号重新进行绑定指向内部的函数,从而实现了系统方法与自己定义的方法进行了交换。也就导致了C的自定义的函数无法修改,只能修改 Mach-O 外部的函数

(2)fishhook通过传递给rebind_symbols的每个符号名称去查找相应的符号位置的,但是我们要hook的sub_xxx不能确定函数的具体名称

_dyld_register_func_for_add_image函数
官方定义:

* The following functions allow you to install callbacks which will be called

* by dyld whenever an image is loaded or unloaded. During a call to _dyld_register_func_for_add_image()

* the callback func is called for every existing image. Later, it is called as each new image

* is loaded and bound (but initializers not yet run). The callback registered with

* _dyld_register_func_for_remove_image() is called after any terminators in an image are run

* and before the image is un-memory-mapped.

1. 以下函数当dyld装载镜像并绑定的时候会被调用,为每个镜像加载时的回调函数

void _dyld_register_func_for_add_image(void (*func)(const struct mach_header* mh, intptr_t vmaddr_slide))

2. 以下函数当dyld移除镜像以及绑定的时候会被调用

void _dyld_register_func_for_remove_image(void (*func)(const struct mach_header* mh, intptr_t vmaddr_slide))

 

Dl_info
Dl_info结构体用于存储一些镜像信息,比如路径,基地址,它的相关定义在#include <dlfcn.h>文件中;

通过dladdr函数,填充Mach-O Header以及Dl_info结构体,获取Dl_info的一些信息,获取Header偏移地址即ASLR偏移量;

 

CydiaSubstrate
CydiaSubstrate是大多数tweaks工程的基础,由MobileHooker,MobileLoader,Safe Mode组成;

其中MobileHooker经常被用来替换系统函数的调用,它主要有两个函数:

MSHookMessageEx用来hook Objective-C函数;

MSHookFunction用来hook C/C++函数;

 

MSHookFunction
MSHookFunction的三个参数作用分别为:替换的原函数,替换函数,以及MobileHooker保持的原函数。

Hook原理:

1. 修改目标函数前N字节,跳转到自定义函数入口;

2. 备份目标函数前N个字节,跳转回目标函数。

 

__attribute__((constructor))
要想让一个被加载的动态库在加载后自动运行某一段代码有好几种方法:

1. 建立OC某个类的分类,并在类的+load方法中添加代码;

2. 在动态库中定义带有__attribute__((constructor))声明的函数,并在函数内添加特定的代码。

 

hook sub_xxx函数的原理:
1. 计算公式:

模块偏移后的地址 = 模块偏移前的基地址 + ASLR偏移量

注:ida中所看到的地址都是模块偏移前的基地址

 

2. 原理

在ida中找到函数地址即模块偏移前的基地址,然后在hook代码中计算ASLR偏移量,然后相加,使用MSHookFunction函数进行hook;

说明:看到有些帖子上说此处需要((模块偏移前的基地址 + ASLR偏移量)| 0x1),亲测了一下arm64上是不需要的,猜测可能是架构的原因吧,具体没做太深入的研究,后期会补上,如果有遇到我的方式不成功的也可尝试一下此方法。

示例解析(github源码):
说明:因为我自己模拟没显示sub_xxx的函数,但是情况和sub_xxx一致的,这点可忽略;

1. 测试的原始函数

%title插图%num

2. ida函数sub_xxx,如果想要hook它

%title插图%num

3. 查找sub_xxx对应的内存地址

%title插图%num

4. 编写hook代码

这里使用的是使用MonkeyDev新建的工程,仅列出了部分函数代码,具体请下载源代码查看:

 

//保存模块偏移基地址的值

static void _register_func_for_add_image(const struct mach_header *header, intptr_t slide) {
Dl_info image_info;

int result = dladdr(header, &image_info);

if (result == 0) {
NSLog(@”load mach_header failed”);

return;

}

//获取当前的可执行文件路径

NSString *execName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@”CFBundleExecutable”];

NSString *execPath = [[[NSBundle mainBundle] bundlePath] stringByAppendingFormat:@”/%@”, execName];

if (strcmp([execPath UTF8String], image_info.dli_fname) == 0) {
g_slide = slide;

}

}

 

//hook后会来到这里

void hook_testMethod(void) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@”hook了我” message:@”message” delegate:nil cancelButtonTitle:nil otherButtonTitles:@”other”, nil];

[alert show];

}

 

static void __attribute__((constructor)) __init__() {
//注册添加镜像回调

_dyld_register_func_for_add_image(_register_func_for_add_image);

//通过 模块偏移前的基地址 + ASLR偏移量 找到函数真正的地址进行hook

MSHookFunction((void *)(0x100006934+g_slide), (void *)hook_testMethod, (void **)&orig_testMethod);

}

 

5. hook前效果图

%title插图%num

6. hook后效果图

%title插图%num

 

iOS 判断UITableView是否滚动在*底部

iOS 根据判断UITableView或者UIScrollView是否滚动在*底部,然后对接收到的信息进行处理,判断新消息来的时候是否滚动,提升用户体验。
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGFloat height = scrollView.frame.size.height;
CGFloat contentOffsetY = scrollView.contentOffset.y;
CGFloat bottomOffset = scrollView.contentSize.height – contentOffsetY;
if (bottomOffset <= height)
{
//在*底部
self.currentIsInBottom = YES;
}
else
{
self.currentIsInBottom = NO;
}
}
然后根据self.currentIsInBottom在接收消息的方法中对新消息进行处理

伪代码:

if(是本人发送的消息)
{
调用滚动方法
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:[self.dataArray count] – 1 inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
else
{
if(在底部)
{
调用滚动方法
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:[self.dataArray count] – 1 inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
}

 

Objective-C runtime黑魔法,交换iOS系统类库方法

#pragma mark – 使用分类重写NSObject的load方法
+(void)load{
//只需要运行一次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
//交换viewWillAppear: 和 glt_viewWillAppear: 方法
SEL oriSelector = @selector(viewWillAppear:);
SEL swiSelctor = @selector(glt_viewWillAppear:);

//注意:导入头文件 #import <objc/message.h>
Method oriMethod = class_getInstanceMethod(class, oriSelector);
Method swiMethod = class_getInstanceMethod(class, swiSelctor);

BOOL success = class_addMethod(class, oriSelector, method_getImplementation(swiMethod), method_getTypeEncoding(swiMethod));

if (success) {
class_replaceMethod(class, swiSelctor, method_getImplementation(oriMethod), method_getTypeEncoding(oriMethod));
}else{
method_exchangeImplementations(oriMethod, swiMethod);
}
});
}

#pragma mark – 替换后的子类viewWillAppear
-(void)glt_viewWillAppear:(BOOL)animated{
[self glt_viewWillAppear:animated];
}

 

iOS 改变UIPickerView分割线颜色

有时候我们根据项目的需要,需要对UIPickerView做处理,这就需要对UIPickerView进行自定义,下面是改变其分割线颜色的方法,原理就是找到UIPickerView的子View高度小于1的View,然后改变线的颜色,即可实现,此外在iOS10下分割线颜色默认是透明的。

注意:这个方法只有放到下面的方法才有效果,获取pickerView:viewForRow:forComponent:reusingView:中定义的View,当pickerView:viewForRow:forComponent:reusingView:未实现或者行或分组不可用时返回nil。

– (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view

#pragma mark – 改变分割线的颜色
– (void)changeSpearatorLineColor
{
for(UIView *speartorView in picker.subviews)
{
if (speartorView.frame.size.height < 1)//取出分割线view
{
speartorView.backgroundColor = LINE_BACKGROUND_COLOR;//隐藏分割线
}
}
}

运行效果如下图:

%title插图%num

iOS UITableView局部刷新 刷新单个cell或section

/**
* 单个cell的刷新
*/
//1.当前所要刷新的cell,传入要刷新的 行数 和 组数
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
//2.将indexPath添加到数组
NSArray <NSIndexPath *> *indexPathArray = @[indexPath];
//3.传入数组,对当前cell进行刷新
[tableView reloadRowsAtIndexPaths:indexPathArray withRowAnimation:UITableViewRowAnimationAutomatic];

/**
* 单个Section的刷新
*/
//1.传入要刷新的组数
NSIndexSet *indexSet=[[NSIndexSet alloc] initWithIndex:0];
//2.传入NSIndexSet进行刷新
[tableView reloadSections:indexSet withRowAnimation:UITableViewRowAnimationAutomatic];

%title插图%num

iOS ScrollView嵌套ScrolloView解决方案 – Swift

1. 基础版实现思路
1.1:层次结构

底部是一个UITableView,上面黄色部分为tableView的tableHeaderView,cell的数量为1,cell的contentView上防止了一个LTPageView, pageView上放置了一个scrollView且可以左右滑动分页,scrollView上放置控制器view,控制器view上放置各自的scrollView(tableView或collectionView)

1.2:使用方法(具体查看Demo)

(1)创建LTSimpleMabager,并添加到视图,传入frame,子控制器数组,标题数组,当前的控制器以及pageView的样式设置

LTSimpleManager(frame: <#T##CGRect#>, viewControllers: <#T##[UIViewController]#>, titles: <#T##[String]#>, currentViewController: <#T##UIViewController#>, layout: <#T##LTLayout#>)
(2)初始化子控制器的scrollView(tableView或collectionView),且tableView的y值从pageTitleView的高度开始,Demo中为44,具体可根据产品需求而定,tableView的height则为父view的高减去44

(3)将子控制器的scrollView(tableView或collectionView)赋值给glt_scollView即glt_scrollView = tableView,以下会说明原因。

1.3:实现思路

(1)当滑动底部tableView的时候,当tableView的contentOffset.y 小于 header的高的时候,将内容ScrollView的contentOffset设置为.zero

private func contentScrollViewScrollConfig(_ viewController: UIViewController) {
viewController.glt_scrollView?.scrollHandle = {[weak self] scrollView in
guard let `self` = self else { return }
self.contentTableView = scrollView
if self.tableView.contentOffset.y < self.kHeaderHeight {
scrollView.contentOffset = .zero;
scrollView.showsVerticalScrollIndicator = false
}else{
scrollView.showsVerticalScrollIndicator = true
}
}
}
(2)当滑动内容ScrollView的时候, 当内容contentOffset.y 大于 0(说明滑动的是内容ScrollView) 或者 当底部tableview的contentOffset.y大于 header的高度的时候,将底部tableView的偏移量设置为kHeaderHeight, 并将其他的scrollView的contentOffset置为.zero

public func scrollViewDidScroll(_ scrollView: UIScrollView) {
guard scrollView == tableView, let contentTableView = contentTableView else { return }
let offsetY = scrollView.contentOffset.y
if contentTableView.contentOffset.y > 0.0 || offsetY > kHeaderHeight {
tableView.contentOffset = CGPoint(x: 0.0, y: kHeaderHeight)
}
if scrollView.contentOffset.y < kHeaderHeight {
for viewController in viewControllers {
guard viewController.glt_scrollView != scrollView else { continue }
viewController.glt_scrollView?.contentOffset = .zero
}
}
}
(3)headerView添加以及各个点击事件回调处理。

simpleManager.configHeaderView {[weak self] in
guard let strongSelf = self else { return nil }
let headerView = strongSelf.testLabel()
return headerView
}
simpleManager.didSelectIndexHandle { (index) in

}
simpleManager.refreshTableViewHandle { (scrollView, index) in
scrollView.mj_header = MJRefreshNormalHeader {
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0, execute: {
scrollView.mj_header.endRefreshing()
})
}
}
2.进阶版实现思路
2.1:层次结构

底部是LTPageView, pageView上放置了一个scrollView且可以左右滑动分页,scrollView上放置控制器view,headerView是一个单独的View在顶部,利用scrollView的contentInset将其放置在了*上面

2.2:使用方法(具体查看Demo)

(1)创建LTAdvancedManager,并添加到视图,传入frame,子控制器数组,标题数组,当前的控制器以及pageView的样式设置和自己的headerView(在闭包中返回即可)

LTAdvancedManager(frame: <#T##CGRect#>, viewControllers: <#T##[UIViewController]#>, titles: <#T##[String]#>, currentViewController: <#T##UIViewController#>, layout: <#T##LTLayout#>, headerViewHandle: <#T##() -> UIView#>)
(2)初始化子控制器的scrollView(tableView或collectionView),且tableView的y值从0开始,tableView的height则为父view的高减去44

(3)将子控制器的的scrollView(tableView或collectionView)赋值给glt_scollView即glt_scrollView = tableView,以下会说明原因。

2.3:实现思路

(1)主要是利用子控制的滚动来控制headerView

//MARK: 当前控制器的滑动方法事件处理
private func contentScrollViewDidScroll(_ contentScrollView: UIScrollView, _ absOffset: CGFloat) {

//获取当前控制器
let currentVc = viewControllers[currentSelectIndex]

//外部监听当前ScrollView的偏移量
self.delegate?.glt_scrollViewOffsetY?((currentVc.glt_scrollView?.contentOffset.y ?? kHeaderHeight) + self.kHeaderHeight + layout.sliderHeight)

//获取偏移量
let offsetY = contentScrollView.contentOffset.y

//获取当前pageTitleView的Y值
var pageTitleViewY = pageView.pageTitleView.frame.origin.y

//pageTitleView从初始位置上升的距离
let titleViewBottomDistance = offsetY + kHeaderHeight + layout.sliderHeight

let headerViewOffset = titleViewBottomDistance + pageTitleViewY

if absOffset > 0 && titleViewBottomDistance > 0 {//向上滑动
if headerViewOffset >= kHeaderHeight {
pageTitleViewY += -absOffset
if pageTitleViewY <= hoverY {
pageTitleViewY = hoverY
}
}
}else{//向下滑动
if headerViewOffset < kHeaderHeight {
pageTitleViewY = -titleViewBottomDistance + kHeaderHeight
if pageTitleViewY >= kHeaderHeight {
pageTitleViewY = kHeaderHeight
}
}
}

pageView.pageTitleView.frame.origin.y = pageTitleViewY
headerView?.frame.origin.y = pageTitleViewY – kHeaderHeight
let lastDiffTitleToNavOffset = pageTitleViewY – lastDiffTitleToNav
lastDiffTitleToNav = pageTitleViewY
//使其他控制器跟随改变
for subVC in viewControllers {
guard subVC != currentVc else { continue }
guard let vcGlt_scrollView = subVC.glt_scrollView else { continue }
vcGlt_scrollView.contentOffset.y += (-lastDiffTitleToNavOffset)
subVC.glt_upOffset = String(describing: vcGlt_scrollView.contentOffset.y)
}
}
3.问题以及补充
3.1:问题1:全局监听scrollView的滚动

实现思路是根据runtime,交换方法的实现,主要的坑就是当时采用的交换scrollViewDidScroll(_:),但它是UIScrollViewDelegate的协议方法,发现行不通,后来找到了UIScrollView中的一个方法Selector((“_notifyDidScroll”)),发现拦截它是可行的,当然Swift中拦截方法,也是有很多坑的,和OC有很大不同,具体操作看源码吧!

3.2:进阶版遗留小问题

当headerView中有按钮需要响应事件的时候,我这里采用的是利用override func point(inside point: CGPoint, with event: UIEvent?) -> Bool方法进行判断,因为上面headerView的交互只有关闭了才能滑动headerView左右切换,所以在这里我只允许要响应事件的控件交互打开了,其他区域交互关闭。如果有更好的思路欢迎联系!!!不胜感激!

//MARK: 暂用,待优化。
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
for tempView in self.subviews {
if tempView.isKind(of: UILabel.self) {
let button = tempView as! UILabel
let newPoint = self.convert(point, to: button)
if button.bounds.contains(newPoint) {
return true
}
}
}
return false
}

Cycript基本语法与使用-iOS逆向工程

1.常用数据类型(字符串、数组、字典、Bool, NSNumber)

cy# @”Hello Gao Liutong”
@”Hello Gao Liutong”
cy# @[1, 2, 3]
@[1,2,3]
cy# @{“key”:”value”}
@{“key”:”value”}
cy# @YES
@true
cy# @(5+7)
@12

2.instanceof 判断是什么类型

cy# @”Hello Gao Liutong” instanceof String
true
cy# @[1, 2, 3] instanceof Array
true
cy# @”Hello Gao Liutong” instanceof Array
false

3.substr字符串截取,slice数组截取(slice(0,2)从0下标长度为2,即下标0和1)

cy# @”Hello Gao Liutong”.substr(0, 5)
“Hello”
cy# @[1,2,3,”hello”].slice(0, 2)
[@1,@2]

4.数组与字典的一些操作

cy# var array = [NSMutableArray arrayWithObjects:@”1″,@”2″, nil];
@[“1″,”2”]
cy# array.length
2
cy# array.count
2
cy# array[5] = 6; array;
@[“1″,”2”,,,,6]
cy# array.length = 3; array;
@[“1″,”2″,,]
cy# array[0]
@”1″
cy# @{@”key”:@”55555″}[“key”]
@”55555″

5.js转换为oc类型

cy# ?debug
debug == true
cy# @(2+7)
cy= Instance.box((9))
@9
cy# @”test”
cy= Instance.box(“test”)
@”test”

6.调用方法,实际上是oc中的函数 objc_msgSend(receiver, selector, arg1, arg2, …);

cy# [@”hello world” stringByReplacingOccurrencesOfString:@”hello” withString:@”niho”]
cy= objc_msgSend(Instance.box(“hello world”),”stringByReplacingOccurrencesOfString:withString:”,Instance.box(“hello”),Instance.box(“nihao”))
@”nihao world”

7.字典与数组操作

cy# var a = [1, 2, 3]
[1,2,3]
cy# [a objectAtIndex:0]
@1
cy# [a setObject:@”123″ atIndex:2]; a
[1,2,@”123″]
cy# var dict = {“key”:”value”}
{key:”value”}
cy# [dict setObject:a forKey:”array”]
cy# dict
{key:”value”,array:[1,2,@”123″]}

8.初始化一个对象

cy# [[NSObject alloc] init]
#”<NSObject: 0x16f7c590>”
cy# [new NSObject init]
#”<NSObject: 0x182e5cb0>”

9.#+内存地址 代表了一个对象

cy# UIApp
#”<SpringBoard: 0x17af0400>”
cy# #0x17af0400
#”<SpringBoard: 0x17af0400>”

10. 获取window的子view

cy# UIApplication.sharedApplication()
#”<SpringBoard: 0x17af0400>”
cy# UIApplication.sharedApplication().windows[0]
#”<SBSecureWindow: 0x16fb81f0; baseClass = UIWindow; frame = (0 0; 320 568); userInteractionEnabled = NO; gestureRecognizers = <NSArray: 0x16fb8ee0>; layer = <UIWindowLayer: 0x16fb8600>>”
cy# UIApplication.sharedApplication().windows[0].contentView()
#”<UIView: 0x18075620; frame = (0 0; 320 568); clipsToBounds = YES; layer = <CALayer: 0x180757c0>>”
cy# UIApplication.sharedApplication().windows[0].contentView().subviews[0]
cy# UIApplication.sharedApplication().windows[0].contentView().subviews()[0]
#”<SBFStaticWallpaperView: 0x16fcb4b0; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0x16fcb740>>”

11.打印视图的层次

cy# UIApp.keyWindow.recursiveDescription().toString()

12.获取所有的framworks

cy# function loadFramework(fw)
{
var h=”/System/Library/”,t=”Frameworks/”+fw+”.framework”;
[[NSBundle bundleWithPath:h+t]||
[NSBundle bundleWithPath:h+”Private”+t] load];

}

13.CG…Make函数

cy# function CGPointMake(x, y) { return {x:x, y:y}; }
cy# function CGSizeMake(w, h) { return {width:w, height:h}; }
cy# function CGRectMake(x, y, w, h) { return {origin:CGPointMake(x,y), size:CGSizeake(w, h)}; }

14.获取对象的属性

方法一、

cy# [i for (i in *UIApp)]

方法二、

cy# function tryPrintIvars(a){ var x={}; for(i in *a){ try{ x[i] = (*a)[i]; } catc(e){} } return x; }

15.根据类获取方法

cy# function printMethods(className, isa) {
var count = new new Type(“I”);
var classObj = (isa != undefined) ? objc_getClass(className)->isa :
objc_getClass(className);
var methods = class_copyMethodList(classObj, count);
var methodsArray = [];
for(var i = 0; i < *count; i++) {
var method = methods[i];
methodsArray.push({selector:method_getame(method),
implementation:method_getImpleentation(method)});
}
free(methods);
return metodsArray;

16.获取当前的控制器

cy# function currentVC() {
var app = [UIApplication sharedApplication]
var keyWindow = app.keyWindow
var rootController = keyWindow.rootViewController
var visibleController = rootController.visibleViewController
if (!visibleController){
return rootController
}
return visibleController.childViewControlles[0]
}

17.choose(类),可以查看这个类的所有对象

cy# choose(UIView)
[#”<UIStatusBarDataNetworkItemView: 0x15c118e0; frame = (25 0; 13 20); autoresize = RM+BM; userInteractionEnabled = NO; layer = <CALayer: 0x15c11a80>> [Item = <UIStatusBarItem: 0x15c11480> [DataNetwork (Left)]]”,#”<UIStatusBarBatteryItemView: 0x15c120c0; frame = (282 0; 33 20); autoresize = LM+BM; userInteractionEnabled = NO; layer = <CALayer: 0x15c110c0>> [Item = <UIStatusBarItem: 0x14762a10> [Battery (Right)]]”,#”<UIImageView: 0x15c121e0; frame = (27.5 0; 5.5 20); opaque = NO; autoresize = LM+TM+BM; userInteractionEnabled = NO; layer = <CALayer: 0x15ac6b30>>”,#”<UIStatusBarBatteryPercentItemView: 0x15c123f0; frame = (248 0; 31 20); autoresize = LM+BM; userInteractionEnabled = NO; layer = <CALayer: 0x147bb8a0>> [Item = <UIStatusBarItem: 0x15ae59f0> [BatteryPercent (Right)]]”,#”<UIStatusBarTimeItemView: 0x15c124b0; frame = (144 0; 32 20); autoresize = LM+RM+BM; userInteractionEnabled = NO; layer = <CALayer: 0x14683410>> [Item = <UIStatusBarItem: 0x159f21c0> [Time (Center)]]”,#”<UISegment: 0x15c14d40; frame = (0 0; 155 29); opaque = NO; layer = <CALayer: 0x15c14f80>>”,#”<UISegmentLabel: 0x15c15150; frame = (64.5 6.5; 26 16); text = ‘\xe4\xbb\x8a\xe5\xa4\xa9’; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x15c153a0>>”,#”<UISegment: 0x15c15a10; frame = (156 0; 156 29); opaque = NO; layer = <CALayer: 0x15c16cc0>>”,#”<UIImageView: 0x15c15bc0; frame = (156 0; 1 29); alpha = 0; opaque = NO; autoresize = LM; userInteractionEnabled = NO; tag = -1030; layer = <CALayer: 0x15c16050>>”,#”<UILabel: 0x15c15cb0; frame = (48 20.5; 272 40); text = ‘3\xe6\x9c\x8824\xe6\x97\xa5 \xe6\x98\x9f\xe6\x9c\x9f\xe4\xba\x94’; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x15c17e70>>”,#”<UIImageView: 0x15c160b0; frame = (155 0; 1 29); opaque = NO; autoresize = LM; userInteractionEnabled = NO; tag = -1030; layer = <CALayer: 0x15c16160>>”,#”<UISegment: 0x15c16320; frame = (156 0; 156 29); opaque = NO; layer = <CALayer: 0x15c162d0>>”,#”<UISegment: 0x15c164f0; frame = (0 0; 155 29); opaque = NO; layer = <CALayer: 0x1
18.分类
cy# @implementation NSObject (TestCategory)
-description {return “test”}
-(double)calculate:(float)num {return num * 10;}
@end
cy# test = [new NSObject init]
#”test”
cy# [test calculate:10]
100
19.类与方法的调用注意用的是@implementation
cy# @implementation MyClass : NSObject
{
int num;
}
-(int)returnNum {return this -> num;}
@end
MyClass
cy# var aNum = [new MyClass init]
#”<MyClass: 0x159279e0>”
cy# aNum->num = 10;
10
cy# [aNum returnNum]
10
20.Block用法
cy# testBlock = ^ int (int value) {return value * 10}
^int(int){}
cy# testBlock(10)
100

 

各种ios语法

@property (nonatomic, strong) a1--@synthesize a1 的用法:

一般在别的类如a2中使用 类a.a1属性/ self.a1 的时候才用到,a.m中可以不用(但self.a1除外),直接对a1进行赋值操作

true和false、TRUE和FALSE、YES和NO、1和0都是一样的

1、类型不同
bool为布尔型
BOOL为int型

2、长度不同
bool单独占一个字节
BOOL长度视实际环境来定,一般可认为是4个字节

3、跨平台特性
bool是标准C++中的布尔量,占一个字节大小内存,只有false或者true。具有跨平台特性。
BOOL是Microsoft MFC定义的宏:  typedef int BOOL;
也就是说BOOL类型实际上是int类型不是bool类型,取值为:TRUE或FALSE、定义语法为:
#define FALSE 0
#define TRUE  1
其实是个int类型,占四个字节大小内存,其值为FALSE或TRUE。不具有跨平台特性。

4、取值不同
bool取值false和true,是0和1的区别。
BOOL取值FALSE和TRUE,是0和非0的区别
例子
bool x=3;  // 警告
bool x=1;  // 正确
BOOL x=3;  // 正确
BOOL x=3.3;// 警告

5、类别不同
bool属于标准C语言、C++数据类型;
BOOL是微软定义的typedef int BOOL。与bool不同,它是一个三值逻辑,TRUE/FALSE/ERROR,返回值为>0的整数为TRUE,0为FALSE,-1为ERROR。

6、执行效率
BOOL类型要比bool类型处理快一些。因为BOOL类型正好等于一个机器能处理的*大字长。bool处理过程中需要一些转换。

7、其他
这两种不同的布尔类型可以在同一个程序中共存,但在编写Cocoa代码时要使用BOOL。
但是如果没有严格要求的话,布尔类型和Int类型是通用的。非0值的int类型可以转为布尔类型的true,称为隐式转换。0的Int值则可以转为布尔类型的false。

消息调度 @selector参数列表
@selector(…) 语句是Objective-C 中用来指定某个特定方法。关键在于,*不能忘记函数后面的冒号!冒号告诉 Objective-C:“去找一个名为XXX的方法,这个方法有且只有一个参数”。如果忘记写冒号,编译器还是可以通过的,但是程序一运行就会发生崩溃。在 Debugger Console 窗口中,你会看到这样的错误日志:“unrecoginized  selector sent to instance…”。
@selector(…) 中的冒号个数一定要与所指定的方法参数个数相同。例如:

– (void) example:(ccTime)delta sender:(id)sender flag:(bool)aBool

那么,对应的@selector语句就应该是:

@selector(example:sender:flag:);
->运算是间接寻址:

比.运算的寻址速度更快,而且你用多指针的话会发现指针用->这种调用方式更简洁,几乎没人会使用p[0].k或*p.k这样的调用方式。

对于以下变量定义,以下表达式正确的是:()
struct node{
char s[10];
int k;
}p[4];

A.p->k=2 B.p[0].s=”abc” C.p[0]->k=2 D.p->s=’a’

答案选A。->是指针特用的,p->k表示p对象中的k变量
B错,只有在声明字符数组的时候才能将一个字符串赋给数组。如:char a[5] = “abc”;而
char a[5]; a=”abc”;这种写法是错的。
C错,p[0]不是指针,不能用->。
D错,不能将一个 const char 类型的字符 ‘a’ 赋给 数组 s。双引号就对了

我这样理解:
p指代的是地址 即p[0]的地址,所以p->k=2 等价于p[0].k=2;->是指针特用的

^异或运算符,位值相同为0,不同为1.

a1 = 0x01;    //0000 0001
a2 = 0x00;    //0000 0000
a3 = 0x03;    //0000 0011
a4 = 0x02;    //0000 0010

b1 = a1 ^ a2; //0000 0001
b2 = a1 ^ a3; //0000 0010
b3 = a1 ^ a4; //0000 0011
完整的式子是这样的x^=y,书上写的x^=y,等价于x=x^y,是怎么算的??
^=   是个异或并赋值的操作符。属于位操作符。二者不同返回1,相同返回 0;
意思是   x与y异或的结果存入 x。
假如 x的二进制是 00000000 00000000 00000000 11111111; 或者更长
y的二进制是  00000000 00000000 11111111 00001111;
则 x^=y;之后  x 的二进制是 00000000 00000000 11111111 11110000;

c语言中的\a怎么用?
#include <stdio.h>
int main()
{
printf(“test:\a”);
return 0;
}
测试的时候,会听到嘟的一声,它不能自动播放音乐的,就一个“嘟”

“>>=”在C语言什么意思?
比如a>>=2,等同于a=a>>2,是指a右移两位,这里的右移指的是a的二进制数形式的右移,相当于a缩小为原来的四分之一。>>是位运算中的右移符,后跟数字,表示右移的位数,右移后原数(二进制形式)高位补零,与此相对的还有左移符<<,用法类似

 

苹果iOS语法总结汇总

这篇文章我们来对iOS做一个总结吧,看看要学习iOS,知识点到底要有多少。—-网上的是知识点太多了,花点时间给大家都放一起。
前言:
iOS是由苹果公司开发的移动操作系统[1]  。苹果公司*早于2007年1月9日的Macworld大会上公布这个系统,*初是设计给iPhone使用的,后来陆续套用到iPod touch、iPad以及Apple TV等产品上。iOS与苹果的Mac OS X操作系统一样,属于类Unix的商业操作系统。
——出自百度百科

1、Cocoa是什么?
Cocoa是OS X和 iOS操作系统的程序的运行环境。

是什么因素使一个程序成为Cocoa程序呢?不是编程语言,因为在Cocoa开发中你可以使用各种语言;也不是开发工具,你可以在命令行上就可以创建 Cocoa程序。Cocoa程序可以这么说,它是由一些对象组成,而这些对象的类*后都是继承于它们的根类 :NSObject。而且它们都是基于Objective-C运行环境的。

1.1、Cocoa框架
iOS中,Cocoa众多框架中*重要*基本的两个框架是:Foundation 和 UIKit。(前者是基础库,后者主要是UI库,高级对象)

Foundation 和界面无关,也可以说和界面无关的类基本是Foundation框架的,和界面相关的是UIKit框架。
这两个框架在系统中处于的位置如图:(以下两个图基本类似,主要是为了方便理解)

%title插图%num

%title插图%num

下面来解析下上面的图:
a、Core OS是位于iOS系统架构*下面的一层是核心操作系统层,它包括内存管理、文件系统、电源管理以及一些其他的操作系统任务。它可以直接和硬件设备进行交互。作为app开发者不需要与这一层打交道。
b、Core Services是核心服务层,可以通过它来访问iOS的一些服务。
c、Media是媒体层,通过它我们可以在应用程序中使用各种媒体文件,进行音频与视频的录制,图形的绘制,以及制作基础的动画效果。
d、Cocoa Touch是可触摸层,这一层为我们的应用程序开发提供了各种有用的框架,并且大部分与用户界面有关,本质上来说它负责用户在iOS设备上的触摸交互操作。

1.2、Foundation框架
好吧,那我们看看两个框架的类组织架构图,*个先看Foundation的,三个图,包括了Foundation所以的类,图中灰色的是iOS不支持的,灰色部分是OS X系统的。
%title插图%num

%title插图%num

%title插图%num

将上图Foundation框架中的类进行逻辑分类如下:
值对象
集合
操作系统服务 包括下面三个:文件系统和URL   进程间通讯。 这个范畴中的大部分类代表不同的系统端口、套接字、和名字服务器,对实现底层的IPC很有用。NSPipe代表一个BSD管道,即一种进程间的单向通讯通道。   线程和子任务。 NSThread类使您可以创建多线程的程序,而各种锁(lock)类则为彼此竞争的线程在访问进程资源时提供各种控制机制。通过NSTask,您的程序可以分出      一个子进程来执行其它工作或进行进度监控。
通知
归档和序列化
表达式和条件判断
Objective-C语言服务

1.3 UIKit框架
应用程序可以通过三种方式使用UIKit创建界面
在用户界面工具(interface Buidler)从对象库里 拖拽窗口,视图或者其他的对象使用。
用代码创建
通过继承UIView类或间接继承UIView类实现自定义用户界面

框架类组织架构图:

%title插图%num

在图中可以看出,responder 类是图中*大分支的根类,UIResponder为处理响应事件和响应链 定义了界面和默认行为。当用户用手指滚动列表或者在虚拟键盘上输入时,UIKit就生成时间传送给UIResponder响应链,直到链中有对象处理这个 事件。相应的核心对象,比如:UIApplication  ,UIWindow,UIView都直接或间接的从UIResponder继承。

2、Cocoa对象
2.1 Objective-C是面向对象的语言
Objective-C和Java C++一样,有封装,继承,多态,重用。但是它不像C++那样有重载操作法、模版和多继承,也没有Java的垃圾回收机制。

2.2 Objective-C的优点
Objective-C语言有C++ Java等面向对象的特点,那是远远不能体现它的优点的。Objective-C的优点是它是动态的。动态能力有三种:

动态类-运行时确定类的对象

动态绑定-运行时确定要调用的方法

动态加载–运行时为程序加载新的模块

2.3 动态能力相关的isa指针
每个Objective-C对象都有一个隐藏的数据结构,这个数据结构是Objective-C对象的*个成员变量,它就是isa指针。这个指针指向哪 呢?它指向一个类对象(class object  记住它是个对象,是占用内存空间的一个变量,这个对象在编译的时候编译器就生成了,专门来描述某个类的定义),这个类对象包含了Objective-C 对象的一些信息(为了区分两个对象,我把前面提到的对象叫Objective-C对象),包括Objective-C对象的方法调度表,实现了什么协议等 等。这个包含信息就是Objective-C动态能力的根源了。

那我们看看isa指针类型的数据结构是什么样的?如果抛开NSObject对象的其他的成员数据和变量,NSObject可以看成这样:

 

@interface NSObject <NSObject> {
Class    isa;
}
不考虑@interface关键字在编译时的作用,可以把NSObject更接近C语言结构表示为:

 

struct NSObject{
Class isa;
}
Class是用typedef 定义的

 

typedef struct objc_class *Class;
那NSObject可以这么写了

 

struct NSObject{
objc_class *isa
}
那objc_class的结构是什么样的呢?大概是这样的:

 

struct objc_class {
Class isa;

Class super_class;

const char *name;

long version;
long info;

long instance_size;
struct objc_ivar_list *ivars;
struct objc_method_list **methodLists;

struct objc_cache *cache;
struct objc_protocol_list *protocols;
}
这里会看到, 在这个结构体里还有一个isa指针,又是一重指向,是不是有种到了盗梦空间的感觉。不用紧张,take easy,不会有那么多层次的,这里的isa指针指向的是元类对象(metaclass object),带有元字,证明快到头了。那元对象有啥用呢?它用来存储的关于类的版本,名字,类方法等信息。所有的元类对象(metaclass object)都指向 NSObject的元类对象,到头还是NSObject。一共三次:类对象->元类对象->NSObject元类对象。

 

为了得到整个类组织架构的信息,objc_class结构里定义了第二个成员变量Class super_class,它指向父类的类对象。说了这么多,可能关系缕不清楚,有道是一张图胜过千言万语

%title插图%num
图中可以看出,D3继承D2,D2继承D1,D1*终继承NSObject。下图从D3的一个对象开始,排列出D3 D2 D1 NSObject 类对象,元类对象等关系。

%title插图%num
图中的箭头都是指针的指向。

2.4 根类 NSObject
NSObject是大部分Objective-C类的根类,它没有父类。其它类继承NSObject,访问Objective-C运行时系统的基本接口,这样其他类的实例可以获得运行时的能力。

%title插图%num
2.4.1 根类和根类协议
NSObject不但是个类名,NSObject也是个协议的名称,参考NSObject协议 , NSObject协议指定了根类必须实现的接口。

 

2.4.2 根类的主要方法:

分配、初始化、和复制:
alloc和allocWithZone:方法用于从某内存区域中分配一个对象内存,并使对象指向其运行时的类定义。
init方法是对象初始化。
new是一个将简单的内存分配和初始化结合起来的方法。
copy和copyWithZone:

 

 

对象的保持和清理:
retain方法增加对象的保持次数。
release方法减少对象的保持次数。
autorelease方法也是减少对象的保持次数,但是以推迟的方式。
retainCount方法返回对当前的保持次数。
dealloc方法由需要释放对象的实例变量以及释放动态分配的内存的类实现。

内省和比较
NSObjec有很多方法可以查询对象的运行时信息。这些内省方法有助于找出对象在类层次中的位置,确定对象是否实现特定的方法,以及测试对象是否遵循某种协议。下面是部分方法
superclass和class方法(实现为类和实例方法)分别以Class对象的形式返回接收者的父类和类。
您可以通过isKindOfClass:和isMemberOfClass:方法来确定对象属于哪个类。后者用于测试接收者是否为指定类的实例。isSubclassOfClass:类方法则用于测试类的继承性。
respondsToSelector:方法用于测试接收者是否实现由选择器参数标识的方法。instancesRespondToSelector:类方法则用于测试给定类的实例是否实现指定的方法。
conformsToProtocol:方法用于测试接收者(对象或类)是否遵循给定的协议。
isEqual:和hash方法用于对象的比较。
description方法允许对象返回一个内容描述字符串;这个方法的输出经常用于调试(“print object”命令),以及在格式化字符串中和“%@”指示符一起表示对象。
对象的编码和解码
下面的方法和对象的编解码(作为归档过程的一部分)有关:
encodeWithCoder:和initWithCoder:是NSCoding协议仅有的方法。前者使对象可以对其实例变量进行编码,后者则使对象可以根据解码过的实例变量对自身进行初始化。
NSObject类中声明了一些于对象编码有关的方法:classForCoder:、replacementObjectForCoder:、和awakeAfterUsingCoder:。
消息的转发
forwardInvocation:允许一个对象将消息转发给另一个对象。
消息的派发
在performSelector开头的一些方法允许你延迟后派发指定消息,而且可以将消息(同步或异步的消息)从辅助线程派发到主线程。
2.5 Cocoa对象生命周期
对象的四种内存管理方式,如下图所示

 

对象的生命周期—简化视图
%title插图%num

保持接收到的对象
%title插图%num

拷贝接收到的对象
%title插图%num

自动释放池
%title插图%num

另附一张网上的思维导图,这份图讲述了包括iOS开发知识与能力体系

分辨率为2042*3917,点击查看大图:
%title插图%num