0%

Swift Array基本操作

Array使用了copy-on-write技术.Array是结构体类型,因此是值类型.和OC里的NSArray引用类型是不同的.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
//定义一个数组
let arr1: Array<String> = ["213", "fdsf", "sdfdsg"] //let定义的是常量,所以arr1不能再进行append操作.

//追加元素
//追加一个元素
var arr2: Array<String> = ["llllfs", "dqafas", "21231"]
arr2 += ["sfsdfsd"]
print("arr2:\(arr2)")
var arr3: Array<String> = arr2
arr3.append("hhehehe") //append()会改变自身的值.但arr2的值并不会改变,和OC不一样.
print("arr2:\(arr2)")
print("arr3:\(arr3)")

//追加一个数组的元素
arr3.append(contentsOf: ["sfsdf", "dfsfds", "dgfdg"])
print("arr3:\(arr3)")
var arr4: Array<People> = [People]()
for index: UInt in 0..<4 {
let p = People.init(name: "渣渣辉\(index)", sex: true, age: index * UInt(arc4random()%20))
arr4.append(p)
}
print("arr4:\(arr4)")

//移除元素
var arr5: Array = arr4
//移除最后一个元素
arr5.removeLast() //改变自身.被操作的数组不能为空.
print("arr5:\(arr5)")

//移除元素,原数组不受影响,得到一个类型为ArraySlice的新数组.
var arr6: Array = Array.init(arr5.dropLast()) //ArraySlice转Array
print("arr5:\(arr5)")
print("arr6:\(arr6)")
var arr7: ArraySlice = ArraySlice.init(arr4) //Array转ArraySlice

//移除某个范围里的元素
var arr8: Array = [1, 2, 3, 4, 5, 6]
arr8.removeSubrange(1...) //移除从第1个位置处开始一直到末尾的元素
print("arr8:\(arr8)")
var arr9: Array = [1]
arr9.removeSubrange(1...) //如果数组只有一个元素,但移除从第一个位置处的元素并不会越界崩溃.但是移除从第二个位置处的元素将会导致越界崩溃
print("arr9:\(arr9)") //arr9:[1]
var arr10: Array = [1, 2, 3, 4, 5, 6]
arr10.removeSubrange(1...3) //移除从第1个位置处开始随后的3个元素
print("arr10:\(arr10)")
var arr11: Array = [1, 2, 3, 4, 5, 6]
arr11.removeSubrange(1..<3) //移除从第1个位置处开始随后的2个元素
print("arr11:\(arr11)")
//便捷操作
public mutating func removeLast(_ n: Int) //移除后几个
public mutating func removeFirst(_ n: Int) //移除前几个
public mutating func remove(at index: Int) -> Array.Element //移除指定某个位置的元素
public mutating func removeAll(keepingCapacity keepCapacity: Bool = default) //移除所有元素

let tanwanlanyue = ["渣渣辉", "纯扰春", "咕天落", "森红雷"]
//“迭代除了第一个元素以外的数组其余部分”
for item in tanwanlanyue.dropFirst() {
print(item)
}
//“迭代除了最后 1 个元素以外的数组”
for item in tanwanlanyue.dropLast() {
print(item)
}
//“遍历数组中的元素和对应的下标”
for (index, element) in tanwanlanyue.enumerated() {
print(index, element)
}
//“寻找一个指定元素的位置”
let idx = tanwanlanyue.index(of: "咕天落")
let i: Int = idx! + 1
print(i)
//使用Where闭包
let index = tanwanlanyue.index {
$0 == "纯扰春"
}
let j: Int = index! + 3
print(j)
if let sidx = tanwanlanyue.index(where: { (item) -> Bool in
item.hasSuffix("落")
}) {
print(sidx)
}
let didx = tanwanlanyue.index(where: { (element) -> Bool in
return element.hasPrefix("渣渣")
})
print(didx)
//“对数组中的所有元素进行变形,得到一个新数组”
let tan2 = tanwanlanyue.map { (item) -> String in
let idx = tanwanlanyue.index(of: item)
return item + "\(idx!)"
}
print(tan2)
//“筛选出符合某个标准的元素,得到一个新数组”
let tan3 = tan2.filter { (item) -> Bool in
let ch = item.last!
return ch > "1"
}
print(tan3)

let names = ["Paula", "Elena", "Zoe"]
var lastNameEndingInA: String?
//(for in) + where条件,仅遍历符合条件的元素
for name in names.reversed() where name.hasSuffix("a") {
lastNameEndingInA = name
break
}
print(lastNameEndingInA)
for name in names.reversed() where name.hasSuffix("a") {
print(name)
}

