ios block self 要 weak中为什么要对weakSelf进行strong

使用Block时何时需要WeakSelf和StrongSelf_百度知道
使用Block时何时需要WeakSelf和StrongSelf
在viewDidLoad中断点打印showDataLabel是这样的 Printing description of self-&_showDataLabel:(id)sender { if (self: 是有值的 . 在CCViewController中我把myblock声明成属性 回调写在返回事件中 - (IBAction)backPageAction.myblock) { se
其他类似问题
为您推荐:
block的相关知识
等待您来回答
下载知道APP
随时随地咨询
出门在外也不愁<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
您的访问请求被拒绝 403 Forbidden - ITeye技术社区
您的访问请求被拒绝
亲爱的会员,您的IP地址所在网段被ITeye拒绝服务,这可能是以下两种情况导致:
一、您所在的网段内有网络爬虫大量抓取ITeye网页,为保证其他人流畅的访问ITeye,该网段被ITeye拒绝
二、您通过某个代理服务器访问ITeye网站,该代理服务器被网络爬虫利用,大量抓取ITeye网页
请您点击按钮解除封锁&关于一些 iOS 面试问题的解答 - 推酷
关于一些 iOS 面试问题的解答
这篇 post 主要是对知乎上
的回答, 也算是对自己已有的知识进行整理.
如果你对本篇 post 中的回答有所疑问, 可以在下面留言. 如果有问题, 我一定会修改的 :-)
问题以及回答
1. 什么是 ARC? (ARC 是为了解决什么问题而诞生的?)
ARC 是 Automatic Reference Counting 的缩写, 即自动引用计数. 这是苹果在 iOS5 中引入的内存管理机制. Objective-C 和 Swift 使用 ARC 追踪和管理应用的内存使用. 这一机制使得开发者无需键入
, 这不仅能够降低程序崩溃和内存泄露的风险, 而且可以减少开发者的工作量, 能够大幅度提升程序的
. 但是 ARC 不适用于 Core Foundation 框架中, 仍然需要手动管理内存.
2. 以下 keywords 有什么区别:
assign vs weak
__block vs __weak
是用于在声明属性时, 为属性指定内存管理的语义.
用于简单的赋值, 不改变属性的引用计数, 用于 Objective-C 中的
以及 C 语言中
等数据类型.
用于对象类型, 由于
同样不改变对象的引用计数且不持有对象实例, 当该对象废弃时, 该弱引用自动失效并且被赋值为
, 所以它可以用于避免两个强引用产生的
导致内存无法释放的问题.
之间的却是确实极大的, 不过它们都用于修饰变量.
前者用于指明当前声明的变量在被 block 捕获之后, 可以在 block 中改变变量的值. 因为在 block 声明的同时会截获该 block 所使用的全部自动变量的值, 而这些值只在 block 中
只具有&使用权&而不具有&修改权&
说明符就为 block 提供了变量的修改权.
所有权修饰符
, 什么是所有权修饰符? 这里涉及到另一个问题, 因为在 ARC 有效时, id 类型和对象类型同 C 语言中的其他类型不同, 必须附加所有权修饰符. 所有权修饰符一种有 4 种:
__unsafe_unretained
__autorelease
的区别只在于, 前者用于变量的声明, 而后者用于属性的声明.
在 ARC 和非 ARC 下含义一样吗?
在 ARC 下捕获的变量会被 block retain
, 这样可能导致循环引用, 所以必须要使用弱引用才能解决该问题. 而在非 ARC 下, 可以直接使用
说明符修饰变量, 因为在非 ARC 下, block 不会 retain 捕获的变量.
一定是线程安全的吗?
的内存管理语义是
的, 非原子的操作本来就是线程不安全的, 而
的操作是原子的, 但是
并不意味着它是线程安全的
, 它会增加正确的几率, 能够更好的避免线程的错误, 但是它仍然是线程不安全的.
的时候, 属性的 setter 和 getter 操作是非原子的, 所以当多个线程同时对某一属性进行读和写的操作, 属性的最终结果是不能预测的.
时, 虽然对属性的读和写是原子的, 但是仍然可能出现线程错误: 当线程 A 进行写操作, 这时其他线程的读或写操作会因为该操作的进行而等待. 当 A 线程的写操作结束后, B 线程进行写操作, 然后当 A 线程进行读操作时, 却获得了在 B 线程中的值, 这就破坏了线程安全, 如果有线程 C 在 A 线程读操作前 release 了该属性, 那么还会导致程序崩溃. 所以仅仅使用
并不会使得线程安全, 我们还需要为线程添加
来确保线程的安全.
都不是一定线程安全的,
就更不必多说了.
描述一个你遇到过的 retain cycle 例子.
有什么用处?
当类对象被引入项目时, runtime 会向每一个类对象发送
方法还是非常的神奇的, 因为它会在
每一个类甚至分类
被引入时仅调用一次, 调用的顺序是父类优先于子类, 子类优先于分类. 而且
方法不会被类自动继承, 每一个类中的
方法都不需要像
viewDidLoad
方法一样调用父类的方法. 由于
方法会在类被
时调用一次, 而这时往往是改变类的行为的最佳时机. 我在
method swizlling
来修改原有的方法时, 就是在分类
initialize
方法有一些不同, 它虽然也会在整个 runtime 过程中调用一次, 但是它是在
该类的第一个方法执行之前
调用, 也就是说
initialize
的, 它的实现也与我们在平时使用的惰性初始化属性时基本相同. 我在实际的项目中并没有遇到过必须使用这个方法的情况, 在该方法中主要做
静态变量的设置
确保在实例初始化前某些条件必须满足
7. 为什么其他语言里叫函数调用, Objective-C 中是给对象发送消息 (谈下对 runtime 的理解)
我们在其他语言中比如说: C, Python, Java, C++, Haskell ... 中提到函数调用或者方法调用(面向对象). 函数调用是在编译期就已经决定了会调用哪个函数(方法), 编译器在编译期就能检查出函数的执行是否正确.
然而 Objective-C(ObjC) 是一门动态的语言, 整个 ObjC 语言都是尽可能的将所有的工作推迟到运行时才决定. 它基于 runtime 来工作, runtime 就是 ObjC 的灵魂, 其核心就是消息发送
objc_msgSend
What makes Objective-C truly powerful is its runtime.
所有的消息都会在运行时才会确定,
[obj message]
在运行时会被转化为
objc_msgSend(id self, SEL cmd, ...)
来执行, 它会在运行时从
选择子表中寻找对应的选择子
并将选择子与实现进行绑定. 而如果没有找到对应的实现, 就会进入类似黑魔法的消息转发流程. 调用
+ (BOOL)resolveInstanceMethod:(SEL)aSelector
方法, 我们可以在这个方法中
为类动态地生成方法
我们几乎可以使用 runtime 魔改 Objective-C 中的一切:
, 而下面就是它的主要应用:
为分类动态的添加属性
使用方法调剂修改原有的方法实现
8. 什么是 Method Swizzling?
method swizzling
实际上就是一种在运行时动态修改原有方法的技术, 它实际上是基于 ObjC runtime 的特性, 而
method swizzling
的核心方法就是
method_exchangeImplementations(SEL origin, SEL swizzle)
. 使用这个方法就可以在运行时动态地改变原有的方法实现, 在
(为 iOS 应用添加夜间模式) 中能够看到大量
method swizzling
的使用, 方法的调用时机就是在上面提到的
方法中, 不在
initialize
方法中改变方法实现的原因是
initialize
可能会被子类所继承并重新执行最终导致错误
并不会被继承并重新执行.
有什么关系?
看到这个问题不禁想到大一在网易面试时的经历, 当时的两位面试官就问了我这么一个问题,
是什么关系, 为什么要这么设计? 我已经忘记了当时是怎么回答的. 隐约记得当时说每一个
都会对应一个
至于为什么这样, 当时的我实在是太弱无法回答出来了.
的身后对应一个
Core Animation
Many of the methods you call on UIView simply delegate to the layer
在 iOS 上 当你处理一个一个有一个的
时实际上是在操作
. 尽管有的时候你并不知道 (直接操作
并不会在对效率有着显著的提升).
实际上就是对
的轻量级的封装.
UIResponder
处理来自用户的事件;
主要用于图层的渲染和动画. 这么设计有以下几个原因:
你可以通过操作
在一个更高的层级上处理与用户的交互, 触摸, 点击, 拖拽等事件, 这些都是在
这个层级上完成的.
NSView(AppKit)
的实现极其不同, 而使用
Core Animation
可以实现底层代码地重用, 因为在 Mac 和 iOS 平台上都使用着近乎相同的
Core Animation
代码, 这样我们可以对这个层级进行抽象在两种平台上产生
用于不同平台的框架.
的唯一原因大概是便于移植到不同的平台, 如果仅仅使用
Core Animation
层级, 处理用户的交互时间需要写更多的代码.
10. 如何高性能的给
UIImageView
加个圆角? (不准说
layer.cornerRadius
一般情况下给 UIImageView 或者说 UIKit 的控件添加圆角都是改变
clipsToBounds
layer.cornerRadius
, 这样大约两行代码就可以解决. 但是, 这样使用这样的方法会
强制 Core Animation 提前渲染屏幕的离屏绘制
, 而离屏绘制就会为性能带来负面影响.
我们也可以使用另一种比较复杂的方式来为图片添加圆角, 这里就用到了贝塞尔曲线.
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
imageView.center = CGPointMake(200, 300);
UIImage *anotherImage = [UIImage imageNamed:@&image&];
UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, NO, 1.0);
[[UIBezierPath bezierPathWithRoundedRect:imageView.bounds
cornerRadius:50] addClip];
[anotherImage drawInRect:imageView.bounds];
imageView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[self.view addSubview:imageView];
在这里使用了贝塞尔曲线&切割&个这个图片, 给
UIImageView
添加了的圆角.
有什么影响?
这个问题对于我来说确实有些难以回答, 我记得我在我人生的第一个 iOS 项目 SportJoin 中曾经使用这个方法来绘制图形, 但是具体怎么做的, 我已经忘记了.
这个方法的主要作用是根据传入的
来绘制图像
. 这个方法的默认实现没有做任何事情, 我们
在这个方法中使用
Core Graphics
来绘制视图的内容.
这个方法的调用机制也是非常特别. 当你调用
setNeedsDisplay
方法时, UIKit 将会把当前图层标记为 dirty, 但还是会显示原来的内容, 直到下一次的视图渲染周期, 才会重新建立 Core Graphics 上下文, 然后将内存中的数据恢复出来, 使用 CGContextRef 进行绘制.
12. ASIHttpRequest 或者 SDWebImage 里面给 UIImageView 加载图片的逻辑是什么样的?
我曾经阅读过 SDWebImage 的源代码, 就在这里对如何给 UIImageView 加载图片做一个总结吧, SDWebImage 中为 UIView 提供了一个分类叫做 WebCache, 这个分类中有一个最常用的接口,
sd_setImageWithURL:placeholderImage:
, 这个分类同时提供了很多类似的方法, 这些方法最终会调用一个同时具有
progressBlock
completionBlock
的方法, 而在这个类最终被调用的方法首先会检查是否传入了
placeholderImage
以及对应的参数, 并设置
placeholderImage
然后会获取
SDWebImageManager
中的单例调用一个
downloadImageWithURL:...
的方法来获取图片, 而这个 manager 获取图片的过程有大体上分为两部分, 它首先会在
SDWebImageCache
中寻找图片是否有对应的缓存, 它会以 url 作为数据的索引先在内存中寻找是否有对应的缓存, 如果缓存未命中就会在磁盘中利用 MD5 处理过的 key 来继续查询对应的数据, 如果找到了, 就会把磁盘中的缓存备份到内存中.
然而, 假设我们在内存和磁盘缓存中都没有命中, 那么 manager 就会调用它持有的一个
SDWebImageDownloader
对象的方法
downloadImageWithURL:...
来下载图片, 这个方法会在执行的过程中调用另一个方法
addProgressCallback:andCompletedBlock:fotURL:createCallback:
来存储下载过程中和下载完成的回调, 当回调块是第一次添加的时候, 方法会实例化一个
NSMutableURLRequest
SDWebImageDownloaderOperation
, 并将后者加入 downloader 持有的下载队列开始图片的异步下载.
而在图片下载完成之后, 就会在主线程设置 image, 完成整个图像的异步下载和配置.
13. 设计一个简单的图片内存缓存器 (包含移除策略)
待我阅读完 path 开源的
的源代码就来回答.
讲讲你用Instrument优化动画性能的经历
的作用是什么?
This method loads or creates a view and assigns it to the
UIViewController
的实例方法, 我们永远不要直接调用这个方法
[self loadView]
. 这在苹果的
中已经明确的写出了.
会在获取视图控制器的
但是却得到
的具体实现会做下面两件事情中的一件:
如果你的视图控制器关联了一个 storyboard, 那么它就会加载 storyboard 中的视图.
如果试图控制器没有关联的 storyboard, 那么就会创建一个空的视图, 并分配给
如果你需要覆写
你需要创建一个根视图.
创建并初始化
的子视图, 调用
addSubview:
方法将它们添加到父视图上.
如果你使用了自动布局, 提供足够的约束来保证视图的位置.
将根视图分配给
永远不要在这个方法中调用
[super loadView]
viewWillLayoutSubviews
的作用是什么?
viewWillLayoutSubviews
方法会在视图的 bounds 改变时, 视图会调整子视图的位置, 我们可以在视图控制器中覆写这个方法在视图放置子视图前做出改变, 当屏幕的方向改变时, 这个方法会被调用.
17. GCD 里面有哪几种 Queue? 背后的线程模型是什么样的?
GCD 中 Queue 的种类还要看我们怎么进行分类, 如果根据同一时间内处理的操作数分类的话, GCD 中的 Queue 分为两类
Serial Dispatch Queue
Concurrent Dispatch Queue
一类是串行派发队列, 它只使用一个线程, 会等待当前执行的操作结束后才会执行下一个操作, 它按照追加的顺序进行处理. 另一类是并行派发队列, 它同时使用多个线程, 如果当前的线程数足够, 那么就不会等待正在执行的操作, 使用多个线程同时执行多个处理.
另外的一种分类方式如下:
Main Dispatch Queue
Global Dispatch Queue
Custom Dispatch Queue
主线程只有一个, 它是一个串行的进程. 所有追加到 Main Dispatch Queue 中的处理都会在 RunLoop 在执行. Global Dispatch Queue 是所有应用程序都能使用的并行派发队列, 它有 4 个执行优先级 High, Default, Low, Background. 当然我们也可以使用
dispatch_queue_create
创建派发队列.
18. Core Data 或者 sqlite 的读写是分线程的吗? 死锁如何解决?
Core Data 和 sqlite 这两个我还真没深入用过, 我只在小的玩具应用上使用过 Core Data, 但是发现这货实在是太难用了, 我就果断放弃了, sqlite 我也用过, 每次输入 SQL 语句的时候我多想吐槽, 写一些简单的还好, 复杂的就直接 Orz 了. 所以我一般会使用 levelDB 对进行数据的持久存储.
数据库读取操作一般都是多线程的, 在对数据进行读取的时候, 我们要确保当前的状态不会被修改, 所以加锁, 防止由于线程竞争而出现的错误. 在 Core Data 中使用并行的最重要的规则是:
NSManagedObjectContext
必须只从创建它的进程中访问
19. http 的 POST 和 GET 有什么区别?
根据 HTTP 协议的定义 GET 类型的请求是幂等的, 而 POST 请求是有副作用的, 也就是说 GET 用于获取一些资源, 而 POST 用于改变一些资源, 这可能会创建新的资源或更新已有的资源.
POST 请求比 GET 请求更加的安全, 因为你不会把信息添加到 URL 上的查询字符串上. 所以使用 GET 来收集密码或者一些敏感信息并不是什么好主意.
最后, POST 请求比 GET 请求也可以传输更多的信息.
20. 什么是 Binary search tree, 它的时间复杂度是多少?
二叉搜索树是一棵以二叉树来组织的, 它搜索的时间复杂度 $O(h)$ 与树的高度成正比, 最坏的运行时间是 $\Theta(\lg n)$.
已发表评论数()
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见
正文不准确
标题不准确
排版有问题
没有分页内容
图片无法显示
视频无法显示
与原文不一致转载,原文: /blog//when-should-use-weakself-and-strongself-in-objc-block/
当然有一个更详细的专题:&http://tanqisen.github.io/blog//gcd-block-cycle-retain/
Objective C 的&Block&是一个很实用的语法,特别是与GCD结合使用,可以很方便地实现并发、异步任务。但是,如果使用不当,Block 也会引起一些循环引用问题(retain cycle)&& Block 会 retain &self&,而 &self& 又 retain 了 Block。因为在 ObjC 中,直接调用一个实例变量,会被编译器处理成 &self-&theVar&,&self& 是一个 strong 类型的变量,引用计数会加 1,于是,self retains queue, queue retains block,block retains self。
解决 retain circle
Apple 官方的建议是,传进 Block 之前,把 &self& 转换成 weak automatic 的变量,这样在 Block 中就不会出现对 self 的强引用。如果在 Block 执行完成之前,self 被释放了,weakSelf 也会变为 nil。
示例代码:
__weak __typeof__(self) weakSelf =
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[weakSelf doSomething];
clang 的文档表示,在 doSomething 内,weakSelf 不会被释放。但,下面的情况除外:
__weak __typeof__(self) weakSelf =
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
__strong __typeof(self) strongSelf = weakS
[strongSelf doSomething];
[strongSelf doOtherThing];
在 doSomething 中,weakSelf 不会变成 nil,不过在 doSomething 执行完成,调用第二个方法 doOtherThing 的时候,weakSelf 有可能被释放,于是,strongSelf 就派上用场了:
__weak __typeof__(self) weakSelf =
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
__strong __typeof(self) strongSelf = weakS
[strongSelf doSomething];
[strongSelf doOtherThing];
__strong&确保在 Block 内,strongSelf 不会被释放。
在 Block 内如果需要访问 self 的方法、变量,建议使用 weakSelf。
如果在 Block 内需要多次 访问 self,则需要使用 strongSelf。
原文作者:&原文链接:&版权声明:自由转载-非商用-非衍生-保持署名 |&
阅读(...) 评论()闭包中的[weak self]在什么情况下需要使用,什么情况下可以不加?
按投票排序
如图,A中有B,B中有A;则当我们初始化A之后,a.b和b.a形成了【引用循环】这是我们不希望看到的,因为此时 A 和 B 两个类的实例都不能被 deinit这样改写之后, A 和 B 两个类的实例就都可以被 deinit 了这样改写之后, A 和 B 两个类的实例就都可以被 deinit 了因为我们在 var a前面加上了 weak,就是向编译器表明我们不希望持有 a至于题主所问的闭包中的情况,其实道理是一样的,就是有无循环引用的问题---------------至此问题回答完毕,以下为延伸---------------显然,标记为 weak 的成员是个 Optional 值,因为它引用的内容可能被释放掉(deinit),那时它会从原来的值自动变成 nil,这也是 weak 友好的地方。相对应的,还有一个不太友好的是 unowned,这个关键字在 Swift 中像是之前 OC 里面的 unsafe_unretained,它不会变成 nil,而是会保持持有引用的状态,即便在引用的内容被释放掉之后,它也会保持一个无效的引用。以上。
我跟大家分享想唐巧在YTKNetWorking里怎么处理这件事的YTKNetWorking的block是不需要weak引用的,项目地址在这:唐巧在文档中也说明了,可以在block里直接用self。唐巧在文档中也说明了,可以在block里直接用self。原理其实也很简单,在网络请求结束的时候,调用了这个方法把block置空,就打破了循环引用。话说- -之前去面试,很多人都不知道这个技巧,自己封装的时候,这么做,就能少写好多weakself&strongself...最后还是要感谢巧神开源了这么好的项目~~
细讲确实需要很长篇幅。不过我不同意楼上某位仁兄的“block 都要使用weak self”。至少我在试验Malloc(一般的匿名block都是这种类型)类型的block时是不用weak self的。如有错误还请勘误,话不能说死不是?
如果block没有直接或者间接被self存储,就不会产生循环引用。循环引用只要不依赖release打断,也应该不会产生内存泄漏问题。自己设计的模块都可以在合适时机进行打断。难就难在对系统类加扩展方法导致的循环引用。如果找得到合适的时机打断,也是没问题的。另外有个简单的方法可以绕过这个问题,如果self引用了一个block,block又需要调用self,可以把self通过参数回传给block,这样就不会产生循环引用了。block回传的self可以声明成id类型,这样使用的时候可以在入参声明具体self类型,避免显式类型转换,方便开发。typedef void (^Block) (id selfRef);Block block = ^(XXX *selfRef){};
这个真要细讲,需要很长的文章。建议搜索:引用计数、循环引用、弱引用、自动引用计数(ARC)。搞明白了上面那些概念,剩下的语法细节就很容易了。
分析个最常见的循环引用好了,MJRefresh,是最常用的下拉刷新控件MJRefreshAutoFooter *footer = [MJRefreshAutoFooter footerWithRefreshingBlock:^{
[weakSelf footerRefresh];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[weakSelf.list.footer endRefreshing];
}];self.list.footer =为什么这里要用weakSelf呢?+ (instancetype)footerWithRefreshingBlock:(MJRefreshComponentRefreshingBlock)refreshingBlock{
MJRefreshFooter *cmp = [[self alloc] init];
cmp.refreshingBlock = refreshingB}跳到footerWithRefreshingBlock里就知道了,这里的refreshingBlock是属于MJRefreshFooter的强引用属性,最后footer会成为我们自己list的强引用属性,也就是说self.list 强引用 footer, footer 强引用 refreshingBlock,如果refreshingBlock里面强引用self,就成了循环引用,所以你要用weakSelf,破掉这个循环另外多说一句,在block里最好用__strong class *strongSelf = weakSelf,防止block被释放,造成野指针错误(BAD_ADDRESS_ACCESS)RAC里提供了一对儿@weakifySelf和@strogifySelf宏,专门干这个事儿。
首先,block中为什么会用到weakself是因为要避免循环引用,一旦出现循环引用那么对象就会常驻内存。如果一个应用程序里面你有很多循环引用,那么内存占用就会比较大,这当然是谁都不想看到的结果。那么问题的重点就是:什么时候会出现循环引用?先来看一个例子:NSArray *anArray = @[@"1", @"2", @"3"];
[anArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[self doSomething:idx];
这种情况下,block中retain了self,当block中的代码被执行完后,self就会被ARC释放。所以不需要处理weakself的情况。再来看一个例子:@interface aViewController ()
@property (nonatomic, strong) void(^aBlock)(id obj, NSUInteger idx, BOOL *stop);
__weak aViewController *weakSelf = self;
self.aBlock = ^(id obj, NSUInteger idx, BOOL *stop) {
[weakSelf doSomething:idx];
这个例子的区别在于:block被self strong引用。所以结果就是block中引用了self,self引用了block。那么这个时候,如果你不使用weakself,则self和block永远都不会被释放。那么是不是遇到block都要使用weakself呢?当然不是,而且如果全部使用weakself,会出现你想执行block中的代码时,self已经被释放掉了的情况。另外,在处理weakself时,有两种做法:__weak和__unsafe_unretained。两种做法各有推荐,有的人觉得后者从字面上更好理解,而有的人觉得前者更加安全,因为self被释放时会自动指向nil。有的人又说了,就是应该让app崩溃才能发现问题所在。这个,看个人吧。
一句话解释:只有当block直接或间接的被self持有时,才需要weak self。// 这种情况没必要
[self fetchDataWithSucess:^{
[self doSomething];
//这种情况就有必要
self.onTapEvent = ^{
[self doSomething];
当多人协作开发的时候, 99.9%选择__weak~~~
有时候会选择 __block. 直接引用self的有可能会被处女座给拖出去弹shi!
weak应该是用来解决循环引用的,看看基础就能明白
已有帐号?
无法登录?
社交帐号登录

我要回帖

更多关于 weakself strongself 的文章

 

随机推荐