【iOS开发】block使用与理解

一、使用block的好处:
(1)使用block可以改变代码从上到下的执行的顺序   
(2)让代码集中在一处,避免了使用代理那样需要在不同代码中来回跳转,便于代码维护/阅读

二、block的核心用途
(1)block是用来封装一段代码,在需要的时候执行
(2)block是一种数据类型,可以当做参数传递(使用多),也可以当做返回值(用的少)

三、block的三种定义方式
(0)使用提示快速输入:
 inlineblock,能够快速敲出一个block的基本结构

(1)无参数,无返回值:   
<span style="font-size:14px;">void (^myBlock)() = ^ {
        NSLog(@"hello");
    };</span>

(2)有参数,无返回值
<span style="font-size:14px;">   // 定义带参数的block
    // 格式:void (^block名称)(参数列表) = ^ (参数列表) { // 代码实现; }
    void (^sumBlock)(int, int) = ^ (int x, int y) {
        NSLog(@"%d", x + y);
    };</span>

(3)有参数,有返回值
<span style="font-size:14px;">// 定义带返回值的block
    // 格式:返回类型 (^block名称)(参数列表) = ^ 返回类型 (参数列表) { // 代码实现; }
    int (^sumBlock2)(int, int) = ^ int (int a, int b) {
        return a + b;
    };</span><span style="font-size: 18px;">
</span>


四、block的用法详解
1、定义格式:返回值类型 + (^名字)+(参数列表)
2、预先封装好一段代码,合适的时候再次调用block就会执行这段代码
3、是一种数据类型,可以用来指定变量类型
<span style="font-size:14px;">- (void)downloadImage:(NSString *)urlString completion:(void (^) (UIImage *image))completion;</span>
此时,completion的数据类型就是(void (^)(UIImage *image))类型的

4、可以作为方法的参数使用

(1)简单的传参,在同一个控制器中:

<span style="font-size:14px;">void myWork(void (^dayWork)()) {
    // 是一个预先准备好的代码,在需要的时候执行!
    dayWork();
}
void dailyWork(int day) {
     void (^work)() =  ^{
                NSLog(@"入职报道");
            };
   // block可以当作参数传递
    myWork(work);
}
//调用
void dailyWork(1) ;</span>

(2)复杂传参,在多个控制器之间

***WebImageManager.m文件中:

实现WebImageOperation类的的声明de方法,封装了一段代码到completion,block中,作为参数传递到WebImageOperation.m文件中使用。

<span style="font-size:14px;">[</span><span style="font-family: 'Hiragino Sans GB';"><span style="font-size:12px;">webImageOperationWithURLString</span></span><span style="font-size:14px;">:urlString completion:^(UIImage *image) {
	// your code
    }];</span>


***WebImageOperation.h文件中,声明这个方法
<span style="font-size:14px;">+ (instancetype)webImageOperationWithURLString:(NSString *)urlString completion:(void (^)(UIImage*image))completion;</span>


***WebImageOperation.m文件中:

(1)定义了一个block类型的属性:

@property(nonatomic, copy) void (^completion) (UIImage*image);

 (2)调用这个方法:

<span style="font-size:14px;">+ (instancetype)webImageOperationWithURLString:(NSString *)urlString completion:(void (^)(UIImage*))completion {

    // block 完全就是一个参数
    op.completion = completion;
    return op;
}</span>

此时,completion这个block作为参数,从另一个控制器传过来了,并赋值给当前控制器的completion属性,以便后面使用。

五、关于block,循环引用的理解
(0)循环引用:比如控制器self强引用着block,block里面又有某个对象强引用着self
(1)block中使用到外面的对象或者self,要使用__weak来修饰
(2)因为block不确定什么时候调用,所以会强引用着需要用到的对象,防止到时候调用block的时候这个对象已经被释放了
(3)所以如果可以保证block使用的对象在block调用时一定还存在,没有被释放,就不需要使用强引用,改成__weak;

六、关于定义block时,为什么要使用copy,而不使用weak、strong
(1)首先排除weak,因为block是一个数据类型,作用类似于方法,可以封装一段代码,如果使用weak,一创建出来就被销毁了
(2)strong:strong是强指针,会对修饰的对象retain一次;block是定义在函数内部的,是局部变量,所以block对象是保存在栈区的,栈区的对象在作用域过掉之后,就应该被释放掉的,但是如果使用strong修饰的话,会导致作用域过掉,栈区的block还是不能释放,这是不对的。
(3)copy:copy修饰的对象会把该对象的地址以及内容从栈区通通拷贝一份,放到堆区,那么当block所在的作用域过掉,block从栈区被释放,但是在堆区还有保留,所以这样既可以满足block不被过早释放,也不会破坏栈区对象生命周期的管理原则。至于堆区的block什么时候释放,在ARC环境下不需要程序员考虑。

七、关于block内部使用外部变量时的问题
1、   
  // 在默认情况下,不允许block内部修改外部变量的数值!因为会破坏代码的可读性,不易于维护!
 // 在定义block的时候,在定义的那一刻,如果引用了外部变量,会对外部变量做一个copy,记录住定义block时变量的数值
 // 所以在调用block之前再修改x的值,不会影响block内部的数值变化!所以打印10