let last = names.last { $0.hasSuffix("a")
}
print(last)

//插入
略.

//替换
略.

//翻转
略.

//遍历
let names = ["Paula", "Elena", "Zoe"]
var lastNameEndingInA: String?
//(for in) + where条件:仅遍历符合条件的元素
for name in names.reversed() where name.hasSuffix("a") {
lastNameEndingInA = name
break
}
print(lastNameEndingInA)
for name in names.reversed() where name.hasSuffix("a") {
print(name)
}

Swfit提供了三种类型的数组:

  • public struct ContiguousArray : RandomAccessCollection, MutableCollection

  • public struct ArraySlice : RandomAccessCollection, MutableCollection

  • public struct Array : RandomAccessCollection, MutableCollection

Swift String基本操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
//创建字符串  
let s1: String = "dsfdsf"

//遍历字符串
for char in s1 {
print(char)
}

//字符串拼接
let s2 = "dasdfds"
let s3 = s1 + s2
print(s3)

//计算字符串长度
let length: Int = s3.count
print("长度:\(length)")

//判断一个字符串是否为空
let rs1 = s3.isEmpty
print("是否为空:\(rs1)")

//将一个字符串转换为大/小写
let s4: String = "fsdfdsfsd"
let s5 = s4.uppercased() //返回一个大写的字符串,原字符串不受影响
print("s4:\(s4)")
print("s5:\(s5)")

//字符串截取.字符串截取需要使用String.Index中介
let s7: String = "dasd3243232423sfsfdsf13"
//截取前4个字符
let s8: Substring = s7.prefix(4) //返回的是一个Substring类型,因此不能赋值给一个String类型的变量,如果要赋值给lable,需要转换为String类型
let label = UILabel.init()
label.text = String.init(s8)

//String和Substring可以直接相加操作,编译器已经做了强制转换.
let s9: String = s7 + s8

//截取后5个字符
let s10: Substring = s7.suffix(5)
print("s10:\(s10)")

//从第四个(包含)开始截取,截取10个长度的字符
let offset4Index: String.Index = s7.index(s7.startIndex, offsetBy: 4)
let dest14Index = s7.index(offset4Index, offsetBy: 10) //注意:起点+长度不能越界.
let s11: Substring = s7[offset4Index..<dest14Index] //返回的是一个Substring类型,因此不能赋值给一个String类型的变量. "..<"表示包含左边,但不包含右边,"..."表示闭区间,两边都包含.
print("s11:\(s11)")


