Tag: Objective C

快速实现Objective-C-第1部分

Objective-C仍然值得花时间吗? 目前,我只能说swift与Objective-C一起工作, 在我们的应用程序的核心,我们仍然有它的线索。 基础 在Objective-C中,我们有两种文件类型: 头文件和实现文件。 在头文件中,我们将解释在编码结束时应该具有的内容,我们不必将实现文件实现到另一个实现文件中,只需添加头文件,然后完成即可。 在开始编写代码之前,我将从编码的重要部分开始,您应该能够在一个大团队中解释您的代码,因此,这是Objective-C中的一些文档规则 / *! @brief它将温度度从华氏度转换为摄氏度。 @discussion该方法接受一个表示温度的浮点值,该值以华氏度表示,并将其转换为摄氏温度刻度。要使用它,只需调用@c [self toCelsius:50 ]; @param fromFahrenheit输入值,代表华氏度的度数。 @码 float f = [self toCelsius:80]; @return float摄氏度的度数。 * /

Objective-C中的单元测试

飞行猕猴桃 每种语言都有自己的规则。 查看语法,语言功能或语言类型都没关系。 一切都改变了,同时保持不变。 在我们的主题应用程序中,我们将使用swift,但是由于Objective-C是iOS开发的起源,因此我们绕了弯路,以了解此处的具体操作。 我不是XCTest的忠实拥护者。 不要误会我的意思,它确实可以正常工作..但是就这样..就可以了。 了解我们要测试的应用程序和操作的整体状态,取决于我们命名测试的能力。 总的来说,我们很讨厌这样做。 一种替代方法是基于RSpec的框架。 他们在我们的测试中添加了语法糖,可能只是为了了解测试2小时或解决问题而有所不同。 猕猴桃 对于Objective-C,这是猕猴桃。 它提供了我们可以描述,正在测试什么以及处于何种状态的不同阶段: describe :描述被测系统(sut) 上下文 :描述缝合状态 关于这些定义,我们可以编写一个带有演示类的简单测试文件结构: 私下入侵 我已经提到每种语言都有自己的怪癖。 在Objective-C中,私有不存在。 相反,它仅对编译器可见。 我们仍然可以通过运行时将消息发送到对象的特定实例,从而忽略可见性。 好的..我们不会开始使用objc_msgSend。 不用担心 相反,我们可以使用类别来完成这项工作。 在我们的测试类中,我们将添加一个类别,其中包含我们要测试的所有私有属性和方法,这些属性和方法无法从外部访问。 这样做将为编译器提供可见性,即使它们被认为是私有的,我们也可以将任何消息发送给对象。 存根 时不时地,我们不得不依靠我们无法控制的API。 这可以是简单的SDK API或网络服务。 想象一下有一个登录视图控制器。 您如何测试呢? 尤其是当您的测试应该在没有外部依赖性的黑暗洞穴中运行时,该怎么办? 欢迎进行嘲笑和存根。 在本文中,我们将讨论如何在单元测试和集成测试中对方法调用进行存根。 存根的想法是通过指定返回值或简单地替换它来定义方法调用的行为。要存根方法,我们需要对象对此方法做出响应。 有了这个,我们可以简单地通过编写以下代码来定义返回值: [testClass存根:@selector(方法)和Return:Value]; 该值可以是任何值:布尔值,NSString,NSInteger或您返回的任何值。 猕猴桃不在乎。 这样,我们可以简单地替换本地网络服务器的登录方法。 是不是很简单? 但是,当我们无法获得所用类的实例时,我们该怎么办? 铁杆存根 为此,有方法令人毛骨悚然。 这是Objective-C内的黑魔法的一部分,不应粗心使用。 但是,如果您知道自己在做什么,并且将其用于测试,则可能会从中获得一些价值。 那么什么是方法混乱? 基本上在运行时,您将另一个方法添加到一个类中,并用另一个方法切换该方法的签名。 关于我们先前描述的登录方法,让我们添加另一个名为swizzleLogin的方法。 通过使用swizzleLogin修改登录方法,现在每次登录都将执行swizzleLogin,反之亦然。 这很容易做错,所以让我们介绍一下JRSwizzle。 […]

使用DSL简化您的Objective-C代码