void demoBlock1() {
    int x = 10;
    void(^myBlock)() = ^ {
        NSLog(@"%d", x);

    };
    x = 20;
    myBlock();
}
问,打印结果多少? 10

2、
// 如果要在block中,修改外部变量的数值,需要使用 __block 修饰符号
 // 定义block时,如果引用了外部使用__block的变量,block定义之后,外部变量的指针地址同样会变成堆区的地址
void demoBlock2() {
    // 使用 __block,说明不再关心x数值的具体变化
    __block int x = 10;
    NSLog(@"定义前 %p", &x);                 // 栈区
  
    void (^myBlock)() = ^ {
        x = 80;
        NSLog(@"in block %p", &x);          // 堆区
    };
    NSLog(@"定义后 %p", &x);                 // 堆区
   
    myBlock();
    NSLog(@"%d", x);
}
问,打印结果多少? 80

3、 区分清楚修改的是内容还是地址
void demoBlock3() {
   
    // 指针记录的是地址
    NSMutableString *strM = [NSMutableString stringWithString:@"zhangsan"];
    NSLog(@"定义前 %p %p", strM, &strM);
   
    void (^myBlock)() = ^ {
        //情况一: 修改strM指针指向的内容
        [strM setString:@"lisi"];
        NSLog(@"inblock %p %p", strM, &strM);
       
        //情况二: 这句代码是修改strM指针指向的地址
//        strM = [NSMutableString stringWithString:@"wangwu"];
    };
    NSLog(@"定义后 %p %p", strM, &strM);
   
    myBlock();
    NSLog(@"%@", strM);
}
--》可以通过内存分析来理解这两种情况:



--》block中使用外部变量总结

(1)默认情况下,没有__block修饰的局部变量,存放在栈中此时在block中使用外部变量,会copy一份变量值放到堆中,但是地址不是变量真实地址,所以block内部修改变量值无效。

(2)使用__block修饰的外部变量,block内部会把变量内容连同指向它的地址一起copy到堆中,此时变量在堆中的地址跟原来栈中是一样的,所以可以修改外部变量的值。

【by:Leo_zzp,支持原创,转载请说明出处!】
个人邮箱:15999745308@163.com,GitHub链接:MrLeoZou,期待您的交流!

使用Python来安装geopandas包时,由于geopandas依赖于几个其他的Python库(如GDAL, Fiona, Pyproj, Shapely等),因此安装过程可能需要一些额外的步骤。以下是一个基本的安装指南,适用于大多数用户: 使用pip安装 确保Python和pip已安装: 首先,确保你的计算机上已安装了Python和pip。pip是Python的包管理工具,用于安装和管理Python包。 安装依赖库: 由于geopandas依赖于GDAL, Fiona, Pyproj, Shapely等库,你可能需要先安装这些库。通常,你可以通过pip直接安装这些库,但有时候可能需要从其他源下载预编译的二进制包(wheel文件),特别是GDAL和Fiona,因为它们可能包含一些系统级的依赖。 bash pip install GDAL Fiona Pyproj Shapely 注意:在某些系统上,直接使用pip安装GDAL和Fiona可能会遇到问题,因为它们需要编译一些C/C++代码。如果遇到问题,你可以考虑使用conda(一个Python包、依赖和环境管理器)来安装这些库,或者从Unofficial Windows Binaries for Python Extension Packages这样的网站下载预编译的wheel文件。 安装geopandas: 在安装了所有依赖库之后,你可以使用pip来安装geopandas。 bash pip install geopandas 使用conda安装 如果你正在使用conda作为你的Python包管理器,那么安装geopandas和它的依赖可能会更简单一些。 创建一个新的conda环境(可选,但推荐): bash conda create -n geoenv python=3.x anaconda conda activate geoenv 其中3.x是你希望使用的Python版本。 安装geopandas: 使用conda-forge频道来安装geopandas,因为它提供了许多地理空间相关的包。 bash conda install -c conda-forge geopandas 这条命令会自动安装geopandas及其所有依赖。 注意事项 如果你在安装过程中遇到任何问题,比如编译错误或依赖问题,请检查你的Python版本和pip/conda的版本是否是最新的,或者尝试在不同的环境中安装。 某些库(如GDAL)可能需要额外的系统级依赖,如地理空间库(如PROJ和GEOS)。这些依赖可能需要单独安装,具体取决于你的操作系统。 如果你在Windows上遇到问题,并且pip安装失败,尝试从Unofficial Windows Binaries for Python Extension Packages网站下载相应的wheel文件,并使用pip进行安装。 脚本示例 虽然你的问题主要是关于如何安装geopandas,但如果你想要一个Python脚本来重命名文件夹下的文件,在原始名字前面加上字符串"geopandas",以下是一个简单的示例: python import os # 指定文件夹路径 folder_path = 'path/to/your/folder' # 遍历文件夹中的文件 for filename in os.listdir(folder_path): # 构造原始文件路径 old_file_path = os.path.join(folder_path, filename) # 构造新文件名 new_filename = 'geopandas_' + filename # 构造新文件路径 new_file_path = os.path.join(folder_path, new_filename) # 重命名文件 os.rename(old_file_path, new_file_path) print(f'Renamed "{filename}" to "{new_filename}"') 请确保将'path/to/your/folder'替换为你想要重命名文件的实际文件夹路径。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值