//移除最后一个字符.被操作的字符串不能为空字符串.
var s12: String = "sfdsfds"
s12.removeLast()
print("s12:\(s12)"

操作子视图控制器

系统提供了一些方法用于添加和移除子视图控制器:
(UIContainerViewControllerProtectedMethods)类别里的:

1
2
3
- (void)addChildViewController:(UIViewController *)childController NS_AVAILABLE_IOS(5_0);
- (void)removeFromParentViewController NS_AVAILABLE_IOS(5_0);

上述两个方法调用后还要配套调用(UIContainerViewControllerCallbacks)类别里的:

1
2
- (void)willMoveToParentViewController:(nullable UIViewController *)parent NS_AVAILABLE_IOS(5_0);
- (void)didMoveToParentViewController:(nullable UIViewController *)parent NS_AVAILABLE_IOS(5_0);

addChildViewController:的说明:

  1. If the child controller has a different parent controller, it will first be removed from its current parent
    by calling removeFromParentViewController.

  2. addChildViewController: will call [child willMoveToParentViewController:self] before adding the
    child. However, it will not call didMoveToParentViewController:.所以willMoveToParentViewController:就不需要我们自己调用了,但需要我们自己手动调用didMoveToParentViewController:

removeFromParentViewController的说明:

removeFromParentViewController does not call [self willMoveToParentViewController:nil] before removing the
child. This is also the responsibilty of the container subclass.subclasses will typically define a method that removes a child in
the reverse manner by first calling [child willMoveToParentViewController:nil].

正确添加和移除一个子视图控制器

阅读全文 »

访问控制

Access control restricts access to parts of your code from code in other source files and modules.

You can assign specific access levels to individual types (classes, structures, and enumerations), as well as to properties, methods, initializers, and subscripts belonging to those types. Protocols can be restricted to a certain context, as can global constants, variables, and functions.

The various aspects of your code that can have access control applied to them (properties, types, functions, and so on) are referred to as “entities” in the sections below, for brevity.

Modules and Source Files(模块和源文件)

Swift’s access control model is based on the concept of modules and source files.

模块
A module is a single unit of code distribution—a framework or application that is built and shipped as a single
unit and that can be imported by another module with Swift’s import keyword.
(一个模块就是一个独立的代码分发单元,比如一个framework.xq注:比如你提供给别人使用的一个第三方库就是一个单独的模块)
具体一点:
Each build target (such as an app bundle or framework) in Xcode is treated as a separate module in Swift. If you group together aspects of your app’s code as a stand-alone framework—perhaps to encapsulate and reuse that code across multiple applications—then everything you define within that framework will be part of a separate module when it is imported and used within an app, or when it is used within another framework.

源文件
A source file is a single Swift source code file within a module (in effect, a single file within an app or framework). Although it is common to define individual types in separate source files, a single source file can contain definitions for multiple types, functions, and so on.
(一个源文件就是模块里一个单独的Swift源代码文件,通常不同的源文件定义的类型也是不同的,但是一个单独的源文件可以定义多种类型,函数等.xq注:你可以在一个源文件里定义多个类)

Access Levels(访问级别)

Swift provides five different access levels for entities within your code. These access levels are relative to the source file in which an entity is defined, and also relative to the module that source file belongs to.
(Swfit提供5种不同的访问级别.这些访问级别对定义实体的源文件产生影响,也对包含这些源文件的模块产生影响.)

阅读全文 »

给Swift Array类型扩展removeObject方法

由于Array是结构体类型,而removeObject函数会改变结构体的值,所以该函数需要添加mutating关键字.“Notice the use of the mutating keyword in the declaration of SimpleStructure to mark a method that modifies the structure. The declaration of SimpleClass doesn’t need any of its methods marked as mutating because methods on a class can always modify the class.”

1
2
3
4
5
6
7
extension Array where Element: Equatable {
mutating func removeObject(object: Element) {
if let index = index(of: object) {
remove(at: index)
}
}
}

反转一个字符串

对”hello,world!”,进行反转.
错误实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
//反转一个字符串
char * reverseString(char *src)
{
char *rs = src;
while (*src != '\0') {
src++;
}
while (*rs != '\0') {
*rs++ = *--src;
}

return src;
}

这种思路其实是错的.当rs与src相遇时,字符串已经变为”!dlrowworld!”,再进行替换操作就会出问题了,因为字符”hello,”已经被替换而不存在了.所以不应该使用替换,而应该使用交换.

正确实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//反转一个字符串
char * reverseString(char *src)
{
char *forwardPtr = src;
char *backPtr = src;

while (*backPtr != '\0') {
backPtr++;
}

while (forwardPtr < backPtr) {
char tmp = *forwardPtr;
*forwardPtr++ = *--backPtr;
*backPtr = tmp;
}

return src;
}

采用 Modern Objective-C

参考:Adopting Modern Objective-C

These modernizations improve type safety, memory management, performance, and other aspects of Objective-C, making it easier for you to write correct code. It’s important to adopt these changes in your existing and future code to help it become more consistent, readable, and resilient.

instancetype

Use the instancetype keyword as the return type of methods that return an instance of the class they are called on (or a subclass of that class).
Using instancetype instead of id in appropriate places improves type safety in your Objective-C code.
使用instancetype后,如果给对象发送一条不能识别的消息那么编译器就会提示错误,这样就可以提前解决错误,因此提高了类型安全.参考官方文档上的例子说明.

Note that you should replace id with instancetype for return values only, not elsewhere in your code. Unlike id, the instancetype keyword can be used only as the result type in a method declaration.

@property语法

使用properties代替实例变量有许多好处.

  • 自动合成getter和setter.
  • 更好的声明一系列方法的意图.
  • Property关键字可以传递额外的信息.比如assign (vs copy), weak, atomic (vs nonatomic), and so on.

Property methods follow a simple naming convention. The getter is the name of the property (for example, date), and the setter is the name of the property with the set prefix, written in camel case (for example, setDate). The naming convention for Boolean properties is to declare them with a named getter starting with the word “is”:
@property (readonly, getter=isBlue) BOOL blue;

阅读全文 »

didSelectRowAtIndexPath不被调用

vc.view上添加一个UITableView,并且vc.view上添加一个UITapGestureRecognizer手势,那么- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath代理方法将不再被调用.

这是由于UIGestureRecognizer有一个属性:
@property(nonatomic) BOOL cancelsTouchesInView;
官方的注释:default is YES. causes touchesCancelled:withEvent: or pressesCancelled:withEvent: to be sent to the view for all touches or presses recognized as part of this gesture immediately before the action method is called.
该属性默认是YES,在action方法被调用之前系统会迅速调用View的touchesCancelled:withEvent: or pressesCancelled:withEvent:方法.touches事件将立即被cancel.所有的touches或presses对象将转而被用于手势识别.因此当一个View上添加了一个手势那么该View的touchesEnded方法将不会被调用(只会touchesBegan->touchesCancelled).

可以推断出:如果一个button上又添加一个tap手势,则最后只会有tap响应,button的target则不会被调用.这是由于手势识别时会cancel掉后面的Touches.因此当用户触摸一个控件时,手势的处理优先于触摸事件的处理.

因此,要想didSelectRowAtIndexPath方法被调用,有两种解决办法:

  1. 设置tap.cancelsTouchesInView = NO;让touch事件继续进行,从而让view可以继续处理.
  2. 实现UIGestureRecognizerDelegate的代理方法

    1
    2
    3
    4
    5
    6
    - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
    if ([touch.view isKindOfClass:NSClassFromString(@"UITableViewCellContentView")]) {
    return NO;
    }
    return YES;
    }

    当点击到cell.contentView上时,让tap手势不处理touches.