当我第一次学习iOS编程时,我在Objective-C语法上苦苦挣扎。 但是一旦我适应了自己,它就会成为我最喜欢的语言之一。 它包含了很多哲学! 但是,由于Objc具有自己的样式,因此有时代码写得有点冗长。 对我来说,使用Objc语言制作通用模块时遇到了问题。 一旦完成,就需要一堆样板代码才能使用它。 当时,我决定制作自己的DSL(特定于域的语言)来解决我的代码问题,结果得到了结果! 这是东西! 这是我面临的情况 我的任务是制作一些“ 超级易用的团队成员 ”模块,该模块可测量用户首次交互的时间。 例如,当用户进入某个页面时,他们会执行某些操作,例如触摸加载显示,但是一旦该页面花费的时间太长,用户期望的操作仅仅是离开该页面,他们就不够耐心! 那么,名为TTI的交互模块的时间是几点? 它确定哪个组件需要花费多少时间来加载。 我知道Facebook如何请求API,但这是一个简单的示例。 为了显示下面的供稿页面,让我们假设它需要一个API请求来获取全部数据,并需要一个请求来从CDN服务器检索图像数据。 在这种情况下,TTI模块会测量这两个网络请求,并计算获取每个信息所需的时间。 好,那就开始吧! 通用Objective-C风格 我采用的模块设计是在加载页面之前声明所有API调用。 使用上面的示例, “哦,此页面将显示一个供稿。 为此,它需要一个api请求和一个image请求。 然后测量这两件事!” 可以将其更改为类似的代码; //一些viewController -(void)loadView { [TTICenter targetPage:@“ newsFeedPage”]; [TTICenter measureRequest:API_REQUEST名为:@“ feedRequest”]; [TTICenter measureRequest:IMAGE_REQUEST名为:@“ attachedImage”]; [TTICenter启动]; } 使用它似乎有些复杂,因为它必须遵循从“ targetPage”到“ start”的某些“方法调用过程”,这并不方便。 因此,我将其放在如下所示的一个方法调用中; //一些viewController -(void)loadView { [[TTICenter targetPage:@“ newsFeedPage” measureRequests:@ [API_REQUEST,IMAGE_REQUEST] 命名:@ [@“ feedRequest,@” […]

以编程方式在UIStackView中添加视图

堆栈视图使用内部内容大小,因此使用布局约束来定义视图的尺寸。 有一种简单的方法可以快速添加约束(示例): [view1.heightAnchor constraintEqualToConstant:100] .active = true; 完整代码: -(void)setup {//查看1 UIView * view1 = [[UIView alloc] init]; view1.backgroundColor = [UIColor blueColor]; [view1.heightAnchor constraintEqualToConstant:100] .active = true; [view1.widthAnchor constraintEqualToConstant:120] .active = true; //查看2 UIView * view2 = [[UIView alloc] init]; view2.backgroundColor = [UIColor greenColor]; [view2.heightAnchor constraintEqualToConstant:100] .active = true; [view2.widthAnchor constraintEqualToConstant:70] .active = true; //查看3 UIView […]

了解目标C的弱项和强项

积木很棒。 为了避免保持周期,您经常会看到“弱者自我” —“强者自我”舞蹈 __weak __typeof __(self)weakSelf =自我; self.block = ^ { __typeof __(self)strongSelf = weakSelf; [strongSelf doSomething]; [strongSelf doSomethingElse]; }; 复制时块是对象 块在堆栈上创建,并且在其堆栈帧返回时将消失。 在堆栈上时,块对其所访问的任何内容的存储或生存期均无影响。 如果在堆栈帧返回后需要存在块,则可以将它们复制到堆中,并且此操作是显式操作。 这样,一个块将作为可可中的所有对象获得引用计数。 复制它们时,它们会随身携带其捕获的范围,并保留它们引用的所有对象 块可以从封闭范围捕获值 除了包含可执行代码之外,块还具有从其封闭范围捕获状态的能力。 请注意,代码块会捕获变量及其修饰符(即弱限定符), =>这说明了为什么需要将self声明为 __weak 当执行块时,对于第一种方法(doSomething),weakSelf可能为非零;而对于第二种方法(doSomethingElse),不可以使weakSelf不为nil。 您可能会认为,起初,这是在块内使用self来避免保留周期警告的技巧。 不是这种情况。 在块执行时创建对self的强引用,而在块声明时评估在块中使用self,从而保留对象。 但是,为了获得最佳实践,应该使用弱对象为对象创建强引用。 这也不会创建一个保留周期,因为该块内的强指针仅在该块完成之前才存在(唯一的作用域是该块本身)。 =>这说明了为什么您需要声明另一个 __strong 自我 许多人指出,“块是在堆栈上创建的,当它们的堆栈帧返回时,它们就会消失。 在堆栈上时,块不会影响它所访问的任何内容的存储或生存期。” 即使块(在堆栈上声明)增加了对其访问的所有对象的引用计数,这也将是无用的,因为当函数返回时,该块将被丢弃 复制块时(您会看到人们通常为块声明属性(副本)),这将增加对其访问的所有对象的引用计数。 为什么? 因为块是要在以后的时间执行的,所以它需要保持对它访问的所有对象的强引用。 块可以执行很多次,所以运行后它不会释放自己。 当您取消该块时,它将被取消分配,因此将减少对其访问的所有对象的引用计数。 AFNetworking在调用该块后将其清除为零,因此您不必在块内使用weakself 因此,在某些情况下,您不必在块内使用weakself 确保没有复制该块,只需声明并运行它 确保该块在调用后为零 这是更多链接,可帮助您进一步探索 我终于想出了弱自我和强自我 使用块 […]

深入了解Swiftify

复杂转换 正如我们前面提到的,Swiftify执行的转换不是对Objective-C语法的愚蠢搜索和替换操作,而是该工具更像是一个编译器,该编译器具有: 一次扫描一个字符。 通过标记扫描的字符,对代码执行词法分析 。 然后执行语法分析以确保从词法分析中产生的标记顺序正确。 可以产生期望结果的一组关键字的正确顺序称为语法。 然后, 语义分析会生成一种中间语言或结构,以便可以将其转换为等效的Swift。 这个复杂的过程为Swiftify提供了令人印象深刻的优势,即数据类型感知能力。 例如: 转换为: 如果Swiftify不知道i’的数据类型,并且将加法转换为仅var g = i + f ,它将产生一个编译器错误,表明Binary operator ‘+’ cannot be applied to operands of type ‘Int’ and ‘Float’ ; 您可以在此处探索免费的在线代码转换器。 仅此一项就可以向您展示该工具的真正智能程度。 再以下面的代码段为例,该代码段将持久性存储添加到NSPersistentStoreCoordinator中: 转换为: 在这里,您可以看到Swiftify能够try包装该调用,因为此函数throws ,并且,此外,它还可以推断persistentStoreCoordinator是一个可选属性,并为该属性添加了可选的展开功能 ! 您还可以在这里找到许多其他这样的示例。 包起来 我希望这可以帮助您了解为什么我将Swiftify用于所有转换。 从Xcode扩展到离线转换器,Swiftify可以帮助您! 使用Swiftify,您还可以: 生成Swift文档。 增加您的图书馆的用户群。 迁移您的旧代码。 如有任何疑问,请随时在下面的评论部分中提问。

iOS 10中的本地通知

本地通知是一种将通知发送给用户的好方法,而无需Internet连接或服务器端编程。 这些通知对于任何类型的“提醒类型的应用程序”都是完美的,并且自iOS 4发行以来就可用。如果您正在编程一段时间,我确定您已经熟悉UILocalNotification类,现在就熟悉iOS的发布10,我敢打赌,您和我一样高兴(这是非常讽刺的方式), UILocalNotification已过时。 好吧,新的更新随新的代码实现一起出现,作为开发人员,我们应该保持代码更新,对吗? 好的,让我们来聊聊吧,让我们构建一个非常简单的应用程序,它会使用新的UserNotifications Framework’s UNNotificationRequest.提醒我们进行UserNotifications Framework’s UNNotificationRequest. 首先在Xcode中创建一个新项目,然后在“链接框架和库”部分中添加UserNotifications Framework 。 转到您的AppDelegate.m文件,然后像这样导入框架… 现在,在didFinishLaunchingWithOptions:内部didFinishLaunchingWithOptions:复制并粘贴… – (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. if ([application respondsToSelector:@selector(isRegisteredForRemoteNotifications)]) { UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; [center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert + UNAuthorizationOptionSound + UNAuthorizationOptionBadge) completionHandler:^(BOOL granted, NSError * _Nullable error) { if (!granted) { //Show […]

等间距的UICollectionView单元格

大多数iOS开发人员使用UICollectionView,并且应内置等距单元 问题 苹果的iOS类UICollectionView和UICollectionViewFlowLayout被许多应用程序开发人员使用,并且大多数情况下这些类可以很好地协同工作,不需要太多的自定义,但是它们做得不好的一件事是自动为所有设备布局分配单元格。 在开发负责显示大型组织的REST服务的照片缩略图的应用程序时,我发现了这个问题。 使用默认值,甚至使用稍微调整的值,我的布局看起来都类似于以下屏幕截图。 虽然单元格之间的水平空白可能很好,但垂直空白要么不存在,要么与水平空白不一致; 同样,在屏幕边缘周围留有一些空白也很好。 可以调整这些值,但是对于每个设备和方向,每个值可以不同。 当前,Xcode的Interface Builder编辑器和UICollectionViewFlowLayout类都没有提供自动设置这些值的方法。 解决方案 为了解决这个问题,我写了一个UICollectionViewFlowLayout的子类DSSCollectionViewFlowLayout,它重写了prepareLayout方法以计算属性minimumLineSpacing,minimumInteritemSpacing和sectionInset所需的值,因此每当用户更改设备的方向或布局时,它们都具有相同的值。应用程序。 进行此更改后,每个单元格周围的空白区域将变得更加令人愉悦。 尽管每个单元周围的空白空间在设备和方向上都不同,但是在特定布局内,空白空间是一致的。 在为Apple平台开发软件时,与编写源代码相比,我更喜欢在故事板和XIB / NIB中尽可能多地添加设置,因此我决定将属性placeEqualSpaceAroundAllCells添加到我的子类中,以便可以在集合视图的内部打开或关闭此功能。资源“身份检查器”面板。 我的子类DSSCollectionViewFlowLayout的Objective-C标头声明了属性placeEqualSpaceAroundAllCells,因此我的prepareLayout版本将知道是否执行等距的空白计算。 子类的prepareLayout实现的Objective-C源代码显示了如何计算水平或垂直滚动​​的正确空间。 而且,为了好玩,我也写了一个Swift版本。 我目前仅将此代码用于正方形和矩形固定大小的单元格,但它也应与可变大小的单元格的集合视图一起使用。

如何使用Mapkit View / Swift 4获取和显示位置

大家好,在本文中,我将告诉您如何使用iOS应用程序Swift 4的mapkit视图获取和显示位置。 首先,不要忘记“ import MapKit ” 之后,您需要将Mapkitview放入UIView并以IBOutlet的形式连接到控制器类,如下所示; @IBOutlet弱var mapKitView:MKMapView! 我们有两个不同的变量来确定位置,分别称为lat和lng。 例如,伊斯坦布尔的Lat和LNG; lat = 41.015137 lng = 28.979530 公共静态变量lat:Double = 41.015137 公共静态变量lng:Double = 28.979530 然后,您需要使用CLLocationManager在您的viewcontroller类中,定义如下。 var locationManager:CLLocationManager! 在您的视图中,您确实需要同步mapkit委托自己的类。 覆盖func viewDidLoad(){super.viewDidLoad(){ self.mapKitView.delegate =自我 } 现在,由于使用mapkit视图函数,我们必须为UIViewController类添加扩展。 扩展UIViewController:CLLocationManagerDelegate,MKMapViewDelegate { func locationManager(_管理器:CLLocationManager,didUpdateLocations位置:[CLLocation]){ 让userLocation:CLLocation = location [0]作为CLLocation 让中心= CLLocationCoordinate2D(纬度:UIViewController.lat,经度:UIViewController.lng) let region = MKCoordinateRegion(center:center,span:MKCoordinateSpan(latitudeDelta:0.01,longitudeDelta:0.01))self.mapKitView.setRegion(region,animation:true)//在用户当前位置放置图钉 让myAnnotation:MKPointAnnotation = MKPointAnnotation()myAnnotation.coordinate = CLLocationCoordinate2DMake(UIViewController.lat,UIViewController.lng); myAnnotation.title =“伊斯坦布尔位置” mapKitView.addAnnotation(myAnnotation)} […]

NSTimer

创建一个名为main的情节提要,并确保在项目的Deployment info中将其设置为主界面。 创建“开始”和“停止”按钮以及两个标签。 一个显示时间,另一个显示消息。 确保按钮是IBActions 。 在头文件中时,将计时器添加到接口中: { NSTimer * timer; int countInt; } 将我们刚刚分配的countInt整数设置为零,并为其分配标签。 使用以下方式设置计时器 ScheduledTimerWithTimeInterval:target:selector:userInfo:nil repeats:方法。 将间隔设置为1.0 (代表计数速度) ,现在将target和self设置为空,并将其设置为空。 userInfo:nil ,为YES 重复布尔值。 3.创建一个辅助计数器方法,暂时将其留空。 现在,我们可以返回到@selector并填写其余内容,如下所示: @selector(counter) 4.我们的stopCounter按钮应该能够做的就是停止计数。 可以用一种简单的方法做到这一点: [timer invalidate]; 5.让我们继续使用辅助计数器方法。 指定您要向上还是向下计数。 我把我的countInt + = 1 ; 目前。 设置标签 self.label.text = [NSString stringWithFormat:@”%i”,countInt]; 使计时器停止在零位置: [计时器无效]; 点击运行,看看它是否有效。 计数器应从零开始,以每秒一的速度加一。 凉。 现在让我们对其进行一些更改。 将startCouner:方法中的countInt设置为10,然后移至counter方法。 使其倒数: countInt-= 1; 然后让它在时间快到时向我们发出警告,并在时间到时打印出一些内容。 […]