参考:

iOS的事件处理

Event Handling Guide for iOS

CUICatalog-Invalid asset name supplied

iOS11中,
UIImage *highlightedImage = [UIImage imageNamed:highlightedImageName];如果图片名是nil,或者不存在对应的图片,那么控制台会打印下面的提示信息.一般是因为传了nil或要找的资源不存在导致.

2017-09-27 16:46:33.399319+0800 KingDynastyGaming[48313:2122542] [framework] CUICatalog: Invalid asset name supplied: ‘(null)’

问题就是要找到哪行代码导致出现了提示信息.
调试办法:打符号断点:+[UIImage imageNamed:].在出现这条信息的操作之前使能断点,这样根据函数调用栈就可以看到调用该方法的位置,使用命令po $arg3还可以查看图片的名字即第三个参数的值.

调试命令参数

The exception object is passed in as the first argument to objc_exception_throw. LLDB provides $arg1..$argn variables to refer to arguments in the correct calling convention, making it simple to print the exception details:

1
2
3
(lldb) po $arg1
(lldb) po [$arg1 name]
(lldb) po [$arg1 reason]

Make sure to select the objc_exception_throw frame in the call stack before executing these commands. See the “Advanced Debugging and the Address Sanitizer” in the WWDC15 session videos to see this performed on stage.

On the iPhone Simulator, all function arguments are passed on the stack, so the syntax is considerably more horrible. The shortest expression I could construct that gets to it is *(id *)($ebp + 8). To make things less painful, I suggest using a convenience variable:

1
2
3
4
(gdb) set $exception = *(id *)($ebp + 8)
(gdb) po $exception
(gdb) po [$exception name]
(gdb) po [$exception reason]
阅读全文 »

前言

一般上架的安卓apk都会做混淆和加固,如果两个都不做那么被别人反编译后就可以直接看到源码了.而混淆后,那些函数名就会变成一些如”a”,”b”,”c”之类无意义的名称,并且还会添加一些无意义的代码逻辑.从而大大提高了破解的难度,但是花点时间还是能够看懂一些逻辑的.因此为了让apk更加难以反编译,加固也很重要.加固后要想反编译则更加费时,费劲.本文仅仅是自己做个记录,所以不牵涉到对加固后的apk进行反编译.

工具

Apktool

资源反编译,获取apk里面的资源.如果你仅仅只是想获取里面的资源,可不必使用该工具,直接解压缩源apk就可以了.
note:使用Apktool处理后的和直接解压缩后的里面的文件内容还是不一样的,比如里面的AndroidManifest.xml文件,Apktool处理后的就能够读懂,否则是一些乱码.

Apktool安装

按照官网说明,下载后会得到两个文件apktoolapktool.jar,需要注意的是必须赋予这两个文件可执行权限.使用命令chmod +x 对应文件名即可.最后将这两个文件拖入/usr/local/bin文件夹中.

Apktool使用

命令:apktool d 对应apk路径.
执行完毕后,会得到一个同名的文件夹,里面就是一些资源了.找到里面的classes.dex文件,后面代码反编译会用到.

阅读全文 »